Compare commits

...

93 Commits

Author SHA1 Message Date
Mike Williams
b1216bfed3 Merge branch 'unconstrained_swapping' into 'master'
Unconstrained Pin Net / Gate Net Swapping

Closes #1950

See merge request kicad/code/kicad!2306
2025-09-10 16:03:43 -04:00
Seth Hillbrand
7deff606be Update Pybind11 to 3.0.1 2025-09-10 13:02:24 -07:00
Seth Hillbrand
6e2b20ed0e Update BS Threadpool to 5.0 2025-09-10 13:02:24 -07:00
Jeff Young
2f1a91279f Make sure DRC inspection dialogs come up in front
of DRC dialog.
2025-09-10 17:52:09 +01:00
Jeff Young
6e316d9faa ADDED: menu items to control cross-probing from ERC
dialog.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/17916
2025-09-10 17:52:09 +01:00
Jeff Young
3c5fb9d90d CHANGED: progressive disclosure in DRC dialog.
CHANGED: moved Report All Track Errors to config menu.

ADDED: menu items to control cross-probing from DRC
dialog.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/17916
2025-09-10 17:52:09 +01:00
jean-pierre charras
11cc86e586 Hotkey handling: disable usage of keyboard modifier flag wxMOD_ALTGR.
The flag wxMOD_ALTGR is defined in wxWidgets as wxMOD_CONTROL|wxMOD_ALT
So AltGr key cannot used as modifier key because it is the same as
Alt key + Ctrl key that is already handled.
So the previous code did not work with modifiers Alt key, Ctrl key and Altgr key

Fixes https://gitlab.com/kicad/code/kicad/-/issues/21696
2025-09-10 18:10:43 +02: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
Jeff Young
93b0004175 Go back to checking points from the front.
We want to prefer a primary point (ie: a rectangle
corner) to a secondary point (ie: a rounded-rect
radius adjuster).
2025-09-08 18:37:47 +01:00
Jeff Young
0a162ded84 Go back to checking points from the front.
We want to prefer a primary point (ie: a rectangle
corner) to a secondary point (ie: a rounded-rect
radius adjuster).
2025-09-08 18:37:47 +01:00
Jeff Young
01f6776226 Fix hit-testing for rounded corner rectangles. 2025-09-08 18:37:47 +01:00
Jeff Young
adbc80aade Tighten corner radius value clearing.
Move loading of values outside of c'tor (where
they get stomped on my dialog restore code).

Fixes https://gitlab.com/kicad/code/kicad/-/issues/21677
2025-09-08 18:37:47 +01:00
Jeff Young
0b102fc085 Use WX_GRID::ClearRows() to avoid assertions.
Fixes KICAD-VM1.
2025-09-08 18:37:47 +01:00
Mark Roszko
22288e02a2 Fix cairo rendering on windows under wx 3.3 2025-09-08 12:32:12 -04:00
jean-pierre charras
58f4ca7ed6 FOOTPRINT_WIZARD_FRAME: Use the drawing engine selected in Preferences.
The FOOTPRINT_WIZARD_FRAME canvas used previously the Cairo engine,
regardless the drawing engine selected for all other canvases. It uses now
the selected engine.
2025-09-08 18:19:48 +02:00
Jeff Young
8ae2ad3586 Only test parameters under the same conditions as we'd write them to the file.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/21520
2025-09-08 12:55:22 +01:00
508 changed files with 260105 additions and 208763 deletions

View File

