mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-09-14 02:03:12 +02:00
Compare commits
79 Commits
5cbeb02eac
...
498a2bec07
Author | SHA1 | Date | |
---|---|---|---|
|
498a2bec07 | ||
|
5e2fd084b9 | ||
|
a1f816e8be | ||
|
abf3438ed6 | ||
|
cb4c8e6647 | ||
|
d356073798 | ||
|
b38d9d7f81 | ||
|
297ca1bb7b | ||
|
f9e25c2e06 | ||
|
b0a6dc4acf | ||
|
59dcdb4a4f | ||
|
39889f8446 | ||
|
3f8abe3744 | ||
|
0acc659676 | ||
|
78c93ee0fb | ||
|
9622cf8ff1 | ||
|
3cca1e87d8 | ||
|
aac15f4596 | ||
|
ceed9ca5f8 | ||
|
6c4edd178e | ||
|
cfa87cfc2c | ||
|
b3b75270ce | ||
|
f1db857804 | ||
|
cdd43fe5fa | ||
|
836b3267bb | ||
|
66930ea703 | ||
|
0e60722c4b | ||
|
2b3f21b9ff | ||
|
dd8a059925 | ||
|
63fe36847e | ||
|
99a16e4306 | ||
|
5ca8fdb7cd | ||
|
88ee07e910 | ||
|
71907f0888 | ||
|
6397b6d3c6 | ||
|
d1356140fa | ||
|
30d158a60f | ||
|
c674e1749e | ||
|
c783218f93 | ||
|
07885145b3 | ||
|
500da3c15d | ||
|
fbd2e36b79 | ||
|
61fd5e7413 | ||
|
bf8d2d7bf6 | ||
|
2a93e1bfb1 | ||
|
9f097d5ae8 | ||
|
80996e70f8 | ||
|
ec8131c7bf | ||
|
2ad7c345ec | ||
|
a2e0d07c87 | ||
|
4e1dd875ae | ||
|
c737e0b360 | ||
|
c29aa7643c | ||
|
58b2ccb3d5 | ||
|
a05f8b9e36 | ||
|
9f2b84ba52 | ||
|
b345e34423 | ||
|
081b414ad5 | ||
|
58caa5c496 | ||
|
f0f5472e4d | ||
|
0b174b5227 | ||
|
765838ecf7 | ||
|
2276ae05e3 | ||
|
6838d55051 | ||
|
95abb3be8b | ||
|
41425ac32d | ||
|
0b47169387 | ||
|
7b98a8d697 | ||
|
4184e60e24 | ||
|
36eb0e4a11 | ||
|
49c7f62548 | ||
|
35f0213011 | ||
|
cb87f83dfc | ||
|
ead7de69ca | ||
|
dedc10a163 | ||
|
330361d0cb | ||
|
01d32211ba | ||
|
09c40a0e0f | ||
|
fdbf740ee2 |
@ -9,6 +9,7 @@ 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"
|
||||
@ -39,8 +40,6 @@ win64_build:
|
||||
../../
|
||||
- cmake --build . 2>&1 | tee compilation_log.txt
|
||||
- cd ../../
|
||||
after_script:
|
||||
- Get-Content -Path C:\builder\vcpkg\buildtrees\wxpython-33\python3-tool-post-install-err.log
|
||||
artifacts:
|
||||
# Only save the artifacts that are needed for running the tests in the next stage
|
||||
# and the compilation log. The entire build directory is too large to save as an
|
||||
|
@ -35,14 +35,22 @@
|
||||
#include <advanced_config.h>
|
||||
#include <build_version.h>
|
||||
#include <board.h>
|
||||
#include <pad.h>
|
||||
#include <pcb_field.h>
|
||||
#include <reporter.h>
|
||||
#include <gal/opengl/gl_context_mgr.h>
|
||||
#include <core/profile.h> // To use GetRunningMicroSecs or another profiling utility
|
||||
#include <bitmaps.h>
|
||||
#include <kiway_holder.h>
|
||||
#include <kiway.h>
|
||||
#include <macros.h>
|
||||
#include <pgm_base.h>
|
||||
#include <settings/settings_manager.h>
|
||||
#include <tool/tool_dispatcher.h>
|
||||
#include <string_utils.h>
|
||||
#include <mail_type.h>
|
||||
#include <kiway_express.h>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <widgets/wx_busy_indicator.h>
|
||||
|
||||
@ -92,28 +100,12 @@ END_EVENT_TABLE()
|
||||
EDA_3D_CANVAS::EDA_3D_CANVAS( wxWindow* aParent, const wxGLAttributes& aGLAttribs,
|
||||
BOARD_ADAPTER& aBoardAdapter, CAMERA& aCamera,
|
||||
S3D_CACHE* a3DCachePointer ) :
|
||||
HIDPI_GL_3D_CANVAS( EDA_DRAW_PANEL_GAL::GetVcSettings(), aCamera, aParent, aGLAttribs,
|
||||
EDA_3D_CANVAS_ID, wxDefaultPosition,
|
||||
wxDefaultSize, wxFULL_REPAINT_ON_RESIZE ),
|
||||
m_eventDispatcher( nullptr ),
|
||||
m_parentStatusBar( nullptr ),
|
||||
m_parentInfoBar( nullptr ),
|
||||
m_glRC( nullptr ),
|
||||
m_is_opengl_initialized( false ),
|
||||
m_is_opengl_version_supported( true ),
|
||||
m_editing_timeout_timer( this, wxID_HIGHEST + 1 ),
|
||||
m_redraw_trigger_timer( this, wxID_HIGHEST + 2 ),
|
||||
m_render_pivot( false ),
|
||||
m_camera_moving_speed( 1.0f ),
|
||||
m_strtime_camera_movement( 0 ),
|
||||
m_animation_enabled( true ),
|
||||
m_moving_speed_multiplier( 3 ),
|
||||
m_boardAdapter( aBoardAdapter ),
|
||||
m_3d_render( nullptr ),
|
||||
m_opengl_supports_raytracing( true ),
|
||||
m_render_raytracing_was_requested( false ),
|
||||
m_accelerator3DShapes( nullptr ),
|
||||
m_currentRollOverItem( nullptr )
|
||||
HIDPI_GL_3D_CANVAS( EDA_DRAW_PANEL_GAL::GetVcSettings(), aCamera, aParent, aGLAttribs,
|
||||
EDA_3D_CANVAS_ID, wxDefaultPosition,
|
||||
wxDefaultSize, wxFULL_REPAINT_ON_RESIZE ),
|
||||
m_editing_timeout_timer( this, wxID_HIGHEST + 1 ),
|
||||
m_redraw_trigger_timer( this, wxID_HIGHEST + 2 ),
|
||||
m_boardAdapter( aBoardAdapter )
|
||||
{
|
||||
wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::EDA_3D_CANVAS" ) );
|
||||
|
||||
@ -481,8 +473,10 @@ void EDA_3D_CANVAS::DoRePaint()
|
||||
|
||||
if( m_camera_is_moving )
|
||||
{
|
||||
const int64_t curtime_delta = GetRunningMicroSecs() - m_strtime_camera_movement;
|
||||
curtime_delta_s = ( curtime_delta / 1e6 ) * m_camera_moving_speed;
|
||||
const int64_t curtime_delta = GetRunningMicroSecs() - m_strtime_camera_movement;
|
||||
// Convert microseconds to seconds as float and apply speed multiplier
|
||||
curtime_delta_s = static_cast<float>( static_cast<double>( curtime_delta ) / 1e6 )
|
||||
* m_camera_moving_speed;
|
||||
m_camera.Interpolate( curtime_delta_s );
|
||||
|
||||
if( curtime_delta_s > 1.0f )
|
||||
@ -751,7 +745,8 @@ void EDA_3D_CANVAS::RenderToFrameBuffer( unsigned char* buffer, int width, int h
|
||||
if( m_camera_is_moving )
|
||||
{
|
||||
const int64_t curtime_delta = GetRunningMicroSecs() - m_strtime_camera_movement;
|
||||
curtime_delta_s = ( curtime_delta / 1e6 ) * m_camera_moving_speed;
|
||||
curtime_delta_s = static_cast<float>( static_cast<double>( curtime_delta ) / 1e6 )
|
||||
* m_camera_moving_speed;
|
||||
m_camera.Interpolate( curtime_delta_s );
|
||||
|
||||
if( curtime_delta_s > 1.0f )
|
||||
@ -887,7 +882,7 @@ void EDA_3D_CANVAS::OnZoomGesture( wxZoomGestureEvent& aEvent )
|
||||
m_camera.Pan( aEvent.GetPosition() );
|
||||
m_camera.SetCurMousePosition( aEvent.GetPosition() );
|
||||
|
||||
m_camera.Zoom( aEvent.GetZoomFactor() / m_gestureLastZoomFactor );
|
||||
m_camera.Zoom( static_cast<float>( aEvent.GetZoomFactor() / m_gestureLastZoomFactor ) );
|
||||
|
||||
m_gestureLastZoomFactor = aEvent.GetZoomFactor();
|
||||
|
||||
@ -930,7 +925,7 @@ void EDA_3D_CANVAS::OnRotateGesture( wxRotateGestureEvent& aEvent )
|
||||
if( m_camera_is_moving )
|
||||
return;
|
||||
|
||||
m_camera.RotateScreen( m_gestureLastAngle - aEvent.GetRotationAngle() );
|
||||
m_camera.RotateScreen( static_cast<float>( m_gestureLastAngle - aEvent.GetRotationAngle() ) );
|
||||
m_gestureLastAngle = aEvent.GetRotationAngle();
|
||||
|
||||
DisplayStatus();
|
||||
@ -1066,9 +1061,45 @@ void EDA_3D_CANVAS::OnLeftDown( wxMouseEvent& event )
|
||||
{
|
||||
RAY mouseRay = getRayAtCurrentMousePosition();
|
||||
|
||||
BOARD_ITEM *intersectedBoardItem = m_3d_render_raytracing->IntersectBoardItem( mouseRay );
|
||||
BOARD_ITEM* intersectedBoardItem = m_3d_render_raytracing->IntersectBoardItem( mouseRay );
|
||||
|
||||
// !TODO: send a selection item to pcbnew, eg: via kiway?
|
||||
if( intersectedBoardItem )
|
||||
{
|
||||
FOOTPRINT* footprint = nullptr;
|
||||
|
||||
switch( intersectedBoardItem->Type() )
|
||||
{
|
||||
case PCB_FOOTPRINT_T:
|
||||
footprint = static_cast<FOOTPRINT*>( intersectedBoardItem );
|
||||
break;
|
||||
|
||||
case PCB_PAD_T:
|
||||
footprint = static_cast<PAD*>( intersectedBoardItem )->GetParentFootprint();
|
||||
break;
|
||||
|
||||
case PCB_FIELD_T:
|
||||
footprint = static_cast<PCB_FIELD*>( intersectedBoardItem )->GetParentFootprint();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if( footprint )
|
||||
{
|
||||
std::string command =
|
||||
fmt::format( "$SELECT: 0,F{}",
|
||||
EscapeString( footprint->GetReference(), CTX_IPC ).ToStdString() );
|
||||
|
||||
EDA_3D_VIEWER_FRAME* frame = static_cast<EDA_3D_VIEWER_FRAME*>( GetParent() );
|
||||
|
||||
if( frame )
|
||||
{
|
||||
frame->Kiway().ExpressMail( FRAME_PCB_EDITOR, MAIL_SELECTION, command, frame );
|
||||
frame->Kiway().ExpressMail( FRAME_SCH, MAIL_SELECTION, command, frame );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1088,14 +1119,14 @@ void EDA_3D_CANVAS::OnLeftUp( wxMouseEvent& event )
|
||||
int logicalW = logicalSize.GetWidth();
|
||||
int logicalH = logicalSize.GetHeight();
|
||||
|
||||
int gizmo_x, gizmo_y, gizmo_width, gizmo_height;
|
||||
int gizmo_x = 0, gizmo_y = 0, gizmo_width = 0, gizmo_height = 0;
|
||||
std::tie( gizmo_x, gizmo_y, gizmo_width, gizmo_height ) = m_3d_render_opengl->getGizmoViewport();
|
||||
|
||||
float scaleX = static_cast<float>( gizmo_width ) / logicalW;
|
||||
float scaleY = static_cast<float>( gizmo_height ) / logicalH;
|
||||
float scaleX = static_cast<float>( static_cast<double>( gizmo_width ) / static_cast<double>( logicalW ) );
|
||||
float scaleY = static_cast<float>( static_cast<double>( gizmo_height ) / static_cast<double>( logicalH ) );
|
||||
|
||||
int scaledMouseX = static_cast<int>( event.GetX() * scaleX );
|
||||
int scaledMouseY = static_cast<int>( ( logicalH - event.GetY() ) * scaleY );
|
||||
int scaledMouseX = static_cast<int>( static_cast<float>( event.GetX() ) * scaleX );
|
||||
int scaledMouseY = static_cast<int>( static_cast<float>( logicalH - event.GetY() ) * scaleY );
|
||||
|
||||
m_3d_render_opengl->handleGizmoMouseInput( scaledMouseX, scaledMouseY );
|
||||
Refresh();
|
||||
@ -1229,7 +1260,7 @@ void EDA_3D_CANVAS::request_start_moving_camera( float aMovingSpeed, bool aRende
|
||||
|
||||
// Map speed multiplier option to actual multiplier value
|
||||
// [1,2,3,4,5] -> [0.25, 0.5, 1, 2, 4]
|
||||
aMovingSpeed *= ( 1 << m_moving_speed_multiplier ) / 8.0f;
|
||||
aMovingSpeed *= static_cast<float>( ( 1 << m_moving_speed_multiplier ) ) / 8.0f;
|
||||
|
||||
m_render_pivot = aRenderPivot;
|
||||
m_camera_moving_speed = aMovingSpeed;
|
||||
@ -1249,7 +1280,7 @@ void EDA_3D_CANVAS::move_pivot_based_on_cur_mouse_position()
|
||||
{
|
||||
RAY mouseRay = getRayAtCurrentMousePosition();
|
||||
|
||||
float hit_t;
|
||||
float hit_t = 0.0f;
|
||||
|
||||
// Test it with the board bounding box
|
||||
if( m_boardAdapter.GetBBox().Intersect( mouseRay, &hit_t ) )
|
||||
|
@ -42,7 +42,7 @@ class RENDER_3D_RAYTRACE_GL;
|
||||
class RENDER_3D_OPENGL;
|
||||
|
||||
|
||||
#define EDA_3D_CANVAS_ID wxID_HIGHEST + 1321
|
||||
#define EDA_3D_CANVAS_ID (wxID_HIGHEST + 1321)
|
||||
|
||||
/**
|
||||
* Implement a canvas based on a wxGLCanvas
|
||||
@ -61,7 +61,7 @@ public:
|
||||
EDA_3D_CANVAS( wxWindow* aParent, const wxGLAttributes& aGLAttribs, BOARD_ADAPTER& aSettings,
|
||||
CAMERA& aCamera, S3D_CACHE* a3DCachePointer );
|
||||
|
||||
~EDA_3D_CANVAS();
|
||||
~EDA_3D_CANVAS() override;
|
||||
|
||||
/**
|
||||
* Set a dispatcher that processes events and forwards them to tools.
|
||||
@ -302,36 +302,36 @@ private:
|
||||
RAY getRayAtCurrentMousePosition();
|
||||
|
||||
private:
|
||||
TOOL_DISPATCHER* m_eventDispatcher;
|
||||
wxStatusBar* m_parentStatusBar; // Parent statusbar to report progress
|
||||
WX_INFOBAR* m_parentInfoBar;
|
||||
TOOL_DISPATCHER* m_eventDispatcher = nullptr;
|
||||
wxStatusBar* m_parentStatusBar = nullptr; // Parent statusbar to report progress
|
||||
WX_INFOBAR* m_parentInfoBar = nullptr;
|
||||
|
||||
wxGLContext* m_glRC; // Current OpenGL context
|
||||
bool m_is_opengl_initialized;
|
||||
bool m_is_opengl_version_supported;
|
||||
wxGLContext* m_glRC = nullptr; // Current OpenGL context
|
||||
bool m_is_opengl_initialized = false;
|
||||
bool m_is_opengl_version_supported = true;
|
||||
|
||||
wxTimer m_editing_timeout_timer; // Expires after some time signaling that
|
||||
// the mouse / keyboard movements are over
|
||||
wxTimer m_redraw_trigger_timer; // Used to schedule a redraw event
|
||||
std::atomic_flag m_is_currently_painting; // Avoid drawing twice at the same time
|
||||
std::atomic_flag m_is_currently_painting = ATOMIC_FLAG_INIT; // Avoid drawing twice at the same time
|
||||
|
||||
bool m_render_pivot; // Render the pivot while camera moving
|
||||
float m_camera_moving_speed; // 1.0f will be 1:1
|
||||
int64_t m_strtime_camera_movement; // Ticktime of camera movement start
|
||||
bool m_animation_enabled; // Camera animation enabled
|
||||
int m_moving_speed_multiplier; // Camera animation speed multiplier option
|
||||
bool m_render_pivot = false; // Render the pivot while camera moving
|
||||
float m_camera_moving_speed = 1.0f; // 1.0f will be 1:1
|
||||
int64_t m_strtime_camera_movement = 0; // Ticktime of camera movement start
|
||||
bool m_animation_enabled = true; // Camera animation enabled
|
||||
int m_moving_speed_multiplier = 3; // Camera animation speed multiplier option
|
||||
|
||||
BOARD_ADAPTER& m_boardAdapter; // Pre-computed 3D info and settings
|
||||
RENDER_3D_BASE* m_3d_render;
|
||||
RENDER_3D_BASE* m_3d_render = nullptr;
|
||||
RENDER_3D_RAYTRACE_GL* m_3d_render_raytracing;
|
||||
RENDER_3D_OPENGL* m_3d_render_opengl;
|
||||
|
||||
bool m_opengl_supports_raytracing;
|
||||
bool m_render_raytracing_was_requested;
|
||||
bool m_opengl_supports_raytracing = true;
|
||||
bool m_render_raytracing_was_requested = false;
|
||||
|
||||
ACCELERATOR_3D* m_accelerator3DShapes; // used for mouse over searching
|
||||
ACCELERATOR_3D* m_accelerator3DShapes = nullptr; // used for mouse over searching
|
||||
|
||||
BOARD_ITEM* m_currentRollOverItem;
|
||||
BOARD_ITEM* m_currentRollOverItem = nullptr;
|
||||
|
||||
bool m_render3dmousePivot = false; // Render the 3dmouse pivot
|
||||
SFVEC3F m_3dmousePivotPos; // The position of the 3dmouse pivot
|
||||
|
@ -486,9 +486,12 @@ void RENDER_3D_RAYTRACE_BASE::renderBlockTracing( uint8_t* ptrPBO, signed int iB
|
||||
// Initialize ray packets
|
||||
const SFVEC2UI& blockPos = m_blockPositions[iBlock];
|
||||
const SFVEC2I blockPosI = SFVEC2I( blockPos.x + m_xoffset, blockPos.y + m_yoffset );
|
||||
const SFVEC2F randDisp = ( m_camera.GetProjection() == PROJECTION_TYPE::ORTHO ) ?
|
||||
SFVEC2F( 0.0f, 0.0f ) :
|
||||
SFVEC2F( DISP_FACTOR, DISP_FACTOR );
|
||||
|
||||
RAYPACKET blockPacket( m_camera, (SFVEC2F) blockPosI + SFVEC2F( DISP_FACTOR, DISP_FACTOR ),
|
||||
SFVEC2F( DISP_FACTOR, DISP_FACTOR ) /* Displacement random factor */ );
|
||||
RAYPACKET blockPacket( m_camera, (SFVEC2F) blockPosI + randDisp,
|
||||
randDisp /* Displacement random factor */ );
|
||||
|
||||
|
||||
HITINFO_PACKET hitPacket_X0Y0[RAYPACKET_RAYS_PER_PACKET];
|
||||
@ -566,7 +569,7 @@ void RENDER_3D_RAYTRACE_BASE::renderBlockTracing( uint8_t* ptrPBO, signed int iB
|
||||
HITINFO_PACKET_init( hitPacket_AA_X1Y1 );
|
||||
|
||||
RAYPACKET blockPacket_AA_X1Y1( m_camera, (SFVEC2F) blockPosI + SFVEC2F( 0.5f, 0.5f ),
|
||||
SFVEC2F( DISP_FACTOR, DISP_FACTOR ) );
|
||||
randDisp );
|
||||
|
||||
if( !m_accelerator->Intersect( blockPacket_AA_X1Y1, hitPacket_AA_X1Y1 ) )
|
||||
{
|
||||
@ -603,16 +606,16 @@ void RENDER_3D_RAYTRACE_BASE::renderBlockTracing( uint8_t* ptrPBO, signed int iB
|
||||
RAY blockRayPck_AA_X1Y1_half[RAYPACKET_RAYS_PER_PACKET];
|
||||
|
||||
RAYPACKET_InitRays_with2DDisplacement(
|
||||
m_camera, (SFVEC2F) blockPosI + SFVEC2F( 0.5f - DISP_FACTOR, DISP_FACTOR ),
|
||||
SFVEC2F( DISP_FACTOR, DISP_FACTOR ), blockRayPck_AA_X1Y0 );
|
||||
m_camera, (SFVEC2F) blockPosI + SFVEC2F( 0.5f - randDisp.x, randDisp.y ),
|
||||
randDisp, blockRayPck_AA_X1Y0 );
|
||||
|
||||
RAYPACKET_InitRays_with2DDisplacement(
|
||||
m_camera, (SFVEC2F) blockPosI + SFVEC2F( DISP_FACTOR, 0.5f - DISP_FACTOR ),
|
||||
SFVEC2F( DISP_FACTOR, DISP_FACTOR ), blockRayPck_AA_X0Y1 );
|
||||
m_camera, (SFVEC2F) blockPosI + SFVEC2F( randDisp.x, 0.5f - randDisp.y ),
|
||||
randDisp, blockRayPck_AA_X0Y1 );
|
||||
|
||||
RAYPACKET_InitRays_with2DDisplacement(
|
||||
m_camera, (SFVEC2F) blockPosI + SFVEC2F( 0.25f - DISP_FACTOR, 0.25f - DISP_FACTOR ),
|
||||
SFVEC2F( DISP_FACTOR, DISP_FACTOR ), blockRayPck_AA_X1Y1_half );
|
||||
m_camera, (SFVEC2F) blockPosI + SFVEC2F( 0.25f - randDisp.x, 0.25f - randDisp.y ),
|
||||
randDisp, blockRayPck_AA_X1Y1_half );
|
||||
|
||||
renderAntiAliasPackets( bgColor, hitPacket_X0Y0, hitPacket_AA_X1Y1, blockRayPck_AA_X1Y0,
|
||||
hitColor_AA_X1Y0 );
|
||||
|
@ -634,7 +634,8 @@ set( COMMON_GIT_SRCS
|
||||
git/project_git_utils.cpp
|
||||
git/kicad_git_common.cpp
|
||||
git/kicad_git_errors.cpp
|
||||
git/project_git_utils.cpp
|
||||
git/git_backend.cpp
|
||||
git/libgit_backend.cpp
|
||||
)
|
||||
|
||||
set( COMMON_SRCS
|
||||
|
@ -22,16 +22,13 @@
|
||||
*/
|
||||
|
||||
#include "git_add_to_index_handler.h"
|
||||
#include <git/kicad_git_memory.h>
|
||||
#include "git_backend.h"
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include <wx/string.h>
|
||||
#include <wx/log.h>
|
||||
|
||||
GIT_ADD_TO_INDEX_HANDLER::GIT_ADD_TO_INDEX_HANDLER( git_repository* aRepository )
|
||||
GIT_ADD_TO_INDEX_HANDLER::GIT_ADD_TO_INDEX_HANDLER( git_repository* aRepository ) :
|
||||
KIGIT_COMMON( aRepository )
|
||||
{
|
||||
m_repository = aRepository;
|
||||
m_filesToAdd.clear();
|
||||
}
|
||||
|
||||
@ -43,66 +40,11 @@ GIT_ADD_TO_INDEX_HANDLER::~GIT_ADD_TO_INDEX_HANDLER()
|
||||
|
||||
bool GIT_ADD_TO_INDEX_HANDLER::AddToIndex( const wxString& aFilePath )
|
||||
{
|
||||
// Test if file is currently in the index
|
||||
|
||||
git_index* index = nullptr;
|
||||
size_t at_pos = 0;
|
||||
|
||||
if( git_repository_index( &index, m_repository ) != 0 )
|
||||
{
|
||||
wxLogError( "Failed to get repository index" );
|
||||
return false;
|
||||
}
|
||||
|
||||
KIGIT::GitIndexPtr indexPtr( index );
|
||||
|
||||
if( git_index_find( &at_pos, index, aFilePath.ToUTF8().data() ) == GIT_OK )
|
||||
{
|
||||
wxLogError( "%s already in index", aFilePath );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add file to index if not already there
|
||||
m_filesToAdd.push_back( aFilePath );
|
||||
|
||||
return true;
|
||||
return GetGitBackend()->AddToIndex( this, aFilePath );
|
||||
}
|
||||
|
||||
|
||||
bool GIT_ADD_TO_INDEX_HANDLER::PerformAddToIndex()
|
||||
{
|
||||
git_index* index = nullptr;
|
||||
|
||||
m_filesFailedToAdd.clear();
|
||||
|
||||
if( git_repository_index( &index, m_repository ) != 0 )
|
||||
{
|
||||
wxLogError( "Failed to get repository index" );
|
||||
std::copy( m_filesToAdd.begin(), m_filesToAdd.end(), std::back_inserter( m_filesFailedToAdd ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
KIGIT::GitIndexPtr indexPtr( index );
|
||||
|
||||
for( auto& file : m_filesToAdd )
|
||||
{
|
||||
if( git_index_add_bypath( index, file.ToUTF8().data() ) != 0 )
|
||||
{
|
||||
wxLogError( "Failed to add %s to index", file );
|
||||
m_filesFailedToAdd.push_back( file );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if( git_index_write( index ) != 0 )
|
||||
{
|
||||
wxLogError( "Failed to write index" );
|
||||
m_filesFailedToAdd.clear();
|
||||
std::copy( m_filesToAdd.begin(), m_filesToAdd.end(),
|
||||
std::back_inserter( m_filesFailedToAdd ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return GetGitBackend()->PerformAddToIndex( this );
|
||||
}
|
||||
|
@ -24,12 +24,13 @@
|
||||
#ifndef GIT_ADD_TO_INDEX_HANDLER_H_
|
||||
#define GIT_ADD_TO_INDEX_HANDLER_H_
|
||||
|
||||
#include <git2.h>
|
||||
#include <git/kicad_git_common.h>
|
||||
#include <vector>
|
||||
#include <wx/string.h>
|
||||
|
||||
class wxString;
|
||||
class LIBGIT_BACKEND;
|
||||
|
||||
class GIT_ADD_TO_INDEX_HANDLER
|
||||
class GIT_ADD_TO_INDEX_HANDLER : public KIGIT_COMMON
|
||||
{
|
||||
public:
|
||||
GIT_ADD_TO_INDEX_HANDLER( git_repository* aRepository );
|
||||
@ -40,8 +41,7 @@ public:
|
||||
bool PerformAddToIndex();
|
||||
|
||||
private:
|
||||
git_repository* m_repository;
|
||||
|
||||
friend class LIBGIT_BACKEND;
|
||||
std::vector<wxString> m_filesToAdd;
|
||||
std::vector<wxString> m_filesFailedToAdd;
|
||||
};
|
||||
|
36
common/git/git_backend.cpp
Normal file
36
common/git/git_backend.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/gpl-3.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 3 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include "git_backend.h"
|
||||
|
||||
static GIT_BACKEND* s_backend = nullptr;
|
||||
|
||||
GIT_BACKEND* GetGitBackend()
|
||||
{
|
||||
return s_backend;
|
||||
}
|
||||
|
||||
void SetGitBackend( GIT_BACKEND* aBackend )
|
||||
{
|
||||
s_backend = aBackend;
|
||||
}
|
127
common/git/git_backend.h
Normal file
127
common/git/git_backend.h
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* 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 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/gpl-3.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 3 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#ifndef GIT_BACKEND_H_
|
||||
#define GIT_BACKEND_H_
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <wx/string.h>
|
||||
|
||||
class GIT_CLONE_HANDLER;
|
||||
class GIT_COMMIT_HANDLER;
|
||||
class GIT_PUSH_HANDLER;
|
||||
class GIT_STATUS_HANDLER;
|
||||
class GIT_ADD_TO_INDEX_HANDLER;
|
||||
class GIT_REMOVE_FROM_INDEX_HANDLER;
|
||||
class GIT_CONFIG_HANDLER;
|
||||
class GIT_INIT_HANDLER;
|
||||
class GIT_BRANCH_HANDLER;
|
||||
class GIT_PULL_HANDLER;
|
||||
class GIT_REVERT_HANDLER;
|
||||
struct RemoteConfig;
|
||||
struct git_repository;
|
||||
enum class InitResult;
|
||||
enum class BranchResult;
|
||||
enum class PullResult;
|
||||
struct FileStatus;
|
||||
enum class PushResult;
|
||||
|
||||
// Commit result shared across backend and handlers
|
||||
enum class CommitResult
|
||||
{
|
||||
Success,
|
||||
Error,
|
||||
Cancelled
|
||||
};
|
||||
|
||||
class GIT_BACKEND
|
||||
{
|
||||
public:
|
||||
virtual ~GIT_BACKEND() = default;
|
||||
|
||||
virtual void Init() = 0;
|
||||
virtual void Shutdown() = 0;
|
||||
|
||||
// Whether the libgit2 library is available/initialized enough for use
|
||||
virtual bool IsLibraryAvailable() = 0;
|
||||
|
||||
virtual bool Clone( GIT_CLONE_HANDLER* aHandler ) = 0;
|
||||
|
||||
virtual CommitResult Commit( GIT_COMMIT_HANDLER* aHandler,
|
||||
const std::vector<wxString>& aFiles,
|
||||
const wxString& aMessage,
|
||||
const wxString& aAuthorName,
|
||||
const wxString& aAuthorEmail ) = 0;
|
||||
|
||||
virtual PushResult Push( GIT_PUSH_HANDLER* aHandler ) = 0;
|
||||
|
||||
virtual bool HasChangedFiles( GIT_STATUS_HANDLER* aHandler ) = 0;
|
||||
|
||||
virtual std::map<wxString, FileStatus> GetFileStatus( GIT_STATUS_HANDLER* aHandler,
|
||||
const wxString& aPathspec ) = 0;
|
||||
|
||||
virtual wxString GetCurrentBranchName( GIT_STATUS_HANDLER* aHandler ) = 0;
|
||||
|
||||
virtual void UpdateRemoteStatus( GIT_STATUS_HANDLER* aHandler,
|
||||
const std::set<wxString>& aLocalChanges,
|
||||
const std::set<wxString>& aRemoteChanges,
|
||||
std::map<wxString, FileStatus>& aFileStatus ) = 0;
|
||||
|
||||
virtual wxString GetWorkingDirectory( GIT_STATUS_HANDLER* aHandler ) = 0;
|
||||
|
||||
virtual wxString GetWorkingDirectory( GIT_CONFIG_HANDLER* aHandler ) = 0;
|
||||
virtual bool GetConfigString( GIT_CONFIG_HANDLER* aHandler, const wxString& aKey,
|
||||
wxString& aValue ) = 0;
|
||||
|
||||
virtual bool IsRepository( GIT_INIT_HANDLER* aHandler, const wxString& aPath ) = 0;
|
||||
virtual InitResult InitializeRepository( GIT_INIT_HANDLER* aHandler, const wxString& aPath ) = 0;
|
||||
virtual bool SetupRemote( GIT_INIT_HANDLER* aHandler, const RemoteConfig& aConfig ) = 0;
|
||||
|
||||
virtual BranchResult SwitchToBranch( GIT_BRANCH_HANDLER* aHandler, const wxString& aBranchName ) = 0;
|
||||
virtual bool BranchExists( GIT_BRANCH_HANDLER* aHandler, const wxString& aBranchName ) = 0;
|
||||
|
||||
virtual bool PerformFetch( GIT_PULL_HANDLER* aHandler, bool aSkipLock ) = 0;
|
||||
virtual PullResult PerformPull( GIT_PULL_HANDLER* aHandler ) = 0;
|
||||
|
||||
virtual void PerformRevert( GIT_REVERT_HANDLER* aHandler ) = 0;
|
||||
|
||||
virtual git_repository* GetRepositoryForFile( const char* aFilename ) = 0;
|
||||
virtual int CreateBranch( git_repository* aRepo, const wxString& aBranchName ) = 0;
|
||||
virtual bool RemoveVCS( git_repository*& aRepo, const wxString& aProjectPath,
|
||||
bool aRemoveGitDir, wxString* aErrors ) = 0;
|
||||
|
||||
virtual bool AddToIndex( GIT_ADD_TO_INDEX_HANDLER* aHandler, const wxString& aFilePath ) = 0;
|
||||
|
||||
virtual bool PerformAddToIndex( GIT_ADD_TO_INDEX_HANDLER* aHandler ) = 0;
|
||||
|
||||
virtual bool RemoveFromIndex( GIT_REMOVE_FROM_INDEX_HANDLER* aHandler, const wxString& aFilePath ) = 0;
|
||||
|
||||
virtual void PerformRemoveFromIndex( GIT_REMOVE_FROM_INDEX_HANDLER* aHandler ) = 0;
|
||||
};
|
||||
|
||||
GIT_BACKEND* GetGitBackend();
|
||||
void SetGitBackend( GIT_BACKEND* aBackend );
|
||||
|
||||
#endif
|
@ -22,6 +22,7 @@
|
||||
*/
|
||||
|
||||
#include "git_branch_handler.h"
|
||||
#include "git_backend.h"
|
||||
#include <git/kicad_git_common.h>
|
||||
#include <git/kicad_git_memory.h>
|
||||
#include <trace_helpers.h>
|
||||
@ -35,89 +36,12 @@ GIT_BRANCH_HANDLER::~GIT_BRANCH_HANDLER()
|
||||
|
||||
bool GIT_BRANCH_HANDLER::BranchExists( const wxString& aBranchName )
|
||||
{
|
||||
git_repository* repo = GetRepo();
|
||||
|
||||
if( !repo )
|
||||
return false;
|
||||
|
||||
git_reference* branchRef = nullptr;
|
||||
bool exists = LookupBranchReference( aBranchName, &branchRef );
|
||||
|
||||
if( branchRef )
|
||||
git_reference_free( branchRef );
|
||||
|
||||
return exists;
|
||||
}
|
||||
|
||||
bool GIT_BRANCH_HANDLER::LookupBranchReference( const wxString& aBranchName, git_reference** aReference )
|
||||
{
|
||||
git_repository* repo = GetRepo();
|
||||
|
||||
if( !repo )
|
||||
return false;
|
||||
|
||||
// Try direct lookup first
|
||||
if( git_reference_lookup( aReference, repo, aBranchName.mb_str() ) == GIT_OK )
|
||||
return true;
|
||||
|
||||
// Try dwim (Do What I Mean) lookup for short branch names
|
||||
if( git_reference_dwim( aReference, repo, aBranchName.mb_str() ) == GIT_OK )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
return GetGitBackend()->BranchExists( this, aBranchName );
|
||||
}
|
||||
|
||||
BranchResult GIT_BRANCH_HANDLER::SwitchToBranch( const wxString& aBranchName )
|
||||
{
|
||||
git_repository* repo = GetRepo();
|
||||
|
||||
if( !repo )
|
||||
{
|
||||
AddErrorString( _( "No repository available" ) );
|
||||
return BranchResult::Error;
|
||||
}
|
||||
|
||||
// Look up the branch reference
|
||||
git_reference* branchRef = nullptr;
|
||||
|
||||
if( !LookupBranchReference( aBranchName, &branchRef ) )
|
||||
{
|
||||
AddErrorString( wxString::Format( _( "Failed to lookup branch '%s': %s" ),
|
||||
aBranchName, KIGIT_COMMON::GetLastGitError() ) );
|
||||
return BranchResult::BranchNotFound;
|
||||
}
|
||||
|
||||
KIGIT::GitReferencePtr branchRefPtr( branchRef );
|
||||
const char* branchRefName = git_reference_name( branchRef );
|
||||
git_object* branchObj = nullptr;
|
||||
|
||||
if( git_revparse_single( &branchObj, repo, aBranchName.mb_str() ) != GIT_OK )
|
||||
{
|
||||
AddErrorString( wxString::Format( _( "Failed to find branch head for '%s': %s" ),
|
||||
aBranchName, KIGIT_COMMON::GetLastGitError() ) );
|
||||
return BranchResult::Error;
|
||||
}
|
||||
|
||||
KIGIT::GitObjectPtr branchObjPtr( branchObj );
|
||||
|
||||
// Switch to the branch
|
||||
if( git_checkout_tree( repo, branchObj, nullptr ) != GIT_OK )
|
||||
{
|
||||
AddErrorString( wxString::Format( _( "Failed to switch to branch '%s': %s" ),
|
||||
aBranchName, KIGIT_COMMON::GetLastGitError() ) );
|
||||
return BranchResult::CheckoutFailed;
|
||||
}
|
||||
|
||||
// Update the HEAD reference
|
||||
if( git_repository_set_head( repo, branchRefName ) != GIT_OK )
|
||||
{
|
||||
AddErrorString( wxString::Format( _( "Failed to update HEAD reference for branch '%s': %s" ),
|
||||
aBranchName, KIGIT_COMMON::GetLastGitError() ) );
|
||||
return BranchResult::Error;
|
||||
}
|
||||
|
||||
wxLogTrace( traceGit, "Successfully switched to branch '%s'", aBranchName );
|
||||
return BranchResult::Success;
|
||||
return GetGitBackend()->SwitchToBranch( this, aBranchName );
|
||||
}
|
||||
|
||||
void GIT_BRANCH_HANDLER::UpdateProgress( int aCurrent, int aTotal, const wxString& aMessage )
|
||||
|
@ -57,15 +57,6 @@ public:
|
||||
bool BranchExists( const wxString& aBranchName );
|
||||
|
||||
void UpdateProgress( int aCurrent, int aTotal, const wxString& aMessage ) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Look up a branch reference by name
|
||||
* @param aBranchName Name of the branch
|
||||
* @param aReference Output parameter for the reference
|
||||
* @return True if successful, false otherwise
|
||||
*/
|
||||
bool LookupBranchReference( const wxString& aBranchName, git_reference** aReference );
|
||||
};
|
||||
|
||||
#endif // GIT_BRANCH_HANDLER_H
|
@ -23,13 +23,8 @@
|
||||
|
||||
#include "git_clone_handler.h"
|
||||
|
||||
#include <git/kicad_git_common.h>
|
||||
#include <git/kicad_git_memory.h>
|
||||
#include <trace_helpers.h>
|
||||
|
||||
#include <git2.h>
|
||||
#include <wx/filename.h>
|
||||
#include <wx/log.h>
|
||||
#include "git_backend.h"
|
||||
|
||||
GIT_CLONE_HANDLER::GIT_CLONE_HANDLER( KIGIT_COMMON* aCommon ) : KIGIT_REPO_MIXIN( aCommon )
|
||||
{}
|
||||
@ -41,55 +36,7 @@ GIT_CLONE_HANDLER::~GIT_CLONE_HANDLER()
|
||||
|
||||
bool GIT_CLONE_HANDLER::PerformClone()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock( GetCommon()->m_gitActionMutex, std::try_to_lock );
|
||||
|
||||
if( !lock.owns_lock() )
|
||||
{
|
||||
wxLogTrace( traceGit, "GIT_CLONE_HANDLER::PerformClone() could not lock" );
|
||||
return false;
|
||||
}
|
||||
|
||||
wxFileName clonePath( m_clonePath );
|
||||
|
||||
if( !clonePath.DirExists() )
|
||||
{
|
||||
if( !clonePath.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
|
||||
{
|
||||
AddErrorString( wxString::Format( _( "Could not create directory '%s'" ),
|
||||
m_clonePath ) );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
git_clone_options cloneOptions;
|
||||
git_clone_init_options( &cloneOptions, GIT_CLONE_OPTIONS_VERSION );
|
||||
cloneOptions.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
|
||||
cloneOptions.checkout_opts.progress_cb = clone_progress_cb;
|
||||
cloneOptions.checkout_opts.progress_payload = this;
|
||||
cloneOptions.fetch_opts.callbacks.transfer_progress = transfer_progress_cb;
|
||||
cloneOptions.fetch_opts.callbacks.credentials = credentials_cb;
|
||||
cloneOptions.fetch_opts.callbacks.payload = this;
|
||||
|
||||
TestedTypes() = 0;
|
||||
ResetNextKey();
|
||||
git_repository* newRepo = nullptr;
|
||||
wxString remote = GetCommon()->m_remote;
|
||||
|
||||
if( git_clone( &newRepo, remote.mbc_str(), m_clonePath.mbc_str(),
|
||||
&cloneOptions ) != 0 )
|
||||
{
|
||||
AddErrorString( wxString::Format( _( "Could not clone repository '%s'" ), remote ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
GetCommon()->SetRepo( newRepo );
|
||||
|
||||
if( m_progressReporter )
|
||||
m_progressReporter->Hide();
|
||||
|
||||
m_previousProgress = 0;
|
||||
|
||||
return true;
|
||||
return GetGitBackend()->Clone( this );
|
||||
}
|
||||
|
||||
|
||||
|
@ -22,8 +22,7 @@
|
||||
*/
|
||||
|
||||
#include "git_commit_handler.h"
|
||||
#include <git/kicad_git_memory.h>
|
||||
#include <wx/log.h>
|
||||
#include "git_backend.h"
|
||||
|
||||
GIT_COMMIT_HANDLER::GIT_COMMIT_HANDLER( git_repository* aRepo ) :
|
||||
KIGIT_COMMON( aRepo )
|
||||
@ -34,121 +33,13 @@ GIT_COMMIT_HANDLER::~GIT_COMMIT_HANDLER()
|
||||
{}
|
||||
|
||||
|
||||
GIT_COMMIT_HANDLER::CommitResult
|
||||
CommitResult
|
||||
GIT_COMMIT_HANDLER::PerformCommit( const std::vector<wxString>& aFiles,
|
||||
const wxString& aMessage,
|
||||
const wxString& aAuthorName,
|
||||
const wxString& aAuthorEmail )
|
||||
{
|
||||
git_repository* repo = GetRepo();
|
||||
|
||||
if( !repo )
|
||||
return CommitResult::Error;
|
||||
|
||||
git_index* index = nullptr;
|
||||
|
||||
if( git_repository_index( &index, repo ) != 0 )
|
||||
{
|
||||
AddErrorString( wxString::Format( _( "Failed to get repository index: %s" ),
|
||||
KIGIT_COMMON::GetLastGitError() ) );
|
||||
return CommitResult::Error;
|
||||
}
|
||||
|
||||
KIGIT::GitIndexPtr indexPtr( index );
|
||||
|
||||
for( const wxString& file : aFiles )
|
||||
{
|
||||
if( git_index_add_bypath( index, file.mb_str() ) != 0 )
|
||||
{
|
||||
AddErrorString( wxString::Format( _( "Failed to add file to index: %s" ),
|
||||
KIGIT_COMMON::GetLastGitError() ) );
|
||||
return CommitResult::Error;
|
||||
}
|
||||
}
|
||||
|
||||
if( git_index_write( index ) != 0 )
|
||||
{
|
||||
AddErrorString( wxString::Format( _( "Failed to write index: %s" ),
|
||||
KIGIT_COMMON::GetLastGitError() ) );
|
||||
return CommitResult::Error;
|
||||
}
|
||||
|
||||
git_oid tree_id;
|
||||
|
||||
if( git_index_write_tree( &tree_id, index ) != 0 )
|
||||
{
|
||||
AddErrorString( wxString::Format( _( "Failed to write tree: %s" ),
|
||||
KIGIT_COMMON::GetLastGitError() ) );
|
||||
return CommitResult::Error;
|
||||
}
|
||||
|
||||
git_tree* tree = nullptr;
|
||||
|
||||
if( git_tree_lookup( &tree, repo, &tree_id ) != 0 )
|
||||
{
|
||||
AddErrorString( wxString::Format( _( "Failed to lookup tree: %s" ),
|
||||
KIGIT_COMMON::GetLastGitError() ) );
|
||||
return CommitResult::Error;
|
||||
}
|
||||
|
||||
KIGIT::GitTreePtr treePtr( tree );
|
||||
git_commit* parent = nullptr;
|
||||
|
||||
if( git_repository_head_unborn( repo ) == 0 )
|
||||
{
|
||||
git_reference* headRef = nullptr;
|
||||
|
||||
if( git_repository_head( &headRef, repo ) != 0 )
|
||||
{
|
||||
AddErrorString( wxString::Format( _( "Failed to get HEAD reference: %s" ),
|
||||
KIGIT_COMMON::GetLastGitError() ) );
|
||||
return CommitResult::Error;
|
||||
}
|
||||
|
||||
KIGIT::GitReferencePtr headRefPtr( headRef );
|
||||
|
||||
if( git_reference_peel( (git_object**) &parent, headRef, GIT_OBJECT_COMMIT ) != 0 )
|
||||
{
|
||||
AddErrorString( wxString::Format( _( "Failed to get commit: %s" ),
|
||||
KIGIT_COMMON::GetLastGitError() ) );
|
||||
return CommitResult::Error;
|
||||
}
|
||||
}
|
||||
|
||||
KIGIT::GitCommitPtr parentPtr( parent );
|
||||
|
||||
git_signature* author = nullptr;
|
||||
|
||||
if( git_signature_now( &author, aAuthorName.mb_str(), aAuthorEmail.mb_str() ) != 0 )
|
||||
{
|
||||
AddErrorString( wxString::Format( _( "Failed to create author signature: %s" ),
|
||||
KIGIT_COMMON::GetLastGitError() ) );
|
||||
return CommitResult::Error;
|
||||
}
|
||||
|
||||
KIGIT::GitSignaturePtr authorPtr( author );
|
||||
git_oid oid;
|
||||
size_t parentsCount = parent ? 1 : 0;
|
||||
#if( LIBGIT2_VER_MAJOR == 1 && LIBGIT2_VER_MINOR == 8 \
|
||||
&& ( LIBGIT2_VER_REVISION < 2 || LIBGIT2_VER_REVISION == 3 ) )
|
||||
git_commit* const parents[1] = { parent };
|
||||
git_commit** const parentsPtr = parent ? parents : nullptr;
|
||||
#else
|
||||
const git_commit* parents[1] = { parent };
|
||||
const git_commit** parentsPtr = parent ? parents : nullptr;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
if( git_commit_create( &oid, repo, "HEAD", author, author, nullptr,
|
||||
aMessage.mb_str(), tree, parentsCount, parentsPtr ) != 0 )
|
||||
{
|
||||
AddErrorString( wxString::Format( _( "Failed to create commit: %s" ),
|
||||
KIGIT_COMMON::GetLastGitError() ) );
|
||||
return CommitResult::Error;
|
||||
}
|
||||
|
||||
return CommitResult::Success;
|
||||
return GetGitBackend()->Commit( this, aFiles, aMessage, aAuthorName, aAuthorEmail );
|
||||
}
|
||||
|
||||
|
||||
|
@ -27,25 +27,20 @@
|
||||
// Define a class to handle git commit operations
|
||||
|
||||
#include <git/kicad_git_common.h>
|
||||
#include <git2.h>
|
||||
#include "git_backend.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <wx/string.h>
|
||||
|
||||
class LIBGIT_BACKEND;
|
||||
|
||||
class GIT_COMMIT_HANDLER : public KIGIT_COMMON
|
||||
{
|
||||
public:
|
||||
GIT_COMMIT_HANDLER( git_repository* aRepo );
|
||||
virtual ~GIT_COMMIT_HANDLER();
|
||||
|
||||
enum class CommitResult
|
||||
{
|
||||
Success,
|
||||
Error,
|
||||
Cancelled
|
||||
};
|
||||
|
||||
CommitResult PerformCommit( const std::vector<wxString>& aFiles,
|
||||
const wxString& aMessage,
|
||||
const wxString& aAuthorName,
|
||||
@ -54,6 +49,7 @@ public:
|
||||
wxString GetErrorString() const;
|
||||
|
||||
private:
|
||||
friend class LIBGIT_BACKEND;
|
||||
void AddErrorString( const wxString& aErrorString );
|
||||
|
||||
wxString m_errorString;
|
||||
|
@ -22,6 +22,7 @@
|
||||
*/
|
||||
|
||||
#include "git_config_handler.h"
|
||||
#include "git_backend.h"
|
||||
#include <git/kicad_git_common.h>
|
||||
#include <git/kicad_git_memory.h>
|
||||
#include <pgm_base.h>
|
||||
@ -59,48 +60,12 @@ GitUserConfig GIT_CONFIG_HANDLER::GetUserConfig()
|
||||
|
||||
wxString GIT_CONFIG_HANDLER::GetWorkingDirectory()
|
||||
{
|
||||
git_repository* repo = GetRepo();
|
||||
|
||||
if( !repo )
|
||||
return wxEmptyString;
|
||||
|
||||
const char* workdir = git_repository_workdir( repo );
|
||||
|
||||
if( !workdir )
|
||||
return wxEmptyString;
|
||||
|
||||
return wxString( workdir );
|
||||
return GetGitBackend()->GetWorkingDirectory( this );
|
||||
}
|
||||
|
||||
bool GIT_CONFIG_HANDLER::GetConfigString( const wxString& aKey, wxString& aValue )
|
||||
{
|
||||
git_repository* repo = GetRepo();
|
||||
|
||||
if( !repo )
|
||||
return false;
|
||||
|
||||
git_config* config = nullptr;
|
||||
|
||||
if( git_repository_config( &config, repo ) != GIT_OK )
|
||||
{
|
||||
wxLogTrace( traceGit, "Failed to get repository config: %s", KIGIT_COMMON::GetLastGitError() );
|
||||
return false;
|
||||
}
|
||||
|
||||
KIGIT::GitConfigPtr configPtr( config );
|
||||
|
||||
git_config_entry* entry = nullptr;
|
||||
int result = git_config_get_entry( &entry, config, aKey.mb_str() );
|
||||
KIGIT::GitConfigEntryPtr entryPtr( entry );
|
||||
|
||||
if( result != GIT_OK || entry == nullptr )
|
||||
{
|
||||
wxLogTrace( traceGit, "Config key '%s' not found", aKey );
|
||||
return false;
|
||||
}
|
||||
|
||||
aValue = wxString( entry->value );
|
||||
return true;
|
||||
return GetGitBackend()->GetConfigString( this, aKey, aValue );
|
||||
}
|
||||
|
||||
void GIT_CONFIG_HANDLER::UpdateProgress( int aCurrent, int aTotal, const wxString& aMessage )
|
||||
|
@ -22,6 +22,7 @@
|
||||
*/
|
||||
|
||||
#include "git_init_handler.h"
|
||||
#include "git_backend.h"
|
||||
#include <git/kicad_git_common.h>
|
||||
#include <git/kicad_git_memory.h>
|
||||
#include <trace_helpers.h>
|
||||
@ -35,118 +36,17 @@ GIT_INIT_HANDLER::~GIT_INIT_HANDLER()
|
||||
|
||||
bool GIT_INIT_HANDLER::IsRepository( const wxString& aPath )
|
||||
{
|
||||
git_repository* repo = nullptr;
|
||||
int error = git_repository_open( &repo, aPath.mb_str() );
|
||||
|
||||
if( error == 0 )
|
||||
{
|
||||
git_repository_free( repo );
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return GetGitBackend()->IsRepository( this, aPath );
|
||||
}
|
||||
|
||||
InitResult GIT_INIT_HANDLER::InitializeRepository( const wxString& aPath )
|
||||
{
|
||||
// Check if directory is already a git repository
|
||||
if( IsRepository( aPath ) )
|
||||
{
|
||||
return InitResult::AlreadyExists;
|
||||
}
|
||||
|
||||
git_repository* repo = nullptr;
|
||||
|
||||
if( git_repository_init( &repo, aPath.mb_str(), 0 ) != GIT_OK )
|
||||
{
|
||||
if( repo )
|
||||
git_repository_free( repo );
|
||||
|
||||
AddErrorString( wxString::Format( _( "Failed to initialize Git repository: %s" ),
|
||||
KIGIT_COMMON::GetLastGitError() ) );
|
||||
return InitResult::Error;
|
||||
}
|
||||
|
||||
// Update the common repository pointer
|
||||
GetCommon()->SetRepo( repo );
|
||||
|
||||
wxLogTrace( traceGit, "Successfully initialized Git repository at %s", aPath );
|
||||
return InitResult::Success;
|
||||
return GetGitBackend()->InitializeRepository( this, aPath );
|
||||
}
|
||||
|
||||
bool GIT_INIT_HANDLER::SetupRemote( const RemoteConfig& aConfig )
|
||||
{
|
||||
// This is an optional step
|
||||
if( aConfig.url.IsEmpty() )
|
||||
return true;
|
||||
|
||||
git_repository* repo = GetRepo();
|
||||
|
||||
if( !repo )
|
||||
{
|
||||
AddErrorString( _( "No repository available to set up remote" ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update connection settings in common
|
||||
GetCommon()->SetUsername( aConfig.username );
|
||||
GetCommon()->SetPassword( aConfig.password );
|
||||
GetCommon()->SetSSHKey( aConfig.sshKey );
|
||||
|
||||
git_remote* remote = nullptr;
|
||||
wxString fullURL;
|
||||
|
||||
// Build the full URL based on connection type
|
||||
if( aConfig.connType == KIGIT_COMMON::GIT_CONN_TYPE::GIT_CONN_SSH )
|
||||
{
|
||||
fullURL = aConfig.username + "@" + aConfig.url;
|
||||
}
|
||||
else if( aConfig.connType == KIGIT_COMMON::GIT_CONN_TYPE::GIT_CONN_HTTPS )
|
||||
{
|
||||
fullURL = aConfig.url.StartsWith( "https" ) ? "https://" : "http://";
|
||||
|
||||
if( !aConfig.username.empty() )
|
||||
{
|
||||
fullURL.append( aConfig.username );
|
||||
|
||||
if( !aConfig.password.empty() )
|
||||
{
|
||||
fullURL.append( wxS( ":" ) );
|
||||
fullURL.append( aConfig.password );
|
||||
}
|
||||
|
||||
fullURL.append( wxS( "@" ) );
|
||||
}
|
||||
|
||||
// Extract the bare URL (without protocol prefix)
|
||||
wxString bareURL = aConfig.url;
|
||||
if( bareURL.StartsWith( "https://" ) )
|
||||
bareURL = bareURL.Mid( 8 );
|
||||
else if( bareURL.StartsWith( "http://" ) )
|
||||
bareURL = bareURL.Mid( 7 );
|
||||
|
||||
fullURL.append( bareURL );
|
||||
}
|
||||
else
|
||||
{
|
||||
fullURL = aConfig.url;
|
||||
}
|
||||
|
||||
int error = git_remote_create_with_fetchspec( &remote, repo, "origin",
|
||||
fullURL.ToStdString().c_str(),
|
||||
"+refs/heads/*:refs/remotes/origin/*" );
|
||||
|
||||
KIGIT::GitRemotePtr remotePtr( remote );
|
||||
|
||||
if( error != GIT_OK )
|
||||
{
|
||||
AddErrorString( wxString::Format( _( "Failed to create remote: %s" ),
|
||||
KIGIT_COMMON::GetLastGitError() ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
wxLogTrace( traceGit, "Successfully set up remote origin" );
|
||||
return true;
|
||||
return GetGitBackend()->SetupRemote( this, aConfig );
|
||||
}
|
||||
|
||||
void GIT_INIT_HANDLER::UpdateProgress( int aCurrent, int aTotal, const wxString& aMessage )
|
||||
|
@ -22,188 +22,22 @@
|
||||
*/
|
||||
|
||||
#include "git_pull_handler.h"
|
||||
#include <git/kicad_git_common.h>
|
||||
#include <git/kicad_git_memory.h>
|
||||
#include <trace_helpers.h>
|
||||
|
||||
#include <wx/log.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <time.h>
|
||||
#include <memory>
|
||||
#include "git_backend.h"
|
||||
|
||||
GIT_PULL_HANDLER::GIT_PULL_HANDLER( KIGIT_COMMON* aCommon ) : KIGIT_REPO_MIXIN( aCommon )
|
||||
{
|
||||
}
|
||||
|
||||
{}
|
||||
|
||||
GIT_PULL_HANDLER::~GIT_PULL_HANDLER()
|
||||
{
|
||||
}
|
||||
|
||||
{}
|
||||
|
||||
bool GIT_PULL_HANDLER::PerformFetch( bool aSkipLock )
|
||||
{
|
||||
if( !GetRepo() )
|
||||
{
|
||||
wxLogTrace( traceGit, "GIT_PULL_HANDLER::PerformFetch() - No repository found" );
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> lock( GetCommon()->m_gitActionMutex, std::try_to_lock );
|
||||
|
||||
if( !aSkipLock && !lock.owns_lock() )
|
||||
{
|
||||
wxLogTrace( traceGit, "GIT_PULL_HANDLER::PerformFetch() - Could not lock mutex" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fetch updates from remote repository
|
||||
git_remote* remote = nullptr;
|
||||
|
||||
if( git_remote_lookup( &remote, GetRepo(), "origin" ) != 0 )
|
||||
{
|
||||
wxLogTrace( traceGit, "GIT_PULL_HANDLER::PerformFetch() - Failed to lookup remote 'origin'" );
|
||||
AddErrorString( wxString::Format( _( "Could not lookup remote '%s'" ), "origin" ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
KIGIT::GitRemotePtr remotePtr( remote );
|
||||
|
||||
git_remote_callbacks remoteCallbacks;
|
||||
git_remote_init_callbacks( &remoteCallbacks, GIT_REMOTE_CALLBACKS_VERSION );
|
||||
remoteCallbacks.sideband_progress = progress_cb;
|
||||
remoteCallbacks.transfer_progress = transfer_progress_cb;
|
||||
remoteCallbacks.credentials = credentials_cb;
|
||||
remoteCallbacks.payload = this;
|
||||
GetCommon()->SetCancelled( false );
|
||||
|
||||
TestedTypes() = 0;
|
||||
ResetNextKey();
|
||||
|
||||
if( git_remote_connect( remote, GIT_DIRECTION_FETCH, &remoteCallbacks, nullptr, nullptr ) )
|
||||
{
|
||||
wxString errorMsg = KIGIT_COMMON::GetLastGitError();
|
||||
wxLogTrace( traceGit, "GIT_PULL_HANDLER::PerformFetch() - Failed to connect to remote: %s", errorMsg );
|
||||
AddErrorString( wxString::Format( _( "Could not connect to remote '%s': %s" ), "origin", errorMsg ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
git_fetch_options fetchOptions;
|
||||
git_fetch_init_options( &fetchOptions, GIT_FETCH_OPTIONS_VERSION );
|
||||
fetchOptions.callbacks = remoteCallbacks;
|
||||
|
||||
if( git_remote_fetch( remote, nullptr, &fetchOptions, nullptr ) )
|
||||
{
|
||||
wxString errorMsg = KIGIT_COMMON::GetLastGitError();
|
||||
wxLogTrace( traceGit, "GIT_PULL_HANDLER::PerformFetch() - Failed to fetch from remote: %s", errorMsg );
|
||||
AddErrorString( wxString::Format( _( "Could not fetch data from remote '%s': %s" ), "origin", errorMsg ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
wxLogTrace( traceGit, "GIT_PULL_HANDLER::PerformFetch() - Fetch completed successfully" );
|
||||
return true;
|
||||
return GetGitBackend()->PerformFetch( this, aSkipLock );
|
||||
}
|
||||
|
||||
|
||||
PullResult GIT_PULL_HANDLER::PerformPull()
|
||||
{
|
||||
PullResult result = PullResult::Success;
|
||||
std::unique_lock<std::mutex> lock( GetCommon()->m_gitActionMutex, std::try_to_lock );
|
||||
|
||||
if( !lock.owns_lock() )
|
||||
{
|
||||
wxLogTrace( traceGit, "GIT_PULL_HANDLER::PerformPull() - Could not lock mutex" );
|
||||
return PullResult::Error;
|
||||
}
|
||||
|
||||
if( !PerformFetch( true ) )
|
||||
return PullResult::Error;
|
||||
|
||||
git_oid pull_merge_oid = {};
|
||||
|
||||
if( git_repository_fetchhead_foreach( GetRepo(), fetchhead_foreach_cb,
|
||||
&pull_merge_oid ) )
|
||||
{
|
||||
AddErrorString( _( "Could not read 'FETCH_HEAD'" ) );
|
||||
return PullResult::Error;
|
||||
}
|
||||
|
||||
git_annotated_commit* fetchhead_commit;
|
||||
|
||||
if( git_annotated_commit_lookup( &fetchhead_commit, GetRepo(), &pull_merge_oid ) )
|
||||
{
|
||||
AddErrorString( _( "Could not lookup commit" ) );
|
||||
return PullResult::Error;
|
||||
}
|
||||
|
||||
KIGIT::GitAnnotatedCommitPtr fetchheadCommitPtr( fetchhead_commit );
|
||||
const git_annotated_commit* merge_commits[] = { fetchhead_commit };
|
||||
git_merge_analysis_t merge_analysis;
|
||||
git_merge_preference_t merge_preference = GIT_MERGE_PREFERENCE_NONE;
|
||||
|
||||
if( git_merge_analysis( &merge_analysis, &merge_preference, GetRepo(), merge_commits, 1 ) )
|
||||
{
|
||||
AddErrorString( _( "Could not analyze merge" ) );
|
||||
return PullResult::Error;
|
||||
}
|
||||
|
||||
if( merge_analysis & GIT_MERGE_ANALYSIS_UNBORN )
|
||||
{
|
||||
AddErrorString( _( "Invalid HEAD. Cannot merge." ) );
|
||||
return PullResult::MergeFailed;
|
||||
}
|
||||
|
||||
// Nothing to do if the repository is up to date
|
||||
if( merge_analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE )
|
||||
{
|
||||
wxLogTrace( traceGit, "GIT_PULL_HANDLER::PerformPull() - Repository is up to date" );
|
||||
git_repository_state_cleanup( GetRepo() );
|
||||
return PullResult::UpToDate;
|
||||
}
|
||||
|
||||
// Fast-forward is easy, just update the local reference
|
||||
if( merge_analysis & GIT_MERGE_ANALYSIS_FASTFORWARD )
|
||||
{
|
||||
wxLogTrace( traceGit, "GIT_PULL_HANDLER::PerformPull() - Fast-forward merge" );
|
||||
return handleFastForward();
|
||||
}
|
||||
|
||||
if( merge_analysis & GIT_MERGE_ANALYSIS_NORMAL )
|
||||
{
|
||||
wxLogTrace( traceGit, "GIT_PULL_HANDLER::PerformPull() - Normal merge" );
|
||||
|
||||
// Check git config to determine if we should rebase or merge
|
||||
git_config* config = nullptr;
|
||||
|
||||
if( git_repository_config( &config, GetRepo() ) != GIT_OK )
|
||||
{
|
||||
wxLogTrace( traceGit, "GIT_PULL_HANDLER::PerformPull() - Failed to get repository config" );
|
||||
AddErrorString( _( "Could not access repository configuration" ) );
|
||||
return PullResult::Error;
|
||||
}
|
||||
|
||||
KIGIT::GitConfigPtr configPtr( config );
|
||||
|
||||
// Check for pull.rebase config
|
||||
int rebase_value = 0;
|
||||
int ret = git_config_get_bool( &rebase_value, config, "pull.rebase" );
|
||||
|
||||
// If pull.rebase is set to true, use rebase; otherwise use merge
|
||||
if( ret == GIT_OK && rebase_value )
|
||||
{
|
||||
wxLogTrace( traceGit, "GIT_PULL_HANDLER::PerformPull() - Using rebase based on config" );
|
||||
return handleRebase( merge_commits, 1 );
|
||||
}
|
||||
|
||||
wxLogTrace( traceGit, "GIT_PULL_HANDLER::PerformPull() - Using merge based on config" );
|
||||
return handleMerge( merge_commits, 1 );
|
||||
}
|
||||
|
||||
wxLogTrace( traceGit, "GIT_PULL_HANDLER::PerformPull() - Merge needs resolution" );
|
||||
//TODO: handle merges when they need to be resolved
|
||||
|
||||
return result;
|
||||
return GetGitBackend()->PerformPull( this );
|
||||
}
|
||||
|
||||
const std::vector<std::pair<std::string, std::vector<CommitDetails>>>&
|
||||
@ -212,384 +46,7 @@ GIT_PULL_HANDLER::GetFetchResults() const
|
||||
return m_fetchResults;
|
||||
}
|
||||
|
||||
|
||||
std::string GIT_PULL_HANDLER::getFirstLineFromCommitMessage( const std::string& aMessage )
|
||||
{
|
||||
if( aMessage.empty() )
|
||||
return aMessage;
|
||||
|
||||
size_t firstLineEnd = aMessage.find_first_of( '\n' );
|
||||
|
||||
if( firstLineEnd != std::string::npos )
|
||||
return aMessage.substr( 0, firstLineEnd );
|
||||
|
||||
return aMessage;
|
||||
}
|
||||
|
||||
|
||||
std::string GIT_PULL_HANDLER::getFormattedCommitDate( const git_time& aTime )
|
||||
{
|
||||
char dateBuffer[64];
|
||||
time_t time = static_cast<time_t>( aTime.time );
|
||||
struct tm timeInfo;
|
||||
#ifdef _WIN32
|
||||
localtime_s( &timeInfo, &time );
|
||||
#else
|
||||
gmtime_r( &time, &timeInfo );
|
||||
#endif
|
||||
strftime( dateBuffer, sizeof( dateBuffer ), "%Y-%b-%d %H:%M:%S", &timeInfo );
|
||||
return dateBuffer;
|
||||
}
|
||||
|
||||
|
||||
PullResult GIT_PULL_HANDLER::handleFastForward()
|
||||
{
|
||||
git_reference* rawRef = nullptr;
|
||||
|
||||
// Get the current HEAD reference
|
||||
if( git_repository_head( &rawRef, GetRepo() ) )
|
||||
{
|
||||
AddErrorString( _( "Could not get repository head" ) );
|
||||
return PullResult::Error;
|
||||
}
|
||||
|
||||
KIGIT::GitReferencePtr headRef( rawRef );
|
||||
|
||||
git_oid updatedRefOid;
|
||||
const char* currentBranchName = git_reference_name( rawRef );
|
||||
const char* branch_shorthand = git_reference_shorthand( rawRef );
|
||||
wxString remote_name = GetRemotename();
|
||||
wxString remoteBranchName = wxString::Format( "refs/remotes/%s/%s",
|
||||
remote_name, branch_shorthand );
|
||||
|
||||
// Get the OID of the updated reference (remote-tracking branch)
|
||||
if( git_reference_name_to_id( &updatedRefOid, GetRepo(), remoteBranchName.c_str() ) != GIT_OK )
|
||||
{
|
||||
AddErrorString( wxString::Format( _( "Could not get reference OID for reference '%s'" ),
|
||||
remoteBranchName ) );
|
||||
return PullResult::Error;
|
||||
}
|
||||
|
||||
// Get the target commit object
|
||||
git_commit* targetCommit = nullptr;
|
||||
|
||||
if( git_commit_lookup( &targetCommit, GetRepo(), &updatedRefOid ) != GIT_OK )
|
||||
{
|
||||
AddErrorString( _( "Could not look up target commit" ) );
|
||||
return PullResult::Error;
|
||||
}
|
||||
|
||||
KIGIT::GitCommitPtr targetCommitPtr( targetCommit );
|
||||
|
||||
// Get the tree from the target commit
|
||||
git_tree* targetTree = nullptr;
|
||||
|
||||
if( git_commit_tree( &targetTree, targetCommit ) != GIT_OK )
|
||||
{
|
||||
git_commit_free( targetCommit );
|
||||
AddErrorString( _( "Could not get tree from target commit" ) );
|
||||
return PullResult::Error;
|
||||
}
|
||||
|
||||
KIGIT::GitTreePtr targetTreePtr( targetTree );
|
||||
|
||||
// Perform a checkout to update the working directory
|
||||
git_checkout_options checkoutOptions;
|
||||
git_checkout_init_options( &checkoutOptions, GIT_CHECKOUT_OPTIONS_VERSION );
|
||||
auto notify_cb = []( git_checkout_notify_t why, const char* path, const git_diff_file* baseline,
|
||||
const git_diff_file* target, const git_diff_file* workdir, void* payload ) -> int
|
||||
{
|
||||
switch( why )
|
||||
{
|
||||
case GIT_CHECKOUT_NOTIFY_CONFLICT:
|
||||
wxLogTrace( traceGit, "Checkout conflict: %s", path ? path : "unknown" );
|
||||
break;
|
||||
case GIT_CHECKOUT_NOTIFY_DIRTY:
|
||||
wxLogTrace( traceGit, "Checkout dirty: %s", path ? path : "unknown" );
|
||||
break;
|
||||
case GIT_CHECKOUT_NOTIFY_UPDATED:
|
||||
wxLogTrace( traceGit, "Checkout updated: %s", path ? path : "unknown" );
|
||||
break;
|
||||
case GIT_CHECKOUT_NOTIFY_UNTRACKED:
|
||||
wxLogTrace( traceGit, "Checkout untracked: %s", path ? path : "unknown" );
|
||||
break;
|
||||
case GIT_CHECKOUT_NOTIFY_IGNORED:
|
||||
wxLogTrace( traceGit, "Checkout ignored: %s", path ? path : "unknown" );
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
checkoutOptions.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_ALLOW_CONFLICTS;
|
||||
checkoutOptions.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
|
||||
checkoutOptions.notify_cb = notify_cb;
|
||||
|
||||
if( git_checkout_tree( GetRepo(), reinterpret_cast<git_object*>( targetTree ), &checkoutOptions ) != GIT_OK )
|
||||
{
|
||||
AddErrorString( _( "Failed to perform checkout operation." ) );
|
||||
return PullResult::Error;
|
||||
}
|
||||
|
||||
git_reference* updatedRef = nullptr;
|
||||
|
||||
// Update the current branch to point to the new commit
|
||||
if (git_reference_set_target(&updatedRef, rawRef, &updatedRefOid, nullptr) != GIT_OK)
|
||||
{
|
||||
AddErrorString( wxString::Format( _( "Failed to update reference '%s' to point to '%s'" ), currentBranchName,
|
||||
git_oid_tostr_s( &updatedRefOid ) ) );
|
||||
return PullResult::Error;
|
||||
}
|
||||
|
||||
KIGIT::GitReferencePtr updatedRefPtr( updatedRef );
|
||||
|
||||
// Clean up the repository state
|
||||
if( git_repository_state_cleanup( GetRepo() ) != GIT_OK )
|
||||
{
|
||||
AddErrorString( _( "Failed to clean up repository state after fast-forward." ) );
|
||||
return PullResult::Error;
|
||||
}
|
||||
|
||||
git_revwalk* revWalker = nullptr;
|
||||
|
||||
// Collect commit details for updated references
|
||||
if( git_revwalk_new( &revWalker, GetRepo() ) != GIT_OK )
|
||||
{
|
||||
AddErrorString( _( "Failed to initialize revision walker." ) );
|
||||
return PullResult::Error;
|
||||
}
|
||||
|
||||
KIGIT::GitRevWalkPtr revWalkerPtr( revWalker );
|
||||
git_revwalk_sorting( revWalker, GIT_SORT_TIME );
|
||||
|
||||
if( git_revwalk_push_glob( revWalker, currentBranchName ) != GIT_OK )
|
||||
{
|
||||
AddErrorString( _( "Failed to push reference to revision walker." ) );
|
||||
return PullResult::Error;
|
||||
}
|
||||
|
||||
std::pair<std::string, std::vector<CommitDetails>>& branchCommits = m_fetchResults.emplace_back();
|
||||
branchCommits.first = currentBranchName;
|
||||
|
||||
git_oid commitOid;
|
||||
|
||||
while( git_revwalk_next( &commitOid, revWalker ) == GIT_OK )
|
||||
{
|
||||
git_commit* commit = nullptr;
|
||||
|
||||
if( git_commit_lookup( &commit, GetRepo(), &commitOid ) )
|
||||
{
|
||||
AddErrorString( wxString::Format( _( "Could not lookup commit '%s'" ),
|
||||
git_oid_tostr_s( &commitOid ) ) );
|
||||
return PullResult::Error;
|
||||
}
|
||||
|
||||
KIGIT::GitCommitPtr commitPtr( commit );
|
||||
|
||||
CommitDetails details;
|
||||
details.m_sha = git_oid_tostr_s( &commitOid );
|
||||
details.m_firstLine = getFirstLineFromCommitMessage( git_commit_message( commit ) );
|
||||
details.m_author = git_commit_author( commit )->name;
|
||||
details.m_date = getFormattedCommitDate( git_commit_author( commit )->when );
|
||||
|
||||
branchCommits.second.push_back( details );
|
||||
}
|
||||
|
||||
return PullResult::FastForward;
|
||||
}
|
||||
|
||||
|
||||
PullResult GIT_PULL_HANDLER::handleMerge( const git_annotated_commit** aMergeHeads,
|
||||
size_t aMergeHeadsCount )
|
||||
{
|
||||
git_merge_options merge_opts;
|
||||
git_merge_options_init( &merge_opts, GIT_MERGE_OPTIONS_VERSION );
|
||||
|
||||
git_checkout_options checkout_opts;
|
||||
git_checkout_init_options( &checkout_opts, GIT_CHECKOUT_OPTIONS_VERSION );
|
||||
|
||||
checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
|
||||
|
||||
if( git_merge( GetRepo(), aMergeHeads, aMergeHeadsCount, &merge_opts, &checkout_opts ) )
|
||||
{
|
||||
AddErrorString( _( "Could not merge commits" ) );
|
||||
return PullResult::Error;
|
||||
}
|
||||
|
||||
// Get the repository index
|
||||
git_index* index = nullptr;
|
||||
|
||||
if( git_repository_index( &index, GetRepo() ) )
|
||||
{
|
||||
AddErrorString( _( "Could not get repository index" ) );
|
||||
return PullResult::Error;
|
||||
}
|
||||
|
||||
KIGIT::GitIndexPtr indexPtr( index );
|
||||
|
||||
// Check for conflicts
|
||||
git_index_conflict_iterator* conflicts = nullptr;
|
||||
|
||||
if( git_index_conflict_iterator_new( &conflicts, index ) )
|
||||
{
|
||||
AddErrorString( _( "Could not get conflict iterator" ) );
|
||||
return PullResult::Error;
|
||||
}
|
||||
|
||||
KIGIT::GitIndexConflictIteratorPtr conflictsPtr( conflicts );
|
||||
|
||||
const git_index_entry* ancestor = nullptr;
|
||||
const git_index_entry* our = nullptr;
|
||||
const git_index_entry* their = nullptr;
|
||||
std::vector<ConflictData> conflict_data;
|
||||
|
||||
while( git_index_conflict_next( &ancestor, &our, &their, conflicts ) == 0 )
|
||||
{
|
||||
// Case 3: Both files have changed
|
||||
if( ancestor && our && their )
|
||||
{
|
||||
ConflictData conflict_datum;
|
||||
conflict_datum.filename = our->path;
|
||||
conflict_datum.our_oid = our->id;
|
||||
conflict_datum.their_oid = their->id;
|
||||
conflict_datum.our_commit_time = our->mtime.seconds;
|
||||
conflict_datum.their_commit_time = their->mtime.seconds;
|
||||
conflict_datum.our_status = _( "Changed" );
|
||||
conflict_datum.their_status = _( "Changed" );
|
||||
conflict_datum.use_ours = true;
|
||||
|
||||
conflict_data.push_back( conflict_datum );
|
||||
}
|
||||
// Case 4: File added in both ours and theirs
|
||||
else if( !ancestor && our && their )
|
||||
{
|
||||
ConflictData conflict_datum;
|
||||
conflict_datum.filename = our->path;
|
||||
conflict_datum.our_oid = our->id;
|
||||
conflict_datum.their_oid = their->id;
|
||||
conflict_datum.our_commit_time = our->mtime.seconds;
|
||||
conflict_datum.their_commit_time = their->mtime.seconds;
|
||||
conflict_datum.our_status = _( "Added" );
|
||||
conflict_datum.their_status = _( "Added" );
|
||||
conflict_datum.use_ours = true;
|
||||
|
||||
conflict_data.push_back( conflict_datum );
|
||||
}
|
||||
// Case 1: Remote file has changed or been added, local file has not
|
||||
else if( their && !our )
|
||||
{
|
||||
// Accept their changes
|
||||
git_index_add( index, their );
|
||||
}
|
||||
// Case 2: Local file has changed or been added, remote file has not
|
||||
else if( our && !their )
|
||||
{
|
||||
// Accept our changes
|
||||
git_index_add( index, our );
|
||||
}
|
||||
else
|
||||
{
|
||||
wxLogError( wxS( "Unexpected conflict state" ) );
|
||||
}
|
||||
}
|
||||
|
||||
if( conflict_data.empty() )
|
||||
{
|
||||
git_index_conflict_cleanup( index );
|
||||
git_index_write( index );
|
||||
}
|
||||
|
||||
return conflict_data.empty() ? PullResult::Success : PullResult::MergeFailed;
|
||||
}
|
||||
|
||||
|
||||
PullResult GIT_PULL_HANDLER::handleRebase( const git_annotated_commit** aMergeHeads, size_t aMergeHeadsCount )
|
||||
{
|
||||
// Get the current branch reference
|
||||
git_reference* head_ref = nullptr;
|
||||
|
||||
if( git_repository_head( &head_ref, GetRepo() ) )
|
||||
{
|
||||
wxString errorMsg = KIGIT_COMMON::GetLastGitError();
|
||||
wxLogTrace( traceGit, "GIT_PULL_HANDLER::handleRebase() - Failed to get HEAD: %s", errorMsg );
|
||||
return PullResult::Error;
|
||||
}
|
||||
|
||||
KIGIT::GitReferencePtr headRefPtr(head_ref);
|
||||
|
||||
// Initialize rebase operation
|
||||
git_rebase* rebase = nullptr;
|
||||
git_rebase_options rebase_opts = GIT_REBASE_OPTIONS_INIT;
|
||||
rebase_opts.checkout_options.checkout_strategy = GIT_CHECKOUT_SAFE;
|
||||
|
||||
if( git_rebase_init( &rebase, GetRepo(), nullptr, nullptr, aMergeHeads[0], &rebase_opts ) )
|
||||
{
|
||||
wxString errorMsg = KIGIT_COMMON::GetLastGitError();
|
||||
wxLogTrace( traceGit, "GIT_PULL_HANDLER::handleRebase() - Failed to initialize rebase: %s", errorMsg );
|
||||
return PullResult::Error;
|
||||
}
|
||||
|
||||
KIGIT::GitRebasePtr rebasePtr( rebase );
|
||||
git_rebase_operation* operation = nullptr;
|
||||
|
||||
while( git_rebase_next( &operation, rebase ) != GIT_ITEROVER )
|
||||
{
|
||||
// Check for conflicts
|
||||
git_index* index = nullptr;
|
||||
if( git_repository_index( &index, GetRepo() ) )
|
||||
{
|
||||
wxLogTrace( traceGit, "GIT_PULL_HANDLER::handleRebase() - Failed to get index: %s",
|
||||
KIGIT_COMMON::GetLastGitError() );
|
||||
return PullResult::Error;
|
||||
}
|
||||
KIGIT::GitIndexPtr indexPtr( index );
|
||||
|
||||
if( git_index_has_conflicts( index ) )
|
||||
{
|
||||
// Abort the rebase if there are conflicts because we need to merge manually
|
||||
git_rebase_abort( rebase );
|
||||
AddErrorString( _( "Conflicts detected during rebase" ) );
|
||||
return PullResult::MergeFailed;
|
||||
}
|
||||
|
||||
git_oid commit_id;
|
||||
git_signature* committer = nullptr;
|
||||
|
||||
if( git_signature_default( &committer, GetRepo() ) )
|
||||
{
|
||||
wxLogTrace( traceGit, "GIT_PULL_HANDLER::handleRebase() - Failed to create signature: %s",
|
||||
KIGIT_COMMON::GetLastGitError() );
|
||||
return PullResult::Error;
|
||||
}
|
||||
|
||||
KIGIT::GitSignaturePtr committerPtr( committer );
|
||||
|
||||
if( git_rebase_commit( &commit_id, rebase, nullptr, committer, nullptr, nullptr ) != GIT_OK )
|
||||
{
|
||||
wxString errorMsg = KIGIT_COMMON::GetLastGitError();
|
||||
wxLogTrace( traceGit, "GIT_PULL_HANDLER::handleRebase() - Failed to commit operation: %s", errorMsg );
|
||||
git_rebase_abort( rebase );
|
||||
return PullResult::Error;
|
||||
}
|
||||
}
|
||||
|
||||
// Finish the rebase
|
||||
if( git_rebase_finish( rebase, nullptr ) )
|
||||
{
|
||||
wxLogTrace( traceGit, "GIT_PULL_HANDLER::handleRebase() - Failed to finish rebase: %s",
|
||||
KIGIT_COMMON::GetLastGitError() );
|
||||
return PullResult::Error;
|
||||
}
|
||||
|
||||
wxLogTrace( traceGit, "GIT_PULL_HANDLER::handleRebase() - Rebase completed successfully" );
|
||||
git_repository_state_cleanup( GetRepo() );
|
||||
return PullResult::Success;
|
||||
}
|
||||
|
||||
|
||||
void GIT_PULL_HANDLER::UpdateProgress( int aCurrent, int aTotal, const wxString& aMessage )
|
||||
{
|
||||
|
||||
ReportProgress( aCurrent, aTotal, aMessage );
|
||||
}
|
||||
|
@ -29,7 +29,6 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <wx/string.h>
|
||||
#include <git2.h>
|
||||
|
||||
// Structure to store commit details
|
||||
struct CommitDetails
|
||||
@ -50,22 +49,12 @@ enum class PullResult : int
|
||||
FastForward
|
||||
};
|
||||
|
||||
struct ConflictData
|
||||
{
|
||||
std::string filename;
|
||||
std::string our_status;
|
||||
std::string their_status;
|
||||
git_oid our_oid;
|
||||
git_oid their_oid;
|
||||
git_time_t our_commit_time;
|
||||
git_time_t their_commit_time;
|
||||
bool use_ours; // Flag indicating user's choice (true = ours, false = theirs)
|
||||
};
|
||||
|
||||
class LIBGIT_BACKEND;
|
||||
|
||||
class GIT_PULL_HANDLER : public KIGIT_REPO_MIXIN
|
||||
{
|
||||
public:
|
||||
friend class LIBGIT_BACKEND;
|
||||
GIT_PULL_HANDLER( KIGIT_COMMON* aCommon );
|
||||
~GIT_PULL_HANDLER();
|
||||
|
||||
@ -77,15 +66,8 @@ public:
|
||||
// Implementation for progress updates
|
||||
void UpdateProgress( int aCurrent, int aTotal, const wxString& aMessage ) override;
|
||||
|
||||
|
||||
private:
|
||||
std::vector<std::pair<std::string, std::vector<CommitDetails>>> m_fetchResults;
|
||||
|
||||
std::string getFirstLineFromCommitMessage( const std::string& aMessage );
|
||||
std::string getFormattedCommitDate( const git_time& aTime );
|
||||
PullResult handleFastForward();
|
||||
PullResult handleMerge( const git_annotated_commit** aMergeHeads, size_t aMergeHeadsCount );
|
||||
PullResult handleRebase( const git_annotated_commit** aMergeHeads, size_t aMergeHeadsCount );
|
||||
};
|
||||
|
||||
#endif // _GIT_PULL_HANDLER_H_
|
||||
|
@ -22,13 +22,8 @@
|
||||
*/
|
||||
|
||||
#include "git_push_handler.h"
|
||||
#include <git/kicad_git_common.h>
|
||||
#include <git/kicad_git_memory.h>
|
||||
#include <trace_helpers.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <wx/log.h>
|
||||
#include "git_backend.h"
|
||||
|
||||
GIT_PUSH_HANDLER::GIT_PUSH_HANDLER( KIGIT_COMMON* aRepo ) : KIGIT_REPO_MIXIN( aRepo )
|
||||
{}
|
||||
@ -38,79 +33,7 @@ GIT_PUSH_HANDLER::~GIT_PUSH_HANDLER()
|
||||
|
||||
PushResult GIT_PUSH_HANDLER::PerformPush()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock( GetCommon()->m_gitActionMutex, std::try_to_lock );
|
||||
|
||||
if(!lock.owns_lock())
|
||||
{
|
||||
wxLogTrace(traceGit, "GIT_PUSH_HANDLER::PerformPush: Could not lock mutex");
|
||||
return PushResult::Error;
|
||||
}
|
||||
|
||||
PushResult result = PushResult::Success;
|
||||
|
||||
// Fetch updates from remote repository
|
||||
git_remote* remote = nullptr;
|
||||
|
||||
if(git_remote_lookup(&remote, GetRepo(), "origin") != 0)
|
||||
{
|
||||
AddErrorString(_("Could not lookup remote"));
|
||||
return PushResult::Error;
|
||||
}
|
||||
|
||||
KIGIT::GitRemotePtr remotePtr(remote);
|
||||
|
||||
git_remote_callbacks remoteCallbacks;
|
||||
git_remote_init_callbacks(&remoteCallbacks, GIT_REMOTE_CALLBACKS_VERSION);
|
||||
remoteCallbacks.sideband_progress = progress_cb;
|
||||
remoteCallbacks.transfer_progress = transfer_progress_cb;
|
||||
remoteCallbacks.update_tips = update_cb;
|
||||
remoteCallbacks.push_transfer_progress = push_transfer_progress_cb;
|
||||
remoteCallbacks.credentials = credentials_cb;
|
||||
remoteCallbacks.payload = this;
|
||||
GetCommon()->SetCancelled( false );
|
||||
|
||||
TestedTypes() = 0;
|
||||
ResetNextKey();
|
||||
|
||||
if( git_remote_connect( remote, GIT_DIRECTION_PUSH, &remoteCallbacks, nullptr, nullptr ) )
|
||||
{
|
||||
AddErrorString( wxString::Format( _( "Could not connect to remote: %s" ),
|
||||
KIGIT_COMMON::GetLastGitError() ) );
|
||||
return PushResult::Error;
|
||||
}
|
||||
|
||||
git_push_options pushOptions;
|
||||
git_push_init_options( &pushOptions, GIT_PUSH_OPTIONS_VERSION );
|
||||
pushOptions.callbacks = remoteCallbacks;
|
||||
|
||||
// Get the current HEAD reference
|
||||
git_reference* head = nullptr;
|
||||
|
||||
if( git_repository_head( &head, GetRepo() ) != 0 )
|
||||
{
|
||||
git_remote_disconnect( remote );
|
||||
AddErrorString( _( "Could not get repository head" ) );
|
||||
return PushResult::Error;
|
||||
}
|
||||
|
||||
KIGIT::GitReferencePtr headPtr( head );
|
||||
|
||||
// Create refspec for current branch
|
||||
const char* refs[1];
|
||||
refs[0] = git_reference_name( head );
|
||||
const git_strarray refspecs = { (char**) refs, 1 };
|
||||
|
||||
if( git_remote_push( remote, &refspecs, &pushOptions ) )
|
||||
{
|
||||
AddErrorString( wxString::Format( _( "Could not push to remote: %s" ),
|
||||
KIGIT_COMMON::GetLastGitError() ) );
|
||||
git_remote_disconnect( remote );
|
||||
return PushResult::Error;
|
||||
}
|
||||
|
||||
git_remote_disconnect( remote );
|
||||
|
||||
return result;
|
||||
return GetGitBackend()->Push( this );
|
||||
}
|
||||
|
||||
|
||||
|
@ -21,15 +21,14 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include <wx/string.h>
|
||||
#include "git_remove_from_index_handler.h"
|
||||
#include "git_backend.h"
|
||||
|
||||
#include <wx/log.h>
|
||||
|
||||
#include <git/kicad_git_memory.h>
|
||||
#include "git_remove_from_index_handler.h"
|
||||
|
||||
GIT_REMOVE_FROM_INDEX_HANDLER::GIT_REMOVE_FROM_INDEX_HANDLER( git_repository* aRepository )
|
||||
GIT_REMOVE_FROM_INDEX_HANDLER::GIT_REMOVE_FROM_INDEX_HANDLER( git_repository* aRepository ) :
|
||||
KIGIT_COMMON( aRepository )
|
||||
{
|
||||
m_repository = aRepository;
|
||||
m_filesToRemove.clear();
|
||||
}
|
||||
|
||||
@ -41,61 +40,11 @@ GIT_REMOVE_FROM_INDEX_HANDLER::~GIT_REMOVE_FROM_INDEX_HANDLER()
|
||||
|
||||
bool GIT_REMOVE_FROM_INDEX_HANDLER::RemoveFromIndex( const wxString& aFilePath )
|
||||
{
|
||||
// Test if file is currently in the index
|
||||
|
||||
git_index* index = nullptr;
|
||||
size_t at_pos = 0;
|
||||
|
||||
if( git_repository_index( &index, m_repository ) != 0 )
|
||||
{
|
||||
wxLogError( "Failed to get repository index" );
|
||||
return false;
|
||||
}
|
||||
|
||||
KIGIT::GitIndexPtr indexPtr( index );
|
||||
|
||||
if( git_index_find( &at_pos, index, aFilePath.ToUTF8().data() ) != 0 )
|
||||
{
|
||||
wxLogError( "Failed to find index entry for %s", aFilePath );
|
||||
return false;
|
||||
}
|
||||
|
||||
m_filesToRemove.push_back( aFilePath );
|
||||
return true;
|
||||
return GetGitBackend()->RemoveFromIndex( this, aFilePath );
|
||||
}
|
||||
|
||||
|
||||
void GIT_REMOVE_FROM_INDEX_HANDLER::PerformRemoveFromIndex()
|
||||
{
|
||||
for( auto& file : m_filesToRemove )
|
||||
{
|
||||
git_index* index = nullptr;
|
||||
git_oid oid;
|
||||
|
||||
if( git_repository_index( &index, m_repository ) != 0 )
|
||||
{
|
||||
wxLogError( "Failed to get repository index" );
|
||||
return;
|
||||
}
|
||||
|
||||
KIGIT::GitIndexPtr indexPtr( index );
|
||||
|
||||
if( git_index_remove_bypath( index, file.ToUTF8().data() ) != 0 )
|
||||
{
|
||||
wxLogError( "Failed to remove index entry for %s", file );
|
||||
return;
|
||||
}
|
||||
|
||||
if( git_index_write( index ) != 0 )
|
||||
{
|
||||
wxLogError( "Failed to write index" );
|
||||
return;
|
||||
}
|
||||
|
||||
if( git_index_write_tree( &oid, index ) != 0 )
|
||||
{
|
||||
wxLogError( "Failed to write index tree" );
|
||||
return;
|
||||
}
|
||||
}
|
||||
GetGitBackend()->PerformRemoveFromIndex( this );
|
||||
}
|
||||
|
@ -24,12 +24,13 @@
|
||||
#ifndef GIT_REMOVE_FROM_INDEX_HANDLER_H_
|
||||
#define GIT_REMOVE_FROM_INDEX_HANDLER_H_
|
||||
|
||||
#include <git2.h>
|
||||
#include <git/kicad_git_common.h>
|
||||
#include <vector>
|
||||
#include <wx/string.h>
|
||||
|
||||
class wxString;
|
||||
class LIBGIT_BACKEND;
|
||||
|
||||
class GIT_REMOVE_FROM_INDEX_HANDLER
|
||||
class GIT_REMOVE_FROM_INDEX_HANDLER : public KIGIT_COMMON
|
||||
{
|
||||
public:
|
||||
GIT_REMOVE_FROM_INDEX_HANDLER( git_repository* aRepository );
|
||||
@ -41,7 +42,7 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
git_repository* m_repository;
|
||||
friend class LIBGIT_BACKEND;
|
||||
|
||||
std::vector<wxString> m_filesToRemove;
|
||||
};
|
||||
|
@ -22,11 +22,7 @@
|
||||
*/
|
||||
|
||||
#include "git_revert_handler.h"
|
||||
|
||||
#include <wx/log.h>
|
||||
#include <wx/string.h>
|
||||
|
||||
#include <trace_helpers.h>
|
||||
#include "git_backend.h"
|
||||
|
||||
|
||||
GIT_REVERT_HANDLER::GIT_REVERT_HANDLER( git_repository* aRepository )
|
||||
@ -46,74 +42,8 @@ bool GIT_REVERT_HANDLER::Revert( const wxString& aFilePath )
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void checkout_progress_cb( const char *path, size_t cur, size_t tot, void *payload )
|
||||
{
|
||||
wxLogTrace( traceGit, wxS( "checkout_progress_cb: %s %zu/%zu" ), path, cur, tot );
|
||||
}
|
||||
|
||||
|
||||
static int checkout_notify_cb( git_checkout_notify_t why, const char *path,
|
||||
const git_diff_file *baseline,
|
||||
const git_diff_file *target,
|
||||
const git_diff_file *workdir, void *payload )
|
||||
{
|
||||
GIT_REVERT_HANDLER* handler = static_cast<GIT_REVERT_HANDLER*>(payload);
|
||||
|
||||
if( why & ( GIT_CHECKOUT_NOTIFY_CONFLICT | GIT_CHECKOUT_NOTIFY_IGNORED
|
||||
| GIT_CHECKOUT_NOTIFY_UPDATED ) )
|
||||
handler->PushFailedFile( path );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void GIT_REVERT_HANDLER::PerformRevert()
|
||||
{
|
||||
git_object* head_commit = NULL;
|
||||
git_checkout_options opts;
|
||||
git_checkout_init_options( &opts, GIT_CHECKOUT_OPTIONS_VERSION );
|
||||
|
||||
// Get the HEAD commit
|
||||
if( git_revparse_single( &head_commit, m_repository, "HEAD" ) != 0 )
|
||||
{
|
||||
// Handle error. If we cannot get the HEAD, then there's no point proceeding.
|
||||
return;
|
||||
}
|
||||
|
||||
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
|
||||
char** paths = new char*[m_filesToRevert.size()];
|
||||
|
||||
for( size_t ii = 0; ii < m_filesToRevert.size(); ii++ )
|
||||
{
|
||||
// Set paths to the specific file
|
||||
paths[ii] = wxStrdup( m_filesToRevert[ii].ToUTF8() );
|
||||
}
|
||||
|
||||
git_strarray arr = { paths, m_filesToRevert.size() };
|
||||
|
||||
opts.paths = arr;
|
||||
opts.progress_cb = checkout_progress_cb;
|
||||
opts.notify_cb = checkout_notify_cb;
|
||||
opts.notify_payload = static_cast<void*>( this );
|
||||
|
||||
// Attempt to checkout the file(s)
|
||||
if( git_checkout_tree(m_repository, head_commit, &opts ) != 0 )
|
||||
{
|
||||
const git_error *e = git_error_last();
|
||||
|
||||
if( e )
|
||||
{
|
||||
wxLogTrace( traceGit, wxS( "Checkout failed: %d: %s" ), e->klass, e->message );
|
||||
}
|
||||
}
|
||||
|
||||
// Free the HEAD commit
|
||||
for( size_t ii = 0; ii < m_filesToRevert.size(); ii++ )
|
||||
delete( paths[ii] );
|
||||
|
||||
delete[] paths;
|
||||
|
||||
git_object_free( head_commit );
|
||||
GetGitBackend()->PerformRevert( this );
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,10 @@
|
||||
#include <git2.h>
|
||||
#include <vector>
|
||||
#include <wx/string.h>
|
||||
// TEMPORARY HACKFIX INCLUDE FOR STD::VECTOR EXPORT OUT OF KICOMMON ON WINDOWS
|
||||
#include <settings/parameters.h>
|
||||
|
||||
class LIBGIT_BACKEND;
|
||||
|
||||
class GIT_REVERT_HANDLER
|
||||
{
|
||||
@ -44,6 +48,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
friend class LIBGIT_BACKEND;
|
||||
git_repository* m_repository;
|
||||
|
||||
std::vector<wxString> m_filesToRevert;
|
||||
|
@ -22,10 +22,8 @@
|
||||
*/
|
||||
|
||||
#include "git_status_handler.h"
|
||||
#include <git/kicad_git_common.h>
|
||||
#include <git/kicad_git_memory.h>
|
||||
#include <trace_helpers.h>
|
||||
#include <wx/log.h>
|
||||
#include "git_backend.h"
|
||||
|
||||
GIT_STATUS_HANDLER::GIT_STATUS_HANDLER( KIGIT_COMMON* aCommon ) : KIGIT_REPO_MIXIN( aCommon )
|
||||
{}
|
||||
@ -35,171 +33,29 @@ GIT_STATUS_HANDLER::~GIT_STATUS_HANDLER()
|
||||
|
||||
bool GIT_STATUS_HANDLER::HasChangedFiles()
|
||||
{
|
||||
git_repository* repo = GetRepo();
|
||||
|
||||
if( !repo )
|
||||
return false;
|
||||
|
||||
git_status_options opts;
|
||||
git_status_init_options( &opts, GIT_STATUS_OPTIONS_VERSION );
|
||||
|
||||
opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
|
||||
opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX
|
||||
| GIT_STATUS_OPT_SORT_CASE_SENSITIVELY;
|
||||
|
||||
git_status_list* status_list = nullptr;
|
||||
|
||||
if( git_status_list_new( &status_list, repo, &opts ) != GIT_OK )
|
||||
{
|
||||
wxLogTrace( traceGit, "Failed to get status list: %s", KIGIT_COMMON::GetLastGitError() );
|
||||
return false;
|
||||
}
|
||||
|
||||
KIGIT::GitStatusListPtr status_list_ptr( status_list );
|
||||
bool hasChanges = ( git_status_list_entrycount( status_list ) > 0 );
|
||||
|
||||
return hasChanges;
|
||||
return GetGitBackend()->HasChangedFiles( this );
|
||||
}
|
||||
|
||||
std::map<wxString, FileStatus> GIT_STATUS_HANDLER::GetFileStatus( const wxString& aPathspec )
|
||||
{
|
||||
std::map<wxString, FileStatus> fileStatusMap;
|
||||
git_repository* repo = GetRepo();
|
||||
|
||||
if( !repo )
|
||||
return fileStatusMap;
|
||||
|
||||
git_status_options status_options;
|
||||
git_status_init_options( &status_options, GIT_STATUS_OPTIONS_VERSION );
|
||||
status_options.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
|
||||
status_options.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_INCLUDE_UNMODIFIED;
|
||||
|
||||
// Set up pathspec if provided
|
||||
std::string pathspec_str;
|
||||
std::vector<const char*> pathspec_ptrs;
|
||||
|
||||
if( !aPathspec.IsEmpty() )
|
||||
{
|
||||
pathspec_str = aPathspec.ToStdString();
|
||||
pathspec_ptrs.push_back( pathspec_str.c_str() );
|
||||
|
||||
status_options.pathspec.strings = const_cast<char**>( pathspec_ptrs.data() );
|
||||
status_options.pathspec.count = pathspec_ptrs.size();
|
||||
}
|
||||
|
||||
git_status_list* status_list = nullptr;
|
||||
|
||||
if( git_status_list_new( &status_list, repo, &status_options ) != GIT_OK )
|
||||
{
|
||||
wxLogTrace( traceGit, "Failed to get git status list: %s", KIGIT_COMMON::GetLastGitError() );
|
||||
return fileStatusMap;
|
||||
}
|
||||
|
||||
KIGIT::GitStatusListPtr statusListPtr( status_list );
|
||||
|
||||
size_t count = git_status_list_entrycount( status_list );
|
||||
wxString repoWorkDir( git_repository_workdir( repo ) );
|
||||
|
||||
for( size_t ii = 0; ii < count; ++ii )
|
||||
{
|
||||
const git_status_entry* entry = git_status_byindex( status_list, ii );
|
||||
std::string path( entry->head_to_index ? entry->head_to_index->old_file.path
|
||||
: entry->index_to_workdir->old_file.path );
|
||||
|
||||
wxString absPath = repoWorkDir + path;
|
||||
|
||||
FileStatus fileStatus;
|
||||
fileStatus.filePath = absPath;
|
||||
fileStatus.gitStatus = entry->status;
|
||||
fileStatus.status = ConvertStatus( entry->status );
|
||||
|
||||
fileStatusMap[absPath] = fileStatus;
|
||||
}
|
||||
|
||||
return fileStatusMap;
|
||||
return GetGitBackend()->GetFileStatus( this, aPathspec );
|
||||
}
|
||||
|
||||
wxString GIT_STATUS_HANDLER::GetCurrentBranchName()
|
||||
{
|
||||
git_repository* repo = GetRepo();
|
||||
|
||||
if( !repo )
|
||||
return wxEmptyString;
|
||||
|
||||
git_reference* currentBranchReference = nullptr;
|
||||
int rc = git_repository_head( ¤tBranchReference, repo );
|
||||
KIGIT::GitReferencePtr currentBranchReferencePtr( currentBranchReference );
|
||||
|
||||
if( currentBranchReference )
|
||||
{
|
||||
return git_reference_shorthand( currentBranchReference );
|
||||
}
|
||||
else if( rc == GIT_EUNBORNBRANCH )
|
||||
{
|
||||
// Unborn branch - could return empty or a default name
|
||||
return wxEmptyString;
|
||||
}
|
||||
else
|
||||
{
|
||||
wxLogTrace( traceGit, "Failed to lookup current branch: %s", KIGIT_COMMON::GetLastGitError() );
|
||||
return wxEmptyString;
|
||||
}
|
||||
return GetGitBackend()->GetCurrentBranchName( this );
|
||||
}
|
||||
|
||||
void GIT_STATUS_HANDLER::UpdateRemoteStatus( const std::set<wxString>& aLocalChanges,
|
||||
const std::set<wxString>& aRemoteChanges,
|
||||
std::map<wxString, FileStatus>& aFileStatus )
|
||||
const std::set<wxString>& aRemoteChanges,
|
||||
std::map<wxString, FileStatus>& aFileStatus )
|
||||
{
|
||||
git_repository* repo = GetRepo();
|
||||
|
||||
if( !repo )
|
||||
return;
|
||||
|
||||
wxString repoWorkDir( git_repository_workdir( repo ) );
|
||||
|
||||
// Update status based on local/remote changes
|
||||
for( auto& [absPath, fileStatus] : aFileStatus )
|
||||
{
|
||||
// Convert absolute path to relative path for comparison
|
||||
wxString relativePath = absPath;
|
||||
if( relativePath.StartsWith( repoWorkDir ) )
|
||||
{
|
||||
relativePath = relativePath.Mid( repoWorkDir.length() );
|
||||
#ifdef _WIN32
|
||||
relativePath.Replace( wxS( "\\" ), wxS( "/" ) );
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string relativePathStd = relativePath.ToStdString();
|
||||
|
||||
// Only update if the file is not already modified/added/deleted
|
||||
if( fileStatus.status == KIGIT_COMMON::GIT_STATUS::GIT_STATUS_CURRENT )
|
||||
{
|
||||
if( aLocalChanges.count( relativePathStd ) )
|
||||
{
|
||||
fileStatus.status = KIGIT_COMMON::GIT_STATUS::GIT_STATUS_AHEAD;
|
||||
}
|
||||
else if( aRemoteChanges.count( relativePathStd ) )
|
||||
{
|
||||
fileStatus.status = KIGIT_COMMON::GIT_STATUS::GIT_STATUS_BEHIND;
|
||||
}
|
||||
}
|
||||
}
|
||||
GetGitBackend()->UpdateRemoteStatus( this, aLocalChanges, aRemoteChanges, aFileStatus );
|
||||
}
|
||||
|
||||
wxString GIT_STATUS_HANDLER::GetWorkingDirectory()
|
||||
{
|
||||
git_repository* repo = GetRepo();
|
||||
|
||||
if( !repo )
|
||||
return wxEmptyString;
|
||||
|
||||
const char* workdir = git_repository_workdir( repo );
|
||||
|
||||
if( !workdir )
|
||||
return wxEmptyString;
|
||||
|
||||
return wxString( workdir );
|
||||
return GetGitBackend()->GetWorkingDirectory( this );
|
||||
}
|
||||
|
||||
KIGIT_COMMON::GIT_STATUS GIT_STATUS_HANDLER::ConvertStatus( unsigned int aGitStatus )
|
||||
|
@ -29,6 +29,8 @@
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
class LIBGIT_BACKEND;
|
||||
|
||||
struct FileStatus
|
||||
{
|
||||
wxString filePath;
|
||||
@ -80,6 +82,7 @@ public:
|
||||
void UpdateProgress( int aCurrent, int aTotal, const wxString& aMessage ) override;
|
||||
|
||||
private:
|
||||
friend class LIBGIT_BACKEND;
|
||||
/**
|
||||
* Convert git status flags to KIGIT_COMMON::GIT_STATUS
|
||||
* @param aGitStatus Raw git status flags
|
||||
|
@ -33,6 +33,8 @@
|
||||
|
||||
#include <wx/string.h>
|
||||
|
||||
class LIBGIT_BACKEND;
|
||||
|
||||
class KIGIT_COMMON
|
||||
{
|
||||
|
||||
@ -174,6 +176,7 @@ protected:
|
||||
friend class GIT_PUSH_HANDLER;
|
||||
friend class GIT_PULL_HANDLER;
|
||||
friend class GIT_CLONE_HANDLER;
|
||||
friend class LIBGIT_BACKEND;
|
||||
friend class PROJECT_TREE_PANE;
|
||||
|
||||
private:
|
||||
|
1358
common/git/libgit_backend.cpp
Normal file
1358
common/git/libgit_backend.cpp
Normal file
File diff suppressed because it is too large
Load Diff
100
common/git/libgit_backend.h
Normal file
100
common/git/libgit_backend.h
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/gpl-3.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 3 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#ifndef LIBGIT_BACKEND_H_
|
||||
#define LIBGIT_BACKEND_H_
|
||||
|
||||
#include "git_backend.h"
|
||||
|
||||
// Forward declarations to avoid exposing libgit2 headers
|
||||
struct git_annotated_commit;
|
||||
|
||||
class LIBGIT_BACKEND : public GIT_BACKEND
|
||||
{
|
||||
public:
|
||||
void Init() override;
|
||||
void Shutdown() override;
|
||||
bool IsLibraryAvailable() override;
|
||||
|
||||
bool Clone( GIT_CLONE_HANDLER* aHandler ) override;
|
||||
|
||||
CommitResult Commit( GIT_COMMIT_HANDLER* aHandler,
|
||||
const std::vector<wxString>& aFiles,
|
||||
const wxString& aMessage,
|
||||
const wxString& aAuthorName,
|
||||
const wxString& aAuthorEmail ) override;
|
||||
|
||||
PushResult Push( GIT_PUSH_HANDLER* aHandler ) override;
|
||||
|
||||
bool HasChangedFiles( GIT_STATUS_HANDLER* aHandler ) override;
|
||||
|
||||
std::map<wxString, FileStatus> GetFileStatus( GIT_STATUS_HANDLER* aHandler,
|
||||
const wxString& aPathspec ) override;
|
||||
|
||||
wxString GetCurrentBranchName( GIT_STATUS_HANDLER* aHandler ) override;
|
||||
|
||||
void UpdateRemoteStatus( GIT_STATUS_HANDLER* aHandler,
|
||||
const std::set<wxString>& aLocalChanges,
|
||||
const std::set<wxString>& aRemoteChanges,
|
||||
std::map<wxString, FileStatus>& aFileStatus ) override;
|
||||
|
||||
wxString GetWorkingDirectory( GIT_STATUS_HANDLER* aHandler ) override;
|
||||
|
||||
wxString GetWorkingDirectory( GIT_CONFIG_HANDLER* aHandler ) override;
|
||||
bool GetConfigString( GIT_CONFIG_HANDLER* aHandler, const wxString& aKey,
|
||||
wxString& aValue ) override;
|
||||
|
||||
bool IsRepository( GIT_INIT_HANDLER* aHandler, const wxString& aPath ) override;
|
||||
InitResult InitializeRepository( GIT_INIT_HANDLER* aHandler, const wxString& aPath ) override;
|
||||
bool SetupRemote( GIT_INIT_HANDLER* aHandler, const RemoteConfig& aConfig ) override;
|
||||
|
||||
BranchResult SwitchToBranch( GIT_BRANCH_HANDLER* aHandler, const wxString& aBranchName ) override;
|
||||
bool BranchExists( GIT_BRANCH_HANDLER* aHandler, const wxString& aBranchName ) override;
|
||||
|
||||
bool PerformFetch( GIT_PULL_HANDLER* aHandler, bool aSkipLock ) override;
|
||||
PullResult PerformPull( GIT_PULL_HANDLER* aHandler ) override;
|
||||
|
||||
void PerformRevert( GIT_REVERT_HANDLER* aHandler ) override;
|
||||
|
||||
git_repository* GetRepositoryForFile( const char* aFilename ) override;
|
||||
int CreateBranch( git_repository* aRepo, const wxString& aBranchName ) override;
|
||||
bool RemoveVCS( git_repository*& aRepo, const wxString& aProjectPath,
|
||||
bool aRemoveGitDir, wxString* aErrors ) override;
|
||||
|
||||
bool AddToIndex( GIT_ADD_TO_INDEX_HANDLER* aHandler, const wxString& aFilePath ) override;
|
||||
|
||||
bool PerformAddToIndex( GIT_ADD_TO_INDEX_HANDLER* aHandler ) override;
|
||||
|
||||
bool RemoveFromIndex( GIT_REMOVE_FROM_INDEX_HANDLER* aHandler, const wxString& aFilePath ) override;
|
||||
|
||||
void PerformRemoveFromIndex( GIT_REMOVE_FROM_INDEX_HANDLER* aHandler ) override;
|
||||
|
||||
private:
|
||||
PullResult handleFastForward( GIT_PULL_HANDLER* aHandler );
|
||||
PullResult handleMerge( GIT_PULL_HANDLER* aHandler, const git_annotated_commit** aMergeHeads,
|
||||
size_t aMergeHeadsCount );
|
||||
PullResult handleRebase( GIT_PULL_HANDLER* aHandler, const git_annotated_commit** aMergeHeads,
|
||||
size_t aMergeHeadsCount );
|
||||
};
|
||||
|
||||
#endif
|
@ -22,105 +22,25 @@
|
||||
*/
|
||||
|
||||
#include "project_git_utils.h"
|
||||
#include "kicad_git_common.h"
|
||||
#include "kicad_git_memory.h"
|
||||
#include <git/kicad_git_compat.h>
|
||||
#include <trace_helpers.h>
|
||||
#include <wx/log.h>
|
||||
#include <wx/filename.h>
|
||||
#include <gestfich.h>
|
||||
#include "git_backend.h"
|
||||
|
||||
namespace KIGIT
|
||||
{
|
||||
|
||||
git_repository* PROJECT_GIT_UTILS::GetRepositoryForFile( const char* aFilename )
|
||||
{
|
||||
git_repository* repo = nullptr;
|
||||
git_buf repo_path = GIT_BUF_INIT;
|
||||
|
||||
if( git_repository_discover( &repo_path, aFilename, 0, nullptr ) != GIT_OK )
|
||||
{
|
||||
wxLogTrace( traceGit, "Can't repo discover %s: %s", aFilename,
|
||||
KIGIT_COMMON::GetLastGitError() );
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
KIGIT::GitBufPtr repo_path_ptr( &repo_path );
|
||||
|
||||
if( git_repository_open( &repo, repo_path.ptr ) != GIT_OK )
|
||||
{
|
||||
wxLogTrace( traceGit, "Can't open repo for %s: %s", repo_path.ptr,
|
||||
KIGIT_COMMON::GetLastGitError() );
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return repo;
|
||||
return GetGitBackend()->GetRepositoryForFile( aFilename );
|
||||
}
|
||||
|
||||
int PROJECT_GIT_UTILS::CreateBranch( git_repository* aRepo, const wxString& aBranchName )
|
||||
{
|
||||
git_oid head_oid;
|
||||
|
||||
if( int error = git_reference_name_to_id( &head_oid, aRepo, "HEAD" ); error != GIT_OK )
|
||||
{
|
||||
wxLogTrace( traceGit, "Failed to lookup HEAD reference: %s",
|
||||
KIGIT_COMMON::GetLastGitError() );
|
||||
return error;
|
||||
}
|
||||
|
||||
git_commit* commit = nullptr;
|
||||
|
||||
if( int error = git_commit_lookup( &commit, aRepo, &head_oid ); error != GIT_OK )
|
||||
{
|
||||
wxLogTrace( traceGit, "Failed to lookup commit: %s",
|
||||
KIGIT_COMMON::GetLastGitError() );
|
||||
return error;
|
||||
}
|
||||
|
||||
KIGIT::GitCommitPtr commitPtr( commit );
|
||||
git_reference* branchRef = nullptr;
|
||||
|
||||
if( int error = git_branch_create( &branchRef, aRepo, aBranchName.mb_str(), commit, 0 ); error != GIT_OK )
|
||||
{
|
||||
wxLogTrace( traceGit, "Failed to create branch: %s",
|
||||
KIGIT_COMMON::GetLastGitError() );
|
||||
return error;
|
||||
}
|
||||
|
||||
git_reference_free( branchRef );
|
||||
return 0;
|
||||
return GetGitBackend()->CreateBranch( aRepo, aBranchName );
|
||||
}
|
||||
|
||||
bool PROJECT_GIT_UTILS::RemoveVCS( git_repository*& aRepo, const wxString& aProjectPath,
|
||||
bool aRemoveGitDir, wxString* aErrors )
|
||||
{
|
||||
if( aRepo )
|
||||
{
|
||||
git_repository_free( aRepo );
|
||||
aRepo = nullptr;
|
||||
}
|
||||
|
||||
if( aRemoveGitDir )
|
||||
{
|
||||
wxFileName gitDir( aProjectPath, wxEmptyString );
|
||||
gitDir.AppendDir( ".git" );
|
||||
|
||||
if( gitDir.DirExists() )
|
||||
{
|
||||
wxString errors;
|
||||
if( !RmDirRecursive( gitDir.GetPath(), &errors ) )
|
||||
{
|
||||
if( aErrors )
|
||||
*aErrors = errors;
|
||||
|
||||
wxLogTrace( traceGit, "Failed to remove .git directory: %s", errors );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wxLogTrace( traceGit, "Successfully removed VCS from project" );
|
||||
return true;
|
||||
return GetGitBackend()->RemoveVCS( aRepo, aProjectPath, aRemoveGitDir, aErrors );
|
||||
}
|
||||
|
||||
} // namespace KIGIT
|
||||
} // namespace KIGIT
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include <wx/txtstrm.h>
|
||||
#include <wx/wfstream.h>
|
||||
#include <tool/tool_action.h>
|
||||
#include <tool/tool_event.h>
|
||||
|
||||
|
||||
/*
|
||||
@ -159,6 +160,10 @@ static struct hotkey_name_descr hotkeyNameList[] =
|
||||
#define MODIFIER_CMD_MAC wxT( "Cmd+" )
|
||||
#define MODIFIER_CTRL_BASE wxT( "Ctrl+" )
|
||||
#define MODIFIER_SHIFT wxT( "Shift+" )
|
||||
#define MODIFIER_META wxT( "Meta+" )
|
||||
#define MODIFIER_WIN wxT( "Win+" )
|
||||
#define MODIFIER_SUPER wxT( "Super+" )
|
||||
#define MODIFIER_ALTGR wxT( "AltGr+" )
|
||||
|
||||
|
||||
wxString KeyNameFromKeyCode( int aKeycode, bool* aIsFound )
|
||||
@ -175,6 +180,14 @@ wxString KeyNameFromKeyCode( int aKeycode, bool* aIsFound )
|
||||
return wxString( MODIFIER_SHIFT ).BeforeFirst( '+' );
|
||||
else if( aKeycode == WXK_ALT )
|
||||
return wxString( MODIFIER_ALT ).BeforeFirst( '+' );
|
||||
#ifdef WXK_WINDOWS_LEFT
|
||||
else if( aKeycode == WXK_WINDOWS_LEFT || aKeycode == WXK_WINDOWS_RIGHT )
|
||||
return wxString( MODIFIER_WIN ).BeforeFirst( '+' );
|
||||
#endif
|
||||
#ifdef WXK_META
|
||||
else if( aKeycode == WXK_META )
|
||||
return wxString( MODIFIER_META ).BeforeFirst( '+' );
|
||||
#endif
|
||||
|
||||
// Assume keycode of 0 is "unassigned"
|
||||
if( (aKeycode & MD_CTRL) != 0 )
|
||||
@ -186,7 +199,16 @@ wxString KeyNameFromKeyCode( int aKeycode, bool* aIsFound )
|
||||
if( (aKeycode & MD_SHIFT) != 0 )
|
||||
modifier << MODIFIER_SHIFT;
|
||||
|
||||
aKeycode &= ~( MD_CTRL | MD_ALT | MD_SHIFT );
|
||||
if( (aKeycode & MD_META) != 0 )
|
||||
modifier << MODIFIER_META;
|
||||
|
||||
if( (aKeycode & MD_SUPER) != 0 )
|
||||
modifier << MODIFIER_WIN;
|
||||
|
||||
if( (aKeycode & MD_ALTGR) != 0 )
|
||||
modifier << MODIFIER_ALTGR;
|
||||
|
||||
aKeycode &= ~MD_MODIFIER_MASK;
|
||||
|
||||
if( (aKeycode > ' ') && (aKeycode < 0x7F ) )
|
||||
{
|
||||
@ -262,7 +284,7 @@ int KeyCodeFromKeyName( const wxString& keyname )
|
||||
{
|
||||
int ii, keycode = KEY_NON_FOUND;
|
||||
|
||||
// Search for modifiers: Ctrl+ Alt+ and Shift+
|
||||
// Search for modifiers: Ctrl+ Alt+ Shift+ and others
|
||||
// Note: on Mac OSX, the Cmd key is equiv here to Ctrl
|
||||
wxString key = keyname;
|
||||
wxString prefix;
|
||||
@ -292,6 +314,26 @@ int KeyCodeFromKeyName( const wxString& keyname )
|
||||
modifier |= MD_SHIFT;
|
||||
prefix = MODIFIER_SHIFT;
|
||||
}
|
||||
else if( key.StartsWith( MODIFIER_META ) )
|
||||
{
|
||||
modifier |= MD_META;
|
||||
prefix = MODIFIER_META;
|
||||
}
|
||||
else if( key.StartsWith( MODIFIER_WIN ) )
|
||||
{
|
||||
modifier |= MD_SUPER;
|
||||
prefix = MODIFIER_WIN;
|
||||
}
|
||||
else if( key.StartsWith( MODIFIER_SUPER ) )
|
||||
{
|
||||
modifier |= MD_SUPER;
|
||||
prefix = MODIFIER_SUPER;
|
||||
}
|
||||
else if( key.StartsWith( MODIFIER_ALTGR ) )
|
||||
{
|
||||
modifier |= MD_ALTGR;
|
||||
prefix = MODIFIER_ALTGR;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
|
@ -40,8 +40,7 @@ JOB_EXPORT_SCH_BOM::JOB_EXPORT_SCH_BOM() :
|
||||
m_sortField(),
|
||||
m_sortAsc( true ),
|
||||
m_filterString(),
|
||||
m_excludeDNP( false ),
|
||||
m_includeExcludedFromBOM( false )
|
||||
m_excludeDNP( false )
|
||||
{
|
||||
m_params.emplace_back( new JOB_PARAM<wxString>( "field_delimiter",
|
||||
&m_fieldDelimiter,
|
||||
@ -70,13 +69,8 @@ JOB_EXPORT_SCH_BOM::JOB_EXPORT_SCH_BOM() :
|
||||
m_fieldsGroupBy ) );
|
||||
m_params.emplace_back( new JOB_PARAM<wxString>( "sort_field", &m_sortField, m_sortField ) );
|
||||
m_params.emplace_back( new JOB_PARAM<bool>( "sort_asc", &m_sortAsc, m_sortAsc ) );
|
||||
m_params.emplace_back( new JOB_PARAM<wxString>( "filter_string",
|
||||
&m_filterString,
|
||||
m_filterString ) );
|
||||
m_params.emplace_back( new JOB_PARAM<wxString>( "filter_string", &m_filterString, m_filterString ) );
|
||||
m_params.emplace_back( new JOB_PARAM<bool>( "exclude_dnp", &m_excludeDNP, m_excludeDNP ) );
|
||||
m_params.emplace_back( new JOB_PARAM<bool>( "include_excluded_from_bom",
|
||||
&m_includeExcludedFromBOM,
|
||||
m_includeExcludedFromBOM ) );
|
||||
|
||||
m_params.emplace_back( new JOB_PARAM<wxString>( "bom_preset_name",
|
||||
&m_bomPresetName,
|
||||
|
@ -55,7 +55,6 @@ public:
|
||||
bool m_sortAsc;
|
||||
wxString m_filterString;
|
||||
bool m_excludeDNP;
|
||||
bool m_includeExcludedFromBOM;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -24,6 +24,16 @@
|
||||
#include <properties/pg_properties.h>
|
||||
#include <widgets/color_swatch.h>
|
||||
#include <widgets/unit_binder.h>
|
||||
#include <bitmaps.h>
|
||||
#include <frame_type.h>
|
||||
#include <kiway_player.h>
|
||||
#include <kiway.h>
|
||||
#include <wx/filedlg.h>
|
||||
#include <wx/intl.h>
|
||||
#include <eda_doc.h>
|
||||
|
||||
#include <wx/button.h>
|
||||
#include <wx/bmpbuttn.h>
|
||||
|
||||
#include <wx/log.h>
|
||||
|
||||
@ -31,6 +41,8 @@ const wxString PG_UNIT_EDITOR::EDITOR_NAME = wxS( "KiCadUnitEditor" );
|
||||
const wxString PG_CHECKBOX_EDITOR::EDITOR_NAME = wxS( "KiCadCheckboxEditor" );
|
||||
const wxString PG_COLOR_EDITOR::EDITOR_NAME = wxS( "KiCadColorEditor" );
|
||||
const wxString PG_RATIO_EDITOR::EDITOR_NAME = wxS( "KiCadRatioEditor" );
|
||||
const wxString PG_FPID_EDITOR::EDITOR_NAME = wxS( "KiCadFpidEditor" );
|
||||
const wxString PG_URL_EDITOR::EDITOR_NAME = wxS( "KiCadUrlEditor" );
|
||||
|
||||
|
||||
PG_UNIT_EDITOR::PG_UNIT_EDITOR( EDA_DRAW_FRAME* aFrame ) :
|
||||
@ -51,6 +63,9 @@ PG_UNIT_EDITOR::~PG_UNIT_EDITOR()
|
||||
|
||||
wxString PG_UNIT_EDITOR::BuildEditorName( EDA_DRAW_FRAME* aFrame )
|
||||
{
|
||||
if( !aFrame )
|
||||
return EDITOR_NAME + "NoFrame";
|
||||
|
||||
return EDITOR_NAME + aFrame->GetName();
|
||||
}
|
||||
|
||||
@ -458,3 +473,164 @@ void PG_RATIO_EDITOR::UpdateControl( wxPGProperty* aProperty, wxWindow* aCtrl )
|
||||
"properties!" ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PG_FPID_EDITOR::PG_FPID_EDITOR( EDA_DRAW_FRAME* aFrame ) : m_frame( aFrame )
|
||||
{
|
||||
m_editorName = BuildEditorName( aFrame );
|
||||
}
|
||||
|
||||
|
||||
void PG_FPID_EDITOR::UpdateFrame( EDA_DRAW_FRAME* aFrame )
|
||||
{
|
||||
m_frame = aFrame;
|
||||
m_editorName = BuildEditorName( aFrame );
|
||||
}
|
||||
|
||||
|
||||
wxString PG_FPID_EDITOR::BuildEditorName( EDA_DRAW_FRAME* aFrame )
|
||||
{
|
||||
if( !aFrame )
|
||||
return EDITOR_NAME + "NoFrame";
|
||||
|
||||
return EDITOR_NAME + aFrame->GetName();
|
||||
}
|
||||
|
||||
|
||||
wxPGWindowList PG_FPID_EDITOR::CreateControls( wxPropertyGrid* aGrid, wxPGProperty* aProperty,
|
||||
const wxPoint& aPos, const wxSize& aSize ) const
|
||||
{
|
||||
wxPGMultiButton* buttons = new wxPGMultiButton( aGrid, aSize );
|
||||
buttons->Add( KiBitmap( BITMAPS::small_library ) );
|
||||
buttons->Finalize( aGrid, aPos );
|
||||
wxSize textSize = buttons->GetPrimarySize();
|
||||
wxWindow* textCtrl = aGrid->GenerateEditorTextCtrl( aPos, textSize,
|
||||
aProperty->GetValueAsString(), nullptr, 0,
|
||||
aProperty->GetMaxLength() );
|
||||
wxPGWindowList ret( textCtrl, buttons );
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool PG_FPID_EDITOR::OnEvent( wxPropertyGrid* aGrid, wxPGProperty* aProperty, wxWindow* aCtrl,
|
||||
wxEvent& aEvent ) const
|
||||
{
|
||||
if( aEvent.GetEventType() == wxEVT_BUTTON )
|
||||
{
|
||||
wxString fpid = aProperty->GetValue().GetString();
|
||||
|
||||
if( KIWAY_PLAYER* frame = m_frame->Kiway().Player( FRAME_FOOTPRINT_CHOOSER, true, m_frame ) )
|
||||
{
|
||||
if( frame->ShowModal( &fpid, m_frame ) )
|
||||
aGrid->ChangePropertyValue( aProperty, fpid );
|
||||
|
||||
frame->Destroy();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return wxPGTextCtrlEditor::OnEvent( aGrid, aProperty, aCtrl, aEvent );
|
||||
}
|
||||
|
||||
|
||||
PG_URL_EDITOR::PG_URL_EDITOR( EDA_DRAW_FRAME* aFrame ) : m_frame( aFrame )
|
||||
{
|
||||
m_editorName = BuildEditorName( aFrame );
|
||||
}
|
||||
|
||||
|
||||
void PG_URL_EDITOR::UpdateFrame( EDA_DRAW_FRAME* aFrame )
|
||||
{
|
||||
m_frame = aFrame;
|
||||
m_editorName = BuildEditorName( aFrame );
|
||||
}
|
||||
|
||||
|
||||
wxString PG_URL_EDITOR::BuildEditorName( EDA_DRAW_FRAME* aFrame )
|
||||
{
|
||||
if( !aFrame )
|
||||
return EDITOR_NAME + "NoFrame";
|
||||
|
||||
return EDITOR_NAME + aFrame->GetName();
|
||||
}
|
||||
|
||||
|
||||
wxPGWindowList PG_URL_EDITOR::CreateControls( wxPropertyGrid* aGrid, wxPGProperty* aProperty,
|
||||
const wxPoint& aPos, const wxSize& aSize ) const
|
||||
{
|
||||
wxPGMultiButton* buttons = new wxPGMultiButton( aGrid, aSize );
|
||||
// Use a folder icon when no datasheet is set; otherwise use a globe icon.
|
||||
wxString urlValue = aProperty->GetValueAsString();
|
||||
bool hasUrl = !( urlValue.IsEmpty() || urlValue == wxS( "~" ) );
|
||||
buttons->Add( KiBitmap( hasUrl ? BITMAPS::www : BITMAPS::small_folder ) );
|
||||
buttons->Finalize( aGrid, aPos );
|
||||
wxSize textSize = buttons->GetPrimarySize();
|
||||
wxWindow* textCtrl = aGrid->GenerateEditorTextCtrl( aPos, textSize,
|
||||
aProperty->GetValueAsString(), nullptr, 0,
|
||||
aProperty->GetMaxLength() );
|
||||
wxPGWindowList ret( textCtrl, buttons );
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool PG_URL_EDITOR::OnEvent( wxPropertyGrid* aGrid, wxPGProperty* aProperty, wxWindow* aCtrl,
|
||||
wxEvent& aEvent ) const
|
||||
{
|
||||
if( aEvent.GetEventType() == wxEVT_BUTTON )
|
||||
{
|
||||
wxString filename = aProperty->GetValue().GetString();
|
||||
|
||||
if( filename.IsEmpty() || filename == wxS( "~" ) )
|
||||
{
|
||||
wxFileDialog openFileDialog( m_frame, _( "Open file" ), wxS( "" ), wxS( "" ),
|
||||
_( "All Files" ) + wxS( " (*.*)|*.*" ),
|
||||
wxFD_OPEN | wxFD_FILE_MUST_EXIST );
|
||||
|
||||
if( openFileDialog.ShowModal() == wxID_OK )
|
||||
{
|
||||
filename = openFileDialog.GetPath();
|
||||
aGrid->ChangePropertyValue( aProperty, wxString::Format( wxS( "file://%s" ),
|
||||
filename ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GetAssociatedDocument( m_frame, filename, &m_frame->Prj() );
|
||||
}
|
||||
|
||||
// Update the button icon to reflect presence/absence of URL
|
||||
if( wxObject* src = aEvent.GetEventObject() )
|
||||
{
|
||||
wxString newUrl = aProperty->GetValueAsString();
|
||||
bool hasUrl = !( newUrl.IsEmpty() || newUrl == wxS( "~" ) );
|
||||
auto bmp = KiBitmap( hasUrl ? BITMAPS::www : BITMAPS::small_folder );
|
||||
|
||||
if( wxWindow* win = wxDynamicCast( src, wxWindow ) )
|
||||
{
|
||||
if( wxBitmapButton* bb = wxDynamicCast( win, wxBitmapButton ) )
|
||||
{
|
||||
bb->SetBitmap( bmp );
|
||||
}
|
||||
else if( wxButton* b = wxDynamicCast( win, wxButton ) )
|
||||
{
|
||||
b->SetBitmap( bmp );
|
||||
}
|
||||
else if( wxWindow* parent = win->GetParent() )
|
||||
{
|
||||
if( wxPGMultiButton* buttons = wxDynamicCast( parent, wxPGMultiButton ) )
|
||||
{
|
||||
wxWindow* btn0 = buttons->GetButton( 0 );
|
||||
if( wxBitmapButton* bb0 = wxDynamicCast( btn0, wxBitmapButton ) )
|
||||
bb0->SetBitmap( bmp );
|
||||
else if( wxButton* b0 = wxDynamicCast( btn0, wxButton ) )
|
||||
b0->SetBitmap( bmp );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return wxPGTextCtrlEditor::OnEvent( aGrid, aProperty, aCtrl, aEvent );
|
||||
}
|
||||
|
@ -297,7 +297,19 @@ static bool isKeyModifierOnly( int aKeyCode )
|
||||
{
|
||||
static std::vector<enum wxKeyCode> special_keys =
|
||||
{
|
||||
WXK_CONTROL, WXK_RAW_CONTROL, WXK_SHIFT, WXK_ALT
|
||||
WXK_CONTROL, WXK_RAW_CONTROL, WXK_SHIFT, WXK_ALT,
|
||||
#ifdef WXK_WINDOWS_LEFT
|
||||
WXK_WINDOWS_LEFT, WXK_WINDOWS_RIGHT,
|
||||
#endif
|
||||
#ifdef WXK_MENU
|
||||
WXK_MENU,
|
||||
#endif
|
||||
#ifdef WXK_COMMAND
|
||||
WXK_COMMAND,
|
||||
#endif
|
||||
#ifdef WXK_META
|
||||
WXK_META,
|
||||
#endif
|
||||
};
|
||||
|
||||
return alg::contains( special_keys, aKeyCode );
|
||||
@ -515,7 +527,7 @@ void TOOL_DISPATCHER::DispatchWxEvent( wxEvent& aEvent )
|
||||
if( !evt && me->GetWheelRotation() != 0 )
|
||||
{
|
||||
const unsigned modBits =
|
||||
static_cast<unsigned>( mods ) & ( MD_CTRL | MD_ALT | MD_SHIFT );
|
||||
static_cast<unsigned>( mods ) & MD_MODIFIER_MASK;
|
||||
const bool shouldHandle = std::popcount( modBits ) > 1;
|
||||
|
||||
if( shouldHandle )
|
||||
|
@ -149,6 +149,9 @@ const std::string TOOL_EVENT::Format() const
|
||||
{ MD_SHIFT, "shift" },
|
||||
{ MD_CTRL, "ctrl" },
|
||||
{ MD_ALT, "alt" },
|
||||
{ MD_SUPER, "super" },
|
||||
{ MD_META, "meta" },
|
||||
{ MD_ALTGR, "altgr" },
|
||||
{ 0, "" }
|
||||
};
|
||||
|
||||
|
@ -640,15 +640,32 @@ void LIB_TREE::onQueryCharHook( wxKeyEvent& aKeyStroke )
|
||||
{
|
||||
int hotkey = aKeyStroke.GetKeyCode();
|
||||
|
||||
if( aKeyStroke.GetModifiers() & wxMOD_CONTROL )
|
||||
hotkey += MD_CTRL;
|
||||
int mods = aKeyStroke.GetModifiers();
|
||||
|
||||
if( aKeyStroke.GetModifiers() & wxMOD_ALT )
|
||||
hotkey += MD_ALT;
|
||||
if( mods & wxMOD_ALTGR )
|
||||
hotkey += MD_ALTGR;
|
||||
else
|
||||
{
|
||||
if( mods & wxMOD_CONTROL )
|
||||
hotkey += MD_CTRL;
|
||||
|
||||
if( aKeyStroke.GetModifiers() & wxMOD_SHIFT )
|
||||
if( mods & wxMOD_ALT )
|
||||
hotkey += MD_ALT;
|
||||
}
|
||||
|
||||
if( mods & wxMOD_SHIFT )
|
||||
hotkey += MD_SHIFT;
|
||||
|
||||
#ifdef wxMOD_META
|
||||
if( mods & wxMOD_META )
|
||||
hotkey += MD_META;
|
||||
#endif
|
||||
|
||||
#ifdef wxMOD_WIN
|
||||
if( mods & wxMOD_WIN )
|
||||
hotkey += MD_SUPER;
|
||||
#endif
|
||||
|
||||
if( hotkey == ACTIONS::expandAll.GetHotKey()
|
||||
|| hotkey == ACTIONS::expandAll.GetHotKeyAlt() )
|
||||
{
|
||||
@ -860,14 +877,31 @@ void LIB_TREE::onTreeCharHook( wxKeyEvent& aKeyStroke )
|
||||
{
|
||||
int hotkey = aKeyStroke.GetKeyCode();
|
||||
|
||||
if( aKeyStroke.ShiftDown() )
|
||||
int mods = aKeyStroke.GetModifiers();
|
||||
|
||||
if( mods & wxMOD_ALTGR )
|
||||
hotkey |= MD_ALTGR;
|
||||
else
|
||||
{
|
||||
if( mods & wxMOD_ALT )
|
||||
hotkey |= MD_ALT;
|
||||
|
||||
if( mods & wxMOD_CONTROL )
|
||||
hotkey |= MD_CTRL;
|
||||
}
|
||||
|
||||
if( mods & wxMOD_SHIFT )
|
||||
hotkey |= MD_SHIFT;
|
||||
|
||||
if( aKeyStroke.AltDown() )
|
||||
hotkey |= MD_ALT;
|
||||
#ifdef wxMOD_META
|
||||
if( mods & wxMOD_META )
|
||||
hotkey |= MD_META;
|
||||
#endif
|
||||
|
||||
if( aKeyStroke.ControlDown() )
|
||||
hotkey |= MD_CTRL;
|
||||
#ifdef wxMOD_WIN
|
||||
if( mods & wxMOD_WIN )
|
||||
hotkey |= MD_SUPER;
|
||||
#endif
|
||||
|
||||
if( tool->GetManager()->GetActionManager()->RunHotKey( hotkey ) )
|
||||
aKeyStroke.Skip( false );
|
||||
|
@ -693,14 +693,31 @@ long WIDGET_HOTKEY_LIST::MapKeypressToKeycode( const wxKeyEvent& aEvent )
|
||||
*/
|
||||
bool keyIsLetter = key >= 'A' && key <= 'Z';
|
||||
|
||||
if( aEvent.ShiftDown() && ( keyIsLetter || key > 256 || key == 9 || key == 32 ) )
|
||||
int mods = aEvent.GetModifiers();
|
||||
|
||||
if( ( mods & wxMOD_SHIFT ) && ( keyIsLetter || key > 256 || key == 9 || key == 32 ) )
|
||||
key |= MD_SHIFT;
|
||||
|
||||
if( aEvent.ControlDown() )
|
||||
key |= MD_CTRL;
|
||||
if( mods & wxMOD_ALTGR )
|
||||
key |= MD_ALTGR;
|
||||
else
|
||||
{
|
||||
if( mods & wxMOD_CONTROL )
|
||||
key |= MD_CTRL;
|
||||
|
||||
if( aEvent.AltDown() )
|
||||
key |= MD_ALT;
|
||||
if( mods & wxMOD_ALT )
|
||||
key |= MD_ALT;
|
||||
}
|
||||
|
||||
#ifdef wxMOD_META
|
||||
if( mods & wxMOD_META )
|
||||
key |= MD_META;
|
||||
#endif
|
||||
|
||||
#ifdef wxMOD_WIN
|
||||
if( mods & wxMOD_WIN )
|
||||
key |= MD_SUPER;
|
||||
#endif
|
||||
|
||||
return key;
|
||||
}
|
||||
|
@ -77,8 +77,8 @@ public:
|
||||
*/
|
||||
std::vector<std::pair<FIELD_T, wxString>> GetFields() const;
|
||||
|
||||
bool GetKeepSymbol() { return m_keepSymbol; }
|
||||
bool GetPlaceAllUnits() { return m_useUnits; }
|
||||
bool GetKeepSymbol() { return m_keepSymbol->GetValue(); }
|
||||
bool GetPlaceAllUnits() { return m_useUnits->GetValue(); }
|
||||
|
||||
public:
|
||||
static std::mutex g_Mutex;
|
||||
|
@ -269,7 +269,6 @@ DIALOG_SYMBOL_FIELDS_TABLE::DIALOG_SYMBOL_FIELDS_TABLE( SCH_EDIT_FRAME* parent,
|
||||
|
||||
preset.name = m_job->m_bomPresetName;
|
||||
preset.excludeDNP = m_job->m_excludeDNP;
|
||||
preset.includeExcludedFromBOM = m_job->m_includeExcludedFromBOM;
|
||||
preset.filterString = m_job->m_filterString;
|
||||
preset.sortAsc = m_job->m_sortAsc;
|
||||
preset.sortField = m_job->m_sortField;
|
||||
@ -947,7 +946,7 @@ void DIALOG_SYMBOL_FIELDS_TABLE::OnMenu( wxCommandEvent& event )
|
||||
}
|
||||
else if( menu_id == 1 || menu_id == 4205 )
|
||||
{
|
||||
m_dataModel->SetExcludeDNP( m_dataModel->GetIncludeExcludedFromBOM() );
|
||||
m_dataModel->SetIncludeExcludedFromBOM( !m_dataModel->GetIncludeExcludedFromBOM() );
|
||||
m_dataModel->RebuildRows();
|
||||
m_grid->ForceRefresh();
|
||||
|
||||
@ -1300,8 +1299,18 @@ void DIALOG_SYMBOL_FIELDS_TABLE::OnPreviewRefresh( wxCommandEvent& event )
|
||||
|
||||
void DIALOG_SYMBOL_FIELDS_TABLE::PreviewRefresh()
|
||||
{
|
||||
bool saveIncludeExcudedFromBOM = m_dataModel->GetIncludeExcludedFromBOM();
|
||||
|
||||
m_dataModel->SetIncludeExcludedFromBOM( false );
|
||||
m_dataModel->RebuildRows();
|
||||
|
||||
m_textOutput->SetValue( m_dataModel->Export( GetCurrentBomFmtSettings() ) );
|
||||
|
||||
if( saveIncludeExcudedFromBOM )
|
||||
{
|
||||
m_dataModel->SetIncludeExcludedFromBOM( true );
|
||||
m_dataModel->RebuildRows();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1499,7 +1508,6 @@ void DIALOG_SYMBOL_FIELDS_TABLE::OnOk( wxCommandEvent& aEvent )
|
||||
BOM_PRESET presetFields = m_dataModel->GetBomSettings();
|
||||
m_job->m_sortAsc = presetFields.sortAsc;
|
||||
m_job->m_excludeDNP = presetFields.excludeDNP;
|
||||
m_job->m_includeExcludedFromBOM = presetFields.includeExcludedFromBOM;
|
||||
m_job->m_filterString = presetFields.filterString;
|
||||
m_job->m_sortField = presetFields.sortField;
|
||||
|
||||
|
@ -169,7 +169,7 @@ DIALOG_SYMBOL_FIELDS_TABLE_BASE::DIALOG_SYMBOL_FIELDS_TABLE_BASE( wxWindow* pare
|
||||
m_bMenu = new STD_BITMAP_BUTTON( m_panelEdit, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW|0 );
|
||||
m_bMenu->SetMinSize( wxSize( 30,30 ) );
|
||||
|
||||
bControls->Add( m_bMenu, 0, wxRIGHT, 5 );
|
||||
bControls->Add( m_bMenu, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 5 );
|
||||
|
||||
|
||||
bEditSizer->Add( bControls, 0, wxEXPAND|wxLEFT|wxTOP, 5 );
|
||||
|
@ -1384,7 +1384,7 @@
|
||||
</object>
|
||||
<object class="sizeritem" expanded="false">
|
||||
<property name="border">5</property>
|
||||
<property name="flag">wxRIGHT</property>
|
||||
<property name="flag">wxRIGHT|wxALIGN_CENTER_VERTICAL</property>
|
||||
<property name="proportion">0</property>
|
||||
<object class="wxBitmapButton" expanded="false">
|
||||
<property name="BottomDockable">1</property>
|
||||
|
@ -92,6 +92,10 @@ enum id_eeschema_frm
|
||||
// to select one unit among MAX_UNIT_COUNT_PER_PACKAGE in popup menu
|
||||
ID_POPUP_SCH_SELECT_UNIT_END = ID_POPUP_SCH_SELECT_UNIT1 + MAX_UNIT_COUNT_PER_PACKAGE,
|
||||
|
||||
ID_POPUP_SCH_PLACE_UNIT,
|
||||
ID_POPUP_SCH_PLACE_UNIT1,
|
||||
ID_POPUP_SCH_PLACE_UNIT_END = ID_POPUP_SCH_PLACE_UNIT1 + MAX_UNIT_COUNT_PER_PACKAGE,
|
||||
|
||||
ID_POPUP_SCH_SELECT_BODY_STYLE,
|
||||
ID_POPUP_SCH_SELECT_BODY_STYLE1,
|
||||
ID_POPUP_SCH_SELECT_BODY_STYLE_END = ID_POPUP_SCH_SELECT_BODY_STYLE1 + MAX_BODY_STYLE_PER_PACKAGE,
|
||||
|
@ -668,7 +668,6 @@ int EESCHEMA_JOBS_HANDLER::JobExportBom( JOB* aJob )
|
||||
preset.filterString = aBomJob->m_filterString;
|
||||
preset.groupSymbols = ( aBomJob->m_fieldsGroupBy.size() > 0 );
|
||||
preset.excludeDNP = aBomJob->m_excludeDNP;
|
||||
preset.includeExcludedFromBOM = aBomJob->m_includeExcludedFromBOM;
|
||||
}
|
||||
|
||||
dataModel.ApplyBomPreset( preset );
|
||||
|
@ -159,6 +159,13 @@ bool SCH_BITMAP::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) c
|
||||
}
|
||||
|
||||
|
||||
bool SCH_BITMAP::HitTest( const SHAPE_LINE_CHAIN& aPoly, bool aContained ) const
|
||||
{
|
||||
return KIGEOM::BoxHitTest( aPoly, GetBoundingBox(), aContained );
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SCH_BITMAP::Plot( PLOTTER* aPlotter, bool aBackground, const SCH_PLOT_OPTS& aPlotOpts,
|
||||
int aUnit, int aBodyStyle, const VECTOR2I& aOffset, bool aDimmed )
|
||||
{
|
||||
|
@ -100,6 +100,7 @@ public:
|
||||
|
||||
bool HitTest( const VECTOR2I& aPosition, int aAccuracy = 0 ) const override;
|
||||
bool HitTest( const BOX2I& aRect, bool aContained, int aAccuracy = 0 ) const override;
|
||||
bool HitTest( const SHAPE_LINE_CHAIN& aPoly, bool aContained ) const override;
|
||||
|
||||
void Plot( PLOTTER* aPlotter, bool aBackground, const SCH_PLOT_OPTS& aPlotOpts,
|
||||
int aUnit, int aBodyStyle, const VECTOR2I& aOffset, bool aDimmed ) override;
|
||||
|
@ -165,28 +165,6 @@ SCH_ITEM* SCH_ITEM::Duplicate( bool addToParentGroup, SCH_COMMIT* aCommit, bool
|
||||
}
|
||||
|
||||
|
||||
void SCH_ITEM::SetUnitProp( const wxString& aUnit )
|
||||
{
|
||||
if( aUnit == _HKI( "All units" ) )
|
||||
{
|
||||
m_unit = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if( SYMBOL* symbol = GetParentSymbol() )
|
||||
{
|
||||
for( int unit = 1; unit <= symbol->GetUnitCount(); unit++ )
|
||||
{
|
||||
if( symbol->GetUnitDisplayName( unit, false ) == aUnit )
|
||||
{
|
||||
m_unit = unit;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
wxString SCH_ITEM::GetUnitDisplayName( int aUnit, bool aLabel ) const
|
||||
{
|
||||
if( aUnit == 0 )
|
||||
@ -208,13 +186,6 @@ wxString SCH_ITEM::GetBodyStyleDescription( int aBodyStyle, bool aLabel ) const
|
||||
return wxEmptyString;
|
||||
}
|
||||
|
||||
|
||||
wxString SCH_ITEM::GetUnitProp() const
|
||||
{
|
||||
return GetUnitDisplayName( m_unit, false );
|
||||
}
|
||||
|
||||
|
||||
void SCH_ITEM::SetBodyStyleProp( const wxString& aBodyStyle )
|
||||
{
|
||||
if( aBodyStyle == _HKI( "All body styles" ) )
|
||||
@ -734,21 +705,21 @@ static struct SCH_ITEM_DESC
|
||||
return false;
|
||||
};
|
||||
|
||||
propMgr.AddProperty( new PROPERTY<SCH_ITEM, wxString>( _HKI( "Unit" ),
|
||||
&SCH_ITEM::SetUnitProp, &SCH_ITEM::GetUnitProp ) )
|
||||
propMgr.AddProperty( new PROPERTY<SCH_ITEM, int>( _HKI( "Unit" ),
|
||||
&SCH_ITEM::SetUnit, &SCH_ITEM::GetUnit ) )
|
||||
.SetAvailableFunc( multiUnit )
|
||||
.SetIsHiddenFromDesignEditors()
|
||||
.SetChoicesFunc( []( INSPECTABLE* aItem )
|
||||
{
|
||||
wxPGChoices choices;
|
||||
choices.Add( _HKI( "All units" ) );
|
||||
choices.Add( _HKI( "All units" ), 0 );
|
||||
|
||||
if( SCH_ITEM* item = dynamic_cast<SCH_ITEM*>( aItem ) )
|
||||
{
|
||||
if( SYMBOL* symbol = item->GetParentSymbol() )
|
||||
{
|
||||
for( int ii = 1; ii <= symbol->GetUnitCount(); ii++ )
|
||||
choices.Add( symbol->GetUnitDisplayName( ii, false ) );
|
||||
choices.Add( symbol->GetUnitDisplayName( ii, false ), ii );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -240,9 +240,6 @@ public:
|
||||
virtual wxString GetUnitDisplayName( int aUnit, bool aLabel ) const;
|
||||
virtual wxString GetBodyStyleDescription( int aBodyStyle, bool aLabel ) const;
|
||||
|
||||
virtual void SetUnitProp( const wxString& aUnit );
|
||||
virtual wxString GetUnitProp() const;
|
||||
|
||||
virtual void SetBodyStyle( int aBodyStyle ) { m_bodyStyle = aBodyStyle; }
|
||||
int GetBodyStyle() const { return m_bodyStyle; }
|
||||
|
||||
|
@ -2986,7 +2986,7 @@ static struct SCH_SYMBOL_DESC
|
||||
return false;
|
||||
};
|
||||
|
||||
propMgr.AddProperty( new PROPERTY<SCH_SYMBOL, wxString>( _HKI( "Unit" ),
|
||||
propMgr.AddProperty( new PROPERTY<SCH_SYMBOL, int>( _HKI( "Unit" ),
|
||||
&SCH_SYMBOL::SetUnitProp, &SCH_SYMBOL::GetUnitProp ) )
|
||||
.SetAvailableFunc( multiUnit )
|
||||
.SetChoicesFunc( []( INSPECTABLE* aItem )
|
||||
@ -2996,7 +2996,7 @@ static struct SCH_SYMBOL_DESC
|
||||
if( SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( aItem ) )
|
||||
{
|
||||
for( int ii = 1; ii <= symbol->GetUnitCount(); ii++ )
|
||||
choices.Add( symbol->GetUnitDisplayName( ii, false ) );
|
||||
choices.Add( symbol->GetUnitDisplayName( ii, false ), ii );
|
||||
}
|
||||
|
||||
return choices;
|
||||
|
@ -497,24 +497,15 @@ public:
|
||||
SetValueFieldText( aRef );
|
||||
}
|
||||
|
||||
wxString GetUnitProp() const override
|
||||
int GetUnitProp() const
|
||||
{
|
||||
int unit = GetUnitSelection( &Schematic()->CurrentSheet() );
|
||||
|
||||
return GetUnitDisplayName( unit, false );
|
||||
return GetUnitSelection( &Schematic()->CurrentSheet() );
|
||||
}
|
||||
|
||||
void SetUnitProp( const wxString& aUnit ) override
|
||||
void SetUnitProp( int aUnit )
|
||||
{
|
||||
for( int unit = 1; unit <= GetUnitCount(); unit++ )
|
||||
{
|
||||
if( GetUnitDisplayName( unit, false ) == aUnit )
|
||||
{
|
||||
SetUnitSelection( &Schematic()->CurrentSheet(), unit );
|
||||
SetUnit( unit );
|
||||
return;
|
||||
}
|
||||
}
|
||||
SetUnitSelection( &Schematic()->CurrentSheet(), aUnit );
|
||||
SetUnit( aUnit );
|
||||
}
|
||||
|
||||
wxString GetBodyStyleProp() const override
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include <sch_painter.h>
|
||||
#include <wx/log.h>
|
||||
#include <sch_table.h>
|
||||
#include <geometry/geometry_utils.h>
|
||||
|
||||
|
||||
SCH_TABLE::SCH_TABLE( int aLineWidth ) :
|
||||
@ -327,6 +328,12 @@ bool SCH_TABLE::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) co
|
||||
}
|
||||
|
||||
|
||||
bool SCH_TABLE::HitTest( const SHAPE_LINE_CHAIN& aPoly, bool aContained ) const
|
||||
{
|
||||
return KIGEOM::BoxHitTest( aPoly, GetBoundingBox(), aContained );
|
||||
}
|
||||
|
||||
|
||||
void SCH_TABLE::DrawBorders( const std::function<void( const VECTOR2I& aPt1, const VECTOR2I& aPt2,
|
||||
const STROKE_PARAMS& aStroke )>& aCallback ) const
|
||||
{
|
||||
|
@ -214,8 +214,8 @@ public:
|
||||
std::vector<int> ViewGetLayers() const override;
|
||||
|
||||
bool HitTest( const VECTOR2I& aPosition, int aAccuracy = 0 ) const override;
|
||||
|
||||
bool HitTest( const BOX2I& aRect, bool aContained, int aAccuracy = 0 ) const override;
|
||||
bool HitTest( const SHAPE_LINE_CHAIN& aPoly, bool aContained ) const override;
|
||||
|
||||
void DrawBorders( const std::function<void( const VECTOR2I& aPt1, const VECTOR2I& aPt2,
|
||||
const STROKE_PARAMS& aStroke )>& aCallback ) const;
|
||||
|
@ -224,6 +224,7 @@ static struct SCH_TABLECELL_DESC
|
||||
propMgr.Mask( TYPE_HASH( SCH_TABLECELL ), TYPE_HASH( EDA_SHAPE ), _HKI( "Line Width" ) );
|
||||
propMgr.Mask( TYPE_HASH( SCH_TABLECELL ), TYPE_HASH( EDA_SHAPE ), _HKI( "Line Style" ) );
|
||||
propMgr.Mask( TYPE_HASH( SCH_TABLECELL ), TYPE_HASH( EDA_SHAPE ), _HKI( "Line Color" ) );
|
||||
propMgr.Mask( TYPE_HASH( SCH_TABLECELL ), TYPE_HASH( EDA_SHAPE ), _HKI( "Corner Radius" ) );
|
||||
|
||||
propMgr.Mask( TYPE_HASH( SCH_TABLECELL ), TYPE_HASH( EDA_TEXT ), _HKI( "Width" ) );
|
||||
propMgr.Mask( TYPE_HASH( SCH_TABLECELL ), TYPE_HASH( EDA_TEXT ), _HKI( "Height" ) );
|
||||
|
@ -613,6 +613,7 @@ static struct SCH_TEXTBOX_DESC
|
||||
propMgr.InheritsAfter( TYPE_HASH( SCH_TEXTBOX ), TYPE_HASH( EDA_TEXT ) );
|
||||
|
||||
propMgr.Mask( TYPE_HASH( SCH_TEXTBOX ), TYPE_HASH( EDA_SHAPE ), _HKI( "Shape" ) );
|
||||
propMgr.Mask( TYPE_HASH( SCH_TEXTBOX ), TYPE_HASH( EDA_SHAPE ), _HKI( "Corner Radius" ) );
|
||||
|
||||
propMgr.Mask( TYPE_HASH( SCH_TEXTBOX ), TYPE_HASH( EDA_TEXT ), _HKI( "Width" ) );
|
||||
propMgr.Mask( TYPE_HASH( SCH_TEXTBOX ), TYPE_HASH( EDA_TEXT ), _HKI( "Height" ) );
|
||||
|
@ -438,8 +438,8 @@ TOOL_ACTION SCH_ACTIONS::placeNextSymbolUnit( TOOL_ACTION_ARGS()
|
||||
.FriendlyName( _( "Place Next Symbol Unit" ) )
|
||||
.Tooltip( _( "Place the next unit of the current symbol that is missing from the schematic" ) )
|
||||
.Flags( AF_ACTIVATE )
|
||||
// The symbol to use as a reference for the next unit
|
||||
.Parameter<SCH_SYMBOL*>( nullptr ) );
|
||||
// The symbol to use as a reference for the next unit and optionally the unit number
|
||||
.Parameter<SCH_ACTIONS::PLACE_SYMBOL_UNIT_PARAMS>( {} ) );
|
||||
|
||||
TOOL_ACTION SCH_ACTIONS::placePower( TOOL_ACTION_ARGS()
|
||||
.Name( "eeschema.InteractiveDrawing.placePowerSymbol" )
|
||||
|
@ -310,4 +310,12 @@ public:
|
||||
///< If a symbol is provide, reannotate it?
|
||||
bool m_Reannotate = true;
|
||||
};
|
||||
|
||||
struct PLACE_SYMBOL_UNIT_PARAMS
|
||||
{
|
||||
///< Symbol used as reference for unit placement
|
||||
SCH_SYMBOL* m_Symbol = nullptr;
|
||||
///< Unit number to place; 0 means next available unit
|
||||
int m_Unit = 0;
|
||||
};
|
||||
};
|
||||
|
@ -619,7 +619,10 @@ int SCH_DRAWING_TOOLS::PlaceSymbol( const TOOL_EVENT& aEvent )
|
||||
|
||||
int SCH_DRAWING_TOOLS::PlaceNextSymbolUnit( const TOOL_EVENT& aEvent )
|
||||
{
|
||||
SCH_SYMBOL* symbol = aEvent.Parameter<SCH_SYMBOL*>();
|
||||
const SCH_ACTIONS::PLACE_SYMBOL_UNIT_PARAMS& params =
|
||||
aEvent.Parameter<SCH_ACTIONS::PLACE_SYMBOL_UNIT_PARAMS>();
|
||||
SCH_SYMBOL* symbol = params.m_Symbol;
|
||||
int requestedUnit = params.m_Unit;
|
||||
|
||||
// TODO: get from selection
|
||||
if( !symbol )
|
||||
@ -654,8 +657,23 @@ int SCH_DRAWING_TOOLS::PlaceNextSymbolUnit( const TOOL_EVENT& aEvent )
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Find the lowest unit number that is missing
|
||||
const int nextMissing = *std::min_element( missingUnits.begin(), missingUnits.end() );
|
||||
int nextMissing;
|
||||
|
||||
if( requestedUnit > 0 )
|
||||
{
|
||||
if( missingUnits.count( requestedUnit ) == 0 )
|
||||
{
|
||||
m_frame->ShowInfoBarMsg( _( "Requested unit already placed." ) );
|
||||
return 0;
|
||||
}
|
||||
|
||||
nextMissing = requestedUnit;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Find the lowest unit number that is missing
|
||||
nextMissing = *std::min_element( missingUnits.begin(), missingUnits.end() );
|
||||
}
|
||||
|
||||
std::unique_ptr<SCH_SYMBOL> newSymbol = std::make_unique<SCH_SYMBOL>( *symbol );
|
||||
const SCH_SHEET_PATH& sheetPath = m_frame->GetCurrentSheet();
|
||||
|
@ -121,11 +121,16 @@ private:
|
||||
break; // We have used all IDs for these submenus
|
||||
}
|
||||
|
||||
if( !missingUnits.empty() )
|
||||
{
|
||||
AppendSeparator();
|
||||
|
||||
wxMenuItem* item = Add( SCH_ACTIONS::placeNextSymbolUnit );
|
||||
item->Enable( missingUnits.size() );
|
||||
for( int unitNumber : missingUnits )
|
||||
{
|
||||
wxString placeText = wxString::Format( _( "Place unit %s" ),
|
||||
symbol->GetUnitDisplayName( unitNumber, false ) );
|
||||
Append( ID_POPUP_SCH_PLACE_UNIT1 + unitNumber - 1, placeText );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -787,6 +787,16 @@ int SCH_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
|
||||
if( symbol )
|
||||
static_cast<SCH_EDIT_FRAME*>( m_frame )->SelectUnit( symbol, unit );
|
||||
}
|
||||
else if( *evt->GetCommandId() >= ID_POPUP_SCH_PLACE_UNIT
|
||||
&& *evt->GetCommandId() <= ID_POPUP_SCH_PLACE_UNIT_END )
|
||||
{
|
||||
SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( m_selection.Front() );
|
||||
int unit = *evt->GetCommandId() - ID_POPUP_SCH_PLACE_UNIT;
|
||||
|
||||
if( symbol )
|
||||
m_toolMgr->RunAction( SCH_ACTIONS::placeNextSymbolUnit,
|
||||
SCH_ACTIONS::PLACE_SYMBOL_UNIT_PARAMS{ symbol, unit } );
|
||||
}
|
||||
else if( *evt->GetCommandId() >= ID_POPUP_SCH_SELECT_BODY_STYLE
|
||||
&& *evt->GetCommandId() <= ID_POPUP_SCH_SELECT_BODY_STYLE_END )
|
||||
{
|
||||
|
@ -546,15 +546,32 @@ void HIERARCHY_PANE::onCharHook( wxKeyEvent& aKeyStroke )
|
||||
{
|
||||
int hotkey = aKeyStroke.GetKeyCode();
|
||||
|
||||
if( aKeyStroke.GetModifiers() & wxMOD_CONTROL )
|
||||
hotkey += MD_CTRL;
|
||||
int mods = aKeyStroke.GetModifiers();
|
||||
|
||||
if( aKeyStroke.GetModifiers() & wxMOD_ALT )
|
||||
hotkey += MD_ALT;
|
||||
if( mods & wxMOD_ALTGR )
|
||||
hotkey += MD_ALTGR;
|
||||
else
|
||||
{
|
||||
if( mods & wxMOD_CONTROL )
|
||||
hotkey += MD_CTRL;
|
||||
|
||||
if( aKeyStroke.GetModifiers() & wxMOD_SHIFT )
|
||||
if( mods & wxMOD_ALT )
|
||||
hotkey += MD_ALT;
|
||||
}
|
||||
|
||||
if( mods & wxMOD_SHIFT )
|
||||
hotkey += MD_SHIFT;
|
||||
|
||||
#ifdef wxMOD_META
|
||||
if( mods & wxMOD_META )
|
||||
hotkey += MD_META;
|
||||
#endif
|
||||
|
||||
#ifdef wxMOD_WIN
|
||||
if( mods & wxMOD_WIN )
|
||||
hotkey += MD_SUPER;
|
||||
#endif
|
||||
|
||||
if( hotkey == ACTIONS::expandAll.GetHotKey()
|
||||
|| hotkey == ACTIONS::expandAll.GetHotKeyAlt() )
|
||||
{
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include <schematic.h>
|
||||
#include <sch_symbol.h>
|
||||
#include <sch_field.h>
|
||||
#include <template_fieldnames.h>
|
||||
#include <settings/color_settings.h>
|
||||
#include <string_utils.h>
|
||||
#include <tool/tool_manager.h>
|
||||
@ -149,6 +150,32 @@ SCH_PROPERTIES_PANEL::SCH_PROPERTIES_PANEL( wxWindow* aParent, SCH_BASE_FRAME* a
|
||||
{
|
||||
m_colorEditorInstance = static_cast<PG_COLOR_EDITOR*>( it->second );
|
||||
}
|
||||
|
||||
it = wxPGGlobalVars->m_mapEditorClasses.find( PG_FPID_EDITOR::BuildEditorName( m_frame ) );
|
||||
|
||||
if( it != wxPGGlobalVars->m_mapEditorClasses.end() )
|
||||
{
|
||||
m_fpEditorInstance = static_cast<PG_FPID_EDITOR*>( it->second );
|
||||
m_fpEditorInstance->UpdateFrame( m_frame );
|
||||
}
|
||||
else
|
||||
{
|
||||
PG_FPID_EDITOR* fpEditor = new PG_FPID_EDITOR( m_frame );
|
||||
m_fpEditorInstance = static_cast<PG_FPID_EDITOR*>( wxPropertyGrid::RegisterEditorClass( fpEditor ) );
|
||||
}
|
||||
|
||||
it = wxPGGlobalVars->m_mapEditorClasses.find( PG_URL_EDITOR::BuildEditorName( m_frame ) );
|
||||
|
||||
if( it != wxPGGlobalVars->m_mapEditorClasses.end() )
|
||||
{
|
||||
m_urlEditorInstance = static_cast<PG_URL_EDITOR*>( it->second );
|
||||
m_urlEditorInstance->UpdateFrame( m_frame );
|
||||
}
|
||||
else
|
||||
{
|
||||
PG_URL_EDITOR* urlEditor = new PG_URL_EDITOR( m_frame );
|
||||
m_urlEditorInstance = static_cast<PG_URL_EDITOR*>( wxPropertyGrid::RegisterEditorClass( urlEditor ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -156,6 +183,8 @@ SCH_PROPERTIES_PANEL::SCH_PROPERTIES_PANEL( wxWindow* aParent, SCH_BASE_FRAME* a
|
||||
SCH_PROPERTIES_PANEL::~SCH_PROPERTIES_PANEL()
|
||||
{
|
||||
m_unitEditorInstance->UpdateFrame( nullptr );
|
||||
m_fpEditorInstance->UpdateFrame( nullptr );
|
||||
m_urlEditorInstance->UpdateFrame( nullptr );
|
||||
}
|
||||
|
||||
|
||||
@ -226,6 +255,11 @@ wxPGProperty* SCH_PROPERTIES_PANEL::createPGProperty( const PROPERTY_BASE* aProp
|
||||
colorProp->SetBackgroundColor( bg );
|
||||
}
|
||||
|
||||
if( aProperty->Name() == GetCanonicalFieldName( FIELD_T::FOOTPRINT ) )
|
||||
prop->SetEditor( PG_FPID_EDITOR::BuildEditorName( m_frame ) );
|
||||
else if( aProperty->Name() == GetCanonicalFieldName( FIELD_T::DATASHEET ) )
|
||||
prop->SetEditor( PG_URL_EDITOR::BuildEditorName( m_frame ) );
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,8 @@ class PROPERTY_MANAGER;
|
||||
class PG_UNIT_EDITOR;
|
||||
class PG_CHECKBOX_EDITOR;
|
||||
class PG_COLOR_EDITOR;
|
||||
class PG_FPID_EDITOR;
|
||||
class PG_URL_EDITOR;
|
||||
|
||||
class SCH_PROPERTIES_PANEL : public PROPERTIES_PANEL
|
||||
{
|
||||
@ -60,6 +62,8 @@ protected:
|
||||
PG_UNIT_EDITOR* m_unitEditorInstance;
|
||||
PG_CHECKBOX_EDITOR* m_checkboxEditorInstance;
|
||||
PG_COLOR_EDITOR* m_colorEditorInstance;
|
||||
PG_FPID_EDITOR* m_fpEditorInstance;
|
||||
PG_URL_EDITOR* m_urlEditorInstance;
|
||||
|
||||
static std::set<wxString> m_currentFieldNames;
|
||||
wxPGChoices m_nets;
|
||||
|
@ -125,4 +125,58 @@ public:
|
||||
};
|
||||
|
||||
|
||||
class PG_FPID_EDITOR : public wxPGTextCtrlEditor
|
||||
{
|
||||
public:
|
||||
static const wxString EDITOR_NAME;
|
||||
|
||||
PG_FPID_EDITOR( EDA_DRAW_FRAME* aFrame );
|
||||
|
||||
virtual ~PG_FPID_EDITOR() {}
|
||||
|
||||
wxString GetName() const override { return m_editorName; }
|
||||
|
||||
void UpdateFrame( EDA_DRAW_FRAME* aFrame );
|
||||
|
||||
static wxString BuildEditorName( EDA_DRAW_FRAME* aFrame );
|
||||
|
||||
wxPGWindowList CreateControls( wxPropertyGrid* aGrid, wxPGProperty* aProperty,
|
||||
const wxPoint& aPos, const wxSize& aSize ) const override;
|
||||
|
||||
bool OnEvent( wxPropertyGrid* aGrid, wxPGProperty* aProperty, wxWindow* aCtrl,
|
||||
wxEvent& aEvent ) const override;
|
||||
|
||||
private:
|
||||
EDA_DRAW_FRAME* m_frame;
|
||||
wxString m_editorName;
|
||||
};
|
||||
|
||||
|
||||
class PG_URL_EDITOR : public wxPGTextCtrlEditor
|
||||
{
|
||||
public:
|
||||
static const wxString EDITOR_NAME;
|
||||
|
||||
PG_URL_EDITOR( EDA_DRAW_FRAME* aFrame );
|
||||
|
||||
virtual ~PG_URL_EDITOR() {}
|
||||
|
||||
wxString GetName() const override { return m_editorName; }
|
||||
|
||||
void UpdateFrame( EDA_DRAW_FRAME* aFrame );
|
||||
|
||||
static wxString BuildEditorName( EDA_DRAW_FRAME* aFrame );
|
||||
|
||||
wxPGWindowList CreateControls( wxPropertyGrid* aGrid, wxPGProperty* aProperty,
|
||||
const wxPoint& aPos, const wxSize& aSize ) const override;
|
||||
|
||||
bool OnEvent( wxPropertyGrid* aGrid, wxPGProperty* aProperty, wxWindow* aCtrl,
|
||||
wxEvent& aEvent ) const override;
|
||||
|
||||
private:
|
||||
EDA_DRAW_FRAME* m_frame;
|
||||
wxString m_editorName;
|
||||
};
|
||||
|
||||
|
||||
#endif //KICAD_PG_EDITORS_H
|
||||
|
@ -88,16 +88,32 @@ private:
|
||||
static int decodeModifiers( const wxKeyboardState* aState )
|
||||
{
|
||||
int mods = 0;
|
||||
int wxmods = aState->GetModifiers();
|
||||
|
||||
if( aState->ControlDown() )
|
||||
mods |= MD_CTRL;
|
||||
if( wxmods & wxMOD_ALTGR )
|
||||
mods |= MD_ALTGR;
|
||||
else
|
||||
{
|
||||
if( wxmods & wxMOD_CONTROL )
|
||||
mods |= MD_CTRL;
|
||||
|
||||
if( aState->AltDown() )
|
||||
mods |= MD_ALT;
|
||||
if( wxmods & wxMOD_ALT )
|
||||
mods |= MD_ALT;
|
||||
}
|
||||
|
||||
if( aState->ShiftDown() )
|
||||
if( wxmods & wxMOD_SHIFT )
|
||||
mods |= MD_SHIFT;
|
||||
|
||||
#ifdef wxMOD_META
|
||||
if( wxmods & wxMOD_META )
|
||||
mods |= MD_META;
|
||||
#endif
|
||||
|
||||
#ifdef wxMOD_WIN
|
||||
if( wxmods & wxMOD_WIN )
|
||||
mods |= MD_SUPER;
|
||||
#endif
|
||||
|
||||
return mods;
|
||||
}
|
||||
|
||||
|
@ -143,7 +143,10 @@ enum TOOL_MODIFIERS
|
||||
MD_SHIFT = 0x1000,
|
||||
MD_CTRL = 0x2000,
|
||||
MD_ALT = 0x4000,
|
||||
MD_MODIFIER_MASK = MD_SHIFT | MD_CTRL | MD_ALT,
|
||||
MD_SUPER = 0x8000,
|
||||
MD_META = 0x10000,
|
||||
MD_ALTGR = 0x20000,
|
||||
MD_MODIFIER_MASK = MD_SHIFT | MD_CTRL | MD_ALT | MD_SUPER | MD_META | MD_ALTGR,
|
||||
};
|
||||
|
||||
/// Defines when a context menu is opened.
|
||||
|
@ -55,7 +55,7 @@ namespace CLI
|
||||
|
||||
#define DEPRECATED_ARG_PLOT_INVISIBLE_TEXT "--plot-invisible-text"
|
||||
#define DEPRECATED_ARG_PLOT_INVISIBLE_TEXT_DESC "Deprecated. Has no effect."
|
||||
#define DEPRECATED_ARD_PLOT_INVISIBLE_TEXT_WARNING "--plot-invisible-text has been deprecated as of KiCad 9.0.1. It will have no effect."
|
||||
#define DEPRECATED_ARG_PLOT_INVISIBLE_TEXT_WARNING "--plot-invisible-text has been deprecated as of KiCad 9.0.1. It will have no effect."
|
||||
|
||||
#define ARG_FLIP_BOTTOM_PADS "--flip-bottom-pads"
|
||||
#define ARG_UNIQUE_PINS "--unique-pins"
|
||||
|
@ -152,7 +152,7 @@ int CLI::PCB_EXPORT_DXF_COMMAND::doPerform( KIWAY& aKiway )
|
||||
dxfJob->m_checkZonesBeforePlot = m_argParser.get<bool>( ARG_CHECK_ZONES );
|
||||
|
||||
if( m_argParser.get<bool>( DEPRECATED_ARG_PLOT_INVISIBLE_TEXT ) )
|
||||
wxFprintf( stdout, DEPRECATED_ARD_PLOT_INVISIBLE_TEXT_WARNING );
|
||||
wxFprintf( stdout, DEPRECATED_ARG_PLOT_INVISIBLE_TEXT_WARNING );
|
||||
|
||||
int drillShape = m_argParser.get<int>( ARG_DRILL_SHAPE_OPTION );
|
||||
dxfJob->m_drillShapeOption = static_cast<DRILL_MARKS>( drillShape );
|
||||
|
@ -142,7 +142,7 @@ int CLI::PCB_EXPORT_GERBER_COMMAND::populateJob( JOB_EXPORT_PCB_GERBER* aJob )
|
||||
aJob->m_argCommonLayers = From_UTF8( m_argParser.get<std::string>( ARG_COMMON_LAYERS ).c_str() );
|
||||
|
||||
if( m_argParser.get<bool>( DEPRECATED_ARG_PLOT_INVISIBLE_TEXT ) )
|
||||
wxFprintf( stdout, DEPRECATED_ARD_PLOT_INVISIBLE_TEXT_WARNING );
|
||||
wxFprintf( stdout, DEPRECATED_ARG_PLOT_INVISIBLE_TEXT_WARNING );
|
||||
|
||||
if( !wxFile::Exists( aJob->m_filename ) )
|
||||
{
|
||||
|
@ -151,7 +151,7 @@ int CLI::PCB_EXPORT_PDF_COMMAND::doPerform( KIWAY& aKiway )
|
||||
pdfJob->m_subtractSolderMaskFromSilk = m_argParser.get<bool>( ARG_SUBTRACT_SOLDERMASK );
|
||||
|
||||
if( m_argParser.get<bool>( DEPRECATED_ARG_PLOT_INVISIBLE_TEXT ) )
|
||||
wxFprintf( stdout, DEPRECATED_ARD_PLOT_INVISIBLE_TEXT_WARNING );
|
||||
wxFprintf( stdout, DEPRECATED_ARG_PLOT_INVISIBLE_TEXT_WARNING );
|
||||
|
||||
pdfJob->m_mirror = m_argParser.get<bool>( ARG_MIRROR );
|
||||
pdfJob->m_blackAndWhite = m_argParser.get<bool>( ARG_BLACKANDWHITE );
|
||||
|
@ -151,7 +151,7 @@ int CLI::PCB_EXPORT_SVG_COMMAND::doPerform( KIWAY& aKiway )
|
||||
svgJob->m_checkZonesBeforePlot = m_argParser.get<bool>( ARG_CHECK_ZONES );
|
||||
|
||||
if( m_argParser.get<bool>( DEPRECATED_ARG_PLOT_INVISIBLE_TEXT ) )
|
||||
wxFprintf( stdout, DEPRECATED_ARD_PLOT_INVISIBLE_TEXT_WARNING );
|
||||
wxFprintf( stdout, DEPRECATED_ARG_PLOT_INVISIBLE_TEXT_WARNING );
|
||||
|
||||
svgJob->m_fitPageToBoard = m_argParser.get<bool>( ARG_FIT_PAGE_TO_BOARD );
|
||||
|
||||
|
@ -82,8 +82,8 @@ CLI::SCH_EXPORT_BOM_COMMAND::SCH_EXPORT_BOM_COMMAND() : COMMAND( "bom" )
|
||||
.help( UTF8STDSTR( _( ARG_EXCLUDE_DNP_DESC ) ) )
|
||||
.flag();
|
||||
|
||||
m_argParser.add_argument( ARG_INCLUDE_EXCLUDED_FROM_BOM )
|
||||
.help( UTF8STDSTR( _( ARG_INCLUDE_EXCLUDED_FROM_BOM_DESC ) ) )
|
||||
m_argParser.add_argument( DEPRECATED_ARG_INCLUDE_EXCLUDED_FROM_BOM )
|
||||
.help( UTF8STDSTR( _( DEPRECATED_ARG_INCLUDE_EXCLUDED_FROM_BOM_DESC ) ) )
|
||||
.flag();
|
||||
|
||||
// Output formatting options
|
||||
@ -141,25 +141,19 @@ int CLI::SCH_EXPORT_BOM_COMMAND::doPerform( KIWAY& aKiway )
|
||||
bomJob->SetConfiguredOutputPath( m_argOutput );
|
||||
|
||||
bomJob->m_bomPresetName = From_UTF8( m_argParser.get<std::string>( ARG_PRESET ).c_str() );
|
||||
bomJob->m_bomFmtPresetName =
|
||||
From_UTF8( m_argParser.get<std::string>( ARG_FMT_PRESET ).c_str() );
|
||||
bomJob->m_bomFmtPresetName = From_UTF8( m_argParser.get<std::string>( ARG_FMT_PRESET ).c_str() );
|
||||
|
||||
// Format options
|
||||
bomJob->m_fieldDelimiter =
|
||||
From_UTF8( m_argParser.get<std::string>( ARG_FIELD_DELIMITER ).c_str() );
|
||||
bomJob->m_stringDelimiter =
|
||||
From_UTF8( m_argParser.get<std::string>( ARG_STRING_DELIMITER ).c_str() );
|
||||
bomJob->m_fieldDelimiter = From_UTF8( m_argParser.get<std::string>( ARG_FIELD_DELIMITER ).c_str() );
|
||||
bomJob->m_stringDelimiter = From_UTF8( m_argParser.get<std::string>( ARG_STRING_DELIMITER ).c_str() );
|
||||
bomJob->m_refDelimiter = From_UTF8( m_argParser.get<std::string>( ARG_REF_DELIMITER ).c_str() );
|
||||
bomJob->m_refRangeDelimiter =
|
||||
From_UTF8( m_argParser.get<std::string>( ARG_REF_RANGE_DELIMITER ).c_str() );
|
||||
bomJob->m_refRangeDelimiter = From_UTF8( m_argParser.get<std::string>( ARG_REF_RANGE_DELIMITER ).c_str() );
|
||||
bomJob->m_keepTabs = m_argParser.get<bool>( ARG_KEEP_TABS );
|
||||
bomJob->m_keepLineBreaks = m_argParser.get<bool>( ARG_KEEP_LINE_BREAKS );
|
||||
|
||||
// Output fields options
|
||||
bomJob->m_fieldsOrdered =
|
||||
convertStringList( From_UTF8( m_argParser.get<std::string>( ARG_FIELDS ).c_str() ) );
|
||||
bomJob->m_fieldsLabels =
|
||||
convertStringList( From_UTF8( m_argParser.get<std::string>( ARG_LABELS ).c_str() ) );
|
||||
bomJob->m_fieldsOrdered = convertStringList( From_UTF8( m_argParser.get<std::string>( ARG_FIELDS ).c_str() ) );
|
||||
bomJob->m_fieldsLabels = convertStringList( From_UTF8( m_argParser.get<std::string>( ARG_LABELS ).c_str() ) );
|
||||
|
||||
// We only apply the default labels if the default fields are used
|
||||
if( m_argParser.is_used( ARG_FIELDS ) && !m_argParser.is_used( ARG_LABELS ) )
|
||||
@ -167,13 +161,14 @@ int CLI::SCH_EXPORT_BOM_COMMAND::doPerform( KIWAY& aKiway )
|
||||
bomJob->m_fieldsLabels.clear();
|
||||
}
|
||||
|
||||
bomJob->m_fieldsGroupBy =
|
||||
convertStringList( From_UTF8( m_argParser.get<std::string>( ARG_GROUP_BY ).c_str() ) );
|
||||
bomJob->m_fieldsGroupBy = convertStringList( From_UTF8( m_argParser.get<std::string>( ARG_GROUP_BY ).c_str() ) );
|
||||
bomJob->m_sortField = From_UTF8( m_argParser.get<std::string>( ARG_SORT_FIELD ).c_str() );
|
||||
bomJob->m_sortAsc = m_argParser.get<bool>( ARG_SORT_ASC );
|
||||
bomJob->m_filterString = From_UTF8( m_argParser.get<std::string>( ARG_FILTER ).c_str() );
|
||||
bomJob->m_excludeDNP = m_argParser.get<bool>( ARG_EXCLUDE_DNP );
|
||||
bomJob->m_includeExcludedFromBOM = m_argParser.get<bool>( ARG_INCLUDE_EXCLUDED_FROM_BOM );
|
||||
|
||||
if( m_argParser.get<bool>( DEPRECATED_ARG_INCLUDE_EXCLUDED_FROM_BOM ) )
|
||||
wxFprintf( stdout, DEPRECATED_ARG_INCLUDE_EXCLUDED_FROM_BOM_WARNING );
|
||||
|
||||
if( !wxFile::Exists( bomJob->m_filename ) )
|
||||
{
|
||||
|
@ -27,8 +27,7 @@ namespace CLI
|
||||
{
|
||||
// Options for selecting presets of the export, e.g. GroupedByValue and CSV
|
||||
#define ARG_PRESET "--preset"
|
||||
#define ARG_PRESET_DESC "Use a named BOM preset setting from the schematic, e.g. " \
|
||||
"\"Grouped By Value\"."
|
||||
#define ARG_PRESET_DESC "Use a named BOM preset setting from the schematic, e.g. \"Grouped By Value\"."
|
||||
|
||||
#define ARG_FMT_PRESET "--format-preset"
|
||||
#define ARG_FMT_PRESET_DESC "Use a named BOM format preset setting from the schematic, e.g. CSV."
|
||||
@ -44,20 +43,17 @@ namespace CLI
|
||||
#define ARG_REF_DELIMITER_DESC "Character to place between individual references."
|
||||
|
||||
#define ARG_REF_RANGE_DELIMITER "--ref-range-delimiter"
|
||||
#define ARG_REF_RANGE_DELIMITER_DESC "Character to place in ranges of references. Leave " \
|
||||
"blank for no ranges."
|
||||
#define ARG_REF_RANGE_DELIMITER_DESC "Character to place in ranges of references. Leave blank for no ranges."
|
||||
|
||||
#define ARG_KEEP_TABS "--keep-tabs"
|
||||
#define ARG_KEEP_TABS_DESC "Keep tab characters from input fields. Stripped by default."
|
||||
|
||||
#define ARG_KEEP_LINE_BREAKS "--keep-line-breaks"
|
||||
#define ARG_KEEP_LINE_BREAKS_DESC "Keep line break characters from input fields. Stripped " \
|
||||
"by default."
|
||||
#define ARG_KEEP_LINE_BREAKS_DESC "Keep line break characters from input fields. Stripped by default."
|
||||
|
||||
//Options for controlling the fields and the grouping
|
||||
#define ARG_FIELDS "--fields"
|
||||
#define ARG_FIELDS_DESC \
|
||||
"An ordered list of fields to export. See documentation for special substitutions."
|
||||
#define ARG_FIELDS_DESC "An ordered list of fields to export. See documentation for special substitutions."
|
||||
|
||||
#define ARG_LABELS "--labels"
|
||||
#define ARG_LABELS_DESC "An ordered list of labels to apply the exported fields."
|
||||
@ -77,8 +73,10 @@ namespace CLI
|
||||
#define ARG_EXCLUDE_DNP "--exclude-dnp"
|
||||
#define ARG_EXCLUDE_DNP_DESC "Exclude symbols marked Do-Not-Populate."
|
||||
|
||||
#define ARG_INCLUDE_EXCLUDED_FROM_BOM "--include-excluded-from-bom"
|
||||
#define ARG_INCLUDE_EXCLUDED_FROM_BOM_DESC "Include symbols marked 'Exclude from BOM'."
|
||||
#define DEPRECATED_ARG_INCLUDE_EXCLUDED_FROM_BOM "--include-excluded-from-bom"
|
||||
#define DEPRECATED_ARG_INCLUDE_EXCLUDED_FROM_BOM_DESC "Deprecated. Has no effect."
|
||||
#define DEPRECATED_ARG_INCLUDE_EXCLUDED_FROM_BOM_WARNING "--include-excluded-from-bom has been deprecated as of " \
|
||||
"KiCad 10.0.0. It will have no effect."
|
||||
|
||||
class SCH_EXPORT_BOM_COMMAND : public COMMAND
|
||||
{
|
||||
|
@ -30,96 +30,10 @@
|
||||
#include <wx/dir.h>
|
||||
#include <wx/dirdlg.h>
|
||||
#include <wx/settings.h>
|
||||
#include <wx/bitmap.h>
|
||||
#include "template_default_html.h"
|
||||
|
||||
static const wxString GetWelcomeHtml()
|
||||
{
|
||||
return wxString(
|
||||
"<!DOCTYPE html>"
|
||||
"<html lang=\"en\">"
|
||||
"<head>"
|
||||
"<meta charset=\"UTF-8\">"
|
||||
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
|
||||
"<title>KiCad Project Template Selector</title>"
|
||||
"<style>"
|
||||
"body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; margin: 0; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: #333; min-height: 100vh; box-sizing: border-box; }"
|
||||
".container { max-width: 800px; margin: 0 auto; background: rgba(255, 255, 255, 0.95); border-radius: 12px; padding: 30px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); backdrop-filter: blur(10px); }"
|
||||
".header { text-align: center; margin-bottom: 30px; }"
|
||||
".logo { font-size: 2.5rem; font-weight: bold; color: #4a5568; margin-bottom: 10px; text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1); }"
|
||||
".subtitle { font-size: 1.2rem; color: #666; margin-bottom: 20px; }"
|
||||
".welcome-card { "
|
||||
#if defined( __MINGW32__ )
|
||||
"background: #4299e1;" // linear-gradient does not work with webview used on MSYS2
|
||||
#else
|
||||
"background: linear-gradient(135deg, #4299e1, #3182ce);"
|
||||
#endif
|
||||
"color: white; padding: 25px; border-radius: 10px; margin-bottom: 25px; box-shadow: 0 4px 15px rgba(66, 153, 225, 0.3); }"
|
||||
".welcome-card h2 { margin-top: 0; font-size: 1.8rem; margin-bottom: 15px; }"
|
||||
".instructions { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; margin-bottom: 25px; }"
|
||||
".instruction-card { background: #f7fafc; border: 2px solid #e2e8f0; border-radius: 8px; padding: 20px; transition: all 0.3s ease; position: relative; overflow: hidden; }"
|
||||
".instruction-card:hover { transform: translateY(-2px); box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); border-color: #4299e1; }"
|
||||
".instruction-card::before { content: ''; position: absolute; top: 0; left: 0; width: 4px; height: 100%; background: linear-gradient(135deg, #4299e1, #3182ce); }"
|
||||
".instruction-card h3 { color: #2d3748; margin-top: 0; margin-bottom: 10px; font-size: 1.3rem; }"
|
||||
".instruction-card p { color: #4a5568; line-height: 1.6; margin: 0; }"
|
||||
".features { background: #f0fff4; border: 2px solid #9ae6b4; border-radius: 8px; padding: 20px; margin-bottom: 25px; }"
|
||||
".features h3 { color: #22543d; margin-top: 0; margin-bottom: 15px; font-size: 1.4rem; }"
|
||||
".features ul { color: #2f855a; line-height: 1.8; margin: 0; padding-left: 20px; }"
|
||||
".features li { margin-bottom: 8px; }"
|
||||
".tips { background: #fffaf0; border: 2px solid #fbd38d; border-radius: 8px; padding: 20px; }"
|
||||
".tips h3 { color: #c05621; margin-top: 0; margin-bottom: 15px; font-size: 1.4rem; }"
|
||||
".tips p { color: #c05621; line-height: 1.6; margin: 0 0 10px 0; }"
|
||||
".highlight { background: linear-gradient(120deg, #a8edea 0%, #fed6e3 100%); padding: 2px 6px; border-radius: 4px; font-weight: 600; }"
|
||||
"</style>"
|
||||
"</head>"
|
||||
"<body>"
|
||||
"<div class=\"container\">"
|
||||
"<div class=\"header\">"
|
||||
"<div class=\"logo\">KiCad 📑</div>"
|
||||
"<div class=\"subtitle\">" + _( "Project Template Selector" ) + "</div>"
|
||||
"</div>"
|
||||
"<div class=\"welcome-card\">"
|
||||
"<h2>" + _( "Welcome to Template Selection!" ) + "</h2>"
|
||||
"<p>" + _( "Choose from a variety of pre-configured project templates to jumpstart your PCB design. Templates provide ready-to-use project structures with common components, libraries, and design rules." ) + "</p>"
|
||||
"</div>"
|
||||
"<div class=\"instructions\">"
|
||||
"<div class=\"instruction-card\">"
|
||||
"<h3>→ " + _( "Browse Templates" ) + "</h3>"
|
||||
"<p>" + _( "Navigate through the template tabs above to explore different categories of project templates. Each tab contains templates organized by type or complexity." ) + "</p>"
|
||||
"</div>"
|
||||
"<div class=\"instruction-card\">"
|
||||
"<h3>→ " + _( "Select a Template" ) + "</h3>"
|
||||
"<p>" + _( "Click on any template in the list to " ) + "<span class=\"highlight\">" + _( "preview its details" ) + "</span>. " + _( "The template information will appear in this panel, showing descriptions, included components, and project structure." ) + "</p>"
|
||||
"</div>"
|
||||
"<div class=\"instruction-card\">"
|
||||
"<h3>→ " + _( "Customize Path" ) + "</h3>"
|
||||
"<p>" + _( "Use the " ) + "<span class=\"highlight\">" + _( "folder path field" ) + "</span> " + _( "above to browse custom template directories. Click the folder icon to browse, or the refresh icon to reload templates." ) + "</p>"
|
||||
"</div>"
|
||||
"<div class=\"instruction-card\">"
|
||||
"<h3>→ " + _( "Create Project" ) + "</h3>"
|
||||
"<p>" + _( "Once you've found the right template, click " ) + "<span class=\"highlight\">" + _( "OK" ) + "</span> " + _( "to create a new project based on the selected template. Your project will inherit all template settings and files." ) + "</p>"
|
||||
"</div>"
|
||||
"</div>"
|
||||
"<div class=\"features\">"
|
||||
"<h3>" + _( "What You Get with Templates" ) + "</h3>"
|
||||
"<ul>"
|
||||
"<li><strong>" + _( "Pre-configured libraries" ) + "</strong> " + _( "- Common components and footprints already linked" ) + "</li>"
|
||||
"<li><strong>" + _( "Design rules" ) + "</strong> " + _( "- Appropriate electrical and mechanical constraints" ) + "</li>"
|
||||
"<li><strong>" + _( "Layer stackups" ) + "</strong> " + _( "- Optimized for the intended application" ) + "</li>"
|
||||
"<li><strong>" + _( "Component placement" ) + "</strong> " + _( "- Basic layout and routing guidelines" ) + "</li>"
|
||||
"<li><strong>" + _( "Documentation" ) + "</strong> " + _( "- README files and design notes" ) + "</li>"
|
||||
"<li><strong>" + _( "Manufacturing files" ) + "</strong> " + _( "- Gerber and drill file configurations" ) + "</li>"
|
||||
"</ul>"
|
||||
"</div>"
|
||||
"<div class=\"tips\">"
|
||||
"<h3>" + _( "Pro Tips" ) + "</h3>"
|
||||
"<p><strong>" + _( "Start Simple:" ) + "</strong> " + _( "Begin with basic templates and add more elements as you go." ) + "</p>"
|
||||
"<p><strong>" + _( "Customize Later:" ) + "</strong> " + _( "Templates are starting points - you can modify libraries, rules, and layouts after project creation." ) + "</p>"
|
||||
"<p><strong>" + _( "Save Your Own:" ) + "</strong> " + _( "Once you develop preferred settings, create a custom template for future projects." ) + "</p>"
|
||||
"</div>"
|
||||
"</div>"
|
||||
"</body>"
|
||||
"</html>"
|
||||
);
|
||||
}
|
||||
// Welcome / fallback HTML now provided by template_default_html.h
|
||||
|
||||
TEMPLATE_SELECTION_PANEL::TEMPLATE_SELECTION_PANEL( wxNotebookPage* aParent,
|
||||
const wxString& aPath ) :
|
||||
@ -182,7 +96,12 @@ void TEMPLATE_WIDGET::SetTemplate( PROJECT_TEMPLATE* aTemplate )
|
||||
m_staticTitle->SetFont( KIUI::GetInfoFont( this ) );
|
||||
m_staticTitle->SetLabel( *aTemplate->GetTitle() );
|
||||
m_staticTitle->Wrap( 100 );
|
||||
m_bitmapIcon->SetBitmap( *aTemplate->GetIcon() );
|
||||
wxBitmap* icon = aTemplate->GetIcon();
|
||||
|
||||
if( icon && icon->IsOk() )
|
||||
m_bitmapIcon->SetBitmap( *icon );
|
||||
else
|
||||
m_bitmapIcon->SetBitmap( KiBitmap( BITMAPS::icon_kicad ) );
|
||||
}
|
||||
|
||||
|
||||
@ -229,7 +148,7 @@ void DIALOG_TEMPLATE_SELECTOR::OnPageChange( wxNotebookEvent& event )
|
||||
|
||||
DIALOG_TEMPLATE_SELECTOR::DIALOG_TEMPLATE_SELECTOR( wxWindow* aParent, const wxPoint& aPos,
|
||||
const wxSize& aSize,
|
||||
std::map<wxString, wxFileName> aTitleDirMap,
|
||||
std::vector<std::pair<wxString, wxFileName>> aTitleDirList,
|
||||
const wxFileName& aDefaultTemplate ) :
|
||||
DIALOG_TEMPLATE_SELECTOR_BASE( aParent, wxID_ANY, _( "Project Template Selector" ), aPos,
|
||||
aSize )
|
||||
@ -241,7 +160,7 @@ DIALOG_TEMPLATE_SELECTOR::DIALOG_TEMPLATE_SELECTOR( wxWindow* aParent, const wxP
|
||||
m_defaultTemplatePath = aDefaultTemplate;
|
||||
m_defaultWidget = nullptr;
|
||||
|
||||
for( auto& [title, pathFname] : aTitleDirMap )
|
||||
for( auto& [title, pathFname] : aTitleDirList )
|
||||
{
|
||||
pathFname.Normalize( FN_NORMALIZE_FLAGS | wxPATH_NORM_ENV_VARS );
|
||||
wxString path = pathFname.GetFullPath(); // caller ensures this ends with file separator.
|
||||
@ -291,7 +210,7 @@ DIALOG_TEMPLATE_SELECTOR::DIALOG_TEMPLATE_SELECTOR( wxWindow* aParent, const wxP
|
||||
{
|
||||
wxFileName htmlFile = m_selectedWidget->GetTemplate()->GetHtmlFile();
|
||||
|
||||
if( htmlFile.FileExists() && htmlFile.IsFileReadable() )
|
||||
if( htmlFile.FileExists() && htmlFile.IsFileReadable() && htmlFile.GetSize() > 100 /* Basic HTML */ )
|
||||
m_webviewPanel->LoadURL( wxFileName::FileNameToURL( htmlFile ) );
|
||||
else
|
||||
m_webviewPanel->SetPage( GetWelcomeHtml() );
|
||||
@ -321,21 +240,7 @@ void DIALOG_TEMPLATE_SELECTOR::SetWidget( TEMPLATE_WIDGET* aWidget )
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback to a simple template info page if no HTML file exists
|
||||
wxString templateHtml = wxString::Format(
|
||||
"<!DOCTYPE html>"
|
||||
"<html><head><meta charset='UTF-8'><style>"
|
||||
"body { font-family: Arial, sans-serif; margin: 20px; }"
|
||||
".template-info { background: #f0f8ff; padding: 20px; border-radius: 8px; }"
|
||||
"h1 { color: #333; margin-top: 0; }"
|
||||
"</style></head><body>"
|
||||
"<div class='template-info'>"
|
||||
"<h1>%s</h1>"
|
||||
"<p>Template selected. Click OK to create a new project based on this template.</p>"
|
||||
"</div></body></html>",
|
||||
*aWidget->GetTemplate()->GetTitle()
|
||||
);
|
||||
m_webviewPanel->SetPage( templateHtml );
|
||||
m_webviewPanel->SetPage( GetTemplateInfoHtml( *aWidget->GetTemplate()->GetTitle() ) );
|
||||
}
|
||||
}
|
||||
|
||||
@ -345,12 +250,21 @@ void DIALOG_TEMPLATE_SELECTOR::AddTemplate( int aPage, PROJECT_TEMPLATE* aTempla
|
||||
TEMPLATE_WIDGET* w = new TEMPLATE_WIDGET( m_panels[aPage]->m_scrolledWindow, this );
|
||||
w->SetTemplate( aTemplate );
|
||||
m_panels[aPage]->AddTemplateWidget( w );
|
||||
m_allWidgets.push_back( w );
|
||||
|
||||
wxFileName base = aTemplate->GetHtmlFile();
|
||||
base.RemoveLastDir();
|
||||
|
||||
if( m_defaultTemplatePath.IsOk() && base == m_defaultTemplatePath )
|
||||
if( !m_defaultWidget || (m_defaultTemplatePath.IsOk() && base == m_defaultTemplatePath) )
|
||||
m_defaultWidget = w;
|
||||
|
||||
wxString dirName = base.GetDirs().IsEmpty() ? wxString() : base.GetDirs().back();
|
||||
|
||||
if( dirName.CmpNoCase( "default" ) == 0 )
|
||||
{
|
||||
// Prefer a directory literally named 'default'
|
||||
m_defaultWidget = w;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -359,6 +273,11 @@ PROJECT_TEMPLATE* DIALOG_TEMPLATE_SELECTOR::GetSelectedTemplate()
|
||||
return m_selectedWidget? m_selectedWidget->GetTemplate() : nullptr;
|
||||
}
|
||||
|
||||
PROJECT_TEMPLATE* DIALOG_TEMPLATE_SELECTOR::GetDefaultTemplate()
|
||||
{
|
||||
return m_defaultWidget? m_defaultWidget->GetTemplate() : nullptr;
|
||||
}
|
||||
|
||||
|
||||
void DIALOG_TEMPLATE_SELECTOR::buildPageContent( const wxString& aPath, int aPage )
|
||||
{
|
||||
|
@ -29,7 +29,8 @@
|
||||
#include <widgets/webview_panel.h>
|
||||
#include "project_template.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <wx/filename.h>
|
||||
|
||||
class DIALOG_TEMPLATE_SELECTOR;
|
||||
@ -91,13 +92,14 @@ class DIALOG_TEMPLATE_SELECTOR : public DIALOG_TEMPLATE_SELECTOR_BASE
|
||||
{
|
||||
public:
|
||||
DIALOG_TEMPLATE_SELECTOR( wxWindow* aParent, const wxPoint& aPos, const wxSize& aSize,
|
||||
std::map<wxString, wxFileName> aTitleDirMap,
|
||||
std::vector<std::pair<wxString, wxFileName>> aTitleDirList,
|
||||
const wxFileName& aDefaultTemplate );
|
||||
|
||||
/**
|
||||
* @return the selected template, or NULL
|
||||
*/
|
||||
PROJECT_TEMPLATE* GetSelectedTemplate();
|
||||
PROJECT_TEMPLATE* GetDefaultTemplate();
|
||||
|
||||
void SetWidget( TEMPLATE_WIDGET* aWidget );
|
||||
|
||||
@ -123,6 +125,8 @@ protected:
|
||||
TEMPLATE_WIDGET* m_selectedWidget;
|
||||
wxFileName m_defaultTemplatePath;
|
||||
TEMPLATE_WIDGET* m_defaultWidget;
|
||||
// Keep track of all template widgets so we can pick a sensible default
|
||||
std::vector<TEMPLATE_WIDGET*> m_allWidgets;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
149
kicad/dialogs/template_default_html.h
Normal file
149
kicad/dialogs/template_default_html.h
Normal file
@ -0,0 +1,149 @@
|
||||
/*
|
||||
* KiCad Project Template HTML defaults
|
||||
* Extracted from dialog_template_selector.cpp to allow reuse and to provide
|
||||
* a styled fallback page when a template does not supply an info.html.
|
||||
*/
|
||||
|
||||
#ifndef KICAD_TEMPLATE_DEFAULT_HTML_H
|
||||
#define KICAD_TEMPLATE_DEFAULT_HTML_H
|
||||
|
||||
#include <wx/string.h>
|
||||
|
||||
// Uses _() translation macro which is defined globally in KiCad builds.
|
||||
|
||||
// Welcome (no template selected) page.
|
||||
inline wxString GetWelcomeHtml()
|
||||
{
|
||||
return wxString(
|
||||
"<!DOCTYPE html>"
|
||||
"<html lang=\"en\">"
|
||||
"<head>"
|
||||
"<meta charset=\"UTF-8\">"
|
||||
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
|
||||
"<title>KiCad Project Template Selector</title>"
|
||||
"<style>"
|
||||
"body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; margin: 0; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: #333; min-height: 100vh; box-sizing: border-box; }"
|
||||
".container { max-width: 800px; margin: 0 auto; background: rgba(255, 255, 255, 0.95); border-radius: 12px; padding: 30px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); backdrop-filter: blur(10px); }"
|
||||
".header { text-align: center; margin-bottom: 30px; }"
|
||||
".logo { font-size: 2.5rem; font-weight: bold; color: #4a5568; margin-bottom: 10px; text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1); }"
|
||||
".subtitle { font-size: 1.2rem; color: #666; margin-bottom: 20px; }"
|
||||
".welcome-card { "
|
||||
#if defined( __MINGW32__ )
|
||||
"background: #4299e1;" // linear-gradient does not work with webview used on MSYS2
|
||||
#else
|
||||
"background: linear-gradient(135deg, #4299e1, #3182ce);"
|
||||
#endif
|
||||
"color: white; padding: 25px; border-radius: 10px; margin-bottom: 25px; box-shadow: 0 4px 15px rgba(66, 153, 225, 0.3); }"
|
||||
".welcome-card h2 { margin-top: 0; font-size: 1.8rem; margin-bottom: 15px; }"
|
||||
".instructions { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; margin-bottom: 25px; }"
|
||||
".instruction-card { background: #f7fafc; border: 2px solid #e2e8f0; border-radius: 8px; padding: 20px; transition: all 0.3s ease; position: relative; overflow: hidden; }"
|
||||
".instruction-card:hover { transform: translateY(-2px); box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); border-color: #4299e1; }"
|
||||
".instruction-card::before { content: ''; position: absolute; top: 0; left: 0; width: 4px; height: 100%; background: linear-gradient(135deg, #4299e1, #3182ce); }"
|
||||
".instruction-card h3 { color: #2d3748; margin-top: 0; margin-bottom: 10px; font-size: 1.3rem; }"
|
||||
".instruction-card p { color: #4a5568; line-height: 1.6; margin: 0; }"
|
||||
".features { background: #f0fff4; border: 2px solid #9ae6b4; border-radius: 8px; padding: 20px; margin-bottom: 25px; }"
|
||||
".features h3 { color: #22543d; margin-top: 0; margin-bottom: 15px; font-size: 1.4rem; }"
|
||||
".features ul { color: #2f855a; line-height: 1.8; margin: 0; padding-left: 20px; }"
|
||||
".features li { margin-bottom: 8px; }"
|
||||
".tips { background: #fffaf0; border: 2px solid #fbd38d; border-radius: 8px; padding: 20px; }"
|
||||
".tips h3 { color: #c05621; margin-top: 0; margin-bottom: 15px; font-size: 1.4rem; }"
|
||||
".tips p { color: #c05621; line-height: 1.6; margin: 0 0 10px 0; }"
|
||||
".highlight { background: linear-gradient(120deg, #a8edea 0%, #fed6e3 100%); padding: 2px 6px; border-radius: 4px; font-weight: 600; }"
|
||||
"</style>"
|
||||
"</head>"
|
||||
"<body>"
|
||||
"<div class=\"container\">"
|
||||
"<div class=\"header\">"
|
||||
"<div class=\"logo\">KiCad 📑</div>"
|
||||
"<div class=\"subtitle\">" + _( "Project Template Selector" ) + "</div>"
|
||||
"</div>"
|
||||
"<div class=\"welcome-card\">"
|
||||
"<h2>" + _( "Welcome to Template Selection!" ) + "</h2>"
|
||||
"<p>" + _( "Choose from a variety of pre-configured project templates to jumpstart your PCB design. Templates provide ready-to-use project structures with common components, libraries, and design rules." ) + "</p>"
|
||||
"</div>"
|
||||
"<div class=\"instructions\">"
|
||||
"<div class=\"instruction-card\">"
|
||||
"<h3>→ " + _( "Browse Templates" ) + "</h3>"
|
||||
"<p>" + _( "Navigate through the template tabs above to explore different categories of project templates. Each tab contains templates organized by type or complexity." ) + "</p>"
|
||||
"</div>"
|
||||
"<div class=\"instruction-card\">"
|
||||
"<h3>→ " + _( "Select a Template" ) + "</h3>"
|
||||
"<p>" + _( "Click on any template in the list to " ) + "<span class=\"highlight\">" + _( "preview its details" ) + "</span>. " + _( "The template information will appear in this panel, showing descriptions, included components, and project structure." ) + "</p>"
|
||||
"</div>"
|
||||
"<div class=\"instruction-card\">"
|
||||
"<h3>→ " + _( "Customize Path" ) + "</h3>"
|
||||
"<p>" + _( "Use the " ) + "<span class=\"highlight\">" + _( "folder path field" ) + "</span> " + _( "above to browse custom template directories. Click the folder icon to browse, or the refresh icon to reload templates." ) + "</p>"
|
||||
"</div>"
|
||||
"<div class=\"instruction-card\">"
|
||||
"<h3>→ " + _( "Create Project" ) + "</h3>"
|
||||
"<p>" + _( "Once you've found the right template, click " ) + "<span class=\"highlight\">" + _( "OK" ) + "</span> " + _( "to create a new project based on the selected template. Your project will inherit all template settings and files." ) + "</p>"
|
||||
"</div>"
|
||||
"</div>"
|
||||
"<div class=\"features\">"
|
||||
"<h3>" + _( "What You Get with Templates" ) + "</h3>"
|
||||
"<ul>"
|
||||
"<li><strong>" + _( "Pre-configured libraries" ) + "</strong> " + _( "- Common components and footprints already linked" ) + "</li>"
|
||||
"<li><strong>" + _( "Design rules" ) + "</strong> " + _( "- Appropriate electrical and mechanical constraints" ) + "</li>"
|
||||
"<li><strong>" + _( "Layer stackups" ) + "</strong> " + _( "- Optimized for the intended application" ) + "</li>"
|
||||
"<li><strong>" + _( "Component placement" ) + "</strong> " + _( "- Basic layout and routing guidelines" ) + "</li>"
|
||||
"<li><strong>" + _( "Documentation" ) + "</strong> " + _( "- README files and design notes" ) + "</li>"
|
||||
"<li><strong>" + _( "Manufacturing files" ) + "</strong> " + _( "- Gerber and drill file configurations" ) + "</li>"
|
||||
"</ul>"
|
||||
"</div>"
|
||||
"<div class=\"tips\">"
|
||||
"<h3>" + _( "Pro Tips" ) + "</h3>"
|
||||
"<p><strong>" + _( "Start Simple:" ) + "</strong> " + _( "Begin with basic templates and add more elements as you go." ) + "</p>"
|
||||
"<p><strong>" + _( "Customize Later:" ) + "</strong> " + _( "Templates are starting points - you can modify libraries, rules, and layouts after project creation." ) + "</p>"
|
||||
"<p><strong>" + _( "Save Your Own:" ) + "</strong> " + _( "Once you develop preferred settings, create a custom template for future projects." ) + "</p>"
|
||||
"</div>"
|
||||
"</div>"
|
||||
"</body>"
|
||||
"</html>"
|
||||
);
|
||||
}
|
||||
|
||||
// Fallback when a specific template has no meta/info.html.
|
||||
inline wxString GetTemplateInfoHtml( const wxString& aTemplateName )
|
||||
{
|
||||
return wxString(
|
||||
"<!DOCTYPE html>"
|
||||
"<html lang=\"en\">"
|
||||
"<head>"
|
||||
"<meta charset=\"UTF-8\">"
|
||||
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
|
||||
"<title>" + aTemplateName + " - KiCad Template</title>"
|
||||
"<style>"
|
||||
"body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; margin:0; padding:20px; background:#edf2f7; color:#2d3748; }"
|
||||
".container { max-width: 780px; margin:0 auto; background:#ffffff; border-radius:12px; padding:32px; box-shadow:0 6px 24px rgba(0,0,0,0.08); }"
|
||||
".header { display:flex; align-items:center; gap:12px; margin-bottom:24px; }"
|
||||
".badge { background:#3182ce; color:white; padding:4px 10px; border-radius:6px; font-size:0.75rem; letter-spacing:0.05em; text-transform:uppercase; }"
|
||||
"h1 { font-size:1.9rem; margin:0; }"
|
||||
"p { line-height:1.55; }"
|
||||
".cta { background:#ebf8ff; border:1px solid #bee3f8; padding:16px 20px; border-radius:10px; margin:28px 0 18px; }"
|
||||
".steps { display:grid; gap:16px; margin-top:10px; }"
|
||||
".step { background:#f7fafc; border:1px solid #e2e8f0; padding:14px 16px; border-radius:8px; }"
|
||||
".step h3 { margin:0 0 6px 0; font-size:1.05rem; }"
|
||||
".edit-hint { background:#fffaf0; border:1px solid #fbd38d; padding:14px 16px; border-radius:8px; margin-top:24px; }"
|
||||
"code { background:#f1f5f9; padding:2px 5px; border-radius:4px; font-size:0.85rem; }"
|
||||
"</style>"
|
||||
"</head><body><div class=\"container\">"
|
||||
"<div class=\"header\"><div class=\"badge\">" + _( "Template" ) + "</div><h1>" + aTemplateName + "</h1></div>"
|
||||
"<p>" + _( "This project template doesn't include an info page yet. You can still use it to create a new project." ) + "</p>"
|
||||
"<div class=\"cta\">"
|
||||
"<strong>" + _( "To use this template:" ) + "</strong>"
|
||||
"<div class=\"steps\">"
|
||||
"<div class=\"step\"><h3>1. " + _( "Create the project" ) + "</h3><p>" + _( "Click OK below. KiCad will create a new project folder populated with the contents of this template." ) + "</p></div>"
|
||||
"<div class=\"step\"><h3>2. " + _( "Open schematic and PCB" ) + "</h3><p>" + _( "Use the Project Manager tree or launch Schematic and PCB editors to begin designing." ) + "</p></div>"
|
||||
"<div class=\"step\"><h3>3. " + _( "Review libraries & settings" ) + "</h3><p>" + _( "Confirm symbol/footprint libraries, design rules, and board stackup match your needs." ) + "</p></div>"
|
||||
"</div>" // steps
|
||||
"</div>" // cta
|
||||
"<div class=\"edit-hint\">"
|
||||
"<strong>" + _( "Add an info page later:" ) + "</strong>"
|
||||
"<p>" + _( "Create a file at" ) + " <code>meta/info.html</code> " + _( "inside this template's directory to provide rich documentation, images, or guidance." ) + "</p>"
|
||||
"<p>" + _( "You can copy styling from the default pages for consistency." ) + "</p>"
|
||||
"</div>"
|
||||
"</div></body></html>"
|
||||
);
|
||||
}
|
||||
|
||||
#endif // KICAD_TEMPLATE_DEFAULT_HTML_H
|
@ -49,7 +49,8 @@
|
||||
#include <wildcards_and_files_ext.h>
|
||||
#include <confirm.h>
|
||||
|
||||
#include <git2.h>
|
||||
#include <git/git_backend.h>
|
||||
#include <git/libgit_backend.h>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "pgm_kicad.h"
|
||||
@ -102,8 +103,9 @@ bool PGM_KICAD::OnPgmInit()
|
||||
}
|
||||
#endif
|
||||
|
||||
// Initialize the git library before trying to initialize individual programs
|
||||
git_libgit2_init();
|
||||
// Initialize the git backend before trying to initialize individual programs
|
||||
SetGitBackend( new LIBGIT_BACKEND() );
|
||||
GetGitBackend()->Init();
|
||||
|
||||
static const wxCmdLineEntryDesc desc[] = {
|
||||
{ wxCMD_LINE_OPTION, "f", "frame", "Frame to load", wxCMD_LINE_VAL_STRING, 0 },
|
||||
@ -406,7 +408,9 @@ void PGM_KICAD::OnPgmExit()
|
||||
// especially wxSingleInstanceCheckerImpl earlier than wxApp and earlier
|
||||
// than static destruction would.
|
||||
Destroy();
|
||||
git_libgit2_shutdown();
|
||||
GetGitBackend()->Shutdown();
|
||||
delete GetGitBackend();
|
||||
SetGitBackend( nullptr );
|
||||
}
|
||||
|
||||
|
||||
|
@ -24,7 +24,7 @@
|
||||
*/
|
||||
|
||||
#include <stack>
|
||||
#include <git2.h>
|
||||
#include <git/git_backend.h>
|
||||
|
||||
#include <wx/regex.h>
|
||||
#include <wx/stdpaths.h>
|
||||
@ -802,14 +802,8 @@ void PROJECT_TREE_PANE::onRight( wxTreeEvent& Event )
|
||||
bool vcs_can_switch = vcs_has_repo;
|
||||
bool vcs_menu = Pgm().GetCommonSettings()->m_Git.enableGit;
|
||||
|
||||
// Check if the libgit2 library has been successfully initialized
|
||||
#if ( LIBGIT2_VER_MAJOR >= 1 ) || ( LIBGIT2_VER_MINOR >= 99 )
|
||||
int major, minor, rev;
|
||||
bool libgit_init = ( git_libgit2_version( &major, &minor, &rev ) == GIT_OK );
|
||||
#else
|
||||
//Work around libgit2 API change for supporting older platforms
|
||||
bool libgit_init = true;
|
||||
#endif
|
||||
// Check if the libgit2 library is available via backend
|
||||
bool libgit_init = GetGitBackend() && GetGitBackend()->IsLibraryAvailable();
|
||||
|
||||
vcs_menu &= libgit_init;
|
||||
|
||||
@ -2176,7 +2170,7 @@ void PROJECT_TREE_PANE::onGitCommit( wxCommandEvent& aEvent )
|
||||
auto result = commitHandler.PerformCommit( files, dlg.GetCommitMessage(),
|
||||
dlg.GetAuthorName(), dlg.GetAuthorEmail() );
|
||||
|
||||
if( result != GIT_COMMIT_HANDLER::CommitResult::Success )
|
||||
if( result != CommitResult::Success )
|
||||
{
|
||||
wxMessageBox( wxString::Format( _( "Failed to create commit: %s" ),
|
||||
commitHandler.GetErrorString() ) );
|
||||
|
@ -203,28 +203,28 @@ int KICAD_MANAGER_CONTROL::NewProject( const TOOL_EVENT& aEvent )
|
||||
}
|
||||
|
||||
KICAD_SETTINGS* settings = GetAppSettings<KICAD_SETTINGS>( "kicad" );
|
||||
std::map<wxString, wxFileName> titleDirMap;
|
||||
std::vector<std::pair<wxString, wxFileName>> titleDirList;
|
||||
wxFileName templatePath;
|
||||
|
||||
ENV_VAR_MAP_CITER itUser = Pgm().GetLocalEnvVariables().find( "KICAD_USER_TEMPLATE_DIR" );
|
||||
|
||||
if( itUser != Pgm().GetLocalEnvVariables().end() && itUser->second.GetValue() != wxEmptyString )
|
||||
{
|
||||
templatePath.AssignDir( itUser->second.GetValue() );
|
||||
titleDirList.emplace_back( _( "User Templates" ), templatePath );
|
||||
}
|
||||
|
||||
std::optional<wxString> v = ENV_VAR::GetVersionedEnvVarValue( Pgm().GetLocalEnvVariables(),
|
||||
wxT( "TEMPLATE_DIR" ) );
|
||||
|
||||
if( v && !v->IsEmpty() )
|
||||
{
|
||||
templatePath.AssignDir( *v );
|
||||
titleDirMap.emplace( _( "System Templates" ), templatePath );
|
||||
}
|
||||
|
||||
ENV_VAR_MAP_CITER itUser = Pgm().GetLocalEnvVariables().find( "KICAD_USER_TEMPLATE_DIR" );
|
||||
|
||||
if( itUser != Pgm().GetLocalEnvVariables().end() && itUser->second.GetValue() != wxEmptyString )
|
||||
{
|
||||
templatePath.AssignDir( itUser->second.GetValue() );
|
||||
titleDirMap.emplace( _( "User Templates" ), templatePath );
|
||||
titleDirList.emplace_back( _( "System Templates" ), templatePath );
|
||||
}
|
||||
|
||||
DIALOG_TEMPLATE_SELECTOR ps( m_frame, settings->m_TemplateWindowPos, settings->m_TemplateWindowSize,
|
||||
titleDirMap, defaultTemplate );
|
||||
titleDirList, defaultTemplate );
|
||||
|
||||
int result = ps.ShowModal();
|
||||
|
||||
@ -234,7 +234,12 @@ int KICAD_MANAGER_CONTROL::NewProject( const TOOL_EVENT& aEvent )
|
||||
if( result != wxID_OK )
|
||||
return -1;
|
||||
|
||||
if( !ps.GetSelectedTemplate() )
|
||||
PROJECT_TEMPLATE* selectedTemplate = ps.GetSelectedTemplate();
|
||||
|
||||
if( !selectedTemplate )
|
||||
selectedTemplate = ps.GetDefaultTemplate();
|
||||
|
||||
if( !selectedTemplate )
|
||||
{
|
||||
wxMessageBox( _( "No project template was selected. Cannot generate new project." ), _( "Error" ),
|
||||
wxOK | wxICON_ERROR, m_frame );
|
||||
@ -288,7 +293,7 @@ int KICAD_MANAGER_CONTROL::NewProject( const TOOL_EVENT& aEvent )
|
||||
|
||||
std::vector< wxFileName > destFiles;
|
||||
|
||||
if( ps.GetSelectedTemplate()->GetDestinationFiles( fn, destFiles ) )
|
||||
if( selectedTemplate->GetDestinationFiles( fn, destFiles ) )
|
||||
{
|
||||
std::vector<wxFileName> overwrittenFiles;
|
||||
|
||||
@ -318,7 +323,7 @@ int KICAD_MANAGER_CONTROL::NewProject( const TOOL_EVENT& aEvent )
|
||||
|
||||
wxString errorMsg;
|
||||
|
||||
if( !ps.GetSelectedTemplate()->CreateProject( fn, &errorMsg ) )
|
||||
if( !selectedTemplate->CreateProject( fn, &errorMsg ) )
|
||||
{
|
||||
DisplayErrorMessage( m_frame, _( "A problem occurred creating new project from template." ), errorMsg );
|
||||
return -1;
|
||||
|
@ -31,7 +31,9 @@ using namespace std::placeholders;
|
||||
#include <board_commit.h>
|
||||
#include <board.h>
|
||||
#include <footprint.h>
|
||||
#include <pcb_generator.h>
|
||||
#include <pcb_track.h>
|
||||
#include <generators/pcb_tuning_pattern.h>
|
||||
#include <tool/tool_manager.h>
|
||||
#include <tools/pcb_actions.h>
|
||||
#include <tools/global_edit_tool.h>
|
||||
@ -272,6 +274,15 @@ void DIALOG_GLOBAL_DELETION::DoGlobalDeletions()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for( PCB_GENERATOR* generator : board->Generators() )
|
||||
{
|
||||
if( PCB_TUNING_PATTERN* pattern = dynamic_cast<PCB_TUNING_PATTERN*>( generator ) )
|
||||
{
|
||||
if( pattern->GetBoardItems().empty() )
|
||||
commit.Remove( pattern );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
commit.Push( _( "Global Delete" ) );
|
||||
|
@ -23,6 +23,7 @@
|
||||
*/
|
||||
|
||||
#include <eda_item.h>
|
||||
#include <geometry/geometry_utils.h>
|
||||
#include <pcb_base_edit_frame.h>
|
||||
#include <router/pns_meander.h>
|
||||
#include <router/pns_meander_placer_base.h>
|
||||
|
@ -991,6 +991,39 @@ static struct PCB_SHAPE_DESC
|
||||
propMgr.OverrideAvailability( TYPE_HASH( PCB_SHAPE ), TYPE_HASH( EDA_SHAPE ),
|
||||
_HKI( "Fill" ), isNotBezier );
|
||||
|
||||
auto isCircle =
|
||||
[]( INSPECTABLE* aItem ) -> bool
|
||||
{
|
||||
if( PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( aItem ) )
|
||||
return shape->GetShape() == SHAPE_T::CIRCLE;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
auto isNotCircle =
|
||||
[]( INSPECTABLE* aItem ) -> bool
|
||||
{
|
||||
if( PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( aItem ) )
|
||||
return shape->GetShape() != SHAPE_T::CIRCLE;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
propMgr.OverrideAvailability( TYPE_HASH( PCB_SHAPE ), TYPE_HASH( EDA_SHAPE ),
|
||||
_HKI( "Start X" ), isNotCircle );
|
||||
propMgr.OverrideAvailability( TYPE_HASH( PCB_SHAPE ), TYPE_HASH( EDA_SHAPE ),
|
||||
_HKI( "Start Y" ), isNotCircle );
|
||||
propMgr.OverrideAvailability( TYPE_HASH( PCB_SHAPE ), TYPE_HASH( EDA_SHAPE ),
|
||||
_HKI( "End X" ), isNotCircle );
|
||||
propMgr.OverrideAvailability( TYPE_HASH( PCB_SHAPE ), TYPE_HASH( EDA_SHAPE ),
|
||||
_HKI( "End Y" ), isNotCircle );
|
||||
propMgr.OverrideAvailability( TYPE_HASH( PCB_SHAPE ), TYPE_HASH( EDA_SHAPE ),
|
||||
_HKI( "Center X" ), isCircle );
|
||||
propMgr.OverrideAvailability( TYPE_HASH( PCB_SHAPE ), TYPE_HASH( EDA_SHAPE ),
|
||||
_HKI( "Center Y" ), isCircle );
|
||||
propMgr.OverrideAvailability( TYPE_HASH( PCB_SHAPE ), TYPE_HASH( EDA_SHAPE ),
|
||||
_HKI( "Radius" ), isCircle );
|
||||
|
||||
auto isCopper =
|
||||
[]( INSPECTABLE* aItem ) -> bool
|
||||
{
|
||||
|
@ -225,6 +225,7 @@ static struct PCB_TABLECELL_DESC
|
||||
propMgr.Mask( TYPE_HASH( PCB_TABLECELL ), TYPE_HASH( PCB_SHAPE ), _HKI( "Layer" ) );
|
||||
propMgr.Mask( TYPE_HASH( PCB_TABLECELL ), TYPE_HASH( PCB_SHAPE ), _HKI( "Soldermask" ) );
|
||||
propMgr.Mask( TYPE_HASH( PCB_TABLECELL ), TYPE_HASH( PCB_SHAPE ), _HKI( "Soldermask Margin Override" ) );
|
||||
propMgr.Mask( TYPE_HASH( PCB_TABLECELL ), TYPE_HASH( EDA_SHAPE ), _HKI( "Corner Radius" ) );
|
||||
|
||||
propMgr.Mask( TYPE_HASH( PCB_TABLECELL ), TYPE_HASH( BOARD_CONNECTED_ITEM ), _HKI( "Net" ) );
|
||||
|
||||
|
@ -43,10 +43,11 @@
|
||||
#include <api/api_utils.h>
|
||||
#include <api/board/board_types.pb.h>
|
||||
|
||||
|
||||
PCB_TEXTBOX::PCB_TEXTBOX( BOARD_ITEM* aParent, KICAD_T aType ) :
|
||||
PCB_SHAPE( aParent, aType, SHAPE_T::RECTANGLE ),
|
||||
EDA_TEXT( pcbIUScale ),
|
||||
m_borderEnabled( true )
|
||||
PCB_SHAPE( aParent, aType, SHAPE_T::RECTANGLE ),
|
||||
EDA_TEXT( pcbIUScale ),
|
||||
m_borderEnabled( true )
|
||||
{
|
||||
SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
|
||||
SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
|
||||
@ -59,6 +60,7 @@ PCB_TEXTBOX::PCB_TEXTBOX( BOARD_ITEM* aParent, KICAD_T aType ) :
|
||||
m_marginBottom = defaultMargin;
|
||||
}
|
||||
|
||||
|
||||
PCB_TEXTBOX::~PCB_TEXTBOX()
|
||||
{
|
||||
}
|
||||
@ -839,6 +841,8 @@ static struct PCB_TEXTBOX_DESC
|
||||
propMgr.Mask( TYPE_HASH( PCB_TEXTBOX ), TYPE_HASH( EDA_SHAPE ), _HKI( "Line Width" ) );
|
||||
propMgr.Mask( TYPE_HASH( PCB_TEXTBOX ), TYPE_HASH( EDA_SHAPE ), _HKI( "Line Style" ) );
|
||||
propMgr.Mask( TYPE_HASH( PCB_TEXTBOX ), TYPE_HASH( EDA_SHAPE ), _HKI( "Filled" ) );
|
||||
propMgr.Mask( TYPE_HASH( PCB_TEXTBOX ), TYPE_HASH( EDA_SHAPE ), _HKI( "Corner Radius" ) );
|
||||
|
||||
propMgr.Mask( TYPE_HASH( PCB_TEXTBOX ), TYPE_HASH( EDA_TEXT ), _HKI( "Color" ) );
|
||||
|
||||
propMgr.Mask( TYPE_HASH( PCB_TEXTBOX ), TYPE_HASH( PCB_SHAPE ), _HKI( "Soldermask" ) );
|
||||
|
@ -265,6 +265,7 @@ void PCB_BASE_EDIT_FRAME::PutDataInPreviousState( PICKED_ITEMS_LIST* aList )
|
||||
bool reBuild_ratsnest = false;
|
||||
bool deep_reBuild_ratsnest = false; // true later if pointers must be rebuilt
|
||||
bool solder_mask_dirty = false;
|
||||
bool current_show_ratsnest = GetPcbNewSettings()->m_Display.m_ShowGlobalRatsnest;
|
||||
std::vector<BOX2I> dirty_rule_areas;
|
||||
|
||||
KIGFX::PCB_VIEW* view = GetCanvas()->GetView();
|
||||
@ -283,6 +284,32 @@ void PCB_BASE_EDIT_FRAME::PutDataInPreviousState( PICKED_ITEMS_LIST* aList )
|
||||
|
||||
std::unordered_map<EDA_ITEM*, ITEM_CHANGE_TYPE> item_changes;
|
||||
|
||||
auto clear_local_ratsnest_flags =
|
||||
[&]( EDA_ITEM* item )
|
||||
{
|
||||
switch( item->Type() )
|
||||
{
|
||||
case PCB_TRACE_T:
|
||||
case PCB_ARC_T:
|
||||
case PCB_VIA_T:
|
||||
static_cast<PCB_TRACK*>( item )->SetLocalRatsnestVisible( current_show_ratsnest );
|
||||
break;
|
||||
|
||||
case PCB_ZONE_T:
|
||||
static_cast<ZONE*>( item )->SetLocalRatsnestVisible( current_show_ratsnest );
|
||||
break;
|
||||
|
||||
case PCB_FOOTPRINT_T:
|
||||
for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
|
||||
pad->SetLocalRatsnestVisible( current_show_ratsnest );
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
auto update_item_change_state =
|
||||
[&]( EDA_ITEM* item, ITEM_CHANGE_TYPE change_type )
|
||||
{
|
||||
@ -431,71 +458,79 @@ void PCB_BASE_EDIT_FRAME::PutDataInPreviousState( PICKED_ITEMS_LIST* aList )
|
||||
switch( aList->GetPickedItemStatus( ii ) )
|
||||
{
|
||||
case UNDO_REDO::CHANGED: /* Exchange old and new data for each item */
|
||||
{
|
||||
BOARD_ITEM* item = (BOARD_ITEM*) eda_item;
|
||||
BOARD_ITEM_CONTAINER* parent = GetBoard();
|
||||
|
||||
if( item->GetParentFootprint() )
|
||||
if( eda_item->IsBOARD_ITEM() )
|
||||
{
|
||||
// We need the current item and it's parent, which may be different from what
|
||||
// was stored if we're multiple frames up the undo stack.
|
||||
item = GetBoard()->ResolveItem( item->m_Uuid );
|
||||
parent = item->GetParentFootprint();
|
||||
BOARD_ITEM* item = static_cast<BOARD_ITEM*>( eda_item );
|
||||
BOARD_ITEM* image = static_cast<BOARD_ITEM*>( aList->GetPickedItemLink( ii ) );
|
||||
BOARD_ITEM_CONTAINER* parent = GetBoard();
|
||||
|
||||
if( item->GetParentFootprint() )
|
||||
{
|
||||
// We need the current item and it's parent, which may be different from what
|
||||
// was stored if we're multiple frames up the undo stack.
|
||||
item = GetBoard()->ResolveItem( item->m_Uuid );
|
||||
parent = item->GetParentFootprint();
|
||||
}
|
||||
|
||||
view->Remove( item );
|
||||
parent->Remove( item );
|
||||
|
||||
item->SwapItemData( image );
|
||||
|
||||
clear_local_ratsnest_flags( item );
|
||||
item->ClearFlags( UR_TRANSIENT );
|
||||
image->SetFlags( UR_TRANSIENT );
|
||||
|
||||
view->Add( item );
|
||||
view->Hide( item, false );
|
||||
parent->Add( item );
|
||||
|
||||
if( item->Type() == PCB_ZONE_T && static_cast<ZONE*>( item )->GetIsRuleArea() )
|
||||
{
|
||||
dirty_rule_areas.push_back( item->GetBoundingBox() );
|
||||
dirty_rule_areas.push_back( image->GetBoundingBox() );
|
||||
}
|
||||
|
||||
update_item_change_state( item, ITEM_CHANGE_TYPE::CHANGED );
|
||||
}
|
||||
|
||||
BOARD_ITEM* image = (BOARD_ITEM*) aList->GetPickedItemLink( ii );
|
||||
|
||||
view->Remove( item );
|
||||
|
||||
parent->Remove( item );
|
||||
|
||||
item->SwapItemData( image );
|
||||
|
||||
item->ClearFlags( UR_TRANSIENT );
|
||||
image->SetFlags( UR_TRANSIENT );
|
||||
|
||||
view->Add( item );
|
||||
view->Hide( item, false );
|
||||
parent->Add( item );
|
||||
|
||||
if( item->Type() == PCB_ZONE_T && static_cast<ZONE*>( item )->GetIsRuleArea() )
|
||||
{
|
||||
dirty_rule_areas.push_back( item->GetBoundingBox() );
|
||||
dirty_rule_areas.push_back( image->GetBoundingBox() );
|
||||
}
|
||||
|
||||
update_item_change_state( item, ITEM_CHANGE_TYPE::CHANGED );
|
||||
break;
|
||||
}
|
||||
|
||||
case UNDO_REDO::NEWITEM: /* new items are deleted */
|
||||
aList->SetPickedItemStatus( UNDO_REDO::DELETED, ii );
|
||||
GetModel()->Remove( (BOARD_ITEM*) eda_item, REMOVE_MODE::BULK );
|
||||
update_item_change_state( eda_item, ITEM_CHANGE_TYPE::DELETED );
|
||||
if( eda_item->IsBOARD_ITEM() )
|
||||
{
|
||||
aList->SetPickedItemStatus( UNDO_REDO::DELETED, ii );
|
||||
GetModel()->Remove( static_cast<BOARD_ITEM*>( eda_item ), REMOVE_MODE::BULK );
|
||||
update_item_change_state( eda_item, ITEM_CHANGE_TYPE::DELETED );
|
||||
|
||||
if( eda_item->Type() != PCB_NETINFO_T )
|
||||
view->Remove( eda_item );
|
||||
if( eda_item->Type() != PCB_NETINFO_T )
|
||||
view->Remove( eda_item );
|
||||
|
||||
eda_item->SetFlags( UR_TRANSIENT );
|
||||
eda_item->SetFlags( UR_TRANSIENT );
|
||||
|
||||
if( eda_item->Type() == PCB_ZONE_T && static_cast<ZONE*>( eda_item )->GetIsRuleArea() )
|
||||
dirty_rule_areas.push_back( eda_item->GetBoundingBox() );
|
||||
if( eda_item->Type() == PCB_ZONE_T && static_cast<ZONE*>( eda_item )->GetIsRuleArea() )
|
||||
dirty_rule_areas.push_back( eda_item->GetBoundingBox() );
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case UNDO_REDO::DELETED: /* deleted items are put in List, as new items */
|
||||
aList->SetPickedItemStatus( UNDO_REDO::NEWITEM, ii );
|
||||
if( eda_item->IsBOARD_ITEM() )
|
||||
{
|
||||
aList->SetPickedItemStatus( UNDO_REDO::NEWITEM, ii );
|
||||
|
||||
eda_item->ClearFlags( UR_TRANSIENT );
|
||||
clear_local_ratsnest_flags( eda_item );
|
||||
eda_item->ClearFlags( UR_TRANSIENT );
|
||||
|
||||
GetModel()->Add( (BOARD_ITEM*) eda_item, ADD_MODE::BULK_APPEND );
|
||||
update_item_change_state( eda_item, ITEM_CHANGE_TYPE::ADDED );
|
||||
GetModel()->Add( static_cast<BOARD_ITEM*>( eda_item ), ADD_MODE::BULK_APPEND );
|
||||
update_item_change_state( eda_item, ITEM_CHANGE_TYPE::ADDED );
|
||||
|
||||
if( eda_item->Type() != PCB_NETINFO_T )
|
||||
view->Add( eda_item );
|
||||
if( eda_item->Type() != PCB_NETINFO_T )
|
||||
view->Add( eda_item );
|
||||
|
||||
if( eda_item->Type() == PCB_ZONE_T && static_cast<ZONE*>( eda_item )->GetIsRuleArea() )
|
||||
dirty_rule_areas.push_back( eda_item->GetBoundingBox() );
|
||||
if( eda_item->Type() == PCB_ZONE_T && static_cast<ZONE*>( eda_item )->GetIsRuleArea() )
|
||||
dirty_rule_areas.push_back( eda_item->GetBoundingBox() );
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@ -517,14 +552,16 @@ void PCB_BASE_EDIT_FRAME::PutDataInPreviousState( PICKED_ITEMS_LIST* aList )
|
||||
}
|
||||
|
||||
case UNDO_REDO::PAGESETTINGS:
|
||||
{
|
||||
// swap current settings with stored settings
|
||||
DS_PROXY_UNDO_ITEM alt_item( this );
|
||||
DS_PROXY_UNDO_ITEM* item = static_cast<DS_PROXY_UNDO_ITEM*>( eda_item );
|
||||
item->Restore( this );
|
||||
*item = std::move( alt_item );
|
||||
if( eda_item->Type() == WS_PROXY_UNDO_ITEM_T || eda_item->Type() == WS_PROXY_UNDO_ITEM_PLUS_T )
|
||||
{
|
||||
// swap current settings with stored settings
|
||||
DS_PROXY_UNDO_ITEM alt_item( this );
|
||||
DS_PROXY_UNDO_ITEM* item = static_cast<DS_PROXY_UNDO_ITEM*>( eda_item );
|
||||
item->Restore( this );
|
||||
*item = std::move( alt_item );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
wxFAIL_MSG( wxString::Format( wxT( "PutDataInPreviousState() error (unknown code %X)" ),
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include <pad.h>
|
||||
#include <footprint.h>
|
||||
#include <pcb_field.h>
|
||||
#include <template_fieldnames.h>
|
||||
#include <settings/color_settings.h>
|
||||
#include <string_utils.h>
|
||||
#include <widgets/net_selector.h>
|
||||
@ -244,12 +245,40 @@ PCB_PROPERTIES_PANEL::PCB_PROPERTIES_PANEL( wxWindow* aParent, PCB_BASE_EDIT_FRA
|
||||
{
|
||||
m_netSelectorEditorInstance = static_cast<PG_NET_SELECTOR_EDITOR*>( it->second );
|
||||
}
|
||||
|
||||
it = wxPGGlobalVars->m_mapEditorClasses.find( PG_FPID_EDITOR::BuildEditorName( m_frame ) );
|
||||
|
||||
if( it != wxPGGlobalVars->m_mapEditorClasses.end() )
|
||||
{
|
||||
m_fpEditorInstance = static_cast<PG_FPID_EDITOR*>( it->second );
|
||||
m_fpEditorInstance->UpdateFrame( m_frame );
|
||||
}
|
||||
else
|
||||
{
|
||||
PG_FPID_EDITOR* fpEditor = new PG_FPID_EDITOR( m_frame );
|
||||
m_fpEditorInstance = static_cast<PG_FPID_EDITOR*>( wxPropertyGrid::RegisterEditorClass( fpEditor ) );
|
||||
}
|
||||
|
||||
it = wxPGGlobalVars->m_mapEditorClasses.find( PG_URL_EDITOR::BuildEditorName( m_frame ) );
|
||||
|
||||
if( it != wxPGGlobalVars->m_mapEditorClasses.end() )
|
||||
{
|
||||
m_urlEditorInstance = static_cast<PG_URL_EDITOR*>( it->second );
|
||||
m_urlEditorInstance->UpdateFrame( m_frame );
|
||||
}
|
||||
else
|
||||
{
|
||||
PG_URL_EDITOR* urlEditor = new PG_URL_EDITOR( m_frame );
|
||||
m_urlEditorInstance = static_cast<PG_URL_EDITOR*>( wxPropertyGrid::RegisterEditorClass( urlEditor ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PCB_PROPERTIES_PANEL::~PCB_PROPERTIES_PANEL()
|
||||
{
|
||||
m_unitEditorInstance->UpdateFrame( nullptr );
|
||||
m_fpEditorInstance->UpdateFrame( nullptr );
|
||||
m_urlEditorInstance->UpdateFrame( nullptr );
|
||||
}
|
||||
|
||||
|
||||
@ -342,7 +371,14 @@ wxPGProperty* PCB_PROPERTIES_PANEL::createPGProperty( const PROPERTY_BASE* aProp
|
||||
return ret;
|
||||
}
|
||||
|
||||
return PGPropertyFactory( aProperty, m_frame );
|
||||
wxPGProperty* prop = PGPropertyFactory( aProperty, m_frame );
|
||||
|
||||
if( aProperty->Name() == GetCanonicalFieldName( FIELD_T::FOOTPRINT ) )
|
||||
prop->SetEditor( PG_FPID_EDITOR::BuildEditorName( m_frame ) );
|
||||
else if( aProperty->Name() == GetCanonicalFieldName( FIELD_T::DATASHEET ) )
|
||||
prop->SetEditor( PG_URL_EDITOR::BuildEditorName( m_frame ) );
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
|
||||
|
@ -34,6 +34,8 @@ class PG_UNIT_EDITOR;
|
||||
class PG_CHECKBOX_EDITOR;
|
||||
class PG_RATIO_EDITOR;
|
||||
class PG_NET_SELECTOR_EDITOR;
|
||||
class PG_FPID_EDITOR;
|
||||
class PG_URL_EDITOR;
|
||||
|
||||
class PCB_PROPERTIES_PANEL : public PROPERTIES_PANEL
|
||||
{
|
||||
@ -65,6 +67,8 @@ protected:
|
||||
PG_CHECKBOX_EDITOR* m_checkboxEditorInstance;
|
||||
PG_RATIO_EDITOR* m_ratioEditorInstance;
|
||||
PG_NET_SELECTOR_EDITOR* m_netSelectorEditorInstance;
|
||||
PG_FPID_EDITOR* m_fpEditorInstance;
|
||||
PG_URL_EDITOR* m_urlEditorInstance;
|
||||
|
||||
static std::set<wxString> m_currentFieldNames;
|
||||
wxPGChoices m_nets;
|
||||
|
@ -39,7 +39,7 @@
|
||||
"type": "power_pin_not_driven"
|
||||
},
|
||||
{
|
||||
"description": "Footprint 'TestPoint_Pad_3.0x3.0mm' not found in library 'TestPoint'.",
|
||||
"description": "Footprint 'TestPoint_Pad_3.0x3.0mm' not found in library 'TestPoint'",
|
||||
"items": [
|
||||
{
|
||||
"description": "Symbol J1 [Conn_01x01_Pin]",
|
||||
@ -69,7 +69,7 @@
|
||||
"type": "lib_symbol_issues"
|
||||
},
|
||||
{
|
||||
"description": "Footprint 'TestPoint_Pad_3.0x3.0mm' not found in library 'TestPoint'.",
|
||||
"description": "Footprint 'TestPoint_Pad_3.0x3.0mm' not found in library 'TestPoint'",
|
||||
"items": [
|
||||
{
|
||||
"description": "Symbol J2 [Conn_01x01_Pin]",
|
||||
@ -99,7 +99,7 @@
|
||||
"type": "lib_symbol_issues"
|
||||
},
|
||||
{
|
||||
"description": "Footprint 'R_1206_3216Metric' not found in library 'Resistor_SMD'.",
|
||||
"description": "Footprint 'R_1206_3216Metric' not found in library 'Resistor_SMD'",
|
||||
"items": [
|
||||
{
|
||||
"description": "Symbol R3 [R_US]",
|
||||
@ -114,7 +114,7 @@
|
||||
"type": "footprint_link_issues"
|
||||
},
|
||||
{
|
||||
"description": "Footprint 'TestPoint_Pad_3.0x3.0mm' not found in library 'TestPoint'.",
|
||||
"description": "Footprint 'TestPoint_Pad_3.0x3.0mm' not found in library 'TestPoint'",
|
||||
"items": [
|
||||
{
|
||||
"description": "Symbol J4 [Conn_01x01_Pin]",
|
||||
@ -144,7 +144,7 @@
|
||||
"type": "lib_symbol_issues"
|
||||
},
|
||||
{
|
||||
"description": "Footprint 'C_1206_3216Metric' not found in library 'Capacitor_SMD'.",
|
||||
"description": "Footprint 'C_1206_3216Metric' not found in library 'Capacitor_SMD'",
|
||||
"items": [
|
||||
{
|
||||
"description": "Symbol C1 [C_Small]",
|
||||
@ -204,7 +204,7 @@
|
||||
"type": "lib_symbol_mismatch"
|
||||
},
|
||||
{
|
||||
"description": "Footprint 'R_1206_3216Metric' not found in library 'Resistor_SMD'.",
|
||||
"description": "Footprint 'R_1206_3216Metric' not found in library 'Resistor_SMD'",
|
||||
"items": [
|
||||
{
|
||||
"description": "Symbol R1 [R_US]",
|
||||
@ -219,7 +219,7 @@
|
||||
"type": "footprint_link_issues"
|
||||
},
|
||||
{
|
||||
"description": "Footprint 'SOT-23-5' not found in library 'Package_TO_SOT_SMD'.",
|
||||
"description": "Footprint 'SOT-23-5' not found in library 'Package_TO_SOT_SMD'",
|
||||
"items": [
|
||||
{
|
||||
"description": "Symbol U1 [TLV2371DBV]",
|
||||
@ -264,7 +264,7 @@
|
||||
"type": "lib_symbol_mismatch"
|
||||
},
|
||||
{
|
||||
"description": "Footprint 'R_1206_3216Metric' not found in library 'Resistor_SMD'.",
|
||||
"description": "Footprint 'R_1206_3216Metric' not found in library 'Resistor_SMD'",
|
||||
"items": [
|
||||
{
|
||||
"description": "Symbol R2 [R_US]",
|
||||
@ -279,7 +279,7 @@
|
||||
"type": "footprint_link_issues"
|
||||
},
|
||||
{
|
||||
"description": "Footprint 'TestPoint_Pad_3.0x3.0mm' not found in library 'TestPoint'.",
|
||||
"description": "Footprint 'TestPoint_Pad_3.0x3.0mm' not found in library 'TestPoint'",
|
||||
"items": [
|
||||
{
|
||||
"description": "Symbol J3 [Conn_01x01_Pin]",
|
||||
|
@ -7,28 +7,28 @@ ERC report (2025-08-20T22:34:08+0000, Encoding UTF8)
|
||||
[power_pin_not_driven]: Input Power pin not driven by any Output Power pins
|
||||
; error
|
||||
@(140.97 mm, 86.36 mm): Symbol U1 Pin 2 [V-, Power input, Line]
|
||||
[footprint_link_issues]: Footprint 'TestPoint_Pad_3.0x3.0mm' not found in library 'TestPoint'.
|
||||
[footprint_link_issues]: Footprint 'TestPoint_Pad_3.0x3.0mm' not found in library 'TestPoint'
|
||||
; warning
|
||||
@(105.41 mm, 63.50 mm): Symbol J1 [Conn_01x01_Pin]
|
||||
[lib_symbol_issues]: The current configuration does not include the symbol library 'Connector'
|
||||
; warning
|
||||
@(105.41 mm, 63.50 mm): Symbol J1 [Conn_01x01_Pin]
|
||||
[footprint_link_issues]: Footprint 'TestPoint_Pad_3.0x3.0mm' not found in library 'TestPoint'.
|
||||
[footprint_link_issues]: Footprint 'TestPoint_Pad_3.0x3.0mm' not found in library 'TestPoint'
|
||||
; warning
|
||||
@(105.41 mm, 76.20 mm): Symbol J2 [Conn_01x01_Pin]
|
||||
[lib_symbol_issues]: The current configuration does not include the symbol library 'Connector'
|
||||
; warning
|
||||
@(105.41 mm, 76.20 mm): Symbol J2 [Conn_01x01_Pin]
|
||||
[footprint_link_issues]: Footprint 'R_1206_3216Metric' not found in library 'Resistor_SMD'.
|
||||
[footprint_link_issues]: Footprint 'R_1206_3216Metric' not found in library 'Resistor_SMD'
|
||||
; warning
|
||||
@(115.57 mm, 76.20 mm): Symbol R3 [R_US]
|
||||
[footprint_link_issues]: Footprint 'TestPoint_Pad_3.0x3.0mm' not found in library 'TestPoint'.
|
||||
[footprint_link_issues]: Footprint 'TestPoint_Pad_3.0x3.0mm' not found in library 'TestPoint'
|
||||
; warning
|
||||
@(105.41 mm, 99.06 mm): Symbol J4 [Conn_01x01_Pin]
|
||||
[lib_symbol_issues]: The current configuration does not include the symbol library 'Connector'
|
||||
; warning
|
||||
@(105.41 mm, 99.06 mm): Symbol J4 [Conn_01x01_Pin]
|
||||
[footprint_link_issues]: Footprint 'C_1206_3216Metric' not found in library 'Capacitor_SMD'.
|
||||
[footprint_link_issues]: Footprint 'C_1206_3216Metric' not found in library 'Capacitor_SMD'
|
||||
; warning
|
||||
@(121.92 mm, 80.01 mm): Symbol C1 [C_Small]
|
||||
[lib_symbol_mismatch]: Symbol 'GND' doesn't match copy in library 'power'
|
||||
@ -40,10 +40,10 @@ ERC report (2025-08-20T22:34:08+0000, Encoding UTF8)
|
||||
[lib_symbol_mismatch]: Symbol 'GND' doesn't match copy in library 'power'
|
||||
; warning
|
||||
@(119.38 mm, 96.52 mm): Symbol #PWR01 [GND]
|
||||
[footprint_link_issues]: Footprint 'R_1206_3216Metric' not found in library 'Resistor_SMD'.
|
||||
[footprint_link_issues]: Footprint 'R_1206_3216Metric' not found in library 'Resistor_SMD'
|
||||
; warning
|
||||
@(127.00 mm, 93.98 mm): Symbol R1 [R_US]
|
||||
[footprint_link_issues]: Footprint 'SOT-23-5' not found in library 'Package_TO_SOT_SMD'.
|
||||
[footprint_link_issues]: Footprint 'SOT-23-5' not found in library 'Package_TO_SOT_SMD'
|
||||
; warning
|
||||
@(143.51 mm, 78.74 mm): Symbol U1 [TLV2371DBV]
|
||||
[lib_symbol_issues]: The current configuration does not include the symbol library 'Amplifier_Operational'
|
||||
@ -52,10 +52,10 @@ ERC report (2025-08-20T22:34:08+0000, Encoding UTF8)
|
||||
[lib_symbol_mismatch]: Symbol 'GND' doesn't match copy in library 'power'
|
||||
; warning
|
||||
@(140.97 mm, 87.63 mm): Symbol #PWR02 [GND]
|
||||
[footprint_link_issues]: Footprint 'R_1206_3216Metric' not found in library 'Resistor_SMD'.
|
||||
[footprint_link_issues]: Footprint 'R_1206_3216Metric' not found in library 'Resistor_SMD'
|
||||
; warning
|
||||
@(149.86 mm, 93.98 mm): Symbol R2 [R_US]
|
||||
[footprint_link_issues]: Footprint 'TestPoint_Pad_3.0x3.0mm' not found in library 'TestPoint'.
|
||||
[footprint_link_issues]: Footprint 'TestPoint_Pad_3.0x3.0mm' not found in library 'TestPoint'
|
||||
; warning
|
||||
@(177.80 mm, 78.74 mm): Symbol J3 [Conn_01x01_Pin]
|
||||
[lib_symbol_issues]: The current configuration does not include the symbol library 'Connector'
|
||||
|
@ -7,28 +7,28 @@ ERC report (2025-08-20T22:34:09+0000, Encoding UTF8)
|
||||
[power_pin_not_driven]: Input Power pin not driven by any Output Power pins
|
||||
; error
|
||||
@(5.550 in, 3.400 in): Symbol U1 Pin 2 [V-, Power input, Line]
|
||||
[footprint_link_issues]: Footprint 'TestPoint_Pad_3.0x3.0mm' not found in library 'TestPoint'.
|
||||
[footprint_link_issues]: Footprint 'TestPoint_Pad_3.0x3.0mm' not found in library 'TestPoint'
|
||||
; warning
|
||||
@(4.150 in, 2.500 in): Symbol J1 [Conn_01x01_Pin]
|
||||
[lib_symbol_issues]: The current configuration does not include the symbol library 'Connector'
|
||||
; warning
|
||||
@(4.150 in, 2.500 in): Symbol J1 [Conn_01x01_Pin]
|
||||
[footprint_link_issues]: Footprint 'TestPoint_Pad_3.0x3.0mm' not found in library 'TestPoint'.
|
||||
[footprint_link_issues]: Footprint 'TestPoint_Pad_3.0x3.0mm' not found in library 'TestPoint'
|
||||
; warning
|
||||
@(4.150 in, 3.000 in): Symbol J2 [Conn_01x01_Pin]
|
||||
[lib_symbol_issues]: The current configuration does not include the symbol library 'Connector'
|
||||
; warning
|
||||
@(4.150 in, 3.000 in): Symbol J2 [Conn_01x01_Pin]
|
||||
[footprint_link_issues]: Footprint 'R_1206_3216Metric' not found in library 'Resistor_SMD'.
|
||||
[footprint_link_issues]: Footprint 'R_1206_3216Metric' not found in library 'Resistor_SMD'
|
||||
; warning
|
||||
@(4.550 in, 3.000 in): Symbol R3 [R_US]
|
||||
[footprint_link_issues]: Footprint 'TestPoint_Pad_3.0x3.0mm' not found in library 'TestPoint'.
|
||||
[footprint_link_issues]: Footprint 'TestPoint_Pad_3.0x3.0mm' not found in library 'TestPoint'
|
||||
; warning
|
||||
@(4.150 in, 3.900 in): Symbol J4 [Conn_01x01_Pin]
|
||||
[lib_symbol_issues]: The current configuration does not include the symbol library 'Connector'
|
||||
; warning
|
||||
@(4.150 in, 3.900 in): Symbol J4 [Conn_01x01_Pin]
|
||||
[footprint_link_issues]: Footprint 'C_1206_3216Metric' not found in library 'Capacitor_SMD'.
|
||||
[footprint_link_issues]: Footprint 'C_1206_3216Metric' not found in library 'Capacitor_SMD'
|
||||
; warning
|
||||
@(4.800 in, 3.150 in): Symbol C1 [C_Small]
|
||||
[lib_symbol_mismatch]: Symbol 'GND' doesn't match copy in library 'power'
|
||||
@ -40,10 +40,10 @@ ERC report (2025-08-20T22:34:09+0000, Encoding UTF8)
|
||||
[lib_symbol_mismatch]: Symbol 'GND' doesn't match copy in library 'power'
|
||||
; warning
|
||||
@(4.700 in, 3.800 in): Symbol #PWR01 [GND]
|
||||
[footprint_link_issues]: Footprint 'R_1206_3216Metric' not found in library 'Resistor_SMD'.
|
||||
[footprint_link_issues]: Footprint 'R_1206_3216Metric' not found in library 'Resistor_SMD'
|
||||
; warning
|
||||
@(5.000 in, 3.700 in): Symbol R1 [R_US]
|
||||
[footprint_link_issues]: Footprint 'SOT-23-5' not found in library 'Package_TO_SOT_SMD'.
|
||||
[footprint_link_issues]: Footprint 'SOT-23-5' not found in library 'Package_TO_SOT_SMD'
|
||||
; warning
|
||||
@(5.650 in, 3.100 in): Symbol U1 [TLV2371DBV]
|
||||
[lib_symbol_issues]: The current configuration does not include the symbol library 'Amplifier_Operational'
|
||||
@ -52,10 +52,10 @@ ERC report (2025-08-20T22:34:09+0000, Encoding UTF8)
|
||||
[lib_symbol_mismatch]: Symbol 'GND' doesn't match copy in library 'power'
|
||||
; warning
|
||||
@(5.550 in, 3.450 in): Symbol #PWR02 [GND]
|
||||
[footprint_link_issues]: Footprint 'R_1206_3216Metric' not found in library 'Resistor_SMD'.
|
||||
[footprint_link_issues]: Footprint 'R_1206_3216Metric' not found in library 'Resistor_SMD'
|
||||
; warning
|
||||
@(5.900 in, 3.700 in): Symbol R2 [R_US]
|
||||
[footprint_link_issues]: Footprint 'TestPoint_Pad_3.0x3.0mm' not found in library 'TestPoint'.
|
||||
[footprint_link_issues]: Footprint 'TestPoint_Pad_3.0x3.0mm' not found in library 'TestPoint'
|
||||
; warning
|
||||
@(7.000 in, 3.100 in): Symbol J3 [Conn_01x01_Pin]
|
||||
[lib_symbol_issues]: The current configuration does not include the symbol library 'Connector'
|
||||
|
@ -12,16 +12,22 @@ $BIN_DIR = "$BUILD_ROOT/out/bin/"
|
||||
|
||||
$VCPKG_INSTALL_ROOT = "$BUILD_ROOT/vcpkg_installed"
|
||||
|
||||
# Attempt to load System.Drawing.Common to avoid runtime errors
|
||||
try {
|
||||
Add-Type -AssemblyName System.Drawing.Common -ErrorAction Stop | Out-Null
|
||||
} catch {
|
||||
}
|
||||
|
||||
cmake --install $BUILD_ROOT
|
||||
|
||||
Get-ChildItem -Path $PROJECT_ROOT -Recurse -Filter *.pdb | Where-Object { $_.FullName -notmatch 'build/out/bin' } | ForEach-Object {
|
||||
Get-ChildItem -Path $PROJECT_ROOT -Recurse -Filter *.pdb | Where-Object { $_.FullName -notmatch 'build/out/bin' } | ForEach-Object {
|
||||
$destination = Join-Path -Path $BIN_DIR -ChildPath $_.Name
|
||||
if ($_.FullName -ne $destination) {
|
||||
Copy-Item -Path $_.FullName -Destination $BIN_DIR -Force
|
||||
}
|
||||
}
|
||||
|
||||
Get-ChildItem -Path $VCPKG_INSTALL_ROOT -Recurse -Filter *.dll | ForEach-Object {
|
||||
Get-ChildItem -Path $VCPKG_INSTALL_ROOT -Recurse -Filter *.dll | ForEach-Object {
|
||||
$destination = Join-Path -Path $BIN_DIR -ChildPath $_.Name
|
||||
if ($_.FullName -ne $destination) {
|
||||
Copy-Item -Path $_.FullName -Destination $BIN_DIR -Force
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user