Compare commits

..

79 Commits

Author SHA1 Message Date
Dhineshkumar S
498a2bec07 Merge branch 'feature-revert-all-settings-defaults-7273' into 'master'
Add option to restore the default settings on startup.

Closes #7273

See merge request kicad/code/kicad!2079
2025-09-10 16:05:28 +00:00
Roberto Fernandez Bautista
5e2fd084b9 Disable vcpkg compiler tracking 2025-09-10 09:48:50 +00:00
Seth Hillbrand
a1f816e8be Add additional modifies to hotkeys
Allows (depending on system) AltGr, Meta, Win, Super in combination with
other keys

Fixes https://gitlab.com/kicad/code/kicad/issues/1908
2025-09-09 13:30:01 -07:00
Seth Hillbrand
abf3438ed6 Remove noise from orthographic projection raytracing
We generally add some jitter to the raytracing lines in order to smooth
out over multiple iterations some of the imperfections.  However, when
we do this to an orthographic projection (where all lines are parallel
to start, we don't average and just introduce noticeable noise in the
render

Fixes https://gitlab.com/kicad/code/kicad/issues/8863
2025-09-09 13:16:57 -07:00
Seth Hillbrand
cb4c8e6647 Make global delete tracks remove tuning patterns
If we are removing the generated item, we should remove the generator as
well

Fixes https://gitlab.com/kicad/code/kicad/issues/21572
2025-09-09 13:06:52 -07:00
Seth Hillbrand
d356073798 ADDED: synthetic parameters for graphics
Allows editing start/end/radius instead of underlying properties

Fixes https://gitlab.com/kicad/code/kicad/issues/16279
2025-09-09 12:58:43 -07:00
Mark Roszko
b38d9d7f81 Edit Windows-CI.yml, remove after_script 2025-09-09 15:47:03 +00:00
Mark Roszko
297ca1bb7b Fix windows builds with a temporary hackfix for now 2025-09-09 11:12:39 -04:00
Seth Hillbrand
f9e25c2e06 ADDED: Cross-probing from 3d-viewer
Send cross-probe messages to pcb and schematic editors to highlight
objects when clicked in the 3d viewer
2025-09-09 07:50:51 -07:00
Seth Hillbrand
b0a6dc4acf Use actual value for symbol chooser options
When we moved to the single element, the pointer always resolved to
true, which was sometimes not what we wanted
2025-09-09 07:16:28 -07:00
Seth Hillbrand
59dcdb4a4f ADDED: proper icons for datasheet and fp in props
The properties panel needs to look like the data fields for user
experience.  In wx3.3 we get a helpful action button but until then, we
need to make our own
2025-09-09 07:12:27 -07:00
jean-pierre charras
39889f8446 fix a compil issue. 2025-09-09 15:41:28 +02:00
Seth Hillbrand
3f8abe3744 Update properties to show units
The function duplicated our built-in funcitonality and did not properly
handly outputs

Fixes https://gitlab.com/kicad/code/kicad/issues/21685
2025-09-09 06:39:39 -07:00
Seth Hillbrand
0acc659676 Try to fix MSW build error 2025-09-09 06:15:30 -07:00
Jeff Young
78c93ee0fb Clear stale local ratsnest flags when performing undo/redo.
Also removes some dangerous C-style casts.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/17894
2025-09-09 14:09:19 +01:00
jean-pierre charras
9622cf8ff1 SCH_BITMAP, SCH_TABLE: add missing HitTest(SHAPE_LINE_CHAIN& aPoly, ...)
Fixes https://gitlab.com/kicad/code/kicad/-/issues/21669
2025-09-09 09:45:46 +02:00
jean-pierre charras
3cca1e87d8 French translation update 2025-09-09 09:45:46 +02:00
Seth Hillbrand
aac15f4596 Encapsulate libgit commands into single backend
Sets stage for allowing alternate backends for version control
2025-09-08 21:04:31 -07:00
Seth Hillbrand
ceed9ca5f8 ADDED: Place missing units
Adds a contextual menu for symbols that will look for units that are not
currently instantiated and offer to place them
2025-09-08 16:38:49 -07:00
Alex Shvartzkop
6c4edd178e Update translations 2025-09-09 01:19:11 +03:00
dsa-t
cfa87cfc2c
Translated using Weblate (Hungarian)
Currently translated at 34.7% (3659 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/hu/
2025-09-09 00:18:30 +02:00
CloverGit
b3b75270ce
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 97.4% (10247 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/zh_Hans/
2025-09-09 00:16:20 +02:00
KB
f1db857804
Translated using Weblate (Hungarian)
Currently translated at 34.7% (3658 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/hu/
2025-09-09 00:16:20 +02:00
Frank Sonnenberg
cdd43fe5fa
Translated using Weblate (German)
Currently translated at 99.9% (10518 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/de/
2025-09-09 00:16:20 +02:00
Frank Sonnenberg
836b3267bb
Translated using Weblate (German)
Currently translated at 99.9% (10518 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/de/
2025-09-09 00:16:20 +02:00
Nguyễn Ngọc Khánh
66930ea703
Translated using Weblate (Vietnamese)
Currently translated at 21.7% (2284 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/vi/
2025-09-09 00:16:20 +02:00
ZbeeGin
0e60722c4b
Translated using Weblate (Polish)
Currently translated at 97.9% (10305 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/pl/
2025-09-09 00:16:20 +02:00
Frank Sonnenberg
2b3f21b9ff
Translated using Weblate (German)
Currently translated at 99.9% (10518 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/de/
2025-09-09 00:16:20 +02:00
ssantos
dd8a059925
Translated using Weblate (Portuguese)
Currently translated at 93.6% (9851 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/pt/
2025-09-09 00:16:20 +02:00
Frank Sonnenberg
63fe36847e
Translated using Weblate (German)
Currently translated at 99.9% (10518 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/de/
2025-09-09 00:16:19 +02:00
CloverGit
99a16e4306
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 97.4% (10247 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/zh_Hans/
2025-09-09 00:16:19 +02:00
Frank Sonnenberg
5ca8fdb7cd
Translated using Weblate (German)
Currently translated at 99.9% (10518 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/de/
2025-09-09 00:16:19 +02:00
Frank Sonnenberg
88ee07e910
Translated using Weblate (German)
Currently translated at 99.9% (10518 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/de/
2025-09-09 00:16:19 +02:00
Pferd O
71907f0888
Translated using Weblate (German)
Currently translated at 99.9% (10517 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/de/
2025-09-09 00:16:19 +02:00
Frank Sonnenberg
6397b6d3c6
Translated using Weblate (German)
Currently translated at 99.9% (10517 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/de/
2025-09-09 00:16:19 +02:00
Hesham Eina Abdalla
d1356140fa
Translated using Weblate (Arabic)
Currently translated at 1.0% (115 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/ar/
2025-09-09 00:16:19 +02:00
Frank Sonnenberg
30d158a60f
Translated using Weblate (German)
Currently translated at 100.0% (10519 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/de/
2025-09-09 00:16:19 +02:00
Ivan Chuba
c674e1749e
Translated using Weblate (Estonian)
Currently translated at 3.3% (350 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/et/
2025-09-09 00:16:19 +02:00
Frank Sonnenberg
c783218f93
Translated using Weblate (German)
Currently translated at 99.9% (10518 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/de/
2025-09-09 00:16:19 +02:00
Frank Sonnenberg
07885145b3
Translated using Weblate (German)
Currently translated at 99.9% (10518 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/de/
2025-09-09 00:16:18 +02:00
Frank Sonnenberg
500da3c15d
Translated using Weblate (German)
Currently translated at 99.9% (10518 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/de/
2025-09-09 00:16:18 +02:00
Frank Sonnenberg
fbd2e36b79
Translated using Weblate (German)
Currently translated at 99.9% (10518 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/de/
2025-09-09 00:16:18 +02:00
Frank Sonnenberg
61fd5e7413
Translated using Weblate (German)
Currently translated at 99.9% (10518 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/de/
2025-09-09 00:16:18 +02:00
Frank Sonnenberg
bf8d2d7bf6
Translated using Weblate (German)
Currently translated at 99.9% (10518 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/de/
2025-09-09 00:16:18 +02:00
Pferd O
2a93e1bfb1
Translated using Weblate (German)
Currently translated at 99.9% (10518 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/de/
2025-09-09 00:16:18 +02:00
ZbeeGin
9f097d5ae8
Translated using Weblate (Polish)
Currently translated at 97.9% (10305 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/pl/
2025-09-09 00:16:18 +02:00
2tama3
80996e70f8
Translated using Weblate (Japanese)
Currently translated at 100.0% (10519 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/ja/
2025-09-09 00:16:18 +02:00
Frank Sonnenberg
ec8131c7bf
Translated using Weblate (German)
Currently translated at 99.9% (10518 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/de/
2025-09-09 00:16:18 +02:00
Sárkány Lőrinc
2ad7c345ec
Translated using Weblate (Hungarian)
Currently translated at 34.6% (3648 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/hu/
2025-09-09 00:16:17 +02:00
Pferd O
a2e0d07c87
Translated using Weblate (German)
Currently translated at 99.9% (10518 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/de/
2025-09-09 00:16:17 +02:00
Frank Sonnenberg
4e1dd875ae
Translated using Weblate (German)
Currently translated at 99.9% (10518 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/de/
2025-09-09 00:16:17 +02:00
CloverGit
c737e0b360
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 97.4% (10247 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/zh_Hans/
2025-09-09 00:16:17 +02:00
Sárkány Lőrinc
c29aa7643c
Translated using Weblate (Hungarian)
Currently translated at 34.6% (3641 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/hu/
2025-09-09 00:16:17 +02:00
Pferd O
58b2ccb3d5
Translated using Weblate (German)
Currently translated at 99.6% (10484 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/de/
2025-09-09 00:16:17 +02:00
Pferd O
a05f8b9e36
Translated using Weblate (German)
Currently translated at 99.4% (10458 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/de/
2025-09-09 00:16:17 +02:00
CloverGit
9f2b84ba52
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 97.3% (10245 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/zh_Hans/
2025-09-09 00:16:17 +02:00
Sárkány Lőrinc
b345e34423
Translated using Weblate (Hungarian)
Currently translated at 33.0% (3480 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/hu/
2025-09-09 00:16:17 +02:00
Frank Sonnenberg
081b414ad5
Translated using Weblate (German)
Currently translated at 99.1% (10429 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/de/
2025-09-09 00:16:17 +02:00
Pferd O
58caa5c496
Translated using Weblate (German)
Currently translated at 99.1% (10429 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/de/
2025-09-09 00:16:17 +02:00
Frank Sonnenberg
f0f5472e4d
Translated using Weblate (German)
Currently translated at 99.1% (10429 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/de/
2025-09-09 00:16:17 +02:00
CloverGit
0b174b5227
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 97.0% (10210 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/zh_Hans/
2025-09-09 00:16:17 +02:00
2tama3
765838ecf7
Translated using Weblate (Japanese)
Currently translated at 99.9% (10518 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/ja/
2025-09-09 00:16:16 +02:00
Sárkány Lőrinc
2276ae05e3
Translated using Weblate (Hungarian)
Currently translated at 32.9% (3465 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/hu/
2025-09-09 00:16:16 +02:00
Frank Sonnenberg
6838d55051
Translated using Weblate (German)
Currently translated at 98.9% (10405 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/de/
2025-09-09 00:16:16 +02:00
Максим Горпиніч
95abb3be8b
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (10519 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/uk/
2025-09-09 00:16:16 +02:00
CloverGit
41425ac32d
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 96.8% (10185 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/zh_Hans/
2025-09-09 00:16:16 +02:00
Henrik Kauhanen
0b47169387
Translated using Weblate (Swedish)
Currently translated at 99.8% (10501 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/sv/
2025-09-09 00:16:16 +02:00
dsa-t
7b98a8d697
Translated using Weblate (Russian)
Currently translated at 92.9% (9778 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/ru/
2025-09-09 00:16:16 +02:00
2tama3
4184e60e24
Translated using Weblate (Japanese)
Currently translated at 99.9% (10517 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/ja/
2025-09-09 00:16:16 +02:00
Sárkány Lőrinc
36eb0e4a11
Translated using Weblate (Hungarian)
Currently translated at 32.9% (3464 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/hu/
2025-09-09 00:16:16 +02:00
Frank Sonnenberg
49c7f62548
Translated using Weblate (German)
Currently translated at 98.9% (10405 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/de/
2025-09-09 00:16:15 +02:00
Pferd O
35f0213011
Translated using Weblate (German)
Currently translated at 98.9% (10405 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/de/
2025-09-09 00:16:15 +02:00
Frank Sonnenberg
cb87f83dfc
Translated using Weblate (German)
Currently translated at 98.9% (10405 of 10519 strings)

Translation: KiCad EDA/master source
Translate-URL: https://hosted.weblate.org/projects/kicad/master-source/de/
2025-09-09 00:16:15 +02:00
Jeff Young
ead7de69ca Fix periods in ERC gold files. 2025-09-08 20:44:32 +01:00
Jeff Young
dedc10a163 The Include "Exclude from BOM" setting is for editing, not exporting.
There's no reason to put items marked "Exclude
from BOM" into the BOM.
2025-09-08 20:44:32 +01:00
Jeff Young
330361d0cb Fix typo. 2025-09-08 20:44:32 +01:00
Jeff Young
01d32211ba Corner radius not supported on tablecells. 2025-09-08 20:44:32 +01:00
Seth Hillbrand
09c40a0e0f Make KiCad default template the default
Should clear up new project questions

Fixes https://gitlab.com/kicad/code/kicad/-/issues/21651

Fixes https://gitlab.com/kicad/code/kicad/-/issues/21675
2025-09-08 12:16:59 -07:00
Jeff Young
fdbf740ee2 Corner radius not yet supported on textboxes.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/21598
2025-09-08 19:54:59 +01:00
131 changed files with 224516 additions and 198611 deletions

View File

@ -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

View File

@ -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 ) )

View File

@ -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

View File

@ -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 );

View File

@ -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

View File

@ -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 );
}

View File

@ -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;
};

View 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
View 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

View File

@ -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 )

View File

@ -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

View File

@ -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 );
}

View File

@ -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 );
}

View File

@ -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;

View File

@ -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 )

View File

@ -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 )

View File

@ -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 );
}

View File

@ -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_

View File

@ -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 );
}

View File

@ -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 );
}

View File

@ -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;
};

View File

@ -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 );
}

View File

@ -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;

View File

@ -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( &currentBranchReference, 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 )

View File

@ -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

View File

@ -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:

File diff suppressed because it is too large Load Diff

100
common/git/libgit_backend.h Normal file
View 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

View File

@ -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

View File

@ -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;

View File

@ -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,

View File

@ -55,7 +55,6 @@ public:
bool m_sortAsc;
wxString m_filterString;
bool m_excludeDNP;
bool m_includeExcludedFromBOM;
};
#endif

View File

@ -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 );
}

View File

@ -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 )

View File

@ -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, "" }
};

View File

@ -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 );

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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 );

View File

@ -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>

View File

@ -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,

View File

@ -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 );

View File

@ -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 )
{

View File

@ -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;

View File

@ -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 );
}
}

View File

@ -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; }

View File

@ -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;

View File

@ -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

View File

@ -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
{

View File

@ -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;

View File

@ -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" ) );

View File

@ -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" ) );

View File

@ -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" )

View File

@ -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;
};
};

View File

@ -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();

View File

@ -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 );
}
}
}
};

View File

@ -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 )
{

View File

@ -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() )
{

View File

@ -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;
}

View File

@ -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;

View File

@ -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

View File

@ -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;
}

View File

@ -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.

View File

@ -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"

View File

@ -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 );

View File

@ -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 ) )
{

View File

@ -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 );

View File

@ -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 );

View File

@ -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 ) )
{

View File

@ -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
{

View File

@ -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 )
{

View File

@ -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

View 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

View File

@ -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 );
}

View File

@ -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() ) );

View File

@ -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;

View File

@ -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" ) );

View File

@ -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>

View File

@ -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
{

View File

@ -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" ) );

View File

@ -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" ) );

View File

@ -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)" ),

View File

@ -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;
}

View File

@ -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;

View File

@ -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]",

View File

@ -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'

View File

@ -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'

View File

@ -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