@ -9,6 +9,7 @@ win64_build:
image: registry.gitlab.com/kicad/kicad-ci/windows-build-image/ltsc2022-msvc:latest image: registry.gitlab.com/kicad/kicad-ci/windows-build-image/ltsc2022-msvc:latest
variables: variables:
VCPKG_BINARY_SOURCES: 'nuget,gitlab,readwrite' VCPKG_BINARY_SOURCES: 'nuget,gitlab,readwrite'
VCPKG_DISABLE_COMPILER_TRACKING: '1'
# Switch the compressor to fastzip and reduce the compression level # Switch the compressor to fastzip and reduce the compression level
FF_USE_FASTZIP: "true" FF_USE_FASTZIP: "true"
CACHE_COMPRESSION_LEVEL: "fast" CACHE_COMPRESSION_LEVEL: "fast"
@ -39,8 +40,6 @@ win64_build:
../../ ../../
- cmake --build . 2>&1 | tee compilation_log.txt - cmake --build . 2>&1 | tee compilation_log.txt
- cd ../../ - cd ../../
after_script:
- Get-Content -Path C:\builder\vcpkg\buildtrees\wxpython-33\python3-tool-post-install-err.log
artifacts: artifacts:
# Only save the artifacts that are needed for running the tests in the next stage # 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 # 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 <advanced_config.h>
#include <build_version.h> #include <build_version.h>
#include <board.h> #include <board.h>
#include <pad.h>
#include <pcb_field.h>
#include <reporter.h> #include <reporter.h>
#include <gal/opengl/gl_context_mgr.h> #include <gal/opengl/gl_context_mgr.h>
#include <core/profile.h> // To use GetRunningMicroSecs or another profiling utility #include <core/profile.h> // To use GetRunningMicroSecs or another profiling utility
#include <bitmaps.h> #include <bitmaps.h>
#include <kiway_holder.h>
#include <kiway.h>
#include <macros.h> #include <macros.h>
#include <pgm_base.h> #include <pgm_base.h>
#include <settings/settings_manager.h> #include <settings/settings_manager.h>
#include <tool/tool_dispatcher.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> #include <widgets/wx_busy_indicator.h>
@ -92,28 +100,12 @@ END_EVENT_TABLE()
EDA_3D_CANVAS::EDA_3D_CANVAS( wxWindow* aParent, const wxGLAttributes& aGLAttribs, EDA_3D_CANVAS::EDA_3D_CANVAS( wxWindow* aParent, const wxGLAttributes& aGLAttribs,
BOARD_ADAPTER& aBoardAdapter, CAMERA& aCamera, BOARD_ADAPTER& aBoardAdapter, CAMERA& aCamera,
S3D_CACHE* a3DCachePointer ) : S3D_CACHE* a3DCachePointer ) :
HIDPI_GL_3D_CANVAS( EDA_DRAW_PANEL_GAL::GetVcSettings(), aCamera, aParent, aGLAttribs, HIDPI_GL_3D_CANVAS( EDA_DRAW_PANEL_GAL::GetVcSettings(), aCamera, aParent, aGLAttribs,
EDA_3D_CANVAS_ID, wxDefaultPosition, EDA_3D_CANVAS_ID, wxDefaultPosition,
wxDefaultSize, wxFULL_REPAINT_ON_RESIZE ), wxDefaultSize, wxFULL_REPAINT_ON_RESIZE ),
m_eventDispatcher( nullptr ), m_editing_timeout_timer( this, wxID_HIGHEST + 1 ),
m_parentStatusBar( nullptr ), m_redraw_trigger_timer( this, wxID_HIGHEST + 2 ),
m_parentInfoBar( nullptr ), m_boardAdapter( aBoardAdapter )
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 )
{ {
wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::EDA_3D_CANVAS" ) ); wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::EDA_3D_CANVAS" ) );
@ -481,8 +473,10 @@ void EDA_3D_CANVAS::DoRePaint()
if( m_camera_is_moving ) if( m_camera_is_moving )
{ {
const int64_t curtime_delta = GetRunningMicroSecs() - m_strtime_camera_movement; const int64_t curtime_delta = GetRunningMicroSecs() - m_strtime_camera_movement;
curtime_delta_s = ( curtime_delta / 1e6 ) * m_camera_moving_speed; // 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 ); m_camera.Interpolate( curtime_delta_s );
if( curtime_delta_s > 1.0f ) 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 ) if( m_camera_is_moving )
{ {
const int64_t curtime_delta = GetRunningMicroSecs() - m_strtime_camera_movement; 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 ); m_camera.Interpolate( curtime_delta_s );
if( curtime_delta_s > 1.0f ) if( curtime_delta_s > 1.0f )
@ -887,7 +882,7 @@ void EDA_3D_CANVAS::OnZoomGesture( wxZoomGestureEvent& aEvent )
m_camera.Pan( aEvent.GetPosition() ); m_camera.Pan( aEvent.GetPosition() );
m_camera.SetCurMousePosition( 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(); m_gestureLastZoomFactor = aEvent.GetZoomFactor();
@ -930,7 +925,7 @@ void EDA_3D_CANVAS::OnRotateGesture( wxRotateGestureEvent& aEvent )
if( m_camera_is_moving ) if( m_camera_is_moving )
return; return;
m_camera.RotateScreen( m_gestureLastAngle - aEvent.GetRotationAngle() ); m_camera.RotateScreen( static_cast<float>( m_gestureLastAngle - aEvent.GetRotationAngle() ) );
m_gestureLastAngle = aEvent.GetRotationAngle(); m_gestureLastAngle = aEvent.GetRotationAngle();
DisplayStatus(); DisplayStatus();
@ -1066,9 +1061,45 @@ void EDA_3D_CANVAS::OnLeftDown( wxMouseEvent& event )
{ {
RAY mouseRay = getRayAtCurrentMousePosition(); 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 logicalW = logicalSize.GetWidth();
int logicalH = logicalSize.GetHeight(); 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(); std::tie( gizmo_x, gizmo_y, gizmo_width, gizmo_height ) = m_3d_render_opengl->getGizmoViewport();
float scaleX = static_cast<float>( gizmo_width ) / logicalW; float scaleX = static_cast<float>( static_cast<double>( gizmo_width ) / static_cast<double>( logicalW ) );
float scaleY = static_cast<float>( gizmo_height ) / logicalH; float scaleY = static_cast<float>( static_cast<double>( gizmo_height ) / static_cast<double>( logicalH ) );
int scaledMouseX = static_cast<int>( event.GetX() * scaleX ); int scaledMouseX = static_cast<int>( static_cast<float>( event.GetX() ) * scaleX );
int scaledMouseY = static_cast<int>( ( logicalH - event.GetY() ) * scaleY ); int scaledMouseY = static_cast<int>( static_cast<float>( logicalH - event.GetY() ) * scaleY );
m_3d_render_opengl->handleGizmoMouseInput( scaledMouseX, scaledMouseY ); m_3d_render_opengl->handleGizmoMouseInput( scaledMouseX, scaledMouseY );
Refresh(); 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 // Map speed multiplier option to actual multiplier value
// [1,2,3,4,5] -> [0.25, 0.5, 1, 2, 4] // [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_render_pivot = aRenderPivot;
m_camera_moving_speed = aMovingSpeed; m_camera_moving_speed = aMovingSpeed;
@ -1249,7 +1280,7 @@ void EDA_3D_CANVAS::move_pivot_based_on_cur_mouse_position()
{ {
RAY mouseRay = getRayAtCurrentMousePosition(); RAY mouseRay = getRayAtCurrentMousePosition();
float hit_t; float hit_t = 0.0f;
// Test it with the board bounding box // Test it with the board bounding box
if( m_boardAdapter.GetBBox().Intersect( mouseRay, &hit_t ) ) if( m_boardAdapter.GetBBox().Intersect( mouseRay, &hit_t ) )

View File

@ -42,7 +42,7 @@ class RENDER_3D_RAYTRACE_GL;
class RENDER_3D_OPENGL; 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 * Implement a canvas based on a wxGLCanvas
@ -61,7 +61,7 @@ public:
EDA_3D_CANVAS( wxWindow* aParent, const wxGLAttributes& aGLAttribs, BOARD_ADAPTER& aSettings, EDA_3D_CANVAS( wxWindow* aParent, const wxGLAttributes& aGLAttribs, BOARD_ADAPTER& aSettings,
CAMERA& aCamera, S3D_CACHE* a3DCachePointer ); CAMERA& aCamera, S3D_CACHE* a3DCachePointer );
~EDA_3D_CANVAS(); ~EDA_3D_CANVAS() override;
/** /**
* Set a dispatcher that processes events and forwards them to tools. * Set a dispatcher that processes events and forwards them to tools.
@ -302,36 +302,36 @@ private:
RAY getRayAtCurrentMousePosition(); RAY getRayAtCurrentMousePosition();
private: private:
TOOL_DISPATCHER* m_eventDispatcher; TOOL_DISPATCHER* m_eventDispatcher = nullptr;
wxStatusBar* m_parentStatusBar; // Parent statusbar to report progress wxStatusBar* m_parentStatusBar = nullptr; // Parent statusbar to report progress
WX_INFOBAR* m_parentInfoBar; WX_INFOBAR* m_parentInfoBar = nullptr;
wxGLContext* m_glRC; // Current OpenGL context wxGLContext* m_glRC = nullptr; // Current OpenGL context
bool m_is_opengl_initialized; bool m_is_opengl_initialized = false;
bool m_is_opengl_version_supported; bool m_is_opengl_version_supported = true;
wxTimer m_editing_timeout_timer; // Expires after some time signaling that wxTimer m_editing_timeout_timer; // Expires after some time signaling that
// the mouse / keyboard movements are over // the mouse / keyboard movements are over
wxTimer m_redraw_trigger_timer; // Used to schedule a redraw event 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 bool m_render_pivot = false; // Render the pivot while camera moving
float m_camera_moving_speed; // 1.0f will be 1:1 float m_camera_moving_speed = 1.0f; // 1.0f will be 1:1
int64_t m_strtime_camera_movement; // Ticktime of camera movement start int64_t m_strtime_camera_movement = 0; // Ticktime of camera movement start
bool m_animation_enabled; // Camera animation enabled bool m_animation_enabled = true; // Camera animation enabled
int m_moving_speed_multiplier; // Camera animation speed multiplier option int m_moving_speed_multiplier = 3; // Camera animation speed multiplier option
BOARD_ADAPTER& m_boardAdapter; // Pre-computed 3D info and settings 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_RAYTRACE_GL* m_3d_render_raytracing;
RENDER_3D_OPENGL* m_3d_render_opengl; RENDER_3D_OPENGL* m_3d_render_opengl;
bool m_opengl_supports_raytracing; bool m_opengl_supports_raytracing = true;
bool m_render_raytracing_was_requested; 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 bool m_render3dmousePivot = false; // Render the 3dmouse pivot
SFVEC3F m_3dmousePivotPos; // The position of the 3dmouse pivot SFVEC3F m_3dmousePivotPos; // The position of the 3dmouse pivot

View File

@ -277,7 +277,7 @@ void RENDER_3D_RAYTRACE_BASE::renderTracing( uint8_t* ptrPBO, REPORTER* aStatusR
BS::multi_future<void> futures; BS::multi_future<void> futures;
for( size_t i = 0; i < tp.get_thread_count(); ++i ) for( size_t i = 0; i < tp.get_thread_count(); ++i )
futures.push_back( tp.submit( processBlocks ) ); futures.push_back( tp.submit_task( processBlocks ) );
futures.wait(); futures.wait();
@ -486,9 +486,12 @@ void RENDER_3D_RAYTRACE_BASE::renderBlockTracing( uint8_t* ptrPBO, signed int iB
// Initialize ray packets // Initialize ray packets
const SFVEC2UI& blockPos = m_blockPositions[iBlock]; const SFVEC2UI& blockPos = m_blockPositions[iBlock];
const SFVEC2I blockPosI = SFVEC2I( blockPos.x + m_xoffset, blockPos.y + m_yoffset ); 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 ), RAYPACKET blockPacket( m_camera, (SFVEC2F) blockPosI + randDisp,
SFVEC2F( DISP_FACTOR, DISP_FACTOR ) /* Displacement random factor */ ); randDisp /* Displacement random factor */ );
HITINFO_PACKET hitPacket_X0Y0[RAYPACKET_RAYS_PER_PACKET]; 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 ); HITINFO_PACKET_init( hitPacket_AA_X1Y1 );
RAYPACKET blockPacket_AA_X1Y1( m_camera, (SFVEC2F) blockPosI + SFVEC2F( 0.5f, 0.5f ), 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 ) ) 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]; RAY blockRayPck_AA_X1Y1_half[RAYPACKET_RAYS_PER_PACKET];
RAYPACKET_InitRays_with2DDisplacement( RAYPACKET_InitRays_with2DDisplacement(
m_camera, (SFVEC2F) blockPosI + SFVEC2F( 0.5f - DISP_FACTOR, DISP_FACTOR ), m_camera, (SFVEC2F) blockPosI + SFVEC2F( 0.5f - randDisp.x, randDisp.y ),
SFVEC2F( DISP_FACTOR, DISP_FACTOR ), blockRayPck_AA_X1Y0 ); randDisp, blockRayPck_AA_X1Y0 );
RAYPACKET_InitRays_with2DDisplacement( RAYPACKET_InitRays_with2DDisplacement(
m_camera, (SFVEC2F) blockPosI + SFVEC2F( DISP_FACTOR, 0.5f - DISP_FACTOR ), m_camera, (SFVEC2F) blockPosI + SFVEC2F( randDisp.x, 0.5f - randDisp.y ),
SFVEC2F( DISP_FACTOR, DISP_FACTOR ), blockRayPck_AA_X0Y1 ); randDisp, blockRayPck_AA_X0Y1 );
RAYPACKET_InitRays_with2DDisplacement( RAYPACKET_InitRays_with2DDisplacement(
m_camera, (SFVEC2F) blockPosI + SFVEC2F( 0.25f - DISP_FACTOR, 0.25f - DISP_FACTOR ), m_camera, (SFVEC2F) blockPosI + SFVEC2F( 0.25f - randDisp.x, 0.25f - randDisp.y ),
SFVEC2F( DISP_FACTOR, DISP_FACTOR ), blockRayPck_AA_X1Y1_half ); randDisp, blockRayPck_AA_X1Y1_half );
renderAntiAliasPackets( bgColor, hitPacket_X0Y0, hitPacket_AA_X1Y1, blockRayPck_AA_X1Y0, renderAntiAliasPackets( bgColor, hitPacket_X0Y0, hitPacket_AA_X1Y1, blockRayPck_AA_X1Y0,
hitColor_AA_X1Y0 ); hitColor_AA_X1Y0 );

View File

@ -633,7 +633,8 @@ set( COMMON_GIT_SRCS
git/project_git_utils.cpp git/project_git_utils.cpp
git/kicad_git_common.cpp git/kicad_git_common.cpp
git/kicad_git_errors.cpp git/kicad_git_errors.cpp
git/project_git_utils.cpp git/git_backend.cpp
git/libgit_backend.cpp
) )
set( COMMON_SRCS set( COMMON_SRCS

View File

@ -190,7 +190,7 @@ void DESIGN_BLOCK_LIST_IMPL::loadDesignBlocks()
}; };
for( size_t ii = 0; ii < num_elements; ++ii ) for( size_t ii = 0; ii < num_elements; ++ii )
returns[ii] = tp.submit( db_thread ); returns[ii] = tp.submit_task( db_thread );
for( const std::future<size_t>& ret : returns ) for( const std::future<size_t>& ret : returns )
{ {

View File

@ -167,9 +167,7 @@ void PANEL_EMBEDDED_FILES::resizeGrid()
bool PANEL_EMBEDDED_FILES::TransferDataToWindow() bool PANEL_EMBEDDED_FILES::TransferDataToWindow()
{ {
m_files_grid->ClearGrid(); m_files_grid->ClearGrid();
m_files_grid->ClearRows();
if( m_files_grid->GetNumberRows() > 0 )
m_files_grid->DeleteRows( 0, m_files_grid->GetNumberRows() );
int ii = 0; int ii = 0;

View File

@ -87,6 +87,10 @@ EDA_DRAW_PANEL_GAL::EDA_DRAW_PANEL_GAL( wxWindow* aParentWindow, wxWindowID aWin
m_stealsFocus( true ), m_stealsFocus( true ),
m_statusPopup( nullptr ) m_statusPopup( nullptr )
{ {
#ifdef _WIN32
// need to fix broken cairo rendering on Windows with wx 3.3
SetDoubleBuffered( false );
#endif
m_PaintEventCounter = std::make_unique<PROF_COUNTER>( "Draw panel paint events" ); m_PaintEventCounter = std::make_unique<PROF_COUNTER>( "Draw panel paint events" );
if( Pgm().GetCommonSettings()->m_Appearance.show_scrollbars ) if( Pgm().GetCommonSettings()->m_Appearance.show_scrollbars )

View File

@ -1059,32 +1059,35 @@ std::vector<wxWindow*> EDA_DRAW_FRAME::findDialogs()
} }
void EDA_DRAW_FRAME::FocusOnLocation( const VECTOR2I& aPos ) void EDA_DRAW_FRAME::FocusOnLocation( const VECTOR2I& aPos, bool aAllowScroll )
{ {
bool centerView = false; bool centerView = false;
BOX2D r = GetCanvas()->GetView()->GetViewport();
// Center if we're off the current view, or within 10% of its edge
r.Inflate( - r.GetWidth() / 10.0 );
if( !r.Contains( aPos ) )
centerView = true;
std::vector<BOX2D> dialogScreenRects; std::vector<BOX2D> dialogScreenRects;
for( wxWindow* dialog : findDialogs() ) if( aAllowScroll )
{ {
dialogScreenRects.emplace_back( ToVECTOR2D( GetCanvas()->ScreenToClient( dialog->GetScreenPosition() ) ), BOX2D r = GetCanvas()->GetView()->GetViewport();
ToVECTOR2D( dialog->GetSize() ) );
}
// Center if we're behind an obscuring dialog, or within 10% of its edge // Center if we're off the current view, or within 10% of its edge
for( BOX2D rect : dialogScreenRects ) r.Inflate( - r.GetWidth() / 10.0 );
{
rect.Inflate( rect.GetWidth() / 10 );
if( rect.Contains( GetCanvas()->GetView()->ToScreen( aPos ) ) ) if( !r.Contains( aPos ) )
centerView = true; centerView = true;
for( wxWindow* dialog : findDialogs() )
{
dialogScreenRects.emplace_back( ToVECTOR2D( GetCanvas()->ScreenToClient( dialog->GetScreenPosition() ) ),
ToVECTOR2D( dialog->GetSize() ) );
}
// Center if we're behind an obscuring dialog, or within 10% of its edge
for( BOX2D rect : dialogScreenRects )
{
rect.Inflate( rect.GetWidth() / 10 );
if( rect.Contains( GetCanvas()->GetView()->ToScreen( aPos ) ) )
centerView = true;
}
} }
if( centerView ) if( centerView )

View File

@ -1382,6 +1382,15 @@ bool EDA_SHAPE::hitTest( const VECTOR2I& aPosition, int aAccuracy ) const
return poly.Collide( aPosition, maxdist ); return poly.Collide( aPosition, maxdist );
} }
else if( m_cornerRadius > 0 )
{
ROUNDRECT rr( SHAPE_RECT( GetStart(), GetRectangleWidth(), GetRectangleHeight() ), m_cornerRadius );
SHAPE_POLY_SET poly;
rr.TransformToPolygon( poly );
if( poly.CollideEdge( aPosition, nullptr, maxdist ) )
return true;
}
else else
{ {
std::vector<VECTOR2I> pts = GetRectCorners(); std::vector<VECTOR2I> pts = GetRectCorners();
@ -1393,13 +1402,13 @@ bool EDA_SHAPE::hitTest( const VECTOR2I& aPosition, int aAccuracy ) const
{ {
return true; return true;
} }
if( IsHatchedFill() && GetHatching().Collide( aPosition, maxdist ) )
return true;
return false;
} }
if( IsHatchedFill() && GetHatching().Collide( aPosition, maxdist ) )
return true;
return false;
case SHAPE_T::POLY: case SHAPE_T::POLY:
if( IsFilledForHitTesting() ) if( IsFilledForHitTesting() )
{ {
@ -1441,6 +1450,40 @@ bool EDA_SHAPE::hitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) co
BOX2I bbox = getBoundingBox(); BOX2I bbox = getBoundingBox();
auto checkOutline =
[&]( const SHAPE_LINE_CHAIN& outline )
{
int count = (int) outline.GetPointCount();
for( int ii = 0; ii < count; ii++ )
{
VECTOR2I vertex = outline.GetPoint( ii );
// Test if the point is within aRect
if( arect.Contains( vertex ) )
return true;
if( ii + 1 < count )
{
VECTOR2I vertexNext = outline.GetPoint( ii + 1 );
// Test if this edge intersects aRect
if( arect.Intersects( vertex, vertexNext ) )
return true;
}
else if( outline.IsClosed() )
{
VECTOR2I vertexNext = outline.GetPoint( 0 );
// Test if this edge intersects aRect
if( arect.Intersects( vertex, vertexNext ) )
return true;
}
}
return false;
};
switch( m_shape ) switch( m_shape )
{ {
case SHAPE_T::CIRCLE: case SHAPE_T::CIRCLE:
@ -1487,6 +1530,17 @@ bool EDA_SHAPE::hitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) co
{ {
return arect.Contains( bbox ); return arect.Contains( bbox );
} }
else if( m_cornerRadius > 0 )
{
ROUNDRECT rr( SHAPE_RECT( GetStart(), GetRectangleWidth(), GetRectangleHeight() ), m_cornerRadius );
SHAPE_POLY_SET poly;
rr.TransformToPolygon( poly );
// Account for the width of the line
arect.Inflate( GetWidth() / 2 );
return checkOutline( poly.Outline( 0 ) );
}
else else
{ {
std::vector<VECTOR2I> pts = GetRectCorners(); std::vector<VECTOR2I> pts = GetRectCorners();
@ -1528,34 +1582,8 @@ bool EDA_SHAPE::hitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) co
for( int ii = 0; ii < m_poly.OutlineCount(); ++ii ) for( int ii = 0; ii < m_poly.OutlineCount(); ++ii )
{ {
const SHAPE_LINE_CHAIN& poly = m_poly.Outline( ii ); if( checkOutline( m_poly.Outline( ii ) ) )
int count = (int) poly.GetPointCount(); return true;
for( int jj = 0; jj < count; jj++ )
{
VECTOR2I vertex = poly.GetPoint( jj );
// Test if the point is within aRect
if( arect.Contains( vertex ) )
return true;
if( jj + 1 < count )
{
VECTOR2I vertexNext = poly.GetPoint( jj + 1 );
// Test if this edge intersects aRect
if( arect.Intersects( vertex, vertexNext ) )
return true;
}
else if( poly.IsClosed() )
{
VECTOR2I vertexNext = poly.GetPoint( 0 );
// Test if this edge intersects aRect
if( arect.Intersects( vertex, vertexNext ) )
return true;
}
}
} }
return false; return false;

View File

@ -1364,6 +1364,11 @@ CAIRO_GAL::CAIRO_GAL( GAL_DISPLAY_OPTIONS& aDisplayOptions, wxWindow* aParent,
m_currentTarget = TARGET_NONCACHED; m_currentTarget = TARGET_NONCACHED;
SetTarget( TARGET_NONCACHED ); SetTarget( TARGET_NONCACHED );
#ifdef _WIN32
// need to fix broken cairo rendering on Windows with wx 3.3
SetDoubleBuffered( false );
#endif
m_bitmapBuffer = nullptr; m_bitmapBuffer = nullptr;
m_wxOutput = nullptr; m_wxOutput = nullptr;

View File

@ -22,16 +22,13 @@
*/ */
#include "git_add_to_index_handler.h" #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> #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(); 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 ) bool GIT_ADD_TO_INDEX_HANDLER::AddToIndex( const wxString& aFilePath )
{ {
// Test if file is currently in the index return GetGitBackend()->AddToIndex( this, aFilePath );
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;
} }
bool GIT_ADD_TO_INDEX_HANDLER::PerformAddToIndex() bool GIT_ADD_TO_INDEX_HANDLER::PerformAddToIndex()
{ {
git_index* index = nullptr; return GetGitBackend()->PerformAddToIndex( this );
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;
} }

View File

@ -24,12 +24,13 @@
#ifndef GIT_ADD_TO_INDEX_HANDLER_H_ #ifndef GIT_ADD_TO_INDEX_HANDLER_H_
#define 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 <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: public:
GIT_ADD_TO_INDEX_HANDLER( git_repository* aRepository ); GIT_ADD_TO_INDEX_HANDLER( git_repository* aRepository );
@ -40,8 +41,7 @@ public:
bool PerformAddToIndex(); bool PerformAddToIndex();
private: private:
git_repository* m_repository; friend class LIBGIT_BACKEND;
std::vector<wxString> m_filesToAdd; std::vector<wxString> m_filesToAdd;
std::vector<wxString> m_filesFailedToAdd; 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_branch_handler.h"
#include "git_backend.h"
#include <git/kicad_git_common.h> #include <git/kicad_git_common.h>
#include <git/kicad_git_memory.h> #include <git/kicad_git_memory.h>
#include <trace_helpers.h> #include <trace_helpers.h>
@ -35,89 +36,12 @@ GIT_BRANCH_HANDLER::~GIT_BRANCH_HANDLER()
bool GIT_BRANCH_HANDLER::BranchExists( const wxString& aBranchName ) bool GIT_BRANCH_HANDLER::BranchExists( const wxString& aBranchName )
{ {
git_repository* repo = GetRepo(); return GetGitBackend()->BranchExists( this, aBranchName );
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;
} }
BranchResult GIT_BRANCH_HANDLER::SwitchToBranch( const wxString& aBranchName ) BranchResult GIT_BRANCH_HANDLER::SwitchToBranch( const wxString& aBranchName )
{ {
git_repository* repo = GetRepo(); return GetGitBackend()->SwitchToBranch( this, aBranchName );
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;
} }
void GIT_BRANCH_HANDLER::UpdateProgress( int aCurrent, int aTotal, const wxString& aMessage ) void GIT_BRANCH_HANDLER::UpdateProgress( int aCurrent, int aTotal, const wxString& aMessage )

View File

@ -57,15 +57,6 @@ public:
bool BranchExists( const wxString& aBranchName ); bool BranchExists( const wxString& aBranchName );
void UpdateProgress( int aCurrent, int aTotal, const wxString& aMessage ) override; 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 #endif // GIT_BRANCH_HANDLER_H

View File

@ -23,13 +23,8 @@
#include "git_clone_handler.h" #include "git_clone_handler.h"
#include <git/kicad_git_common.h>
#include <git/kicad_git_memory.h>
#include <trace_helpers.h> #include <trace_helpers.h>
#include "git_backend.h"
#include <git2.h>
#include <wx/filename.h>
#include <wx/log.h>
GIT_CLONE_HANDLER::GIT_CLONE_HANDLER( KIGIT_COMMON* aCommon ) : KIGIT_REPO_MIXIN( aCommon ) 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() bool GIT_CLONE_HANDLER::PerformClone()
{ {
std::unique_lock<std::mutex> lock( GetCommon()->m_gitActionMutex, std::try_to_lock ); return GetGitBackend()->Clone( this );
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;
} }

View File

@ -22,8 +22,7 @@
*/ */
#include "git_commit_handler.h" #include "git_commit_handler.h"
#include <git/kicad_git_memory.h> #include "git_backend.h"
#include <wx/log.h>
GIT_COMMIT_HANDLER::GIT_COMMIT_HANDLER( git_repository* aRepo ) : GIT_COMMIT_HANDLER::GIT_COMMIT_HANDLER( git_repository* aRepo ) :
KIGIT_COMMON( 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, GIT_COMMIT_HANDLER::PerformCommit( const std::vector<wxString>& aFiles,
const wxString& aMessage, const wxString& aMessage,
const wxString& aAuthorName, const wxString& aAuthorName,
const wxString& aAuthorEmail ) const wxString& aAuthorEmail )
{ {
git_repository* repo = GetRepo(); return GetGitBackend()->Commit( this, aFiles, aMessage, aAuthorName, aAuthorEmail );
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;
} }

View File

@ -27,25 +27,20 @@
// Define a class to handle git commit operations // Define a class to handle git commit operations
#include <git/kicad_git_common.h> #include <git/kicad_git_common.h>
#include <git2.h> #include "git_backend.h"
#include <string> #include <string>
#include <vector> #include <vector>
#include <wx/string.h> #include <wx/string.h>
class LIBGIT_BACKEND;
class GIT_COMMIT_HANDLER : public KIGIT_COMMON class GIT_COMMIT_HANDLER : public KIGIT_COMMON
{ {
public: public:
GIT_COMMIT_HANDLER( git_repository* aRepo ); GIT_COMMIT_HANDLER( git_repository* aRepo );
virtual ~GIT_COMMIT_HANDLER(); virtual ~GIT_COMMIT_HANDLER();
enum class CommitResult
{
Success,
Error,
Cancelled
};
CommitResult PerformCommit( const std::vector<wxString>& aFiles, CommitResult PerformCommit( const std::vector<wxString>& aFiles,
const wxString& aMessage, const wxString& aMessage,
const wxString& aAuthorName, const wxString& aAuthorName,
@ -54,6 +49,7 @@ public:
wxString GetErrorString() const; wxString GetErrorString() const;
private: private:
friend class LIBGIT_BACKEND;
void AddErrorString( const wxString& aErrorString ); void AddErrorString( const wxString& aErrorString );
wxString m_errorString; wxString m_errorString;

View File

@ -22,6 +22,7 @@
*/ */
#include "git_config_handler.h" #include "git_config_handler.h"
#include "git_backend.h"
#include <git/kicad_git_common.h> #include <git/kicad_git_common.h>
#include <git/kicad_git_memory.h> #include <git/kicad_git_memory.h>
#include <pgm_base.h> #include <pgm_base.h>
@ -59,48 +60,12 @@ GitUserConfig GIT_CONFIG_HANDLER::GetUserConfig()
wxString GIT_CONFIG_HANDLER::GetWorkingDirectory() wxString GIT_CONFIG_HANDLER::GetWorkingDirectory()
{ {
git_repository* repo = GetRepo(); return GetGitBackend()->GetWorkingDirectory( this );
if( !repo )
return wxEmptyString;
const char* workdir = git_repository_workdir( repo );
if( !workdir )
return wxEmptyString;
return wxString( workdir );
} }
bool GIT_CONFIG_HANDLER::GetConfigString( const wxString& aKey, wxString& aValue ) bool GIT_CONFIG_HANDLER::GetConfigString( const wxString& aKey, wxString& aValue )
{ {
git_repository* repo = GetRepo(); return GetGitBackend()->GetConfigString( this, aKey, aValue );
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;
} }
void GIT_CONFIG_HANDLER::UpdateProgress( int aCurrent, int aTotal, const wxString& aMessage ) 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_init_handler.h"
#include "git_backend.h"
#include <git/kicad_git_common.h> #include <git/kicad_git_common.h>
#include <git/kicad_git_memory.h> #include <git/kicad_git_memory.h>
#include <trace_helpers.h> #include <trace_helpers.h>
@ -35,118 +36,17 @@ GIT_INIT_HANDLER::~GIT_INIT_HANDLER()
bool GIT_INIT_HANDLER::IsRepository( const wxString& aPath ) bool GIT_INIT_HANDLER::IsRepository( const wxString& aPath )
{ {
git_repository* repo = nullptr; return GetGitBackend()->IsRepository( this, aPath );
int error = git_repository_open( &repo, aPath.mb_str() );
if( error == 0 )
{
git_repository_free( repo );
return true;
}
return false;
} }
InitResult GIT_INIT_HANDLER::InitializeRepository( const wxString& aPath ) InitResult GIT_INIT_HANDLER::InitializeRepository( const wxString& aPath )
{ {
// Check if directory is already a git repository return GetGitBackend()->InitializeRepository( this, aPath );
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;
} }
bool GIT_INIT_HANDLER::SetupRemote( const RemoteConfig& aConfig ) bool GIT_INIT_HANDLER::SetupRemote( const RemoteConfig& aConfig )
{ {
// This is an optional step return GetGitBackend()->SetupRemote( this, aConfig );
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;
} }
void GIT_INIT_HANDLER::UpdateProgress( int aCurrent, int aTotal, const wxString& aMessage ) 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_pull_handler.h"
#include <git/kicad_git_common.h> #include "git_backend.h"
#include <git/kicad_git_memory.h>
#include <trace_helpers.h>
#include <wx/log.h>
#include <iostream>
#include <time.h>
#include <memory>
GIT_PULL_HANDLER::GIT_PULL_HANDLER( KIGIT_COMMON* aCommon ) : KIGIT_REPO_MIXIN( aCommon ) GIT_PULL_HANDLER::GIT_PULL_HANDLER( KIGIT_COMMON* aCommon ) : KIGIT_REPO_MIXIN( aCommon )
{ {}
}
GIT_PULL_HANDLER::~GIT_PULL_HANDLER() GIT_PULL_HANDLER::~GIT_PULL_HANDLER()
{ {}
}
bool GIT_PULL_HANDLER::PerformFetch( bool aSkipLock ) bool GIT_PULL_HANDLER::PerformFetch( bool aSkipLock )
{ {
if( !GetRepo() ) return GetGitBackend()->PerformFetch( this, aSkipLock );
{
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;
} }
PullResult GIT_PULL_HANDLER::PerformPull() PullResult GIT_PULL_HANDLER::PerformPull()
{ {
PullResult result = PullResult::Success; return GetGitBackend()->PerformPull( this );
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;
} }
const std::vector<std::pair<std::string, std::vector<CommitDetails>>>& const std::vector<std::pair<std::string, std::vector<CommitDetails>>>&
@ -212,384 +46,7 @@ GIT_PULL_HANDLER::GetFetchResults() const
return m_fetchResults; 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 ) 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 <vector>
#include <string> #include <string>
#include <wx/string.h> #include <wx/string.h>
#include <git2.h>
// Structure to store commit details // Structure to store commit details
struct CommitDetails struct CommitDetails
@ -50,22 +49,12 @@ enum class PullResult : int
FastForward FastForward
}; };
struct ConflictData class LIBGIT_BACKEND;
{
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 GIT_PULL_HANDLER : public KIGIT_REPO_MIXIN class GIT_PULL_HANDLER : public KIGIT_REPO_MIXIN
{ {
public: public:
friend class LIBGIT_BACKEND;
GIT_PULL_HANDLER( KIGIT_COMMON* aCommon ); GIT_PULL_HANDLER( KIGIT_COMMON* aCommon );
~GIT_PULL_HANDLER(); ~GIT_PULL_HANDLER();
@ -77,15 +66,8 @@ public:
// Implementation for progress updates // Implementation for progress updates
void UpdateProgress( int aCurrent, int aTotal, const wxString& aMessage ) override; void UpdateProgress( int aCurrent, int aTotal, const wxString& aMessage ) override;
private: private:
std::vector<std::pair<std::string, std::vector<CommitDetails>>> m_fetchResults; 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_ #endif // _GIT_PULL_HANDLER_H_

View File

@ -22,13 +22,8 @@
*/ */
#include "git_push_handler.h" #include "git_push_handler.h"
#include <git/kicad_git_common.h>
#include <git/kicad_git_memory.h>
#include <trace_helpers.h> #include <trace_helpers.h>
#include "git_backend.h"
#include <iostream>
#include <wx/log.h>
GIT_PUSH_HANDLER::GIT_PUSH_HANDLER( KIGIT_COMMON* aRepo ) : KIGIT_REPO_MIXIN( aRepo ) 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() PushResult GIT_PUSH_HANDLER::PerformPush()
{ {
std::unique_lock<std::mutex> lock( GetCommon()->m_gitActionMutex, std::try_to_lock ); return GetGitBackend()->Push( this );
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;
} }

View File

@ -21,15 +21,14 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * 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 <wx/log.h>
#include <git/kicad_git_memory.h> GIT_REMOVE_FROM_INDEX_HANDLER::GIT_REMOVE_FROM_INDEX_HANDLER( git_repository* aRepository ) :
#include "git_remove_from_index_handler.h" KIGIT_COMMON( aRepository )
GIT_REMOVE_FROM_INDEX_HANDLER::GIT_REMOVE_FROM_INDEX_HANDLER( git_repository* aRepository )
{ {
m_repository = aRepository;
m_filesToRemove.clear(); 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 ) bool GIT_REMOVE_FROM_INDEX_HANDLER::RemoveFromIndex( const wxString& aFilePath )
{ {
// Test if file is currently in the index return GetGitBackend()->RemoveFromIndex( this, aFilePath );
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;
} }
void GIT_REMOVE_FROM_INDEX_HANDLER::PerformRemoveFromIndex() void GIT_REMOVE_FROM_INDEX_HANDLER::PerformRemoveFromIndex()
{ {
for( auto& file : m_filesToRemove ) GetGitBackend()->PerformRemoveFromIndex( this );
{
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;
}
}
} }

View File

@ -24,12 +24,13 @@
#ifndef GIT_REMOVE_FROM_INDEX_HANDLER_H_ #ifndef GIT_REMOVE_FROM_INDEX_HANDLER_H_
#define 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 <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: public:
GIT_REMOVE_FROM_INDEX_HANDLER( git_repository* aRepository ); GIT_REMOVE_FROM_INDEX_HANDLER( git_repository* aRepository );
@ -41,7 +42,7 @@ public:
private: private:
git_repository* m_repository; friend class LIBGIT_BACKEND;
std::vector<wxString> m_filesToRemove; std::vector<wxString> m_filesToRemove;
}; };

View File

@ -22,11 +22,7 @@
*/ */
#include "git_revert_handler.h" #include "git_revert_handler.h"
#include "git_backend.h"
#include <wx/log.h>
#include <wx/string.h>
#include <trace_helpers.h>
GIT_REVERT_HANDLER::GIT_REVERT_HANDLER( git_repository* aRepository ) GIT_REVERT_HANDLER::GIT_REVERT_HANDLER( git_repository* aRepository )
@ -46,74 +42,8 @@ bool GIT_REVERT_HANDLER::Revert( const wxString& aFilePath )
return true; 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() void GIT_REVERT_HANDLER::PerformRevert()
{ {
git_object* head_commit = NULL; GetGitBackend()->PerformRevert( this );
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 );
} }

View File

@ -27,6 +27,10 @@
#include <git2.h> #include <git2.h>
#include <vector> #include <vector>
#include <wx/string.h> #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 class GIT_REVERT_HANDLER
{ {
@ -44,6 +48,7 @@ public:
} }
private: private:
friend class LIBGIT_BACKEND;
git_repository* m_repository; git_repository* m_repository;
std::vector<wxString> m_filesToRevert; std::vector<wxString> m_filesToRevert;

View File

@ -22,10 +22,8 @@
*/ */
#include "git_status_handler.h" #include "git_status_handler.h"
#include <git/kicad_git_common.h>
#include <git/kicad_git_memory.h>
#include <trace_helpers.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 ) 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() bool GIT_STATUS_HANDLER::HasChangedFiles()
{ {
git_repository* repo = GetRepo(); return GetGitBackend()->HasChangedFiles( this );
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;
} }
std::map<wxString, FileStatus> GIT_STATUS_HANDLER::GetFileStatus( const wxString& aPathspec ) std::map<wxString, FileStatus> GIT_STATUS_HANDLER::GetFileStatus( const wxString& aPathspec )
{ {
std::map<wxString, FileStatus> fileStatusMap; return GetGitBackend()->GetFileStatus( this, aPathspec );
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;
} }
wxString GIT_STATUS_HANDLER::GetCurrentBranchName() wxString GIT_STATUS_HANDLER::GetCurrentBranchName()
{ {
git_repository* repo = GetRepo(); return GetGitBackend()->GetCurrentBranchName( this );
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;
}
} }
void GIT_STATUS_HANDLER::UpdateRemoteStatus( const std::set<wxString>& aLocalChanges, void GIT_STATUS_HANDLER::UpdateRemoteStatus( const std::set<wxString>& aLocalChanges,
const std::set<wxString>& aRemoteChanges, const std::set<wxString>& aRemoteChanges,
std::map<wxString, FileStatus>& aFileStatus ) std::map<wxString, FileStatus>& aFileStatus )
{ {
git_repository* repo = GetRepo(); GetGitBackend()->UpdateRemoteStatus( this, aLocalChanges, aRemoteChanges, aFileStatus );
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;
}
}
}
} }
wxString GIT_STATUS_HANDLER::GetWorkingDirectory() wxString GIT_STATUS_HANDLER::GetWorkingDirectory()
{ {
git_repository* repo = GetRepo(); return GetGitBackend()->GetWorkingDirectory( this );
if( !repo )
return wxEmptyString;
const char* workdir = git_repository_workdir( repo );
if( !workdir )
return wxEmptyString;
return wxString( workdir );
} }
KIGIT_COMMON::GIT_STATUS GIT_STATUS_HANDLER::ConvertStatus( unsigned int aGitStatus ) KIGIT_COMMON::GIT_STATUS GIT_STATUS_HANDLER::ConvertStatus( unsigned int aGitStatus )

View File

@ -29,6 +29,8 @@
#include <map> #include <map>
#include <set> #include <set>
class LIBGIT_BACKEND;
struct FileStatus struct FileStatus
{ {
wxString filePath; wxString filePath;
@ -80,6 +82,7 @@ public:
void UpdateProgress( int aCurrent, int aTotal, const wxString& aMessage ) override; void UpdateProgress( int aCurrent, int aTotal, const wxString& aMessage ) override;
private: private:
friend class LIBGIT_BACKEND;
/** /**
* Convert git status flags to KIGIT_COMMON::GIT_STATUS * Convert git status flags to KIGIT_COMMON::GIT_STATUS
* @param aGitStatus Raw git status flags * @param aGitStatus Raw git status flags

View File

@ -33,6 +33,8 @@
#include <wx/string.h> #include <wx/string.h>
class LIBGIT_BACKEND;
class KIGIT_COMMON class KIGIT_COMMON
{ {
@ -174,6 +176,7 @@ protected:
friend class GIT_PUSH_HANDLER; friend class GIT_PUSH_HANDLER;
friend class GIT_PULL_HANDLER; friend class GIT_PULL_HANDLER;
friend class GIT_CLONE_HANDLER; friend class GIT_CLONE_HANDLER;
friend class LIBGIT_BACKEND;
friend class PROJECT_TREE_PANE; friend class PROJECT_TREE_PANE;
private: 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 "project_git_utils.h"
#include "kicad_git_common.h" #include "git_backend.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>
namespace KIGIT namespace KIGIT
{ {
git_repository* PROJECT_GIT_UTILS::GetRepositoryForFile( const char* aFilename ) git_repository* PROJECT_GIT_UTILS::GetRepositoryForFile( const char* aFilename )
{ {
git_repository* repo = nullptr; return GetGitBackend()->GetRepositoryForFile( aFilename );
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;
} }
int PROJECT_GIT_UTILS::CreateBranch( git_repository* aRepo, const wxString& aBranchName ) int PROJECT_GIT_UTILS::CreateBranch( git_repository* aRepo, const wxString& aBranchName )
{ {
git_oid head_oid; return GetGitBackend()->CreateBranch( aRepo, aBranchName );
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;
} }
bool PROJECT_GIT_UTILS::RemoveVCS( git_repository*& aRepo, const wxString& aProjectPath, bool PROJECT_GIT_UTILS::RemoveVCS( git_repository*& aRepo, const wxString& aProjectPath,
bool aRemoveGitDir, wxString* aErrors ) bool aRemoveGitDir, wxString* aErrors )
{ {
if( aRepo ) return GetGitBackend()->RemoveVCS( aRepo, aProjectPath, aRemoveGitDir, aErrors );
{
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;
} }
} // namespace KIGIT } // namespace KIGIT

View File

@ -40,6 +40,7 @@
#include <wx/txtstrm.h> #include <wx/txtstrm.h>
#include <wx/wfstream.h> #include <wx/wfstream.h>
#include <tool/tool_action.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_CMD_MAC wxT( "Cmd+" )
#define MODIFIER_CTRL_BASE wxT( "Ctrl+" ) #define MODIFIER_CTRL_BASE wxT( "Ctrl+" )
#define MODIFIER_SHIFT wxT( "Shift+" ) #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 ) wxString KeyNameFromKeyCode( int aKeycode, bool* aIsFound )
@ -175,6 +180,14 @@ wxString KeyNameFromKeyCode( int aKeycode, bool* aIsFound )
return wxString( MODIFIER_SHIFT ).BeforeFirst( '+' ); return wxString( MODIFIER_SHIFT ).BeforeFirst( '+' );
else if( aKeycode == WXK_ALT ) else if( aKeycode == WXK_ALT )
return wxString( MODIFIER_ALT ).BeforeFirst( '+' ); 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" // Assume keycode of 0 is "unassigned"
if( (aKeycode & MD_CTRL) != 0 ) if( (aKeycode & MD_CTRL) != 0 )
@ -186,7 +199,16 @@ wxString KeyNameFromKeyCode( int aKeycode, bool* aIsFound )
if( (aKeycode & MD_SHIFT) != 0 ) if( (aKeycode & MD_SHIFT) != 0 )
modifier << MODIFIER_SHIFT; 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 ) ) if( (aKeycode > ' ') && (aKeycode < 0x7F ) )
{ {
@ -262,7 +284,7 @@ int KeyCodeFromKeyName( const wxString& keyname )
{ {
int ii, keycode = KEY_NON_FOUND; 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 // Note: on Mac OSX, the Cmd key is equiv here to Ctrl
wxString key = keyname; wxString key = keyname;
wxString prefix; wxString prefix;
@ -292,6 +314,26 @@ int KeyCodeFromKeyName( const wxString& keyname )
modifier |= MD_SHIFT; modifier |= MD_SHIFT;
prefix = MODIFIER_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 else
{ {
break; break;

View File

@ -40,8 +40,7 @@ JOB_EXPORT_SCH_BOM::JOB_EXPORT_SCH_BOM() :
m_sortField(), m_sortField(),
m_sortAsc( true ), m_sortAsc( true ),
m_filterString(), m_filterString(),
m_excludeDNP( false ), m_excludeDNP( false )
m_includeExcludedFromBOM( false )
{ {
m_params.emplace_back( new JOB_PARAM<wxString>( "field_delimiter", m_params.emplace_back( new JOB_PARAM<wxString>( "field_delimiter",
&m_fieldDelimiter, &m_fieldDelimiter,
@ -70,13 +69,8 @@ JOB_EXPORT_SCH_BOM::JOB_EXPORT_SCH_BOM() :
m_fieldsGroupBy ) ); m_fieldsGroupBy ) );
m_params.emplace_back( new JOB_PARAM<wxString>( "sort_field", &m_sortField, m_sortField ) ); 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<bool>( "sort_asc", &m_sortAsc, m_sortAsc ) );
m_params.emplace_back( new JOB_PARAM<wxString>( "filter_string", m_params.emplace_back( new JOB_PARAM<wxString>( "filter_string", &m_filterString, m_filterString ) );
&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>( "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_params.emplace_back( new JOB_PARAM<wxString>( "bom_preset_name",
&m_bomPresetName, &m_bomPresetName,

View File

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

View File

@ -24,6 +24,16 @@
#include <properties/pg_properties.h> #include <properties/pg_properties.h>
#include <widgets/color_swatch.h> #include <widgets/color_swatch.h>
#include <widgets/unit_binder.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> #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_CHECKBOX_EDITOR::EDITOR_NAME = wxS( "KiCadCheckboxEditor" );
const wxString PG_COLOR_EDITOR::EDITOR_NAME = wxS( "KiCadColorEditor" ); const wxString PG_COLOR_EDITOR::EDITOR_NAME = wxS( "KiCadColorEditor" );
const wxString PG_RATIO_EDITOR::EDITOR_NAME = wxS( "KiCadRatioEditor" ); 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 ) : 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 ) wxString PG_UNIT_EDITOR::BuildEditorName( EDA_DRAW_FRAME* aFrame )
{ {
if( !aFrame )
return EDITOR_NAME + "NoFrame";
return EDITOR_NAME + aFrame->GetName(); return EDITOR_NAME + aFrame->GetName();
} }
@ -458,3 +473,164 @@ void PG_RATIO_EDITOR::UpdateControl( wxPGProperty* aProperty, wxWindow* aCtrl )
"properties!" ) ); "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

@ -61,11 +61,8 @@ EDIT_POINT* EDIT_POINTS::FindPoint( const VECTOR2I& aLocation, KIGFX::VIEW *aVie
if( m_allowPoints ) if( m_allowPoints )
{ {
// Check from the end so that we get the topmost point for( EDIT_POINT& point : m_points )
for( auto r_it = m_points.rbegin(); r_it != m_points.rend(); ++r_it )
{ {
EDIT_POINT& point = *r_it;
if( point.WithinPoint( aLocation, size ) ) if( point.WithinPoint( aLocation, size ) )
return &point; return &point;
} }

View File

@ -158,6 +158,43 @@ TOOL_DISPATCHER::~TOOL_DISPATCHER()
delete st; delete st;
} }
int TOOL_DISPATCHER::decodeModifiers( const wxKeyboardState* aState )
{
int mods = 0;
int wxmods = aState->GetModifiers();
// Returns the state of key modifiers (Alt, Ctrl and so on). Be carefull:
// the flag wxMOD_ALTGR is defined in wxWidgets as wxMOD_CONTROL|wxMOD_ALT
// So AltGr key cannot used as modifier key because it is the same as Alt key + Ctrl key.
#if CAN_USE_ALTGR_KEY
if( wxmods & wxMOD_ALTGR )
mods |= MD_ALTGR;
else
#endif
{
if( wxmods & wxMOD_CONTROL )
mods |= MD_CTRL;
if( wxmods & wxMOD_ALT )
mods |= MD_ALT;
}
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;
}
void TOOL_DISPATCHER::ResetState() void TOOL_DISPATCHER::ResetState()
{ {
@ -297,7 +334,19 @@ static bool isKeyModifierOnly( int aKeyCode )
{ {
static std::vector<enum wxKeyCode> special_keys = 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 ); return alg::contains( special_keys, aKeyCode );
@ -515,7 +564,7 @@ void TOOL_DISPATCHER::DispatchWxEvent( wxEvent& aEvent )
if( !evt && me->GetWheelRotation() != 0 ) if( !evt && me->GetWheelRotation() != 0 )
{ {
const unsigned modBits = 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; const bool shouldHandle = std::popcount( modBits ) > 1;
if( shouldHandle ) if( shouldHandle )

View File

@ -149,6 +149,9 @@ const std::string TOOL_EVENT::Format() const
{ MD_SHIFT, "shift" }, { MD_SHIFT, "shift" },
{ MD_CTRL, "ctrl" }, { MD_CTRL, "ctrl" },
{ MD_ALT, "alt" }, { MD_ALT, "alt" },
{ MD_SUPER, "super" },
{ MD_META, "meta" },
{ MD_ALTGR, "altgr" },
{ 0, "" } { 0, "" }
}; };

View File

@ -640,15 +640,36 @@ void LIB_TREE::onQueryCharHook( wxKeyEvent& aKeyStroke )
{ {
int hotkey = aKeyStroke.GetKeyCode(); int hotkey = aKeyStroke.GetKeyCode();
if( aKeyStroke.GetModifiers() & wxMOD_CONTROL ) int mods = aKeyStroke.GetModifiers();
hotkey += MD_CTRL;
if( aKeyStroke.GetModifiers() & wxMOD_ALT ) // the flag wxMOD_ALTGR is defined in wxWidgets as wxMOD_CONTROL|wxMOD_ALT
hotkey += MD_ALT; // So AltGr key cannot used as modifier key because it is the same as Alt key + Ctrl key.
#if CAN_USE_ALTGR_KEY
if( wxmods & wxMOD_ALTGR )
mods |= MD_ALTGR;
else
#endif
{
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; 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() if( hotkey == ACTIONS::expandAll.GetHotKey()
|| hotkey == ACTIONS::expandAll.GetHotKeyAlt() ) || hotkey == ACTIONS::expandAll.GetHotKeyAlt() )
{ {
@ -860,14 +881,31 @@ void LIB_TREE::onTreeCharHook( wxKeyEvent& aKeyStroke )
{ {
int hotkey = aKeyStroke.GetKeyCode(); 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; hotkey |= MD_SHIFT;
if( aKeyStroke.AltDown() ) #ifdef wxMOD_META
hotkey |= MD_ALT; if( mods & wxMOD_META )
hotkey |= MD_META;
#endif
if( aKeyStroke.ControlDown() ) #ifdef wxMOD_WIN
hotkey |= MD_CTRL; if( mods & wxMOD_WIN )
hotkey |= MD_SUPER;
#endif
if( tool->GetManager()->GetActionManager()->RunHotKey( hotkey ) ) if( tool->GetManager()->GetActionManager()->RunHotKey( hotkey ) )
aKeyStroke.Skip( false ); aKeyStroke.Skip( false );

View File

@ -670,7 +670,7 @@ long WIDGET_HOTKEY_LIST::MapKeypressToKeycode( const wxKeyEvent& aEvent )
{ {
long key = aEvent.GetKeyCode(); long key = aEvent.GetKeyCode();
bool is_tab = aEvent.IsKeyInCategory( WXK_CATEGORY_TAB ); bool is_tab = aEvent.IsKeyInCategory( WXK_CATEGORY_TAB );
printf("key %lX mod %X\n", key, aEvent.GetModifiers());
if( key == WXK_ESCAPE ) if( key == WXK_ESCAPE )
{ {
return 0; return 0;
@ -693,14 +693,35 @@ long WIDGET_HOTKEY_LIST::MapKeypressToKeycode( const wxKeyEvent& aEvent )
*/ */
bool keyIsLetter = key >= 'A' && key <= 'Z'; 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; key |= MD_SHIFT;
if( aEvent.ControlDown() ) // the flag wxMOD_ALTGR is defined in wxWidgets as wxMOD_CONTROL|wxMOD_ALT
key |= MD_CTRL; // So AltGr key cannot used as modifier key because it is the same as Alt key + Ctrl key.
#if CAN_USE_ALTGR_KEY
if( wxmods & wxMOD_ALTGR )
mods |= MD_ALTGR;
else
#endif
{
if( mods & wxMOD_CONTROL )
key |= MD_CTRL;
if( aEvent.AltDown() ) if( mods & wxMOD_ALT )
key |= MD_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; return key;
} }

View File

@ -1614,11 +1614,10 @@ void CONNECTION_GRAPH::resolveAllDrivers()
thread_pool& tp = GetKiCadThreadPool(); thread_pool& tp = GetKiCadThreadPool();
auto results = tp.parallelize_loop( dirty_graphs.size(), auto results = tp.submit_loop( 0, dirty_graphs.size(),
[&]( const int a, const int b) [&]( const int ii )
{ {
for( int ii = a; ii < b; ++ii ) update_lambda( dirty_graphs[ii] );
update_lambda( dirty_graphs[ii] );
}); });
results.wait(); results.wait();
@ -2257,11 +2256,10 @@ void CONNECTION_GRAPH::buildConnectionGraph( std::function<void( SCH_ITEM* )>* a
thread_pool& tp = GetKiCadThreadPool(); thread_pool& tp = GetKiCadThreadPool();
auto results = tp.parallelize_loop( m_driver_subgraphs.size(), auto results = tp.submit_loop( 0, m_driver_subgraphs.size(),
[&]( const int a, const int b) [&]( const int ii )
{ {
for( int ii = a; ii < b; ++ii ) m_driver_subgraphs[ii]->UpdateItemConnections();
m_driver_subgraphs[ii]->UpdateItemConnections();
}); });
results.wait(); results.wait();
@ -2464,12 +2462,11 @@ void CONNECTION_GRAPH::buildConnectionGraph( std::function<void( SCH_ITEM* )>* a
return 1; return 1;
}; };
auto results2 = tp.parallelize_loop( m_driver_subgraphs.size(), auto results2 = tp.submit_loop( 0, m_driver_subgraphs.size(),
[&]( const int a, const int b) [&]( const int ii )
{ {
for( int ii = a; ii < b; ++ii ) updateItemConnectionsTask( m_driver_subgraphs[ii] );
updateItemConnectionsTask( m_driver_subgraphs[ii] ); } );
});
results2.wait(); results2.wait();
m_net_code_to_subgraphs_map.clear(); m_net_code_to_subgraphs_map.clear();

View File

@ -42,6 +42,7 @@
#include <id.h> #include <id.h>
#include <confirm.h> #include <confirm.h>
#include <widgets/wx_html_report_box.h> #include <widgets/wx_html_report_box.h>
#include <widgets/std_bitmap_button.h>
#include <dialogs/dialog_text_entry.h> #include <dialogs/dialog_text_entry.h>
#include <string_utils.h> #include <string_utils.h>
#include <kiplatform/ui.h> #include <kiplatform/ui.h>
@ -76,15 +77,15 @@ DIALOG_ERC::DIALOG_ERC( SCH_EDIT_FRAME* parent ) :
m_running( false ), m_running( false ),
m_ercRun( false ), m_ercRun( false ),
m_centerMarkerOnIdle( nullptr ), m_centerMarkerOnIdle( nullptr ),
m_severities( 0 ) m_crossprobe( true ),
m_scroll_on_crossprobe( true )
{ {
m_currentSchematic = &parent->Schematic(); m_currentSchematic = &parent->Schematic();
SetName( DIALOG_ERC_WINDOW_NAME ); // Set a window name to be able to find it SetName( DIALOG_ERC_WINDOW_NAME ); // Set a window name to be able to find it
KIPLATFORM::UI::SetFloatLevel( this ); KIPLATFORM::UI::SetFloatLevel( this );
if( EESCHEMA_SETTINGS* cfg = GetAppSettings<EESCHEMA_SETTINGS>( "eeschema" ) ) m_bMenu->SetBitmap( KiBitmapBundle( BITMAPS::config ) );
m_severities = cfg->m_Appearance.erc_severities;
m_messages->SetImmediateMode(); m_messages->SetImmediateMode();
@ -92,7 +93,7 @@ DIALOG_ERC::DIALOG_ERC( SCH_EDIT_FRAME* parent ) :
m_markerTreeModel = new ERC_TREE_MODEL( parent, m_markerDataView ); m_markerTreeModel = new ERC_TREE_MODEL( parent, m_markerDataView );
m_markerDataView->AssociateModel( m_markerTreeModel ); m_markerDataView->AssociateModel( m_markerTreeModel );
m_markerTreeModel->Update( m_markerProvider, m_severities ); m_markerTreeModel->Update( m_markerProvider, getSeverities() );
m_ignoredList->InsertColumn( 0, wxEmptyString, wxLIST_FORMAT_LEFT, DEFAULT_SINGLE_COL_WIDTH ); m_ignoredList->InsertColumn( 0, wxEmptyString, wxLIST_FORMAT_LEFT, DEFAULT_SINGLE_COL_WIDTH );
@ -129,8 +130,11 @@ DIALOG_ERC::DIALOG_ERC( SCH_EDIT_FRAME* parent ) :
SetFocus(); SetFocus();
syncCheckboxes(); if( EESCHEMA_SETTINGS* cfg = GetAppSettings<EESCHEMA_SETTINGS>( "eeschema" ) )
updateDisplayedCounts(); {
m_crossprobe = cfg->m_ERCDialog.crossprobe;
m_scroll_on_crossprobe = cfg->m_ERCDialog.scroll_on_crossprobe;
}
// Now all widgets have the size fixed, call FinishDialogSettings // Now all widgets have the size fixed, call FinishDialogSettings
finishDialogSettings(); finishDialogSettings();
@ -148,7 +152,10 @@ DIALOG_ERC::~DIALOG_ERC()
g_lastERCIgnored.push_back( { m_ignoredList->GetItemText( ii ), m_ignoredList->GetItemData( ii ) } ); g_lastERCIgnored.push_back( { m_ignoredList->GetItemText( ii ), m_ignoredList->GetItemData( ii ) } );
if( EESCHEMA_SETTINGS* cfg = GetAppSettings<EESCHEMA_SETTINGS>( "eeschema" ) ) if( EESCHEMA_SETTINGS* cfg = GetAppSettings<EESCHEMA_SETTINGS>( "eeschema" ) )
cfg->m_Appearance.erc_severities = m_severities; {
cfg->m_ERCDialog.crossprobe = m_crossprobe;
cfg->m_ERCDialog.scroll_on_crossprobe = m_scroll_on_crossprobe;
}
m_markerTreeModel->DecRef(); m_markerTreeModel->DecRef();
} }
@ -189,6 +196,59 @@ void DIALOG_ERC::UpdateAnnotationWarning()
} }
int DIALOG_ERC::getSeverities()
{
int severities = 0;
if( m_showErrors->GetValue() )
severities |= RPT_SEVERITY_ERROR;
if( m_showWarnings->GetValue() )
severities |= RPT_SEVERITY_WARNING;
if( m_showExclusions->GetValue() )
severities |= RPT_SEVERITY_EXCLUSION;
return severities;
}
void DIALOG_ERC::OnMenu( wxCommandEvent& event )
{
// Build a pop menu:
wxMenu menu;
menu.Append( 4206, _( "Cross-probe Selected Items" ),
_( "Highlight corresponding items on canvas when selected in the ERC list" ),
wxITEM_CHECK );
menu.Check( 4206, m_crossprobe );
menu.Append( 4207, _( "Center on Cross-probe" ),
_( "When cross-probing, scroll the canvas so that the item is visible" ),
wxITEM_CHECK );
menu.Check( 4207, m_scroll_on_crossprobe );
// menu_id is the selected submenu id from the popup menu or wxID_NONE
int menu_id = m_bMenu->GetPopupMenuSelectionFromUser( menu );
if( menu_id == 0 || menu_id == 4206 )
{
m_crossprobe = !m_crossprobe;
}
else if( menu_id == 1 || menu_id == 4207 )
{
m_scroll_on_crossprobe = !m_scroll_on_crossprobe;
}
}
bool DIALOG_ERC::TransferDataToWindow()
{
UpdateData();
return true;
}
bool DIALOG_ERC::updateUI() bool DIALOG_ERC::updateUI()
{ {
// If ERC checks ever get slow enough we'll want a progress indicator... // If ERC checks ever get slow enough we'll want a progress indicator...
@ -217,6 +277,13 @@ void DIALOG_ERC::Report( const wxString& aMessage )
} }
void DIALOG_ERC::UpdateData()
{
m_markerTreeModel->Update( m_markerProvider, getSeverities() );
updateDisplayedCounts();
}
void DIALOG_ERC::updateDisplayedCounts() void DIALOG_ERC::updateDisplayedCounts()
{ {
int numErrors = 0; int numErrors = 0;
@ -348,30 +415,14 @@ void DIALOG_ERC::OnCloseErcDialog( wxCloseEvent& aEvent )
// Dialog is mode-less so let the parent know that it needs to be destroyed. // Dialog is mode-less so let the parent know that it needs to be destroyed.
if( !IsModal() && !IsQuasiModal() ) if( !IsModal() && !IsQuasiModal() )
{ {
wxCommandEvent* evt = new wxCommandEvent( EDA_EVT_CLOSE_ERC_DIALOG, wxID_ANY ); if( wxWindow* parent = GetParent() )
wxQueueEvent( parent, new wxCommandEvent( EDA_EVT_CLOSE_ERC_DIALOG, wxID_ANY ) );
wxWindow* parent = GetParent();
if( parent )
wxQueueEvent( parent, evt );
} }
aEvent.Skip(); aEvent.Skip();
} }
static int RPT_SEVERITY_ALL = RPT_SEVERITY_WARNING | RPT_SEVERITY_ERROR | RPT_SEVERITY_EXCLUSION;
void DIALOG_ERC::syncCheckboxes()
{
m_showAll->SetValue( m_severities == RPT_SEVERITY_ALL );
m_showErrors->SetValue( m_severities & RPT_SEVERITY_ERROR );
m_showWarnings->SetValue( m_severities & RPT_SEVERITY_WARNING );
m_showExclusions->SetValue( m_severities & RPT_SEVERITY_EXCLUSION );
}
void DIALOG_ERC::OnLinkClicked( wxHtmlLinkEvent& event ) void DIALOG_ERC::OnLinkClicked( wxHtmlLinkEvent& event )
{ {
m_parent->OnAnnotate(); m_parent->OnAnnotate();
@ -446,8 +497,7 @@ void DIALOG_ERC::OnRunERCClick( wxCommandEvent& event )
} }
if( m_cancelled ) if( m_cancelled )
// @spellingerror m_messages->Report( _( "-------- ERC cancelled by user.<br><br>" ), RPT_SEVERITY_INFO );
m_messages->Report( _( "-------- ERC canceled by user.<br><br>" ), RPT_SEVERITY_INFO );
else else
m_messages->Report( _( "Done.<br><br>" ), RPT_SEVERITY_INFO ); m_messages->Report( _( "Done.<br><br>" ), RPT_SEVERITY_INFO );
@ -508,7 +558,7 @@ void DIALOG_ERC::testErc()
} }
// Update marker list: // Update marker list:
m_markerTreeModel->Update( m_markerProvider, m_severities ); m_markerTreeModel->Update( m_markerProvider, getSeverities() );
// Display new markers from the current screen: // Display new markers from the current screen:
for( SCH_ITEM* marker : m_parent->GetScreen()->Items().OfType( SCH_MARKER_T ) ) for( SCH_ITEM* marker : m_parent->GetScreen()->Items().OfType( SCH_MARKER_T ) )
@ -523,6 +573,12 @@ void DIALOG_ERC::testErc()
void DIALOG_ERC::OnERCItemSelected( wxDataViewEvent& aEvent ) void DIALOG_ERC::OnERCItemSelected( wxDataViewEvent& aEvent )
{ {
if( !m_crossprobe )
{
aEvent.Skip();
return;
}
const KIID& itemID = RC_TREE_MODEL::ToUUID( aEvent.GetItem() ); const KIID& itemID = RC_TREE_MODEL::ToUUID( aEvent.GetItem() );
SCH_SHEET_PATH sheet; SCH_SHEET_PATH sheet;
SCH_ITEM* item = m_parent->Schematic().ResolveItem( itemID, &sheet, true ); SCH_ITEM* item = m_parent->Schematic().ResolveItem( itemID, &sheet, true );
@ -568,7 +624,7 @@ void DIALOG_ERC::OnERCItemSelected( wxDataViewEvent& aEvent )
m_parent->RedrawScreen( m_parent->GetScreen()->m_ScrollCenter, false ); m_parent->RedrawScreen( m_parent->GetScreen()->m_ScrollCenter, false );
} }
m_parent->FocusOnItem( item ); m_parent->FocusOnItem( item, m_scroll_on_crossprobe );
redrawDrawPanel(); redrawDrawPanel();
} }
@ -762,7 +818,7 @@ void DIALOG_ERC::OnERCItemRClick( wxDataViewEvent& aEvent )
m_parent->GetCanvas()->GetView()->Update( marker ); m_parent->GetCanvas()->GetView()->Update( marker );
// Update view // Update view
if( m_severities & RPT_SEVERITY_EXCLUSION ) if( getSeverities() & RPT_SEVERITY_EXCLUSION )
static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->ValueChanged( node ); static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->ValueChanged( node );
else else
static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->DeleteCurrentItem( false ); static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->DeleteCurrentItem( false );
@ -788,7 +844,7 @@ void DIALOG_ERC::OnERCItemRClick( wxDataViewEvent& aEvent )
} }
// Rebuild model and view // Rebuild model and view
static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->Update( m_markerProvider, m_severities ); static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->Update( m_markerProvider, getSeverities() );
modified = true; modified = true;
break; break;
@ -804,7 +860,7 @@ void DIALOG_ERC::OnERCItemRClick( wxDataViewEvent& aEvent )
} }
// Rebuild model and view // Rebuild model and view
static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->Update( m_markerProvider, m_severities ); static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->Update( m_markerProvider, getSeverities() );
modified = true; modified = true;
break; break;
@ -830,7 +886,7 @@ void DIALOG_ERC::OnERCItemRClick( wxDataViewEvent& aEvent )
ScreenList.DeleteMarkers( MARKER_BASE::MARKER_ERC, rcItem->GetErrorCode() ); ScreenList.DeleteMarkers( MARKER_BASE::MARKER_ERC, rcItem->GetErrorCode() );
// Rebuild model and view // Rebuild model and view
static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->Update( m_markerProvider, m_severities ); static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->Update( m_markerProvider, getSeverities() );
modified = true; modified = true;
} }
break; break;
@ -956,7 +1012,7 @@ void DIALOG_ERC::ExcludeMarker( SCH_MARKER* aMarker )
m_parent->GetCanvas()->GetView()->Update( marker ); m_parent->GetCanvas()->GetView()->Update( marker );
// Update view // Update view
if( m_severities & RPT_SEVERITY_EXCLUSION ) if( getSeverities() & RPT_SEVERITY_EXCLUSION )
m_markerTreeModel->ValueChanged( node ); m_markerTreeModel->ValueChanged( node );
else else
m_markerTreeModel->DeleteCurrentItem( false ); m_markerTreeModel->DeleteCurrentItem( false );
@ -976,28 +1032,14 @@ void DIALOG_ERC::OnEditViolationSeverities( wxHyperlinkEvent& aEvent )
void DIALOG_ERC::OnSeverity( wxCommandEvent& aEvent ) void DIALOG_ERC::OnSeverity( wxCommandEvent& aEvent )
{ {
int flag = 0;
if( aEvent.GetEventObject() == m_showAll ) if( aEvent.GetEventObject() == m_showAll )
flag = RPT_SEVERITY_ALL; {
else if( aEvent.GetEventObject() == m_showErrors ) m_showErrors->SetValue( true );
flag = RPT_SEVERITY_ERROR; m_showWarnings->SetValue( aEvent.IsChecked() );
else if( aEvent.GetEventObject() == m_showWarnings ) m_showExclusions->SetValue( aEvent.IsChecked() );
flag = RPT_SEVERITY_WARNING; }
else if( aEvent.GetEventObject() == m_showExclusions )
flag = RPT_SEVERITY_EXCLUSION;
if( aEvent.IsChecked() ) UpdateData();
m_severities |= flag;
else if( aEvent.GetEventObject() == m_showAll )
m_severities = RPT_SEVERITY_ERROR;
else
m_severities &= ~flag;
syncCheckboxes();
m_markerTreeModel->Update( m_markerProvider, m_severities );
updateDisplayedCounts();
} }

View File

@ -49,6 +49,8 @@ public:
DIALOG_ERC( SCH_EDIT_FRAME* parent ); DIALOG_ERC( SCH_EDIT_FRAME* parent );
~DIALOG_ERC(); ~DIALOG_ERC();
bool TransferDataToWindow() override;
// PROGRESS_REPORTER_BASE calls // PROGRESS_REPORTER_BASE calls
bool updateUI() override; bool updateUI() override;
void AdvancePhase( const wxString& aMessage ) override; void AdvancePhase( const wxString& aMessage ) override;
@ -66,10 +68,14 @@ public:
*/ */
void ExcludeMarker( SCH_MARKER* aMarker = nullptr ); void ExcludeMarker( SCH_MARKER* aMarker = nullptr );
void UpdateData();
void UpdateAnnotationWarning(); void UpdateAnnotationWarning();
private: private:
int getSeverities();
// from DIALOG_ERC_BASE: // from DIALOG_ERC_BASE:
void OnMenu( wxCommandEvent& aEvent ) override;
void OnCloseErcDialog( wxCloseEvent& event ) override; void OnCloseErcDialog( wxCloseEvent& event ) override;
void OnRunERCClick( wxCommandEvent& event ) override; void OnRunERCClick( wxCommandEvent& event ) override;
void OnDeleteOneClick( wxCommandEvent& event ) override; void OnDeleteOneClick( wxCommandEvent& event ) override;
@ -92,8 +98,6 @@ private:
void testErc(); void testErc();
bool writeReport( const wxString& aFullFileName );
void deleteAllMarkers( bool aIncludeExclusions ); void deleteAllMarkers( bool aIncludeExclusions );
void syncCheckboxes(); void syncCheckboxes();
@ -114,7 +118,8 @@ private:
const SCH_MARKER* m_centerMarkerOnIdle; const SCH_MARKER* m_centerMarkerOnIdle;
int m_severities; bool m_crossprobe;
bool m_scroll_on_crossprobe;
}; };

View File

@ -5,6 +5,7 @@
// PLEASE DO *NOT* EDIT THIS FILE! // PLEASE DO *NOT* EDIT THIS FILE!
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
#include "widgets/std_bitmap_button.h"
#include "widgets/wx_html_report_box.h" #include "widgets/wx_html_report_box.h"
#include "widgets/wx_infobar.h" #include "widgets/wx_infobar.h"
@ -29,6 +30,24 @@ DIALOG_ERC_BASE::DIALOG_ERC_BASE( wxWindow* parent, wxWindowID id, const wxStrin
wxBoxSizer* bMainSizer; wxBoxSizer* bMainSizer;
bMainSizer = new wxBoxSizer( wxVERTICAL ); bMainSizer = new wxBoxSizer( wxVERTICAL );
wxGridBagSizer* gbSizerOptions;
gbSizerOptions = new wxGridBagSizer( 0, 0 );
gbSizerOptions->SetFlexibleDirection( wxBOTH );
gbSizerOptions->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
m_bMenu = new STD_BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW|0 );
m_bMenu->SetMinSize( wxSize( 30,30 ) );
gbSizerOptions->Add( m_bMenu, wxGBPosition( 0, 2 ), wxGBSpan( 2, 1 ), wxALIGN_CENTER_VERTICAL, 5 );
gbSizerOptions->AddGrowableCol( 0 );
gbSizerOptions->AddGrowableCol( 1 );
gbSizerOptions->AddGrowableRow( 0 );
gbSizerOptions->AddGrowableRow( 1 );
bMainSizer->Add( gbSizerOptions, 0, wxEXPAND|wxLEFT, 5 );
m_runningResultsBook = new wxSimplebook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); m_runningResultsBook = new wxSimplebook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
running = new wxPanel( m_runningResultsBook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); running = new wxPanel( m_runningResultsBook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
wxBoxSizer* bSizer14; wxBoxSizer* bSizer14;
@ -195,6 +214,7 @@ DIALOG_ERC_BASE::DIALOG_ERC_BASE( wxWindow* parent, wxWindowID id, const wxStrin
// Connect Events // Connect Events
this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_ERC_BASE::OnCloseErcDialog ) ); this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_ERC_BASE::OnCloseErcDialog ) );
m_bMenu->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_ERC_BASE::OnMenu ), NULL, this );
m_messages->Connect( wxEVT_COMMAND_HTML_LINK_CLICKED, wxHtmlLinkEventHandler( DIALOG_ERC_BASE::OnLinkClicked ), NULL, this ); m_messages->Connect( wxEVT_COMMAND_HTML_LINK_CLICKED, wxHtmlLinkEventHandler( DIALOG_ERC_BASE::OnLinkClicked ), NULL, this );
m_markerDataView->Connect( wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED, wxDataViewEventHandler( DIALOG_ERC_BASE::OnERCItemDClick ), NULL, this ); m_markerDataView->Connect( wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED, wxDataViewEventHandler( DIALOG_ERC_BASE::OnERCItemDClick ), NULL, this );
m_markerDataView->Connect( wxEVT_COMMAND_DATAVIEW_ITEM_CONTEXT_MENU, wxDataViewEventHandler( DIALOG_ERC_BASE::OnERCItemRClick ), NULL, this ); m_markerDataView->Connect( wxEVT_COMMAND_DATAVIEW_ITEM_CONTEXT_MENU, wxDataViewEventHandler( DIALOG_ERC_BASE::OnERCItemRClick ), NULL, this );
@ -216,6 +236,7 @@ DIALOG_ERC_BASE::~DIALOG_ERC_BASE()
{ {
// Disconnect Events // Disconnect Events
this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_ERC_BASE::OnCloseErcDialog ) ); this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_ERC_BASE::OnCloseErcDialog ) );
m_bMenu->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_ERC_BASE::OnMenu ), NULL, this );
m_messages->Disconnect( wxEVT_COMMAND_HTML_LINK_CLICKED, wxHtmlLinkEventHandler( DIALOG_ERC_BASE::OnLinkClicked ), NULL, this ); m_messages->Disconnect( wxEVT_COMMAND_HTML_LINK_CLICKED, wxHtmlLinkEventHandler( DIALOG_ERC_BASE::OnLinkClicked ), NULL, this );
m_markerDataView->Disconnect( wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED, wxDataViewEventHandler( DIALOG_ERC_BASE::OnERCItemDClick ), NULL, this ); m_markerDataView->Disconnect( wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED, wxDataViewEventHandler( DIALOG_ERC_BASE::OnERCItemDClick ), NULL, this );
m_markerDataView->Disconnect( wxEVT_COMMAND_DATAVIEW_ITEM_CONTEXT_MENU, wxDataViewEventHandler( DIALOG_ERC_BASE::OnERCItemRClick ), NULL, this ); m_markerDataView->Disconnect( wxEVT_COMMAND_DATAVIEW_ITEM_CONTEXT_MENU, wxDataViewEventHandler( DIALOG_ERC_BASE::OnERCItemRClick ), NULL, this );

View File

@ -14,7 +14,7 @@
<property name="embedded_files_path">res</property> <property name="embedded_files_path">res</property>
<property name="encoding">UTF-8</property> <property name="encoding">UTF-8</property>
<property name="file">dialog_erc_base</property> <property name="file">dialog_erc_base</property>
<property name="first_id">1000</property> <property name="first_id">7100</property>
<property name="internationalize">1</property> <property name="internationalize">1</property>
<property name="lua_skip_events">1</property> <property name="lua_skip_events">1</property>
<property name="lua_ui_table">UI</property> <property name="lua_ui_table">UI</property>
@ -135,6 +135,101 @@
<property name="name">bMainSizer</property> <property name="name">bMainSizer</property>
<property name="orient">wxVERTICAL</property> <property name="orient">wxVERTICAL</property>
<property name="permission">none</property> <property name="permission">none</property>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxEXPAND|wxLEFT</property>
<property name="proportion">0</property>
<object class="wxGridBagSizer" expanded="true">
<property name="empty_cell_size"></property>
<property name="flexible_direction">wxBOTH</property>
<property name="growablecols">0,1</property>
<property name="growablerows">0,1</property>
<property name="hgap">0</property>
<property name="minimum_size"></property>
<property name="name">gbSizerOptions</property>
<property name="non_flexible_grow_mode">wxFLEX_GROWMODE_SPECIFIED</property>
<property name="permission">none</property>
<property name="vgap">0</property>
<object class="gbsizeritem" expanded="true">
<property name="border">5</property>
<property name="colspan">1</property>
<property name="column">2</property>
<property name="flag">wxALIGN_CENTER_VERTICAL</property>
<property name="row">0</property>
<property name="rowspan">2</property>
<object class="wxBitmapButton" expanded="true">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer">0</property>
<property name="aui_name"></property>
<property name="aui_position">0</property>
<property name="aui_row">0</property>
<property name="auth_needed">0</property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="bitmap"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="current"></property>
<property name="default">0</property>
<property name="default_pane">0</property>
<property name="disabled"></property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="drag_accept_files">0</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="focus"></property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Refresh Grouping</property>
<property name="margins"></property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size">30,30</property>
<property name="moveable">1</property>
<property name="name">m_bMenu</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="position"></property>
<property name="pressed"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">STD_BITMAP_BUTTON; widgets/std_bitmap_button.h; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnButtonClick">OnMenu</event>
</object>
</object>
</object>
</object>
<object class="sizeritem" expanded="true"> <object class="sizeritem" expanded="true">
<property name="border">5</property> <property name="border">5</property>
<property name="flag">wxEXPAND|wxTOP|wxRIGHT</property> <property name="flag">wxEXPAND|wxTOP|wxRIGHT</property>

View File

@ -10,6 +10,7 @@
#include <wx/artprov.h> #include <wx/artprov.h>
#include <wx/xrc/xmlres.h> #include <wx/xrc/xmlres.h>
#include <wx/intl.h> #include <wx/intl.h>
class STD_BITMAP_BUTTON;
class WX_HTML_REPORT_BOX; class WX_HTML_REPORT_BOX;
class WX_INFOBAR; class WX_INFOBAR;
@ -20,13 +21,16 @@ class WX_INFOBAR;
#include <wx/colour.h> #include <wx/colour.h>
#include <wx/settings.h> #include <wx/settings.h>
#include <wx/string.h> #include <wx/string.h>
#include <wx/bmpbuttn.h>
#include <wx/bitmap.h>
#include <wx/image.h>
#include <wx/icon.h>
#include <wx/button.h>
#include <wx/gbsizer.h>
#include <wx/html/htmlwin.h> #include <wx/html/htmlwin.h>
#include <wx/gauge.h> #include <wx/gauge.h>
#include <wx/sizer.h> #include <wx/sizer.h>
#include <wx/panel.h> #include <wx/panel.h>
#include <wx/bitmap.h>
#include <wx/image.h>
#include <wx/icon.h>
#include <wx/notebook.h> #include <wx/notebook.h>
#include <wx/dataview.h> #include <wx/dataview.h>
#include <wx/listctrl.h> #include <wx/listctrl.h>
@ -35,12 +39,11 @@ class WX_INFOBAR;
#include <wx/stattext.h> #include <wx/stattext.h>
#include <wx/checkbox.h> #include <wx/checkbox.h>
#include <widgets/number_badge.h> #include <widgets/number_badge.h>
#include <wx/button.h>
#include <wx/dialog.h> #include <wx/dialog.h>
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
#define ID_ERASE_DRC_MARKERS 1000 #define ID_ERASE_DRC_MARKERS 7100
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
/// Class DIALOG_ERC_BASE /// Class DIALOG_ERC_BASE
@ -51,6 +54,7 @@ class DIALOG_ERC_BASE : public DIALOG_SHIM
protected: protected:
WX_INFOBAR* m_infoBar; WX_INFOBAR* m_infoBar;
STD_BITMAP_BUTTON* m_bMenu;
wxSimplebook* m_runningResultsBook; wxSimplebook* m_runningResultsBook;
wxPanel* running; wxPanel* running;
wxNotebook* m_runningNotebook; wxNotebook* m_runningNotebook;
@ -82,6 +86,7 @@ class DIALOG_ERC_BASE : public DIALOG_SHIM
// Virtual event handlers, override them in your derived class // Virtual event handlers, override them in your derived class
virtual void OnCloseErcDialog( wxCloseEvent& event ) { event.Skip(); } virtual void OnCloseErcDialog( wxCloseEvent& event ) { event.Skip(); }
virtual void OnMenu( wxCommandEvent& event ) { event.Skip(); }
virtual void OnLinkClicked( wxHtmlLinkEvent& event ) { event.Skip(); } virtual void OnLinkClicked( wxHtmlLinkEvent& event ) { event.Skip(); }
virtual void OnERCItemDClick( wxDataViewEvent& event ) { event.Skip(); } virtual void OnERCItemDClick( wxDataViewEvent& event ) { event.Skip(); }
virtual void OnERCItemRClick( wxDataViewEvent& event ) { event.Skip(); } virtual void OnERCItemRClick( wxDataViewEvent& event ) { event.Skip(); }

View File

@ -77,8 +77,8 @@ public:
*/ */
std::vector<std::pair<FIELD_T, wxString>> GetFields() const; std::vector<std::pair<FIELD_T, wxString>> GetFields() const;
bool GetKeepSymbol() { return m_keepSymbol; } bool GetKeepSymbol() { return m_keepSymbol->GetValue(); }
bool GetPlaceAllUnits() { return m_useUnits; } bool GetPlaceAllUnits() { return m_useUnits->GetValue(); }
public: public:
static std::mutex g_Mutex; 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.name = m_job->m_bomPresetName;
preset.excludeDNP = m_job->m_excludeDNP; preset.excludeDNP = m_job->m_excludeDNP;
preset.includeExcludedFromBOM = m_job->m_includeExcludedFromBOM;
preset.filterString = m_job->m_filterString; preset.filterString = m_job->m_filterString;
preset.sortAsc = m_job->m_sortAsc; preset.sortAsc = m_job->m_sortAsc;
preset.sortField = m_job->m_sortField; preset.sortField = m_job->m_sortField;
@ -922,16 +921,28 @@ void DIALOG_SYMBOL_FIELDS_TABLE::OnMenu( wxCommandEvent& event )
// Build a pop menu: // Build a pop menu:
wxMenu menu; wxMenu menu;
menu.Append( 4204, _( "Include 'DNP' Symbols" ), wxEmptyString, wxITEM_CHECK ); menu.Append( 4204, _( "Include 'DNP' Symbols" ),
menu.Append( 4205, _( "Include 'Exclude from BOM' Symbols" ), wxEmptyString, wxITEM_CHECK ); _( "Show symbols marked 'DNP' in the table. This setting also controls whether or not 'DNP' "
menu.AppendSeparator(); "symbols are included on export." ),
menu.Append( 4206, _( "Highlight on Cross Probe" ), wxEmptyString, wxITEM_CHECK ); wxITEM_CHECK );
menu.Append( 4207, _( "Select on Cross Probe" ), wxEmptyString, wxITEM_CHECK );
menu.Check( 4204, !m_dataModel->GetExcludeDNP() ); menu.Check( 4204, !m_dataModel->GetExcludeDNP() );
menu.Append( 4205, _( "Include 'Exclude from BOM' Symbols" ),
_( "Show symbols marked 'Exclude from BOM' in the table. Symbols marked 'Exclude from BOM' "
"are never included on export." ),
wxITEM_CHECK );
menu.Check( 4205, m_dataModel->GetIncludeExcludedFromBOM() ); menu.Check( 4205, m_dataModel->GetIncludeExcludedFromBOM() );
menu.AppendSeparator();
menu.Append( 4206, _( "Highlight on Cross-probe" ),
_( "Highlight corresponding item on canvas when it is selected in the table" ),
wxITEM_CHECK );
menu.Check( 4206, cfg.selection_mode == 0 ); menu.Check( 4206, cfg.selection_mode == 0 );
menu.Append( 4207, _( "Select on Cross-probe" ),
_( "Select corresponding item on canvas when it is selected in the table" ),
wxITEM_CHECK );
menu.Check( 4207, cfg.selection_mode == 1 ); menu.Check( 4207, cfg.selection_mode == 1 );
// menu_id is the selected submenu id from the popup menu or wxID_NONE // menu_id is the selected submenu id from the popup menu or wxID_NONE
@ -947,7 +958,7 @@ void DIALOG_SYMBOL_FIELDS_TABLE::OnMenu( wxCommandEvent& event )
} }
else if( menu_id == 1 || menu_id == 4205 ) else if( menu_id == 1 || menu_id == 4205 )
{ {
m_dataModel->SetExcludeDNP( m_dataModel->GetIncludeExcludedFromBOM() ); m_dataModel->SetIncludeExcludedFromBOM( !m_dataModel->GetIncludeExcludedFromBOM() );
m_dataModel->RebuildRows(); m_dataModel->RebuildRows();
m_grid->ForceRefresh(); m_grid->ForceRefresh();
@ -1300,8 +1311,18 @@ void DIALOG_SYMBOL_FIELDS_TABLE::OnPreviewRefresh( wxCommandEvent& event )
void DIALOG_SYMBOL_FIELDS_TABLE::PreviewRefresh() void DIALOG_SYMBOL_FIELDS_TABLE::PreviewRefresh()
{ {
bool saveIncludeExcudedFromBOM = m_dataModel->GetIncludeExcludedFromBOM();
m_dataModel->SetIncludeExcludedFromBOM( false );
m_dataModel->RebuildRows(); m_dataModel->RebuildRows();
m_textOutput->SetValue( m_dataModel->Export( GetCurrentBomFmtSettings() ) ); m_textOutput->SetValue( m_dataModel->Export( GetCurrentBomFmtSettings() ) );
if( saveIncludeExcudedFromBOM )
{
m_dataModel->SetIncludeExcludedFromBOM( true );
m_dataModel->RebuildRows();
}
} }
@ -1499,7 +1520,6 @@ void DIALOG_SYMBOL_FIELDS_TABLE::OnOk( wxCommandEvent& aEvent )
BOM_PRESET presetFields = m_dataModel->GetBomSettings(); BOM_PRESET presetFields = m_dataModel->GetBomSettings();
m_job->m_sortAsc = presetFields.sortAsc; m_job->m_sortAsc = presetFields.sortAsc;
m_job->m_excludeDNP = presetFields.excludeDNP; m_job->m_excludeDNP = presetFields.excludeDNP;
m_job->m_includeExcludedFromBOM = presetFields.includeExcludedFromBOM;
m_job->m_filterString = presetFields.filterString; m_job->m_filterString = presetFields.filterString;
m_job->m_sortField = presetFields.sortField; 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 = new STD_BITMAP_BUTTON( m_panelEdit, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW|0 );
m_bMenu->SetMinSize( wxSize( 30,30 ) ); 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 ); bEditSizer->Add( bControls, 0, wxEXPAND|wxLEFT|wxTOP, 5 );

View File

@ -1384,7 +1384,7 @@
</object> </object>
<object class="sizeritem" expanded="false"> <object class="sizeritem" expanded="false">
<property name="border">5</property> <property name="border">5</property>
<property name="flag">wxRIGHT</property> <property name="flag">wxRIGHT|wxALIGN_CENTER_VERTICAL</property>
<property name="proportion">0</property> <property name="proportion">0</property>
<object class="wxBitmapButton" expanded="false"> <object class="wxBitmapButton" expanded="false">
<property name="BottomDockable">1</property> <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 // 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_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_STYLE,
ID_POPUP_SCH_SELECT_BODY_STYLE1, ID_POPUP_SCH_SELECT_BODY_STYLE1,
ID_POPUP_SCH_SELECT_BODY_STYLE_END = ID_POPUP_SCH_SELECT_BODY_STYLE1 + MAX_BODY_STYLE_PER_PACKAGE, 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.filterString = aBomJob->m_filterString;
preset.groupSymbols = ( aBomJob->m_fieldsGroupBy.size() > 0 ); preset.groupSymbols = ( aBomJob->m_fieldsGroupBy.size() > 0 );
preset.excludeDNP = aBomJob->m_excludeDNP; preset.excludeDNP = aBomJob->m_excludeDNP;
preset.includeExcludedFromBOM = aBomJob->m_includeExcludedFromBOM;
} }
dataModel.ApplyBomPreset( preset ); dataModel.ApplyBomPreset( preset );

View File

@ -189,9 +189,6 @@ EESCHEMA_SETTINGS::EESCHEMA_SETTINGS() :
m_params.emplace_back( new PARAM<int>( "appearance.edit_label_height", m_params.emplace_back( new PARAM<int>( "appearance.edit_label_height",
&m_Appearance.edit_label_height, -1 ) ); &m_Appearance.edit_label_height, -1 ) );
m_params.emplace_back( new PARAM<int>( "appearance.erc_severities",
&m_Appearance.erc_severities, RPT_SEVERITY_ERROR | RPT_SEVERITY_WARNING ) );
m_params.emplace_back( new PARAM<bool>( "appearance.footprint_preview", m_params.emplace_back( new PARAM<bool>( "appearance.footprint_preview",
&m_Appearance.footprint_preview, true ) ); &m_Appearance.footprint_preview, true ) );
@ -591,6 +588,12 @@ EESCHEMA_SETTINGS::EESCHEMA_SETTINGS() :
m_params.emplace_back( new PARAM<int>( "symbol_chooser.sort_mode", m_params.emplace_back( new PARAM<int>( "symbol_chooser.sort_mode",
&m_SymChooserPanel.sort_mode, 0 ) ); &m_SymChooserPanel.sort_mode, 0 ) );
m_params.emplace_back( new PARAM<bool>( "ERC.crossprobe",
&m_ERCDialog.crossprobe, true ) );
m_params.emplace_back( new PARAM<bool>( "ERC.scroll_on_crossprobe",
&m_ERCDialog.scroll_on_crossprobe, true ) );
m_params.emplace_back( new PARAM<bool>( "import_graphics.interactive_placement", m_params.emplace_back( new PARAM<bool>( "import_graphics.interactive_placement",
&m_ImportGraphics.interactive_placement, true ) ); &m_ImportGraphics.interactive_placement, true ) );

View File

@ -68,7 +68,6 @@ public:
int edit_label_width; int edit_label_width;
int edit_label_height; int edit_label_height;
bool edit_label_multiple; bool edit_label_multiple;
int erc_severities;
bool footprint_preview; bool footprint_preview;
bool print_sheet_reference; bool print_sheet_reference;
wxString default_font; wxString default_font;
@ -265,6 +264,12 @@ public:
int sort_mode; int sort_mode;
}; };
struct DIALOG_ERC
{
bool crossprobe;
bool scroll_on_crossprobe;
};
struct DIALOG_IMPORT_GRAPHICS struct DIALOG_IMPORT_GRAPHICS
{ {
bool interactive_placement; bool interactive_placement;
@ -328,40 +333,31 @@ private:
static std::vector<NETLIST_PLUGIN_SETTINGS> netlistSettingsFromJson( const nlohmann::json& aObj ); static std::vector<NETLIST_PLUGIN_SETTINGS> netlistSettingsFromJson( const nlohmann::json& aObj );
public: public:
APPEARANCE m_Appearance; APPEARANCE m_Appearance;
AUI_PANELS m_AuiPanels;
AUTOPLACE_FIELDS m_AutoplaceFields; DRAWING m_Drawing;
INPUT m_Input;
AUTOPLACE_FIELDS m_AutoplaceFields;
SELECTION m_Selection;
AUI_PANELS m_AuiPanels; PAGE_SETTINGS m_PageSettings;
PANEL_ANNOTATE m_AnnotatePanel;
DRAWING m_Drawing; PANEL_BOM m_BomPanel;
FIND_REPLACE_EXTRA m_FindReplaceExtra;
INPUT m_Input;
PAGE_SETTINGS m_PageSettings;
PANEL_ANNOTATE m_AnnotatePanel;
PANEL_BOM m_BomPanel;
PANEL_SYMBOL_FIELDS_TABLE m_FieldEditorPanel; PANEL_SYMBOL_FIELDS_TABLE m_FieldEditorPanel;
PANEL_LIB_VIEW m_LibViewPanel;
PANEL_NETLIST m_NetlistPanel;
PANEL_SYM_CHOOSER m_SymChooserPanel;
PANEL_LIB_VIEW m_LibViewPanel; FIND_REPLACE_EXTRA m_FindReplaceExtra;
DIALOG_ERC m_ERCDialog;
DIALOG_IMPORT_GRAPHICS m_ImportGraphics;
PANEL_NETLIST m_NetlistPanel; SIMULATOR m_Simulator;
PANEL_SYM_CHOOSER m_SymChooserPanel; bool m_RescueNeverShow;
DIALOG_IMPORT_GRAPHICS m_ImportGraphics; wxString m_lastSymbolLibDir;
SELECTION m_Selection;
SIMULATOR m_Simulator;
bool m_RescueNeverShow;
wxString m_lastSymbolLibDir;
}; };

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, void SCH_BITMAP::Plot( PLOTTER* aPlotter, bool aBackground, const SCH_PLOT_OPTS& aPlotOpts,
int aUnit, int aBodyStyle, const VECTOR2I& aOffset, bool aDimmed ) 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 VECTOR2I& aPosition, int aAccuracy = 0 ) const override;
bool HitTest( const BOX2I& aRect, bool aContained, 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, void Plot( PLOTTER* aPlotter, bool aBackground, const SCH_PLOT_OPTS& aPlotOpts,
int aUnit, int aBodyStyle, const VECTOR2I& aOffset, bool aDimmed ) override; int aUnit, int aBodyStyle, const VECTOR2I& aOffset, bool aDimmed ) override;

View File

@ -2032,7 +2032,7 @@ bool SCH_EDIT_FRAME::GetShowAllPins() const
} }
void SCH_EDIT_FRAME::FocusOnItem( EDA_ITEM* aItem ) void SCH_EDIT_FRAME::FocusOnItem( EDA_ITEM* aItem, bool aAllowScroll )
{ {
// nullptr will clear the current focus // nullptr will clear the current focus
if( aItem != nullptr && !aItem->IsSCH_ITEM() ) if( aItem != nullptr && !aItem->IsSCH_ITEM() )
@ -2060,7 +2060,7 @@ void SCH_EDIT_FRAME::FocusOnItem( EDA_ITEM* aItem )
lastBrightenedItemID = aItem->m_Uuid; lastBrightenedItemID = aItem->m_Uuid;
} }
FocusOnLocation( aItem->GetFocusPosition() ); FocusOnLocation( aItem->GetFocusPosition(), aAllowScroll );
} }
} }

View File

@ -755,7 +755,7 @@ public:
int GetSchematicJunctionSize(); int GetSchematicJunctionSize();
double GetSchematicHopOverScale(); double GetSchematicHopOverScale();
void FocusOnItem( EDA_ITEM* aItem ) override; void FocusOnItem( EDA_ITEM* aItem, bool aAllowScroll = true ) override;
bool IsSyncingSelection() { return m_syncingPcbToSchSelection; } bool IsSyncingSelection() { return m_syncingPcbToSchSelection; }

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 wxString SCH_ITEM::GetUnitDisplayName( int aUnit, bool aLabel ) const
{ {
if( aUnit == 0 ) if( aUnit == 0 )
@ -208,13 +186,6 @@ wxString SCH_ITEM::GetBodyStyleDescription( int aBodyStyle, bool aLabel ) const
return wxEmptyString; return wxEmptyString;
} }
wxString SCH_ITEM::GetUnitProp() const
{
return GetUnitDisplayName( m_unit, false );
}
void SCH_ITEM::SetBodyStyleProp( const wxString& aBodyStyle ) void SCH_ITEM::SetBodyStyleProp( const wxString& aBodyStyle )
{ {
if( aBodyStyle == _HKI( "All body styles" ) ) if( aBodyStyle == _HKI( "All body styles" ) )
@ -734,21 +705,21 @@ static struct SCH_ITEM_DESC
return false; return false;
}; };
propMgr.AddProperty( new PROPERTY<SCH_ITEM, wxString>( _HKI( "Unit" ), propMgr.AddProperty( new PROPERTY<SCH_ITEM, int>( _HKI( "Unit" ),
&SCH_ITEM::SetUnitProp, &SCH_ITEM::GetUnitProp ) ) &SCH_ITEM::SetUnit, &SCH_ITEM::GetUnit ) )
.SetAvailableFunc( multiUnit ) .SetAvailableFunc( multiUnit )
.SetIsHiddenFromDesignEditors() .SetIsHiddenFromDesignEditors()
.SetChoicesFunc( []( INSPECTABLE* aItem ) .SetChoicesFunc( []( INSPECTABLE* aItem )
{ {
wxPGChoices choices; wxPGChoices choices;
choices.Add( _HKI( "All units" ) ); choices.Add( _HKI( "All units" ), 0 );
if( SCH_ITEM* item = dynamic_cast<SCH_ITEM*>( aItem ) ) if( SCH_ITEM* item = dynamic_cast<SCH_ITEM*>( aItem ) )
{ {
if( SYMBOL* symbol = item->GetParentSymbol() ) if( SYMBOL* symbol = item->GetParentSymbol() )
{ {
for( int ii = 1; ii <= symbol->GetUnitCount(); ii++ ) 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 GetUnitDisplayName( int aUnit, bool aLabel ) const;
virtual wxString GetBodyStyleDescription( int aBodyStyle, 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; } virtual void SetBodyStyle( int aBodyStyle ) { m_bodyStyle = aBodyStyle; }
int GetBodyStyle() const { return m_bodyStyle; } int GetBodyStyle() const { return m_bodyStyle; }

View File

@ -2986,7 +2986,7 @@ static struct SCH_SYMBOL_DESC
return false; 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 ) ) &SCH_SYMBOL::SetUnitProp, &SCH_SYMBOL::GetUnitProp ) )
.SetAvailableFunc( multiUnit ) .SetAvailableFunc( multiUnit )
.SetChoicesFunc( []( INSPECTABLE* aItem ) .SetChoicesFunc( []( INSPECTABLE* aItem )
@ -2996,7 +2996,7 @@ static struct SCH_SYMBOL_DESC
if( SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( aItem ) ) if( SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( aItem ) )
{ {
for( int ii = 1; ii <= symbol->GetUnitCount(); ii++ ) for( int ii = 1; ii <= symbol->GetUnitCount(); ii++ )
choices.Add( symbol->GetUnitDisplayName( ii, false ) ); choices.Add( symbol->GetUnitDisplayName( ii, false ), ii );
} }
return choices; return choices;

View File

@ -497,24 +497,15 @@ public:
SetValueFieldText( aRef ); SetValueFieldText( aRef );
} }
wxString GetUnitProp() const override int GetUnitProp() const
{ {
int unit = GetUnitSelection( &Schematic()->CurrentSheet() ); return GetUnitSelection( &Schematic()->CurrentSheet() );
return GetUnitDisplayName( unit, false );
} }
void SetUnitProp( const wxString& aUnit ) override void SetUnitProp( int aUnit )
{ {
for( int unit = 1; unit <= GetUnitCount(); unit++ ) SetUnitSelection( &Schematic()->CurrentSheet(), aUnit );
{ SetUnit( aUnit );
if( GetUnitDisplayName( unit, false ) == aUnit )
{
SetUnitSelection( &Schematic()->CurrentSheet(), unit );
SetUnit( unit );
return;
}
}
} }
wxString GetBodyStyleProp() const override wxString GetBodyStyleProp() const override

View File

@ -33,6 +33,7 @@
#include <sch_painter.h> #include <sch_painter.h>
#include <wx/log.h> #include <wx/log.h>
#include <sch_table.h> #include <sch_table.h>
#include <geometry/geometry_utils.h>
SCH_TABLE::SCH_TABLE( int aLineWidth ) : 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, void SCH_TABLE::DrawBorders( const std::function<void( const VECTOR2I& aPt1, const VECTOR2I& aPt2,
const STROKE_PARAMS& aStroke )>& aCallback ) const const STROKE_PARAMS& aStroke )>& aCallback ) const
{ {

View File

@ -214,8 +214,8 @@ public:
std::vector<int> ViewGetLayers() const override; std::vector<int> ViewGetLayers() const override;
bool HitTest( const VECTOR2I& aPosition, int aAccuracy = 0 ) 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 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, void DrawBorders( const std::function<void( const VECTOR2I& aPt1, const VECTOR2I& aPt2,
const STROKE_PARAMS& aStroke )>& aCallback ) const; 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 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 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( "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( "Width" ) );
propMgr.Mask( TYPE_HASH( SCH_TABLECELL ), TYPE_HASH( EDA_TEXT ), _HKI( "Height" ) ); 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.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( "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( "Width" ) );
propMgr.Mask( TYPE_HASH( SCH_TEXTBOX ), TYPE_HASH( EDA_TEXT ), _HKI( "Height" ) ); propMgr.Mask( TYPE_HASH( SCH_TEXTBOX ), TYPE_HASH( EDA_TEXT ), _HKI( "Height" ) );

View File

@ -140,11 +140,10 @@ void SPICE_LIBRARY_PARSER::ReadFile( const wxString& aFilePath, REPORTER& aRepor
// Read all self-contained models in parallel // Read all self-contained models in parallel
thread_pool& tp = GetKiCadThreadPool(); thread_pool& tp = GetKiCadThreadPool();
auto results = tp.parallelize_loop( modelQueue.size(), auto results = tp.submit_loop( 0, modelQueue.size(),
[&]( const int a, const int b ) [&]( const int ii )
{ {
for( int ii = a; ii < b; ++ii ) createModel( ii, true );
createModel( ii, true );
} ); } );
results.wait(); results.wait();

View File

@ -1541,7 +1541,7 @@ const BOX2I SYMBOL_EDIT_FRAME::GetDocumentExtents( bool aIncludeAllVisible ) con
} }
void SYMBOL_EDIT_FRAME::FocusOnItem( EDA_ITEM* aItem ) void SYMBOL_EDIT_FRAME::FocusOnItem( EDA_ITEM* aItem, bool aAllowScroll )
{ {
static KIID lastBrightenedItemID( niluuid ); static KIID lastBrightenedItemID( niluuid );
@ -1587,7 +1587,7 @@ void SYMBOL_EDIT_FRAME::FocusOnItem( EDA_ITEM* aItem )
lastBrightenedItemID = aItem->m_Uuid; lastBrightenedItemID = aItem->m_Uuid;
} }
FocusOnLocation( VECTOR2I( aItem->GetFocusPosition().x, -aItem->GetFocusPosition().y ) ); FocusOnLocation( VECTOR2I( aItem->GetFocusPosition().x, -aItem->GetFocusPosition().y ), aAllowScroll );
} }
} }

View File

@ -377,7 +377,7 @@ public:
void KiwayMailIn( KIWAY_EXPRESS& mail ) override; void KiwayMailIn( KIWAY_EXPRESS& mail ) override;
void FocusOnItem( EDA_ITEM* aItem ) override; void FocusOnItem( EDA_ITEM* aItem, bool aAllowScroll = true ) override;
/** /**
* Load a symbol from the schematic to edit in place. * Load a symbol from the schematic to edit in place.

View File

@ -438,8 +438,8 @@ TOOL_ACTION SCH_ACTIONS::placeNextSymbolUnit( TOOL_ACTION_ARGS()
.FriendlyName( _( "Place Next Symbol Unit" ) ) .FriendlyName( _( "Place Next Symbol Unit" ) )
.Tooltip( _( "Place the next unit of the current symbol that is missing from the schematic" ) ) .Tooltip( _( "Place the next unit of the current symbol that is missing from the schematic" ) )
.Flags( AF_ACTIVATE ) .Flags( AF_ACTIVATE )
// The symbol to use as a reference for the next unit // The symbol to use as a reference for the next unit and optionally the unit number
.Parameter<SCH_SYMBOL*>( nullptr ) ); .Parameter<SCH_ACTIONS::PLACE_SYMBOL_UNIT_PARAMS>( {} ) );
TOOL_ACTION SCH_ACTIONS::placePower( TOOL_ACTION_ARGS() TOOL_ACTION SCH_ACTIONS::placePower( TOOL_ACTION_ARGS()
.Name( "eeschema.InteractiveDrawing.placePowerSymbol" ) .Name( "eeschema.InteractiveDrawing.placePowerSymbol" )

View File

@ -311,4 +311,12 @@ public:
///< If a symbol is provide, reannotate it? ///< If a symbol is provide, reannotate it?
bool m_Reannotate = true; 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 ) 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 // TODO: get from selection
if( !symbol ) if( !symbol )
@ -654,8 +657,23 @@ int SCH_DRAWING_TOOLS::PlaceNextSymbolUnit( const TOOL_EVENT& aEvent )
return 0; return 0;
} }
// Find the lowest unit number that is missing int nextMissing;
const int nextMissing = *std::min_element( missingUnits.begin(), missingUnits.end() );
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 ); std::unique_ptr<SCH_SYMBOL> newSymbol = std::make_unique<SCH_SYMBOL>( *symbol );
const SCH_SHEET_PATH& sheetPath = m_frame->GetCurrentSheet(); const SCH_SHEET_PATH& sheetPath = m_frame->GetCurrentSheet();

View File

@ -124,11 +124,16 @@ private:
break; // We have used all IDs for these submenus break; // We have used all IDs for these submenus
} }
if( !missingUnits.empty() )
{ {
AppendSeparator(); AppendSeparator();
wxMenuItem* item = Add( SCH_ACTIONS::placeNextSymbolUnit ); for( int unitNumber : missingUnits )
item->Enable( missingUnits.size() ); {
wxString placeText = wxString::Format( _( "Place unit %s" ),
symbol->GetUnitDisplayName( unitNumber, false ) );
Append( ID_POPUP_SCH_PLACE_UNIT1 + unitNumber - 1, placeText );
}
} }
} }
}; };

View File

@ -789,6 +789,16 @@ int SCH_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
if( symbol ) if( symbol )
static_cast<SCH_EDIT_FRAME*>( m_frame )->SelectUnit( symbol, unit ); 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 else if( *evt->GetCommandId() >= ID_POPUP_SCH_SELECT_BODY_STYLE
&& *evt->GetCommandId() <= ID_POPUP_SCH_SELECT_BODY_STYLE_END ) && *evt->GetCommandId() <= ID_POPUP_SCH_SELECT_BODY_STYLE_END )
{ {

View File

@ -546,15 +546,36 @@ void HIERARCHY_PANE::onCharHook( wxKeyEvent& aKeyStroke )
{ {
int hotkey = aKeyStroke.GetKeyCode(); int hotkey = aKeyStroke.GetKeyCode();
if( aKeyStroke.GetModifiers() & wxMOD_CONTROL ) int mods = aKeyStroke.GetModifiers();
hotkey += MD_CTRL;
if( aKeyStroke.GetModifiers() & wxMOD_ALT ) // the flag wxMOD_ALTGR is defined in wxWidgets as wxMOD_CONTROL|wxMOD_ALT
hotkey += MD_ALT; // So AltGr key cannot used as modifier key because it is the same as Alt key + Ctrl key.
#if CAN_USE_ALTGR_KEY
if( wxmods & wxMOD_ALTGR )
mods |= MD_ALTGR;
else
#endif
{
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; 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() if( hotkey == ACTIONS::expandAll.GetHotKey()
|| hotkey == ACTIONS::expandAll.GetHotKeyAlt() ) || hotkey == ACTIONS::expandAll.GetHotKeyAlt() )
{ {

View File

@ -34,6 +34,7 @@
#include <schematic.h> #include <schematic.h>
#include <sch_symbol.h> #include <sch_symbol.h>
#include <sch_field.h> #include <sch_field.h>
#include <template_fieldnames.h>
#include <settings/color_settings.h> #include <settings/color_settings.h>
#include <string_utils.h> #include <string_utils.h>
#include <tool/tool_manager.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 ); 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() SCH_PROPERTIES_PANEL::~SCH_PROPERTIES_PANEL()
{ {
m_unitEditorInstance->UpdateFrame( nullptr ); 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 ); 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; return prop;
} }

View File

@ -32,6 +32,8 @@ class PROPERTY_MANAGER;
class PG_UNIT_EDITOR; class PG_UNIT_EDITOR;
class PG_CHECKBOX_EDITOR; class PG_CHECKBOX_EDITOR;
class PG_COLOR_EDITOR; class PG_COLOR_EDITOR;
class PG_FPID_EDITOR;
class PG_URL_EDITOR;
class SCH_PROPERTIES_PANEL : public PROPERTIES_PANEL class SCH_PROPERTIES_PANEL : public PROPERTIES_PANEL
{ {
@ -60,6 +62,8 @@ protected:
PG_UNIT_EDITOR* m_unitEditorInstance; PG_UNIT_EDITOR* m_unitEditorInstance;
PG_CHECKBOX_EDITOR* m_checkboxEditorInstance; PG_CHECKBOX_EDITOR* m_checkboxEditorInstance;
PG_COLOR_EDITOR* m_colorEditorInstance; PG_COLOR_EDITOR* m_colorEditorInstance;
PG_FPID_EDITOR* m_fpEditorInstance;
PG_URL_EDITOR* m_urlEditorInstance;
static std::set<wxString> m_currentFieldNames; static std::set<wxString> m_currentFieldNames;
wxPGChoices m_nets; wxPGChoices m_nets;

View File

@ -306,14 +306,14 @@ public:
* *
* @param aPos is the point to go to. * @param aPos is the point to go to.
*/ */
void FocusOnLocation( const VECTOR2I& aPos ); void FocusOnLocation( const VECTOR2I& aPos, bool aAllowScroll = true );
/** /**
* Focus on a particular canvas item. * Focus on a particular canvas item.
* *
* @param aItem is the item to focus on. nullptr clears the focus. * @param aItem is the item to focus on. nullptr clears the focus.
*/ */
virtual void FocusOnItem( EDA_ITEM* aItem ) {} virtual void FocusOnItem( EDA_ITEM* aItem, bool aAllowScroll = true ) {}
virtual void ClearFocus() { FocusOnItem( nullptr ); } virtual void ClearFocus() { FocusOnItem( nullptr ); }

View File

@ -220,9 +220,10 @@ public:
EDA_ITEM* ResolveItem( const KIID& aId, bool aAllowNullptrReturn = false ) const override; EDA_ITEM* ResolveItem( const KIID& aId, bool aAllowNullptrReturn = false ) const override;
void FocusOnItem( EDA_ITEM* aItem ) override; void FocusOnItem( EDA_ITEM* aItem, bool aAllowScroll = true ) override;
void FocusOnItem( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer = UNDEFINED_LAYER ); void FocusOnItem( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer = UNDEFINED_LAYER, bool aAllowScroll = true );
void FocusOnItems( std::vector<BOARD_ITEM*> aItems, PCB_LAYER_ID aLayer = UNDEFINED_LAYER ); void FocusOnItems( std::vector<BOARD_ITEM*> aItems, PCB_LAYER_ID aLayer = UNDEFINED_LAYER,
bool aAllowScroll = true );
void HideSolderMask(); void HideSolderMask();
void ShowSolderMask(); void ShowSolderMask();

View File

@ -110,7 +110,7 @@ public:
*/ */
void BuildArgvUtf8(); void BuildArgvUtf8();
BS::thread_pool& GetThreadPool() { return *m_singleton.m_ThreadPool; } BS::thread_pool<0>& GetThreadPool() { return *m_singleton.m_ThreadPool; }
GL_CONTEXT_MANAGER* GetGLContextManager() { return m_singleton.m_GLContextManager; } GL_CONTEXT_MANAGER* GetGLContextManager() { return m_singleton.m_GLContextManager; }

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 #endif //KICAD_PG_EDITORS_H

View File

@ -25,6 +25,7 @@
class GL_CONTEXT_MANAGER; class GL_CONTEXT_MANAGER;
namespace BS namespace BS
{ {
template <std::uint8_t>
class thread_pool; class thread_pool;
} }
@ -42,7 +43,7 @@ public:
void Init(); void Init();
public: public:
BS::thread_pool* m_ThreadPool; BS::thread_pool<0>* m_ThreadPool;
GL_CONTEXT_MANAGER* m_GLContextManager; GL_CONTEXT_MANAGER* m_GLContextManager;
}; };

View File

@ -28,7 +28,7 @@
#include <bs_thread_pool.hpp> #include <bs_thread_pool.hpp>
#include <import_export.h> #include <import_export.h>
using thread_pool = BS::thread_pool; using thread_pool = BS::thread_pool<0>;
/** /**
* Get a reference to the current thread pool. N.B., you cannot copy the thread pool * Get a reference to the current thread pool. N.B., you cannot copy the thread pool

View File

@ -84,22 +84,9 @@ private:
/// Returns the instance of VIEW, used by the application. /// Returns the instance of VIEW, used by the application.
KIGFX::VIEW* getView(); KIGFX::VIEW* getView();
/// Saves the state of key modifiers (Alt, Ctrl and so on). /// Returns the state of key modifiers (Alt, Ctrl and so on) as OR'ed list
static int decodeModifiers( const wxKeyboardState* aState ) /// of bits (MD_CTRL, MD_ALT ...)
{ static int decodeModifiers( const wxKeyboardState* aState );
int mods = 0;
if( aState->ControlDown() )
mods |= MD_CTRL;
if( aState->AltDown() )
mods |= MD_ALT;
if( aState->ShiftDown() )
mods |= MD_SHIFT;
return mods;
}
private: private:
/// The time threshold for a mouse button press that distinguishes between a single mouse /// The time threshold for a mouse button press that distinguishes between a single mouse

View File

@ -143,7 +143,10 @@ enum TOOL_MODIFIERS
MD_SHIFT = 0x1000, MD_SHIFT = 0x1000,
MD_CTRL = 0x2000, MD_CTRL = 0x2000,
MD_ALT = 0x4000, 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. /// Defines when a context menu is opened.

View File

@ -244,10 +244,10 @@ public:
* to the number of rows (even if the delete count is 0). Needless to say, this makes using * to the number of rows (even if the delete count is 0). Needless to say, this makes using
* DeleteRows for clearing a lot more cumbersome so we add a helper here. * DeleteRows for clearing a lot more cumbersome so we add a helper here.
*/ */
void ClearRows() void ClearRows( bool aUpdateLabels = true )
{ {
if( GetNumberRows() ) if( GetNumberRows() > 0 )
DeleteRows( 0, GetNumberRows() ); DeleteRows( 0, GetNumberRows(), aUpdateLabels );
} }
/** /**

View File

@ -55,7 +55,7 @@ namespace CLI
#define DEPRECATED_ARG_PLOT_INVISIBLE_TEXT "--plot-invisible-text" #define DEPRECATED_ARG_PLOT_INVISIBLE_TEXT "--plot-invisible-text"
#define DEPRECATED_ARG_PLOT_INVISIBLE_TEXT_DESC "Deprecated. Has no effect." #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_FLIP_BOTTOM_PADS "--flip-bottom-pads"
#define ARG_UNIQUE_PINS "--unique-pins" #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 ); dxfJob->m_checkZonesBeforePlot = m_argParser.get<bool>( ARG_CHECK_ZONES );
if( m_argParser.get<bool>( DEPRECATED_ARG_PLOT_INVISIBLE_TEXT ) ) 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 ); int drillShape = m_argParser.get<int>( ARG_DRILL_SHAPE_OPTION );
dxfJob->m_drillShapeOption = static_cast<DRILL_MARKS>( drillShape ); 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() ); 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 ) ) 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 ) ) 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 ); pdfJob->m_subtractSolderMaskFromSilk = m_argParser.get<bool>( ARG_SUBTRACT_SOLDERMASK );
if( m_argParser.get<bool>( DEPRECATED_ARG_PLOT_INVISIBLE_TEXT ) ) 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_mirror = m_argParser.get<bool>( ARG_MIRROR );
pdfJob->m_blackAndWhite = m_argParser.get<bool>( ARG_BLACKANDWHITE ); 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 ); svgJob->m_checkZonesBeforePlot = m_argParser.get<bool>( ARG_CHECK_ZONES );
if( m_argParser.get<bool>( DEPRECATED_ARG_PLOT_INVISIBLE_TEXT ) ) 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 ); 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 ) ) ) .help( UTF8STDSTR( _( ARG_EXCLUDE_DNP_DESC ) ) )
.flag(); .flag();
m_argParser.add_argument( ARG_INCLUDE_EXCLUDED_FROM_BOM ) m_argParser.add_argument( DEPRECATED_ARG_INCLUDE_EXCLUDED_FROM_BOM )
.help( UTF8STDSTR( _( ARG_INCLUDE_EXCLUDED_FROM_BOM_DESC ) ) ) .help( UTF8STDSTR( _( DEPRECATED_ARG_INCLUDE_EXCLUDED_FROM_BOM_DESC ) ) )
.flag(); .flag();
// Output formatting options // Output formatting options
@ -141,25 +141,19 @@ int CLI::SCH_EXPORT_BOM_COMMAND::doPerform( KIWAY& aKiway )
bomJob->SetConfiguredOutputPath( m_argOutput ); bomJob->SetConfiguredOutputPath( m_argOutput );
bomJob->m_bomPresetName = From_UTF8( m_argParser.get<std::string>( ARG_PRESET ).c_str() ); bomJob->m_bomPresetName = From_UTF8( m_argParser.get<std::string>( ARG_PRESET ).c_str() );
bomJob->m_bomFmtPresetName = bomJob->m_bomFmtPresetName = From_UTF8( m_argParser.get<std::string>( ARG_FMT_PRESET ).c_str() );
From_UTF8( m_argParser.get<std::string>( ARG_FMT_PRESET ).c_str() );
// Format options // Format options
bomJob->m_fieldDelimiter = bomJob->m_fieldDelimiter = From_UTF8( m_argParser.get<std::string>( ARG_FIELD_DELIMITER ).c_str() );
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_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_refDelimiter = From_UTF8( m_argParser.get<std::string>( ARG_REF_DELIMITER ).c_str() );
bomJob->m_refRangeDelimiter = bomJob->m_refRangeDelimiter = From_UTF8( m_argParser.get<std::string>( ARG_REF_RANGE_DELIMITER ).c_str() );
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_keepTabs = m_argParser.get<bool>( ARG_KEEP_TABS );
bomJob->m_keepLineBreaks = m_argParser.get<bool>( ARG_KEEP_LINE_BREAKS ); bomJob->m_keepLineBreaks = m_argParser.get<bool>( ARG_KEEP_LINE_BREAKS );
// Output fields options // Output fields options
bomJob->m_fieldsOrdered = bomJob->m_fieldsOrdered = convertStringList( From_UTF8( m_argParser.get<std::string>( ARG_FIELDS ).c_str() ) );
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_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 // 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 ) ) 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_fieldsLabels.clear();
} }
bomJob->m_fieldsGroupBy = bomJob->m_fieldsGroupBy = convertStringList( From_UTF8( m_argParser.get<std::string>( ARG_GROUP_BY ).c_str() ) );
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_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_sortAsc = m_argParser.get<bool>( ARG_SORT_ASC );
bomJob->m_filterString = From_UTF8( m_argParser.get<std::string>( ARG_FILTER ).c_str() ); 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_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 ) ) 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 // Options for selecting presets of the export, e.g. GroupedByValue and CSV
#define ARG_PRESET "--preset" #define ARG_PRESET "--preset"
#define ARG_PRESET_DESC "Use a named BOM preset setting from the schematic, e.g. " \ #define ARG_PRESET_DESC "Use a named BOM preset setting from the schematic, e.g. \"Grouped By Value\"."
"\"Grouped By Value\"."
#define ARG_FMT_PRESET "--format-preset" #define ARG_FMT_PRESET "--format-preset"
#define ARG_FMT_PRESET_DESC "Use a named BOM format preset setting from the schematic, e.g. CSV." #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_DELIMITER_DESC "Character to place between individual references."
#define ARG_REF_RANGE_DELIMITER "--ref-range-delimiter" #define ARG_REF_RANGE_DELIMITER "--ref-range-delimiter"
#define ARG_REF_RANGE_DELIMITER_DESC "Character to place in ranges of references. Leave " \ #define ARG_REF_RANGE_DELIMITER_DESC "Character to place in ranges of references. Leave blank for no ranges."
"blank for no ranges."
#define ARG_KEEP_TABS "--keep-tabs" #define ARG_KEEP_TABS "--keep-tabs"
#define ARG_KEEP_TABS_DESC "Keep tab characters from input fields. Stripped by default." #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 "--keep-line-breaks"
#define ARG_KEEP_LINE_BREAKS_DESC "Keep line break characters from input fields. Stripped " \ #define ARG_KEEP_LINE_BREAKS_DESC "Keep line break characters from input fields. Stripped by default."
"by default."
//Options for controlling the fields and the grouping //Options for controlling the fields and the grouping
#define ARG_FIELDS "--fields" #define ARG_FIELDS "--fields"
#define ARG_FIELDS_DESC \ #define ARG_FIELDS_DESC "An ordered list of fields to export. See documentation for special substitutions."
"An ordered list of fields to export. See documentation for special substitutions."
#define ARG_LABELS "--labels" #define ARG_LABELS "--labels"
#define ARG_LABELS_DESC "An ordered list of labels to apply the exported fields." #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 "--exclude-dnp"
#define ARG_EXCLUDE_DNP_DESC "Exclude symbols marked Do-Not-Populate." #define ARG_EXCLUDE_DNP_DESC "Exclude symbols marked Do-Not-Populate."
#define ARG_INCLUDE_EXCLUDED_FROM_BOM "--include-excluded-from-bom" #define DEPRECATED_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_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 class SCH_EXPORT_BOM_COMMAND : public COMMAND
{ {

View File

@ -30,96 +30,10 @@
#include <wx/dir.h> #include <wx/dir.h>
#include <wx/dirdlg.h> #include <wx/dirdlg.h>
#include <wx/settings.h> #include <wx/settings.h>
#include <wx/bitmap.h>
#include "template_default_html.h"
static const wxString GetWelcomeHtml() // Welcome / fallback HTML now provided by template_default_html.h
{
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>"
);
}
TEMPLATE_SELECTION_PANEL::TEMPLATE_SELECTION_PANEL( wxNotebookPage* aParent, TEMPLATE_SELECTION_PANEL::TEMPLATE_SELECTION_PANEL( wxNotebookPage* aParent,
const wxString& aPath ) : const wxString& aPath ) :
@ -182,7 +96,12 @@ void TEMPLATE_WIDGET::SetTemplate( PROJECT_TEMPLATE* aTemplate )
m_staticTitle->SetFont( KIUI::GetInfoFont( this ) ); m_staticTitle->SetFont( KIUI::GetInfoFont( this ) );
m_staticTitle->SetLabel( *aTemplate->GetTitle() ); m_staticTitle->SetLabel( *aTemplate->GetTitle() );
m_staticTitle->Wrap( 100 ); 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, DIALOG_TEMPLATE_SELECTOR::DIALOG_TEMPLATE_SELECTOR( wxWindow* aParent, const wxPoint& aPos,
const wxSize& aSize, const wxSize& aSize,
std::map<wxString, wxFileName> aTitleDirMap, std::vector<std::pair<wxString, wxFileName>> aTitleDirList,
const wxFileName& aDefaultTemplate ) : const wxFileName& aDefaultTemplate ) :
DIALOG_TEMPLATE_SELECTOR_BASE( aParent, wxID_ANY, _( "Project Template Selector" ), aPos, DIALOG_TEMPLATE_SELECTOR_BASE( aParent, wxID_ANY, _( "Project Template Selector" ), aPos,
aSize ) aSize )
@ -241,7 +160,7 @@ DIALOG_TEMPLATE_SELECTOR::DIALOG_TEMPLATE_SELECTOR( wxWindow* aParent, const wxP
m_defaultTemplatePath = aDefaultTemplate; m_defaultTemplatePath = aDefaultTemplate;
m_defaultWidget = nullptr; m_defaultWidget = nullptr;
for( auto& [title, pathFname] : aTitleDirMap ) for( auto& [title, pathFname] : aTitleDirList )
{ {
pathFname.Normalize( FN_NORMALIZE_FLAGS | wxPATH_NORM_ENV_VARS ); pathFname.Normalize( FN_NORMALIZE_FLAGS | wxPATH_NORM_ENV_VARS );
wxString path = pathFname.GetFullPath(); // caller ensures this ends with file separator. 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(); 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 ) ); m_webviewPanel->LoadURL( wxFileName::FileNameToURL( htmlFile ) );
else else
m_webviewPanel->SetPage( GetWelcomeHtml() ); m_webviewPanel->SetPage( GetWelcomeHtml() );
@ -321,21 +240,7 @@ void DIALOG_TEMPLATE_SELECTOR::SetWidget( TEMPLATE_WIDGET* aWidget )
} }
else else
{ {
// Fallback to a simple template info page if no HTML file exists m_webviewPanel->SetPage( GetTemplateInfoHtml( *aWidget->GetTemplate()->GetTitle() ) );
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 );
} }
} }
@ -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 ); TEMPLATE_WIDGET* w = new TEMPLATE_WIDGET( m_panels[aPage]->m_scrolledWindow, this );
w->SetTemplate( aTemplate ); w->SetTemplate( aTemplate );
m_panels[aPage]->AddTemplateWidget( w ); m_panels[aPage]->AddTemplateWidget( w );
m_allWidgets.push_back( w );
wxFileName base = aTemplate->GetHtmlFile(); wxFileName base = aTemplate->GetHtmlFile();
base.RemoveLastDir(); base.RemoveLastDir();
if( m_defaultTemplatePath.IsOk() && base == m_defaultTemplatePath ) if( !m_defaultWidget || (m_defaultTemplatePath.IsOk() && base == m_defaultTemplatePath) )
m_defaultWidget = w; 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; 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 ) void DIALOG_TEMPLATE_SELECTOR::buildPageContent( const wxString& aPath, int aPage )
{ {

View File

@ -29,7 +29,8 @@
#include <widgets/webview_panel.h> #include <widgets/webview_panel.h>
#include "project_template.h" #include "project_template.h"
#include <map> #include <vector>
#include <utility>
#include <wx/filename.h> #include <wx/filename.h>
class DIALOG_TEMPLATE_SELECTOR; class DIALOG_TEMPLATE_SELECTOR;
@ -91,13 +92,14 @@ class DIALOG_TEMPLATE_SELECTOR : public DIALOG_TEMPLATE_SELECTOR_BASE
{ {
public: public:
DIALOG_TEMPLATE_SELECTOR( wxWindow* aParent, const wxPoint& aPos, const wxSize& aSize, 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 ); const wxFileName& aDefaultTemplate );
/** /**
* @return the selected template, or NULL * @return the selected template, or NULL
*/ */
PROJECT_TEMPLATE* GetSelectedTemplate(); PROJECT_TEMPLATE* GetSelectedTemplate();
PROJECT_TEMPLATE* GetDefaultTemplate();
void SetWidget( TEMPLATE_WIDGET* aWidget ); void SetWidget( TEMPLATE_WIDGET* aWidget );
@ -123,6 +125,8 @@ protected:
TEMPLATE_WIDGET* m_selectedWidget; TEMPLATE_WIDGET* m_selectedWidget;
wxFileName m_defaultTemplatePath; wxFileName m_defaultTemplatePath;
TEMPLATE_WIDGET* m_defaultWidget; TEMPLATE_WIDGET* m_defaultWidget;
// Keep track of all template widgets so we can pick a sensible default
std::vector<TEMPLATE_WIDGET*> m_allWidgets;
}; };
#endif #endif

View File

@ -518,9 +518,7 @@ void PANEL_JOBSET::RemoveDestination( PANEL_DESTINATION* aPanel )
void PANEL_JOBSET::rebuildJobList() void PANEL_JOBSET::rebuildJobList()
{ {
if( m_jobsGrid->GetNumberRows() ) m_jobsGrid->ClearRows();
m_jobsGrid->DeleteRows( 0, m_jobsGrid->GetNumberRows() );
m_jobsGrid->AppendRows( (int) m_jobsFile->GetJobs().size() ); m_jobsGrid->AppendRows( (int) m_jobsFile->GetJobs().size() );
int num = 1; int num = 1;

Some files were not shown because too many files have changed in this diff Show More