Compare commits

...

121 Commits

Author SHA1 Message Date
Chris Hennes 53767601ea Measure: Translate measurement types 2025-11-25 14:53:08 -03:00
Frank Martinez 08dabacfed Build: Remove deprecated pylint option
(cherry picked from commit 863d77562d)
2025-11-25 13:56:58 -03:00
Frank Martinez 9b934a45db Build: Update pylint minimum python version to 3.10
(cherry picked from commit 2ef70f6094)
2025-11-25 13:56:58 -03:00
Louis Gombert a0a8d7760b CMake: fix parsing error in Sandbox/Gui
Introduced by commit 755229d

(cherry picked from commit 0fca304912)
2025-11-25 14:49:51 +01:00
paul 4962bbbffe [ArchWall] Fix Bug: Center-Align with Centered Multi-Material (#25572)
* [ArchWall] Fix Bug: Center-Align with Centered Multi-Material

Fix #25485

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
(cherry picked from commit bcc30869b7)
2025-11-25 08:42:38 +01:00
Bas Ruigrok 7c6d70ab02 Gui: Simplify QObject::connect in openPopupMenu
Co-authored-by: xtemp09 <9303235+xtemp09@users.noreply.github.com>
(cherry picked from commit 6e5a318ceb)
2025-11-25 08:42:30 +01:00
Bas Ruigrok 30e94d0b3f Gui: Fix Gesture style context menu segfault
(cherry picked from commit dec0ea238a)
2025-11-25 08:42:30 +01:00
PaddleStroke 70c69bbebc Assembly: Bom: replace QtWidget by QtGui for Qt6
(cherry picked from commit 59f672eb94)
2025-11-25 08:42:18 +01:00
PaddleStroke 1cd0927a07 Assembly: Joint task: fix spinbox not being populated
(cherry picked from commit 629eff4a26)
2025-11-25 06:04:24 +01:00
Billy Huddleston e5b5fb38a4 CAM: Make CAM tests and serialization robust to locale-dependent decimal separators
Updated CAM unit tests and LinuxCNC serializer to handle decimal separators consistently, ensuring tests pass regardless of system locale. Assertions now compare FreeCAD.Units.Quantity objects directly or normalize decimal separators in strings. LinuxCNC serializer output is forced to use periods for decimals.

src/Mod/CAM/CAMTests/TestPathToolBitListWidget.py:
- Normalize decimal separators in tool description assertions for locale robustness.

src/Mod/CAM/CAMTests/TestPathToolBitPropertyEditorWidget.py:
- Use FreeCAD.Units.Quantity for direct quantity comparisons in property editor tests.

src/Mod/CAM/CAMTests/TestPathToolBitSerializer.py:
- Compare deserialized and serialized quantities using FreeCAD.Units.Quantity for consistency.

src/Mod/CAM/CAMTests/TestPathToolShapeClasses.py:
- Compare parameter values and units directly instead of relying on string formatting.

src/Mod/CAM/Path/Tool/library/serializers/linuxcnc.py:
- Force period as decimal separator in LinuxCNC serializer output to avoid locale issues.

(cherry picked from commit 9cab1f8f52)
2025-11-25 06:03:46 +01:00
FEA-eng e55305fb25 FEM: add new cyclic symm centrif example
(cherry picked from commit d2f082c7bc)
2025-11-25 06:03:29 +01:00
Furgo be96c07587 Assembly: Prevent zero-length line creation in ExplodedView
Safeguards the generation of explosion trail lines to avoid creating
invalid geometry.

Previously, if a part displacement was zero (or effectively zero),
`Part.LineSegment` would attempt to create a line with identical start
and end points, causing OpenCascade to throw a `Part.OCCError`. This
resulted in failures during document restoration and recomputes.

This commit introduces a `_createSafeLine` helper method that checks the
distance against a `1e-7` (near-zero) tolerance before attempting to
generate the shape.

(cherry picked from commit 2763bb962f)
2025-11-24 23:08:54 -03:00
Furgo b5dee9832f App: Handle uncleared Python exceptions during object restoration
When a Python-based object throws an exception during the restoration
process (e.g. inside `onDocumentRestored`), the error is caught by a
generic `catch(...)` block in `Document::afterRestore`.

Previously, this block swallowed the C++ exception wrapper but failed to
clear the underlying Python error state. Leaving the interpreter in this
"dirty" state caused a segmentation fault or `SystemError` later in the
loading process when C++ attempted to interact with the Python API
(specifically in `App::Application::setActiveDocument`).

This commit adds a check for `PyErr_Occurred()` inside the catch block.
If an error is detected, the traceback is printed to the console for
debugging, and `PyErr_Clear()` is called to reset the interpreter state,
allowing the application to continue loading the document without
crashing.

(cherry picked from commit b571889ab5)
2025-11-24 23:08:54 -03:00
PaddleStroke 69f6f9e4b0 Assembly: Joint task: Fix sel gate not being cleared if doc closed
(cherry picked from commit 8b1dce3437)
2025-11-24 23:06:43 -03:00
Chris Hennes 9c24507531 Gui: Switch to defusedxml for document recovery
(cherry picked from commit 4536a54f8f)
2025-11-24 22:45:07 -03:00
Adrian Insaurralde Avalos ada3b7c97e CI: improve release workflow
adapt weekly build workflow to do normal releases too, rename accordingly
skip macos singning setup if certificate not available (useful to run on forks)
add missing dmgbuild dependency for badge icons on macos
build windows installer in workflow, add needed dependencies to pixi.toml
reorganize packaging scripts that can be useful outside rattler-build too
do some cleanup
add .gitignore to rattler-build
Properly configure appimage updating depending on release type and upload zsync file

(cherry picked from commit 46f3cf2f19)
2025-11-24 21:32:52 +01:00
Adrián Insaurralde Avalos bc5b7d66c1 WindowsInstaller: multiple improvements and fixes
move windowsinstaller to /package
update artwork
adapt to conda builds
make msvc redist directory optional
automate version information definition
use relative directories for file locations definitions
improve/update readme
partially update Delete.bat for qt6 libpack
add .gitignore
update signing.bat and add hashing command

WindowsInstaller: use --safe-mode in freecadcmd commands [skip ci]

WindowsInstaller: make windows 8 the minimum version [skip ci]

WindowsInstaller: allow configuring some values via command line

windows installer update for qt6 build

(cherry picked from commit d466ba037b)
2025-11-24 21:32:52 +01:00
Adrian Insaurralde Avalos 9931e01be7 CI: specify environment for fedora-daily workflow
otherwise it doesn't have access to the secrets it needs

(cherry picked from commit a847a794c4)
2025-11-24 21:32:52 +01:00
pre-commit-ci[bot] 7176a5c3bb [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci

(cherry picked from commit 59715114f5)
2025-11-24 19:19:09 +01:00
chris ac39784aac core: fix various issues exporting step files, ie. #25540 #24962 #25567
(cherry picked from commit e226e9b06e)
2025-11-24 19:19:09 +01:00
chris da432a77af export: step file preserve last feature when exporting step file, fix issue #25567
(cherry picked from commit 9c48c9a3e3)
2025-11-24 19:19:09 +01:00
chris e6e0d03034 core: preserve body name in step file when exporting part that contains body with feature, fixes #24962
(cherry picked from commit 246218b28b)
2025-11-24 19:19:09 +01:00
PaddleStroke c5387aba66 Sketcher: External Face: decide between defining and construction (#25390)
(cherry picked from commit 74aafcee75)
2025-11-24 19:18:59 +01:00
Syres916 1d739f9773 [Gui] Reduce separator from 2px to 1px
(cherry picked from commit fc6a39658a)
2025-11-24 19:18:46 +01:00
Syres916 7c0c421401 [Gui] increase contrast of menu separators and down arrows in comboboxes
(cherry picked from commit cda9a107aa)
2025-11-24 19:18:46 +01:00
Kacper Donat 45c5b33db5 PartDesign: Use true tool positions for boolean preview
This fixes positioning of some previews for booleans that were misplaced
after #24750 was merged. It restores previous code that was correct for
most cases. The reason for some previews being misaligned is described
in the #25578 - the preview actually shows how the result should be but
due to some shortcuts taken in code the result is incorrect.

(cherry picked from commit 88543612e4)
2025-11-24 19:18:40 +01:00
tarman3 501f29a247 CAM: Engrave - Fix #22380 - duplicates 2025-11-24 18:37:13 +01:00
chris 57a1ac5812 part design: reimp feature to move datum objects using modal / dialogue box from right click menu in model tab
(cherry picked from commit 0fc96a1a6f)
2025-11-24 18:37:07 +01:00
marioalexis 680ccc0ecf Fem: Improve VTK import/export preference page
(cherry picked from commit 2457715d84)
2025-11-24 18:26:53 +01:00
pre-commit-ci[bot] 7a7cba5587 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci

(cherry picked from commit ada21269aa)
2025-11-24 18:26:53 +01:00
Chris Hennes c90f7d67df FEM: Add missing translation in InOutVTK
(cherry picked from commit 128dec534a)
2025-11-24 18:26:53 +01:00
marioalexis 402032c230 Fem: Improve Gmsh preference page implementation
(cherry picked from commit 7d218b3f9b)
2025-11-24 18:12:56 +01:00
Chris Hennes 4ba4d1b848 FEM: Translate log verbosity settings
(cherry picked from commit 16eb18abb2)
2025-11-24 18:12:56 +01:00
sliptonic 024e904c9a Merge pull request #25554 from tarman3/1-1_leadinout_overtravel6
CAM: 1.1 - LeadInOut - Fix regression after #24829
2025-11-24 11:00:24 -06:00
Kacper Donat 74879b6738 Attacher: Reduce unnecessary rotation around normal for midpoint
(cherry picked from commit 3e356491e2)
2025-11-24 17:48:21 +01:00
PaddleStroke e76fdd82dc Sketcher: Prevent bad coincidences (#25304)
(cherry picked from commit e38154474a)
2025-11-24 17:48:14 +01:00
pjcreath a7486c6fa9 Sketcher: prevent undo/redo crash when selected geometry goes away.
Fixes #25497

(cherry picked from commit efb10e1b28)
2025-11-24 17:32:32 +01:00
pjcreath 4a331340fe Sketcher: prevent crash on undo/redo while dragging.
Fixes #25500

(cherry picked from commit 16aff10544)
2025-11-24 16:00:22 +01:00
xtemp09 e3d0fe6bb6 [GUI] Handle ampersand in windowTitle
(cherry picked from commit eb0c980c1d)
2025-11-24 15:19:44 +01:00
Syres916 8c36c5390a [Gui] Fix string coding on selection changed
(cherry picked from commit a10fbf2e8c)
2025-11-24 08:00:33 +01:00
Syres916 2ed6528d61 [Part] Fix string encoding for document name and object name
(cherry picked from commit 76071ffbeb)
2025-11-24 08:00:33 +01:00
Syres916 1c54777405 [Gui] Fix string encoding for document name
(cherry picked from commit 83c01fae68)
2025-11-24 08:00:33 +01:00
pre-commit-ci[bot] 4cd7fdc2df [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci

(cherry picked from commit 6d8c5bd2f9)
2025-11-24 08:00:33 +01:00
Syres916 c637b532f1 [Gui] Fix string encoding for preselect, select and deselect
(cherry picked from commit eb08fe2a78)
2025-11-24 08:00:33 +01:00
pre-commit-ci[bot] d5da0227ee [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci

(cherry picked from commit 23df7e4cad)
2025-11-24 08:00:33 +01:00
Syres916 3446b933cb [Gui] Fix string encoding for document, object and subojects
(cherry picked from commit d19ab48231)
2025-11-24 08:00:33 +01:00
pre-commit-ci[bot] 6f999f8885 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci

(cherry picked from commit 78bb40c419)
2025-11-24 08:00:33 +01:00
Syres916 95c547bd5a [PartDesign] Fix string encoding for document name and face reference
(cherry picked from commit 1e30362dd4)
2025-11-24 08:00:33 +01:00
Syres916 7aed77aaec [Gui] Fix string encoding for document name
(cherry picked from commit 61db439744)
2025-11-24 08:00:33 +01:00
pre-commit-ci[bot] aa02543896 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci

(cherry picked from commit 4c8d140373)
2025-11-24 08:00:33 +01:00
Syres916 a6da44aa03 [Gui] Fix string encoding document name and object name
(cherry picked from commit fcd487e0ad)
2025-11-24 08:00:33 +01:00
Syres916 c8f629b8b4 [Gui] Fix incorrect document name when updating PropertyLink
(cherry picked from commit 093a070439)
2025-11-24 08:00:33 +01:00
chris b71344c07b sketcher: fix issue #13852 resolve issue with state not updating constraint text
(cherry picked from commit d806b4e5f3)
2025-11-24 06:11:50 +01:00
PaddleStroke 8e73bddec1 Sketcher: Rotate: Fix line length ignored (#25434)
(cherry picked from commit 1d30d079b0)
2025-11-24 06:11:21 +01:00
Roy-043 aa010759d3 Draft: fix number_length for imperial dimensions (#25369)
* The number_length function did not take imperial dimensions into account. For `9" + 7/8"` it would return 1.
* In ArchRoof reference number_length function from DraftGui.py to avoid duplicate code.

(cherry picked from commit 98240a6de5)
2025-11-24 06:11:04 +01:00
FEA-eng 383a51075c FEM: Add Thick Pipe Internal Pressure 2D example (#25191)
Co-authored-by: Kacper Donat <kadet1090@gmail.com>
(cherry picked from commit 44bf1c35d6)
2025-11-24 06:10:44 +01:00
PaddleStroke aeb6fca064 Assembly: BOM: fix subassembly
(cherry picked from commit 40e4eb8770)
2025-11-24 06:10:22 +01:00
Chris 32bfee0ada Sketcher: allow grid snapping while hovering mouse over axis (#25226)
(cherry picked from commit 72d021108f)
2025-11-24 06:10:06 +01:00
Billy Huddleston cb4d6b9cb7 CAM: Ensure Chipload is restored and displayed with user-preferred units
Fixes an issue where Chipload and similar mixin-defined properties were added to
the schema but not reliably restored, because parameter restoration did not always
apply them to the toolbit object. This change ensures Chipload is restored from saved
data and displayed using the user's preferred unit schema, even when no document
is open. This update also ensures the toolbit editor respects the user’s unit schema
preference during editing, even outside an open document.

src/Mod/CAM/Path/Tool/toolbit/models/base.py:
- Restore Chipload (and other parameters) to toolbit.obj if the property exists.

src/Mod/CAM/Path/Tool/toolbit/ui/editor.py:
- Set units schema to user preference when opening the toolbit editor, so Chipload and other values display correctly.

(cherry picked from commit 4432575243)
2025-11-23 20:40:39 +01:00
sliptonic d2a99e83f7 Merge pull request #25573 from FreeCAD/backport-22738-to-releases/FreeCAD-1-1
[Backport releases/FreeCAD-1-1] CAM: Pocket BSplineSurface
2025-11-23 12:41:17 -06:00
sliptonic 78df9dd937 Merge pull request #25575 from FreeCAD/backport-25106-to-releases/FreeCAD-1-1
[Backport releases/FreeCAD-1-1] [CAM] Offer automatic migration of tools in (old) custom working directory to the new tool system
2025-11-23 12:40:58 -06:00
David Kaufman 0b92c77685 Hopefully placate CodeQL
(cherry picked from commit 767ee484ba)
2025-11-23 18:34:06 +00:00
David Kaufman b73a2cca0d migrate silently, no prompts
(cherry picked from commit f22d0e2292)
2025-11-23 18:34:06 +00:00
David Kaufman 205d024b74 remove unused import
(cherry picked from commit e2b4ea58b8)
2025-11-23 18:34:06 +00:00
David Kaufman fc317b16b0 automatically offer migration for tool libraries in custom working dir
(cherry picked from commit 21319f95d2)
2025-11-23 18:34:06 +00:00
David Kaufman 3b80c25339 [CAM] bugfix tools not imported properly during library import
(cherry picked from commit bb9ae3540e)
2025-11-23 18:34:06 +00:00
pre-commit-ci[bot] ccd3e778ff [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2025-11-23 18:06:12 +00:00
tarman3 7bf96e2ef0 CAM: LeadInOut - Fix regression after #24829 2025-11-23 20:02:15 +02:00
tarman3 387c3be2bf CAM: Pocket BSplineSurface
(cherry picked from commit 889dc7059c)
2025-11-23 18:02:10 +00:00
sliptonic 2c775840da Merge pull request #25403 from Connor9220/backport-24829-to-releases/FreeCAD-1-1
CAM: LeadInOut - Fix overtravel
2025-11-23 11:46:45 -06:00
sliptonic 14d93d24fb Merge pull request #25438 from Connor9220/FixBullnoseToolBit
CAM: Introduce unified ToolBit parameter migration logic for Bullnose tools
2025-11-23 11:39:34 -06:00
Andrew Burks c7b3353414 #25474 Added read-only warnings when saving documents (#25532)
* #25474 Added read-only warnings when saving documents

Block saving a file and notify user if windows is unable to save to the file for any reason, or the read-only attribute is checked. Also check std::filesystem::perms for write permission and other checks in FileInfo::isWritable(), although it doesn't seem to matter on windows.

Co-authored-by: Chris Hennes <chennes@pioneerlibrarysystem.org>
(cherry picked from commit bad2989b7b)
2025-11-23 07:24:49 +01:00
Adrian Insaurralde Avalos a469c45a2d pixi: enable building reverse engineering
(cherry picked from commit 3ab8dbe194)
2025-11-21 16:54:37 -06:00
Roy-043 4e6bb3f524 BIM: fix handling of BIM_Sketch view properties (#25339)
* BIM: fix handling of BIM_Sketch view properties

The handling of the sketch view properties was not correct:
* AutoColor was not set to `False`.
* PointSize was missing.
* PointColor was not set to the DefaultShapeVertexColor preference.
* Grid values were never applied. Sketches do not have a GridSnap property and gridSize is not the correct Draft preference.
* Sketch grid snap is a global setting that affects all sketches, the code should not change that setting IMO.

* Remove trailing white space

(cherry picked from commit 63c9b176e2)
2025-11-21 11:40:34 +01:00
Logstor ba5652e046 Removed sys in 'from os import sys, path'
(cherry picked from commit e802aa1821)
2025-11-21 08:47:32 +01:00
Jacob Oursland 40cd4fe772 Enable Reverse Engineering WB.
The Reverse Engineering WB was temporarily disabled as it didn't support Qt6.As it now supports Qt6,
it is time to re-enable the WB.

(cherry picked from commit f48db1f855)
2025-11-21 08:08:55 +01:00
Adrian Insaurralde Avalos 5f68507f6d set 1.1.0rc1 version in packaging files 2025-11-20 23:14:04 -03:00
PaddleStroke ae864aedb4 Assembly: Make the 'ground first part' dialog closable
(cherry picked from commit 2266a42009)
2025-11-20 07:58:52 +01:00
Chris Hennes d4f2c904f8 Gui: Translate 'Don't show me again'
(cherry picked from commit deab0abf8e)
2025-11-20 07:57:37 +01:00
Kacper Donat 2ff62e9727 Gui: Fix stylesheet loading order
For some platforms due to event loop and timer triggering being not
deterministic stylesheets could be loaded before style parameters were
fully loaded. This caused warnings about style parameters to appear
potentially confusing users.

This commit changes the stylesheet loading to happen much earlier in the
process so the stylesheet is applied before theme preselection has
chance to happen.

(cherry picked from commit 8995f43a15)
2025-11-19 20:18:03 +01:00
Kacper Donat 302fbc6765 Gui: Save defaults layout after first launch
(cherry picked from commit d1a8f99509)
2025-11-19 18:41:30 +01:00
PaddleStroke 456bc178c6 Assembly: Fix joint on Body-LCS not working
(cherry picked from commit 1cf6f5f3a6)
2025-11-19 14:09:37 +01:00
PaddleStroke 66ed85bb19 Assembly/TechDraw: Fix exploded view views (#25426)
(cherry picked from commit be7d41dea7)
2025-11-18 15:59:27 -06:00
pre-commit-ci[bot] cd2e26bbea [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2025-11-18 20:20:45 +00:00
Billy Huddleston 6058a47f58 Cleanup for 1.1 2025-11-18 15:17:45 -05:00
PaddleStroke 092ae3f8ea Assembly: Fix crash on moving unconnected parts
(cherry picked from commit 2a8d648ad3)
2025-11-18 20:28:06 +01:00
Billy Huddleston 3971ce2bf7 CAM: Introduce unified ToolBit parameter migration logic for Bullnose tools
- Added new migration system to handle legacy parameter conversion for ToolBit assets and objects.
- Implemented ParameterAccessor abstraction for consistent access to dicts and FreeCAD objects.
- Centralized migration logic for CornerRadius from TorusRadius or FlatRadius/Diameter, restricted to Bullnose shape-type.
- Updated asset and object initialization to use migration logic and filter Bullnose parameters.

src/Mod/CAM/Path/Tool/camassets.py:
- Integrate new migration logic for ToolBit assets using ParameterAccessor and migrate_parameters.
- Ensure shape-type is set before parameter migration; only write changes if migration occurred.

src/Mod/CAM/Path/Tool/toolbit/migration.py:
- Add ParameterAccessor class and migrate_parameters function for unified migration.
- Handle legacy parameter conversion for Bullnose tools.

src/Mod/CAM/Path/Tool/toolbit/models/base.py:
- Apply migration logic to ToolBit objects on restore.
- Filter Bullnose parameters to remove FlatRadius after migration.

src/Mod/CAM/Path/Tool/shape/models/bullnose.py:
- Add filter_parameters method to remove FlatRadius for Bullnose.

src/Mod/CAM/Path/Op/SurfaceSupport.py:
- Derive FlatRadius from CornerRadius and Diameter if both are present.

src/Mod/CAM/Path/Op/Surface.py:
- Remove obsolete setOclCutter method.
2025-11-18 13:38:39 -05:00
Chris 69498be228 Part: fix splitting of a split that is a container / group (#25293)
(cherry picked from commit 3f42824ffa)
2025-11-18 19:22:57 +01:00
tetektoza 08cceec9cf Gui: Use UTF-8 string instead of UTF-16 for Reset in Expression Editor
Currently `ExpLineEdit::apply()` passes `QString::constData()` to a
printf-style format string with %s. `QString::constData() returns a
const QChar* (UTF-16, 2 bytes per char), but %s expects a const char*
(UTF-8/ASCII with 1 byte per char).

This results in a string being interpreted as in "Container":
Byte 0: 'C'
Byte 1: 0x00 (high byte of UTF-16 'C', which gets interpreted as null
terminator)

So this patch uses proper conversion to null-terminated C-String before
passing it further.

(cherry picked from commit ca1528e2a9)
2025-11-18 17:51:51 +01:00
tetektoza 3c6cdb17e0 Gui: Differentiate completion activation modes in Expression Editor
Currently if user tries to press TAB during Expression Editor, it
inserts both the entry and its first subentry. Also, if user browses the
dropdown with arrows keys, it inserts it's values.

Root cause of that is the regression made in latest changes to chaining
completion logic which is triggered for all completion modes including
TAB, which already has its own refresh mechanism. Also,
ExpressionTextEdit connected both activated (Enter/click) and highlight
signals to the same slot, resulting in arrow key navigation inserting
completions.

So, this adds separate slots for ExpressionTextEdit to differentiate
completion modes. And also updates tab handling to pass
ActivationMode::Highlighted to prevent double chaining.

(cherry picked from commit 3f6a0ee78b)
2025-11-18 17:51:51 +01:00
PaddleStroke 15de64e30b Sketcher: ConstraintList: prevent N transaction on box selection (#25254)
(cherry picked from commit 6f90c5ea61)
2025-11-18 09:18:59 -06:00
captain0xff d936ba474c PartDesign: fix the interactive controls for revolution/groove
(cherry picked from commit 5d387ae014)
2025-11-18 16:11:54 +01:00
Billy Huddleston b44af1f768 CAM: Fix crash when Path panel widgets are deleted
Prevent RuntimeError by safely handling missing or deleted geometry import
widgets in Path operation panels. This ensures the UI update code does not
fail if the underlying C++ objects are destroyed, improving stability when
panels are closed or the UI changes.

src/Mod/CAM/Path/Op/Base.py:
- Wrap geometry import widget access in try/except to avoid crashes
- Add error handling for panel widget deletion scenarios

(cherry picked from commit 900d5ae255)
2025-11-18 14:12:09 +01:00
Chris Hennes f308fb92a3 Gui: Fix context of translation strings
(cherry picked from commit 0aec9c3b21)
2025-11-18 14:11:52 +01:00
Gaël Écorchard 98a1dd2f14 Remove functions that require Kuka files (#24984)
(cherry picked from commit c77e1d840c)
2025-11-18 14:11:42 +01:00
Chris Hennes 0887aa188b Gui: Add translation to 'No style sheet'
(cherry picked from commit 0a73f53990)
2025-11-18 14:11:31 +01:00
PaddleStroke 9a4cedf1a4 Assembly: Sub-assembly: fix support for Part-wb objects (#25279)
* Assembly: Sub-assembly: fix support for Part-wb objects

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
(cherry picked from commit 551ab85076)
2025-11-18 11:05:47 +01:00
tarman3 894e1d9ae2 CAM: Engrave - Fix #9114 - Unable to engrave an arc
(cherry picked from commit a514cfdf4b)
2025-11-18 11:04:48 +01:00
PaddleStroke 37f014998c Sketcher: Remove transaction from TaskSketcherConstraints::doSetVisible (#25255)
* Sketcher: Remove transaction from TaskSketcherConstraints::doSetVisible

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
(cherry picked from commit 33d1d80555)
2025-11-18 11:04:42 +01:00
chris 1d21e56ee1 sketcher wb: fix issue #25076, resolve grid snapping placement
(cherry picked from commit 1ec1f1506e)
2025-11-18 11:04:32 +01:00
PaddleStroke c43b615301 Assembly: Insert link: Handle partially loaded documents (#25275)
(cherry picked from commit 10cc4dea16)
2025-11-18 09:05:59 +01:00
Chris 125d3b65c7 Import: STEP export preserve body names (#25042)
(cherry picked from commit f218c5f25c)
2025-11-18 08:14:09 +01:00
pre-commit-ci[bot] 9946819e9f [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2025-11-18 05:38:37 +01:00
Chris Hennes 06d55d50ce Build: Add network-failure fault tolerance to macOS signing (#25265)
(cherry picked from commit e0c5c89bd9)
2025-11-18 05:38:37 +01:00
pre-commit-ci[bot] 1678981185 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2025-11-17 20:23:55 +00:00
tarman3 4e2d2e3206 CAM: LeadInOut - Fix overtravel
(cherry picked from commit 2e3a4d541c)
2025-11-17 15:15:04 -05:00
David Carter 3fc8528692 Materials: Assigning material without appearance
Assigning a material without an appearance reset the appearance to the
default appearance.

There were two main problems. One was the comparison function for
App::Material objects. It would return false when the UUID or MatType
values were different although there are many circumstances where this
could be true and the appearance be the same. It also incorrectly
compared the imagePath.

The second problem was the logic for detecting if an object has already
been assigned an appearance by assigning a material or manually setting
the appearance. If assigned a material, the appearance should update but
not if it has been set manually. This logic has been corrected.

(cherry picked from commit f4e78e3163)
2025-11-17 18:32:15 +01:00
Chris Hennes 6508023e95 CI: Update macOS Intel target version in workflow
GitHub is deprecating macOS 13 runners -- the only remaining Intel runner is now macOS 15, and it is scheduled for retirement in 2027, at which point we will no longer be able to provide Intel weeklies.

(cherry picked from commit 50392855bf)
2025-11-17 18:07:17 +01:00
Chris Hennes ce6d9f361a Core: Convert transparency to alpha (#24891)
* Core: Convert transparency to alpha

Create new `Base::getVersion()` function for extracting a program
version enumeration given a version string.

Convert transparency to alpha value for old project files.

* Base/App: Address review comments

---------

Co-authored-by: wmayer <wmayer@freecad.org>
(cherry picked from commit 4d0c35dd2d)
2025-11-17 17:18:42 +01:00
Florian Foinant-Willig 95fe960485 TechDraw: fix "%.0w" format spec (#25367)
(cherry picked from commit 526f7f2550)
2025-11-17 06:41:03 +01:00
Connor9220 49d4375f70 Fix carriage returns in the Migrator Dialog Title. (#25379)
(cherry picked from commit 11eba3f25c)
2025-11-16 22:53:36 -06:00
Jiří Pinkava b0ffa6728c Tests: Fix value for Imperial pressure tests
The decimal was (wrongly) cahnged in 5379378626

(cherry picked from commit 5fc650b2ff)
2025-11-16 22:48:42 -06:00
PaddleStroke 38fccbacf6 TaskAttacher: Remove 'reset edit' causing trouble to assembly (#25277)
(cherry picked from commit 5da1e3d899)
2025-11-15 17:33:43 +01:00
theo-vt a8ba9b82a5 Sketcher: Proceed to chamfer/fillet on a vertex if there are more than 2 lines but only two non-construction lines
(cherry picked from commit 15d9e14851)
2025-11-15 10:09:45 -06:00
theo-vt 647877384a Sketcher.planegcs: solve reference constraints with lower priority (#25318)
(cherry picked from commit d4bfa82161)
2025-11-15 10:08:53 -06:00
Pieter Hijma a60b4698be Doc: Improve the documentation of transactions (#21494)
(cherry picked from commit 0083364dbe)
2025-11-15 08:03:16 +01:00
Roy-043 1a7fa655f3 Core: workaround for Building US unit system bug (#25288)
* Core: workaround for Building US unit system bug

Fixes #11345

This workaround should hopefully fix the Building US unit system bug at the level of the InputField code. This is the most feasible solution given that we are currently in the v1.1 feature freeze.

I use the word "hopefully" because I have not compiled and tested the code. But replacing `+` with `--` works in Python examples.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix typo in comment

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
(cherry picked from commit c422404020)
2025-11-15 06:24:11 +01:00
tarman3 6d023afaac CAM: DressupArray - Fix python import
(cherry picked from commit b0a79a6f2c)
2025-11-14 19:33:48 +01:00
Furgo 4642fecba3 Define DlgScale task panel's tab order (#25300)
* Define DlgScale task panel's tab order

To improve keyboard navigation.

Fixes: https://github.com/FreeCAD/FreeCAD/issues/23772

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
(cherry picked from commit 93868d54b7)
2025-11-14 19:33:16 +01:00
Stephan T. Lavavej 2ae9f89982 Avoid using non-Standard std::locale::empty() for MSVC
(cherry picked from commit 9e6509a3a4)
2025-11-14 19:32:50 +01:00
Chris Hennes a6ec30a8fc Build: Change package version suffix from 'dev' to 'rc1' 2025-11-13 08:53:47 -06:00
251 changed files with 4919 additions and 7278 deletions
@@ -1,5 +1,7 @@
name: Weekly Build
name: Build Release
on:
release:
types: [created]
schedule:
- cron: "0 0 * * 3"
workflow_dispatch:
@@ -8,10 +10,10 @@ permissions:
contents: write
jobs:
tag_build:
upload_src:
runs-on: ubuntu-latest
outputs:
build_tag: ${{ steps.tag_build.outputs.build_tag }}
build_tag: ${{ steps.get_tag.outputs.build_tag }}
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
@@ -21,20 +23,25 @@ jobs:
- name: Checkout Source
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
ref: ${{ github.sha }}
fetch-depth: 2
fetch-tags: true
submodules: 'recursive'
- name: Tag Build
id: tag_build
- name: get tag and create release if weekly
id: get_tag
shell: bash -l {0}
env:
GH_TOKEN: ${{ github.token }}
run: |
export BUILD_TAG=weekly-$(date "+%Y.%m.%d")
if [ "${{ github.event_name }}" = "release" ]; then
export BUILD_TAG="${{ github.event.release.tag_name }}"
else
export BUILD_TAG=weekly-$(date "+%Y.%m.%d")
gh release create ${BUILD_TAG} --title "Development Build ${BUILD_TAG}" -F .github/workflows/weekly-build-notes.md --prerelease || true
fi
echo "BUILD_TAG=${BUILD_TAG}" >> "$GITHUB_ENV"
echo "build_tag=${BUILD_TAG}" >> "$GITHUB_OUTPUT"
gh release create ${BUILD_TAG} --title "Development Build ${BUILD_TAG}" -F .github/workflows/weekly-build-notes.md --prerelease || true
- name: Upload Source
id: upload_source
@@ -42,10 +49,10 @@ jobs:
env:
GH_TOKEN: ${{ github.token }}
run: |
python3 package/rattler-build/scripts/make_version_file.py ../freecad_version.txt
python3 package/scripts/write_version_info.py ../freecad_version.txt
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
git config user.name 'github-actions[bot]'
git apply package/rattler-build/scripts/disable_git_info.patch
git apply package/disable_git_info.patch
git commit -a -m "Disable git info write to Version.h"
git archive HEAD -o freecad_source_${BUILD_TAG}.tar
git submodule foreach --recursive \
@@ -57,13 +64,13 @@ jobs:
gh release upload --clobber ${BUILD_TAG} "freecad_source_${BUILD_TAG}.tar.gz" "freecad_source_${BUILD_TAG}.tar.gz-SHA256.txt"
build:
needs: tag_build
needs: upload_src
strategy:
matrix:
include:
- { target: linux-64, os: ubuntu-22.04 }
- { target: linux-arm64, os: ubuntu-22.04-arm }
- { target: osx-64, os: macos-13 }
- { target: osx-64, os: macos-15-intel }
- { target: osx-arm64, os: macos-latest }
- { target: win-64, os: windows-latest }
fail-fast: false
@@ -88,10 +95,8 @@ jobs:
- name: Set Platform Environment Variables
shell: bash -l {0}
env:
BUILD_TAG: ${{ needs.tag_build.outputs.build_tag }}
OPERATING_SYSTEM: ${{ runner.os }}
run: |
echo "BUILD_TAG=${BUILD_TAG}" >> "$GITHUB_ENV"
if [[ $OPERATING_SYSTEM == 'Windows' ]]; then
echo 'PIXI_CACHE_DIR=D:\rattler' >> "$GITHUB_ENV"
echo 'RATTLER_CACHE_DIR=D:\rattler' >> "$GITHUB_ENV"
@@ -100,6 +105,7 @@ jobs:
- name: Checkout Source
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
ref: ${{ github.sha }}
fetch-depth: 2
fetch-tags: true
submodules: 'recursive'
@@ -110,6 +116,7 @@ jobs:
cache: false
- name: Install the Apple certificate and provisioning profile
id: get_cert
if: runner.os == 'macOS'
env:
APP_SPECIFIC_PASSWORD: ${{ secrets.APP_SPECIFIC_PASSWORD }}
@@ -120,9 +127,15 @@ jobs:
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
run: |
if [ -z "$BUILD_CERTIFICATE_BASE64" ]; then
echo "has_cert=false" >> $GITHUB_OUTPUT
echo "No certificate avalable... skipping" && exit 0
else
echo "has_cert=true" >> $GITHUB_OUTPUT
fi
# create variables
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
PP_PATH=$RUNNER_TEMP/FreeCAD_Weekly.provisionprofile
PP_PATH=$RUNNER_TEMP/FreeCAD_bundle.provisionprofile
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
export KEYCHAIN_PASSWORD=$(openssl rand -base64 8)
@@ -152,12 +165,13 @@ jobs:
env:
GH_TOKEN: ${{ github.token }}
SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }}
SIGN_RELEASE: "true"
SIGN_RELEASE: ${{ steps.get_cert.outputs.has_cert }}
TARGET_PLATFORM: ${{ matrix.target }}
MAKE_INSTALLER: "true"
UPLOAD_RELEASE: "true"
BUILD_TAG: ${{ needs.upload_src.outputs.build_tag }}
run: |
python3 package/rattler-build/scripts/make_version_file.py ../freecad_version.txt
git apply package/rattler-build/scripts/disable_git_info.patch
python3 package/scripts/write_version_info.py ../freecad_version.txt
cd package/rattler-build
pixi install
pixi run -e package create_bundle
+1
View File
@@ -8,6 +8,7 @@ on:
jobs:
trigger-copr-build:
environment: fedora-daily
env:
copr_token: ${{ secrets.COPR_TOKEN }}
runs-on: ubuntu-latest
+1 -1
View File
@@ -3,7 +3,7 @@ files_to_sync:
- .packit.yaml
actions:
post-upstream-clone:
- bash -c 'BUILD_TAG=dev /usr/bin/python3 package/rattler-build/scripts/make_version_file.py freecad_version.txt'
- bash -c '/usr/bin/python3 package/scripts/write_version_info.py freecad_version.txt'
- rm -f freecad-sources.tar.gz
changelog-entry:
- bash -c 'git log --no-merges --pretty="format:- %s (%an)" $(git describe --tags --abbrev=0 )..HEAD -- |sed 's/%/%%/g''
+1 -5
View File
@@ -82,15 +82,11 @@ persistent=yes
# Minimum Python version to use for version dependent checks. Will default to
# the version used to run pylint.
py-version=3.8
py-version=3.10
# Discover python modules and packages in the file system subtree.
recursive=no
# When enabled, pylint would attempt to guess common misconfiguration and emit
# user-friendly hints instead of false-positive error messages.
suggestion-mode=yes
# Allow loading of arbitrary C extensions. Extensions are imported into the
# active Python interpreter and may run arbitrary code.
unsafe-load-any-extension=no
+1 -1
View File
@@ -53,7 +53,7 @@ project(FreeCAD)
set(PACKAGE_VERSION_MAJOR "1")
set(PACKAGE_VERSION_MINOR "1")
set(PACKAGE_VERSION_PATCH "0") # number of patch release (e.g. "4" for the 0.18.4 release)
set(PACKAGE_VERSION_SUFFIX "dev") # either "dev" for development snapshot or "" (empty string)
set(PACKAGE_VERSION_SUFFIX "rc1") # either "dev" for development snapshot or "" (empty string)
set(PACKAGE_BUILD_VERSION "0") # used when the same FreeCAD version will be re-released (for example using an updated LibPack)
set(PACKAGE_VERSION "${PACKAGE_VERSION_MAJOR}.${PACKAGE_VERSION_MINOR}.${PACKAGE_VERSION_PATCH}")
-4
View File
@@ -71,10 +71,6 @@
"type": "BOOL",
"value": "ON"
},
"BUILD_REVERSEENGINEERING": {
"type": "BOOL",
"value": "OFF"
},
"ENABLE_DEVELOPER_TESTS": {
"type": "BOOL",
"value": "ON"
+5
View File
@@ -0,0 +1,5 @@
*.exe
*-SHA256.txt
MSVCRedist
FreeCAD
version.nsh
+47
View File
@@ -0,0 +1,47 @@
del /S *_d.*
del /S *_debug.*
del /S *.pyc
del /S *.pdb
del /S boost*-gd-*.dll
cd bin
del assistant.exe
del Coin4d.dll
del designer.exe
del freetyped.dll
del libcrypto-3d.dll
del libEGLd.dll
del libGLESv2d.dll
del libssl-3d.dll
del linguist.exe
del qdoc.exe
del Qt6Concurrentd.dll
del Qt6Cored.dll
del Qt6DBusd.dll
del Qt6DesignerComponentsd.dll
del Qt6Designerd.dll
del Qt6Guid.dll
del Qt6Helpd.dll
del Qt6LabsAnimationd.dll
del Qt6LabsFolderListModeld.dll
del Qt6LabsPlatformd.dll
del Qt6LabsSettingsd.dll
del Qt6LabsSharedImaged.dll
del Qt6LabsWavefrontMeshd.dll
del Qt6MultimediaWidgetsd.dll
del Qt6Multimediad.dll
del Qt6Networkd.dll
del Qt6OpenGLWidgetsd.dll
del Qt6OpenGLd.dll
del Qt6PrintSupportd.dll
del Qt6SpatialAudiod.dll
del Qt6Sqld.dll
del Qt6SvgWidgetsd.dll
del Qt6Svgd.dll
del Qt6Testd.dll
del Qt6UiToolsd.dll
del Qt6Widgetsd.dll
del Qt6Xmld.dll
del QtWebEngineProcessd.exe
del Quarter1d.dll
del xerces-c_3_2D.dll
del zlibd.dll
@@ -1,7 +1,7 @@
# Creating a Windows installer for FreeCAD
These are instructions for building an NSIS-based installer for FreeCAD. They were designed for FreeCAD 0.21 and later,
and presume that you have cloned a copy of FreeCAD's source code, and therefore have the directory *src/WindowsInstaller*.
and presume that you have cloned a copy of FreeCAD's source code, and therefore have the directory *package/WindowsInstaller*.
## Install NSIS
To set up your system for building an NSIS installer:
@@ -15,16 +15,13 @@ To set up your system for building an NSIS installer:
4. Download and install the nsProcess plugin from https://nsis.sourceforge.io/NsProcess_plugin -- you will need the version that supports Unicode, so make sure to follow the appropriate instructions on their site to install that one (as of this writing it involves manually copying and renaming the plugin DLL file).
## Build the installer
Next, update the installer settings for the current version of FreeCAD. Starting from the *src/WindowsInstaller* folder in the FreeCAD source tree:
1. Set the appropriate version strings for the release you are creating. These are used to construct the filename of the installer, among other things. If you have to upload a new version of the installer for the exact same release of FreeCAD, increment `APP_VERSION BUILD` as needed.
```
!define APP_VERSION_MAJOR 0
!define APP_VERSION_MINOR 21
!define APP_VERSION_REVISION 0
Next, update the installer settings for the current version of FreeCAD. Starting from the *package/WindowsInstaller* folder in the FreeCAD source tree:
1. Set the appropriate version strings for the release you are creating. These are used to construct the filename of the installer, among other things. If you have to upload a new version of the installer for the exact same release of FreeCAD, increment `APP_VERSION BUILD` as needed. The main version numbers are dynamically obtained by calling `freecadcmd.exe`.
```nsis
!define APP_VERSION_EMERGENCY "RC1"
!define APP_VERSION_BUILD 1
```
2. Within the folder *src/WindowsInstaller*, create a new folder called MSVCRedist and copy the following files from your MSVC installation into it:
2. If the installer will be made from a LibPack build create a new folder called MSVCRedist within the folder *package/WindowsInstaller* and copy the following files from your MSVC installation into it:
```
vcruntime140.dll
concrt140.dll
@@ -33,28 +30,38 @@ vcamp140.dll
vccorlib140.dll
vcomp140.dll
```
3. Open the file *Settings.nsh* with a text editor (both jEdit and Visual Studio Code are good editors for NSIS files). Edit the following paths to correspond to your system: `FILES_FREECAD` corresponds to your installation directory (e.g. `CMAKE_INSTALL_PREFIX` if you self-compiled) and `FILES_DEPS` is the folder you created with the MSVC redistributable files in it.
```
!define FILES_FREECAD "C:\FreeCAD\Installer\FreeCAD"
!define FILES_DEPS "C:\FreeCAD\Installer\MSVCRedist"
3. If required open the file *Settings.nsh* with a text editor (both jEdit and Visual Studio Code are good editors for NSIS files). Edit the following paths to correspond to your system: `FILES_FREECAD` corresponds to your installation directory (e.g. `CMAKE_INSTALL_PREFIX` if you self-compiled), `FILES_THUMBS` is the directory where the thumbnailer dll is located and `FILES_DEPS` is the folder you created with the MSVC redistributable files in it. `FILES_DEPS` is not needed if the installer is created from a conda bundle so it is not set by default. These can be set via /D argument for `makensis.exe` or by editing *Settings.nsh*.
```nsis
!ifndef FILES_FREECAD
!define FILES_FREECAD "${__FILEDIR__}\FreeCAD"
!endif
!ifndef FILES_THUMBS
!define FILES_THUMBS "${__FILEDIR__}\thumbnail"
!endif
#!define FILES_DEPS "${__FILEDIR__}\MSVC_Redist"
```
4. Ensure the FreeCAD files are in place. Here you have two options:
* If you are working from an already-compiled version of FreeCAD provided to you by an outside source: in this case, simply ensure that `FILES_FREECAD` is set to the directory containing those files.
* If you compiled FreeCAD on your own as described [here](https://wiki.freecad.org/Compile_on_Windows) (and using the Install option outlined there). Then:
* Open the file *Settings.nsh* as described in step 3. above and set there</br>
`!define FILES_FREECAD` to the folder you specified as `CMAKE_INSTALL_PREFIX`
* Copy into that folder the file *Delete.bat* that is part of the installer
* Copy into the installation folder the file *Delete.bat* that is part of the installer
* open a command line in Windows and change to the folder
* run the comamand</br>
* run the command</br>
`Delete.bat`
* (These steps assure that the installer only contains files users need. Moreover it assures that the
overall files size is below 2 GB and we can use the most compact compression for the installer.)
5. Right-click on the file *FreeCAD-installer.nsi* and choose **Compile NSIS script**
to compile the installer.
to compile the installer. You can also run from command line to specify some settings
```cmd
%your_nsis_path%\makensis.exe /D'FILES_FREECAD="D:\some\path\FreeCAD"' /D'FILES_DEPS="${__FILEDIR__}\MSVC_Redist" /D'ExeFile="my-FreeCAD-installer.exe"' FreeCAD-installer.nsi
```
NOTE: For test builds of the installer you can turn off the compression. This speeds up
NOTE: For test builds of the installer you can turn off compression. This speeds up
the build time for the installer a lot but increases its file size. The compression
is turned off by uncommenting the line</br>
is turned off by commenting the line</br>
`SetCompressor /SOLID lzma`</br>
in the file *Settings.nsh*.
in the file *Settings.nsh* or by defining `FC_TEST_BUILD` in command line
```cmd
%your_nsis_path%\makensis.exe [OPTIONS] /DFC_TEST_BUILD FreeCAD-installer.nsi
```
+62
View File
@@ -0,0 +1,62 @@
/*
Settings for FreeCAD installer
These typically need to be modified for each FreeCAD release
*/
# Make the installer as small as possible
# Using /SOLID is usually better for file size but it can't be used if the original size is
# more than 2GB, if building with /SOLID fails try disabling it
# comment this or use /DFC_TEST_BUILD command line option for testing builds since it will reduce
# the time to create an installer a lot at the cost of a much greater file size.
# So assure it is active for release builds!
!ifndef FC_TEST_BUILD
SetCompressor /SOLID lzma
!endif
#--------------------------------
# File locations
# !!! you may need to adjust them to the folders in your Windows system !!!
# can be specified with /D command line argument to makensis.exe
!ifndef FILES_FREECAD
!define FILES_FREECAD "${__FILEDIR__}\FreeCAD"
!endif
!ifndef FILES_THUMBS
!define FILES_THUMBS "${__FILEDIR__}\thumbnail"
!endif
# msvc redistributables location is required for LibPack builds but not conda
# when using a LibPack build set the redistributables directory location here
# or with /D command line argument to makensis.exe
#!define FILES_DEPS "${__FILEDIR__}\MSVCRedist"
#--------------------------------
# get version info from freecadcmd
!system "${FILES_FREECAD}\bin\freecadcmd.exe --safe-mode -c $\"import datetime; print(f'!define COPYRIGHT_YEAR {datetime.date.today().year}')$\">${__FILEDIR__}\version.nsh" = 0
!system "${FILES_FREECAD}\bin\freecadcmd.exe --safe-mode -c $\"print(f'!define APP_VERSION_MAJOR \$\"{App.Version()[0]}\$\"')$\">>${__FILEDIR__}\version.nsh" = 0
!system "${FILES_FREECAD}\bin\freecadcmd.exe --safe-mode -c $\"print(f'!define APP_VERSION_MINOR \$\"{App.Version()[1]}\$\"')$\">>${__FILEDIR__}\version.nsh" = 0
!system "${FILES_FREECAD}\bin\freecadcmd.exe --safe-mode -c $\"print(f'!define APP_VERSION_PATCH \$\"{App.Version()[2]}\$\"')$\">>${__FILEDIR__}\version.nsh" = 0
!system "${FILES_FREECAD}\bin\freecadcmd.exe --safe-mode -c $\"print(f'!define APP_VERSION_REVISION \$\"{App.Version()[3].split()[0]}\$\"')$\">>${__FILEDIR__}\version.nsh" = 0
!include "${__FILEDIR__}\version.nsh"
!delfile "${__FILEDIR__}\version.nsh"
!define APP_VERSION_EMERGENCY "" # use "1" for an emergency release of FreeCAD otherwise ""
# alternatively you can use APP_VERSION_EMERGENCY for a custom suffix of the version number
!define APP_EMERGENCY_DOT "" # use "." for an emergency release of FreeCAD otherwise ""
!define APP_VERSION_BUILD 1 # Start with 1 for the installer releases of each version
!define APP_VERSION "${APP_VERSION_MAJOR}.${APP_VERSION_MINOR}.${APP_VERSION_PATCH}${APP_EMERGENCY_DOT}${APP_VERSION_EMERGENCY}" # Version to display
#--------------------------------
# Installer file name
# Typical names for the release are "FreeCAD-020-Installer-1.exe" etc.
!ifndef ExeFile
!define ExeFile "${APP_NAME}_${APP_VERSION_MAJOR}.${APP_VERSION_MINOR}.${APP_VERSION_PATCH}${APP_VERSION_EMERGENCY}-Windows-x86_64-installer-${APP_VERSION_BUILD}.exe"
!endif
#--------------------------------
# installer bit type - FreeCAD is only provided as 64bit build
!define MULTIUSER_USE_PROGRAMFILES64
@@ -1 +1,2 @@
signtool.exe sign /f FCweborg.pfx /p FreeCADIsCool /fd sha512 /tr http://timestamp.digicert.com /td sha512 /v %1
certutil -hashfile %1 SHA256 > %1-SHA256.txt
Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Before

Width:  |  Height:  |  Size: 116 KiB

After

Width:  |  Height:  |  Size: 116 KiB

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 64 KiB

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 91 KiB

@@ -13,13 +13,13 @@ Configuration and variables of FreeCAD installer
# Names and version
!define APP_NAME "FreeCAD"
!define APP_VERSION_NUMBER "${APP_VERSION_MAJOR}.${APP_VERSION_MINOR}.${APP_VERSION_REVISION}.${APP_VERSION_BUILD}"
!define APP_VERSION_NUMBER "${APP_VERSION_MAJOR}.${APP_VERSION_MINOR}.${APP_VERSION_PATCH}.${APP_VERSION_BUILD}"
# For the proposed install folder we use the scheme "FreeCAD 0.18"
# however for the Registry, we need the scheme "FreeCAD 0.18.x" in order
# to check if it is exactly this version (to support side-by-side installations)
!define APP_SERIES_NAME "${APP_VERSION_MAJOR}.${APP_VERSION_MINOR}"
!define APP_SERIES_KEY "${APP_VERSION_MAJOR}${APP_VERSION_MINOR}${APP_VERSION_REVISION}${APP_VERSION_EMERGENCY}"
!define APP_SERIES_KEY2 "${APP_VERSION_MAJOR}.${APP_VERSION_MINOR}.${APP_VERSION_REVISION}${APP_EMERGENCY_DOT}${APP_VERSION_EMERGENCY}"
!define APP_SERIES_KEY "${APP_VERSION_MAJOR}${APP_VERSION_MINOR}${APP_VERSION_PATCH}${APP_VERSION_EMERGENCY}"
!define APP_SERIES_KEY2 "${APP_VERSION_MAJOR}.${APP_VERSION_MINOR}.${APP_VERSION_PATCH}${APP_EMERGENCY_DOT}${APP_VERSION_EMERGENCY}"
!define APP_DIR_REGKEY "Software\Microsoft\Windows\CurrentVersion\App Paths\${APP_NAME}.exe"
!define APP_DIR "${APP_NAME} ${APP_SERIES_NAME}"
# Fixme: FC should use different preferences folder for every release
@@ -60,7 +60,7 @@ Configuration and variables of FreeCAD installer
!define SETUP_ICON "icons\FreeCAD.ico"
!define SETUP_HEADERIMAGE "graphics\header.bmp"
!define SETUP_WIZARDIMAGE "graphics\orange.bmp"
!define SETUP_WIZARDIMAGE "graphics\banner.bmp"
!define SETUP_UNINSTALLER "Uninstall-${APP_NAME}.exe"
!define SETUP_UNINSTALLER_KEY "${APP_NAME}${APP_SERIES_KEY}"
@@ -92,7 +92,7 @@ BrandingText " "
VIProductVersion "${APP_VERSION_NUMBER}"
VIAddVersionKey /LANG=${LANG_ENGLISH} "ProductName" "${APP_NAME}"
VIAddVersionKey /LANG=${LANG_ENGLISH} "ProductVersion" "${APP_DIR}.${APP_VERSION_REVISION}"
VIAddVersionKey /LANG=${LANG_ENGLISH} "ProductVersion" "${APP_DIR}.${APP_VERSION_PATCH}"
VIAddVersionKey /LANG=${LANG_ENGLISH} "FileDescription" "${APP_INFO}"
VIAddVersionKey /LANG=${LANG_ENGLISH} "FileVersion" "${APP_VERSION}"
VIAddVersionKey /LANG=${LANG_ENGLISH} "LegalCopyright" "${APP_COPYRIGHT}"
@@ -50,7 +50,7 @@ Function PostMultiUserPageInit
# check if there is an existing FreeCAD installation of the same FreeCAD series
# we usually don't release more than 10 versions so with 20 we are safe to check if a newer version is installed
IntOp $4 ${APP_VERSION_REVISION} + 20
IntOp $4 ${APP_VERSION_PATCH} + 20
${for} $5 0 $4
ReadRegStr $0 SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}${APP_VERSION_MAJOR}${APP_VERSION_MINOR}$5" "DisplayVersion"
# also check for an emergency release
@@ -110,7 +110,8 @@ Function .onInit
${orif} $R0 == "5.1" # XP
${orif} $R0 == "5.2" # 2003
${orif} $R0 == "6.0" # Vista
MessageBox MB_OK|MB_ICONSTOP "${APP_NAME} ${APP_VERSION} requires Windows 7 or newer." /SD IDOK
${orif} $R0 == "6.1" # 7
MessageBox MB_OK|MB_ICONSTOP "${APP_NAME} ${APP_VERSION} requires Windows 8 or newer." /SD IDOK
Quit
${endif}
@@ -130,7 +131,7 @@ Function .onInit
# plugin must be unloaded
${nsProcess::Unload}
# initialize the multi-uder installer UI
# initialize the multi-user installer UI
!insertmacro MULTIUSER_INIT
# this can be reset to "true" in section SecDesktop
@@ -18,8 +18,8 @@
# and returns the number of the character in the FindStr where the SearchStr was found (Pointer)
# if nothing was found or the search is impossible the Pointer is set to -1
StrLen $R2 ${SearchStr}
StrLen $R4 ${FindStr}
StrLen $R2 "${SearchStr}"
StrLen $R4 "${FindStr}"
StrCpy $R5 0
${if} $R2 == 0
${orif} $R4 == 0
@@ -27,8 +27,8 @@
${endif}
IntCmp $R4 $R2 loopA NotFound
loopA:
StrCpy $R3 ${FindStr} $R2 $R5
StrCmp $R3 ${SearchStr} Found
StrCpy $R3 "${FindStr}" $R2 $R5
StrCmp $R3 "${SearchStr}" Found
IntOp $R5 $R5 + 1
IntCmp $R4 $R5 loopA NotFound
Goto loopA
@@ -8,14 +8,15 @@ Installation of program files, dictionaries and external components
#--------------------------------
# Program files
!include LogicLib.nsh
Section -ProgramFiles SecProgramFiles
# if the $INSTDIR does not contain "FreeCAD" we must add a subfolder to avoid that FreeCAD will e.g.
# be installed directly to C:\programs - the uninstaller will then delete the whole
# C:\programs directory
StrCpy $String $INSTDIR
StrCpy $Search ${APP_NAME}
StrCpy $String "$INSTDIR"
StrCpy $Search "${APP_NAME}"
Call StrPoint # function from Utils.nsh
${if} $Pointer == "-1"
StrCpy $INSTDIR "$INSTDIR\${APP_DIR}"
@@ -38,8 +39,11 @@ Section -ProgramFiles SecProgramFiles
File /r "${FILES_FREECAD}\bin\*.*"
# MSVC redistributable DLLs
SetOutPath "$INSTDIR\bin"
File "${FILES_DEPS}\*.*"
!ifdef FILES_DEPS
!echo "Including MSVC Redist files from ${FILES_DEPS}"
SetOutPath "$INSTDIR\bin"
File "${FILES_DEPS}\*.*"
!endif
# Others
SetOutPath "$INSTDIR\data"
@@ -52,10 +56,6 @@ Section -ProgramFiles SecProgramFiles
File /r "${FILES_FREECAD}\lib\*.*"
SetOutPath "$INSTDIR\Mod"
File /r "${FILES_FREECAD}\Mod\*.*"
SetOutPath "$INSTDIR\resources"
File /r "${FILES_FREECAD}\resources\*.*"
SetOutPath "$INSTDIR\translations"
File /r "${FILES_FREECAD}\translations\*.*"
SetOutPath "$INSTDIR"
File /r "${FILES_THUMBS}"
+2 -2
View File
@@ -16,7 +16,7 @@
Name: freecad
Epoch: 1
Version: 1.1.0~dev
Version: 1.1.0~rc1
Release: 1%{?dist}
Summary: A general purpose 3D CAD modeler
@@ -44,7 +44,7 @@ Source0: freecad-sources.tar.gz
# Utilities
BuildRequires: cmake gcc-c++ gettext doxygen swig graphviz gcc-gfortran desktop-file-utils tbb-devel ninja-build strace
%if %{with tests}
BuildRequires: xorg-x11-server-Xvfb python3-typing-extensions
BuildRequires: xorg-x11-server-Xvfb python3-typing-extensions
%if %{without bundled_gtest}
BuildRequires: gtest-devel gmock-devel
%endif
+12
View File
@@ -0,0 +1,12 @@
linux/AppDir/usr
linux/AppDir/*.desktop
linux/AppDir/*.svg
linux/AppDir/packages.txt
windows/FreeCAD_*Windows*
osx/FreeCAD.app
**.AppImage
**.AppImage.zsync
**.dmg
**.7z
**.exe
**-SHA256.txt
+26 -7
View File
@@ -1,7 +1,7 @@
#!/bin/bash
set -e
set -x
set -x
conda_env="AppDir/usr"
@@ -9,9 +9,6 @@ mkdir -p ${conda_env}
cp -a ../.pixi/envs/default/* ${conda_env}
export PATH="${PWD}/${conda_env}/bin:${PATH}"
export CONDA_PREFIX="${PWD}/${conda_env}"
echo -e "\nDelete unnecessary stuff"
rm -rf ${conda_env}/include
find ${conda_env} -name \*.a -delete
@@ -49,7 +46,7 @@ rm -rf ${conda_env}/lib/cmake/
find . -name "*.h" -type f -delete
find . -name "*.cmake" -type f -delete
python_version=$(python -c 'import platform; print("py" + platform.python_version_tuple()[0] + platform.python_version_tuple()[1])')
python_version=$(${conda_env}/bin/python -c 'import platform; print("py" + platform.python_version_tuple()[0] + platform.python_version_tuple()[1])')
version_name="FreeCAD_${BUILD_TAG}-Linux-$(uname -m)-${python_version}"
echo -e "\################"
@@ -62,6 +59,20 @@ sed -i "1s/.*/\nLIST OF PACKAGES:/" AppDir/packages.txt
curl -LO https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-$(uname -m).AppImage
chmod a+x appimagetool-$(uname -m).AppImage
if [ "${UPLOAD_RELEASE}" == "true" ]; then
case "${BUILD_TAG}" in
*weekly*)
GH_UPDATE_TAG="weeklies"
;;
*rc*)
GH_UPDATE_TAG="${BUILD_TAG}"
;;
*)
GH_UPDATE_TAG="latest"
;;
esac
fi
echo -e "\nCreate the appimage"
# export GPG_TTY=$(tty)
chmod a+x ./AppDir/AppRun
@@ -69,7 +80,7 @@ chmod a+x ./AppDir/AppRun
--comp zstd \
--mksquashfs-opt -Xcompression-level \
--mksquashfs-opt 22 \
-u "gh-releases-zsync|FreeCAD|FreeCAD|${BUILD_TAG}|FreeCAD*$(uname -m)*.AppImage.zsync" \
-u "gh-releases-zsync|FreeCAD|FreeCAD|${GH_UPDATE_TAG}|FreeCAD*$(uname -m)*.AppImage.zsync" \
AppDir ${version_name}.AppImage
# -s --sign-key ${GPG_KEY_ID} \
@@ -77,5 +88,13 @@ echo -e "\nCreate hash"
sha256sum ${version_name}.AppImage > ${version_name}.AppImage-SHA256.txt
if [ "${UPLOAD_RELEASE}" == "true" ]; then
gh release upload --clobber ${BUILD_TAG} "${version_name}.AppImage" "${version_name}.AppImage-SHA256.txt"
gh release upload --clobber ${BUILD_TAG} "${version_name}.AppImage" "${version_name}.AppImage.zsync" "${version_name}.AppImage-SHA256.txt"
if [ "${GH_UPDATE_TAG}" == "weeklies" ]; then
generic_name="FreeCAD_weekly-Linux-$(uname -m)"
mv "${version_name}.AppImage" "${generic_name}.AppImage"
mv "${version_name}.AppImage.zsync" "${generic_name}.AppImage.zsync"
mv "${version_name}.AppImage-SHA256.txt" "${generic_name}.AppImage-SHA256.txt"
gh release create weeklies --prerelease | true
gh release upload --clobber weeklies "${generic_name}.AppImage" "${generic_name}.AppImage.zsync" "${generic_name}.AppImage-SHA256.txt"
fi
fi
+2 -5
View File
@@ -9,9 +9,6 @@ mkdir -p ${conda_env}
cp -a ../.pixi/envs/default/* ${conda_env}
export PATH="${PWD}/${conda_env}/bin:${PATH}"
export CONDA_PREFIX="${PWD}/${conda_env}"
# delete unnecessary stuff
rm -rf ${conda_env}/include
find ${conda_env} -name \*.a -delete
@@ -50,7 +47,7 @@ cmake --build build
mkdir -p FreeCAD.app/Contents/MacOS
cp build/FreeCAD FreeCAD.app/Contents/MacOS/FreeCAD
python_version=$(python -c 'import platform; print("py" + platform.python_version_tuple()[0] + platform.python_version_tuple()[1])')
python_version=$(${conda_env}/bin/python -c 'import platform; print("py" + platform.python_version_tuple()[0] + platform.python_version_tuple()[1])')
version_name="FreeCAD_${BUILD_TAG}-macOS-$(uname -m)-${python_version}"
application_menu_name="FreeCAD_${BUILD_TAG}"
@@ -71,7 +68,7 @@ rm -rf ${conda_env}/Library
if [[ "${SIGN_RELEASE}" == "true" ]]; then
# create the signed dmg
./macos_sign_and_notarize.zsh -p "FreeCAD" -k ${SIGNING_KEY_ID} -o "${version_name}.dmg"
../../scripts/macos_sign_and_notarize.zsh -p "FreeCAD" -k ${SIGNING_KEY_ID} -o "${version_name}.dmg"
else
# create the dmg
dmgbuild -s dmg_settings.py "FreeCAD" "${version_name}.dmg"
@@ -1,3 +1,5 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# Ensure default values are set in defines if they are not already provided
defines.setdefault('containing_folder', '.')
defines.setdefault('app_name', 'FreeCAD.app')
File diff suppressed because it is too large Load Diff
+9 -16
View File
@@ -8,7 +8,7 @@ preview = ["pixi-build"]
[package]
name = "freecad"
version = "1.1.0dev"
version = "1.1.0rc1"
homepage = "https://freecad.org"
repository = "https://github.com/FreeCAD/FreeCAD"
description = "FreeCAD"
@@ -22,44 +22,37 @@ freecad = { path = "." }
[feature.package.dependencies]
python = ">=3.11,<3.12"
[feature.package.tasks]
create_bundle = 'bash -c "cd $(bash scripts/get_os.bash) && bash create_bundle.sh"'
## Linux (x86-64)
[feature.package.target.linux-64.dependencies]
coreutils = "*"
[feature.package.target.linux-64.tasks]
create_bundle = 'bash -c "cd linux && bash create_bundle.sh"'
## Linux (aarch64)
[feature.package.target.linux-aarch64.dependencies]
coreutils = "*"
[feature.package.target.linux-aarch64.tasks]
create_bundle = 'bash -c "cd linux && bash create_bundle.sh"'
## macOS (Intel)
[feature.package.target.osx-64.dependencies]
coreutils = "*"
dmgbuild = "*"
pyobjc-framework-Quartz = "*"
sed = "*"
[feature.package.target.osx-64.tasks]
create_bundle = 'bash -c "cd osx && bash create_bundle.sh"'
## macOS (Apple Silicon)
[feature.package.target.osx-arm64.dependencies]
coreutils = "*"
dmgbuild = "*"
pyobjc-framework-Quartz = "*"
sed = "*"
[feature.package.target.osx-arm64.tasks]
create_bundle = 'bash -c "cd osx && bash create_bundle.sh"'
## Windows dependencies (x86-64)
[feature.package.target.win-64.dependencies]
git = "*"
[feature.package.target.win-64.tasks]
create_bundle = 'bash -c "cd windows && bash create_bundle.sh"'
nsis = { version = "*", build = "*_log*" }
7zip = "*"
vs2022_win-64 = "*"
[environments]
default = ["freecad"]
+1 -2
View File
@@ -1,5 +1,5 @@
context:
version: "1.1.0dev"
version: "1.1.0rc1"
package:
name: freecad
@@ -145,7 +145,6 @@ requirements:
- gmsh
- graphviz
- ifcopenshell
- jinja2
- lark
- lxml
- matplotlib-base
@@ -1,13 +0,0 @@
diff --git a/src/Tools/SubWCRev.py b/src/Tools/SubWCRev.py
index 1f3f0a436343..c314003736f1 100644
--- a/src/Tools/SubWCRev.py
+++ b/src/Tools/SubWCRev.py
@@ -523,7 +523,7 @@ def main():
inp = open("%s/src/Build/Version.h.in" % (bindir))
lines = inp.readlines()
inp.close()
- lines = i.writeVersion(lines)
+ #lines = i.writeVersion(lines)
out = open("%s/src/Build/Version.h.out" % (bindir), "w")
out.writelines(lines)
out.write("\n")
@@ -1,53 +0,0 @@
import sys
import os
import subprocess
import platform
from datetime import datetime
import freecad
import FreeCAD
package_manager = "conda"
system = platform.platform().split("-")[0]
arch = platform.machine()
# Windows uses a different syntax
if arch == "AMD64":
arch = "x86_64"
if "ARCH" in os.environ:
if os.environ["ARCH"] != "":
arch = os.environ["ARCH"]
python_version = platform.python_version().split(".")
python_version = "py" + python_version[0] + python_version[1]
date = str(datetime.now()).split(" ")[0]
version_info = FreeCAD.Version()
build_version_suffix = FreeCAD.ConfigGet("BuildVersionSuffix")
dev_version = version_info[0] + "." + version_info[1] + "." + version_info[2] + build_version_suffix
revision = version_info[3].split(" ")[0]
if system == "macOS":
import jinja2
print("create plist from template")
osx_directory = os.path.join(os.path.dirname(__file__), "..", "osx")
with open(os.path.join(osx_directory, "Info.plist.template")) as template_file:
template_str = template_file.read()
template = jinja2.Template(template_str)
rendered_str = template.render( FREECAD_VERSION="{}-{}".format(dev_version, revision),
APPLICATION_MENU_NAME="FreeCAD-{}-{}".format(dev_version, revision) )
with open(os.path.join(osx_directory, "FreeCAD.app", "Contents", "Info.plist"), "w") as rendered_file:
rendered_file.write(rendered_str)
if "DEPLOY_RELEASE" in os.environ and os.environ["DEPLOY_RELEASE"] == "weekly-builds":
dev_version = "weekly-builds"
revision_separator = "-"
else:
revision_separator = ""
revision = ""
bundle_name = f"FreeCAD_{dev_version}{revision_separator}{revision}-{package_manager}-{system}-{arch}-{python_version}"
with open("bundle_name.txt", "w") as bundle_name_file:
bundle_name_file.write(bundle_name)
@@ -0,0 +1,8 @@
#!/usr/bin/env bash
if [[ "$OSTYPE" =~ (msys*|cygwin*|mingw*) ]]; then
echo windows
elif [[ "$OSTYPE" == darwin* ]]; then
echo osx
elif [[ "$OSTYPE" == linux* ]]; then
echo linux
fi
@@ -1,56 +0,0 @@
set conda_env="fc_env"
robocopy ..\.pixi\envs\default\* %conda_env% /S /MT:%NUMBER_OF_PROCESSORS% > nul
%conda_env%\python ..\scripts\get_freecad_version.py
set /p freecad_version_name= <bundle_name.txt
echo **********************
echo %freecad_version_name%
echo **********************
REM remove arm binaries that fail to extract unless using latest 7zip
for /r %conda_env% %%i in (*arm*.exe) do (@echo "%%i will be removed" & @del "%%i")
set copy_dir="FreeCAD_Conda_Build"
mkdir %copy_dir%
REM Copy Conda's Python and (U)CRT to FreeCAD/bin
robocopy %conda_env%\DLLs %copy_dir%\bin\DLLs /S /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Lib %copy_dir%\bin\Lib /XD __pycache__ /S /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Scripts %copy_dir%\bin\Scripts /S /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\python*.* %copy_dir%\bin\ /XF *.pdb /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\msvc*.* %copy_dir%\bin\ /XF *.pdb /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\ucrt*.* %copy_dir%\bin\ /XF *.pdb /MT:%NUMBER_OF_PROCESSORS% > nul
REM Copy meaningful executables
robocopy %conda_env%\Library\bin %copy_dir%\bin\ ccx.exe /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Library\bin %copy_dir%\bin\ gmsh.exe /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Library\bin %copy_dir%\bin\ dot.exe /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Library\bin %copy_dir%\bin\ unflatten.exe /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Library\mingw-w64\bin * %copy_dir%\bin\ /MT:%NUMBER_OF_PROCESSORS% > nul
REM Copy Conda's QT5/plugins to FreeCAD/bin
robocopy %conda_env%\Library\plugins %copy_dir%\bin\ /S /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Library\resources %copy_dir%\resources /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Library\translations %copy_dir%\translations /MT:%NUMBER_OF_PROCESSORS% > nul
REM get all the dependency .dlls
robocopy %conda_env%\Library\bin *.dll %copy_dir%\bin /XF *.pdb /XF api*.* /MT:%NUMBER_OF_PROCESSORS% > nul
REM Copy FreeCAD build
robocopy %conda_env%\Library\bin FreeCAD* %copy_dir%\bin /XF *.pdb /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Library\data %copy_dir%\data /XF *.txt /S /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Library\Ext %copy_dir%\Ext /S /XD __pycache__ /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Library\lib %copy_dir%\lib /XF *.lib /XF *.prl /XF *.sh /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Library\Mod %copy_dir%\Mod /S /XD __pycache__ /MT:%NUMBER_OF_PROCESSORS% > nul
robocopy %conda_env%\Library\doc %copy_dir%\doc ThirdPartyLibraries.html LICENSE.html /MT:%NUMBER_OF_PROCESSORS% > nul
REM Apply Patches
rename %copy_dir%\bin\Lib\ssl.py ssl-orig.py
copy ssl-patch.py %copy_dir%\bin\Lib\ssl.py
cd %copy_dir%\..
ren %copy_dir% %freecad_version_name%
dir
REM if errorlevel1 exit 1
"%ProgramFiles%\7-Zip\7z.exe" a -t7z -mx9 -mmt=%NUMBER_OF_PROCESSORS% %freecad_version_name%.7z %freecad_version_name%\ -bb
certutil -hashfile "%freecad_version_name%.7z" SHA256 > "%freecad_version_name%.7z"-SHA256.txt
echo %date%-%time% >>"%freecad_version_name%.7z"-SHA256.txt
+45 -21
View File
@@ -3,23 +3,9 @@
set -e
set -x
conda_env="fc_env"
conda_env="$(pwd)/../.pixi/envs/default/"
mkdir -p ${conda_env}
cp -a ../.pixi/envs/default/* ${conda_env}
export PATH="${PWD}/${conda_env}/bin:${PATH}"
export CONDA_PREFIX="${PWD}/${conda_env}"
# remove arm binaries that fail to extract unless using latest 7zip
rm $(find ${conda_env} -name \*arm\*.exe)
# delete unnecessary stuff
rm -rf ${conda_env}/include
find ${conda_env} -name \*.a -delete
copy_dir="FreeCAD_Conda_Build"
copy_dir="FreeCAD_Windows"
mkdir -p ${copy_dir}/bin
# Copy Conda's Python and (U)CRT to FreeCAD/bin
@@ -48,16 +34,28 @@ cp -a ${conda_env}/Library/lib ${copy_dir}/lib
cp -a ${conda_env}/Library/Mod ${copy_dir}/Mod
mkdir -p ${copy_dir}/doc
cp -a ${conda_env}/Library/doc/{ThirdPartyLibraries.html,LICENSE.html} ${copy_dir}/doc
rm -rf ${conda_env}/bin_tmp
# delete unnecessary stuff
find ${copy_dir} -name \*.a -delete
find ${copy_dir} -name \*.lib -delete
find ${copy_dir} -name \*arm\*.exe -delete # arm binaries that fail to extract unless using latest 7zip
# Apply Patches
mv ${copy_dir}/bin/Lib/ssl.py ssl-orig.py
mv ${copy_dir}/bin/Lib/ssl.py .ssl-orig.py
cp ssl-patch.py ${copy_dir}/bin/Lib/ssl.py
echo '[Paths]' >> ${copy_dir}/bin/qt6.conf
echo 'Prefix = ../lib/qt6' >> ${copy_dir}/bin/qt6.conf
python_version=$(python -c 'import platform; print("py" + platform.python_version_tuple()[0] + platform.python_version_tuple()[1])')
# convenient shortcuts to run the binaries
if [ -x /c/ProgramData/chocolatey/tools/shimgen.exe ]; then
pushd ${copy_dir}
/c/ProgramData/chocolatey/tools/shimgen.exe -p bin/freecadcmd.exe -i "$(pwd)/../../../WindowsInstaller/icons/FreeCAD.ico" -o "$(pwd)/FreeCADCmd.exe"
/c/ProgramData/chocolatey/tools/shimgen.exe --gui -p bin/freecad.exe -i "$(pwd)/../../../WindowsInstaller/icons/FreeCAD.ico" -o "$(pwd)/FreeCAD.exe"
popd
fi
python_version=$("${copy_dir}"/bin/python.exe -c 'import platform; print("py" + platform.python_version_tuple()[0] + platform.python_version_tuple()[1])')
version_name="FreeCAD_${BUILD_TAG}-Windows-$(uname -m)-${python_version}"
echo -e "################"
@@ -69,11 +67,37 @@ sed -i '1s/.*/\nLIST OF PACKAGES:/' ${copy_dir}/packages.txt
mv ${copy_dir} ${version_name}
"${PROGRAMFILES}/7-Zip/7z.exe" a -t7z -mx9 -mmt=${NUMBER_OF_PROCESSORS} ${version_name}.7z ${version_name} -bb
7z a -t7z -mx9 -mmt=${NUMBER_OF_PROCESSORS} ${version_name}.7z ${version_name} -bb
# create hash
sha256sum ${version_name}.7z > ${version_name}.7z-SHA256.txt
if [ "${MAKE_INSTALLER}" == "true" ]; then
FILES_FREECAD="$(cygpath -w $(pwd))\\${version_name}"
nsis_cpdir=$(pwd)/.nsis_tmp
cp -r "${CONDA_PREFIX}/NSIS" "${nsis_cpdir}"
# curl -L -o ".nsis-log.zip" http://prdownloads.sourceforge.net/nsis/nsis-3.11-log.zip # we use the log variant of the package already
# curl -L -o ".nsis-strlen_8192.zip" "http://prdownloads.sourceforge.net/nsis/nsis-3.11-strlen_8192.zip"
curl -L -o ".NsProcess.7z" "https://nsis.sourceforge.io/mediawiki/images/1/18/NsProcess.zip"
if [ ! $(echo fc19fc66a5219a233570fafd5daeb0c9b85387b379f6df5ac8898159a57c5944 .NsProcess.7z | sha256sum --check --status) ]; then
7z x .NsProcess.7z -o"${nsis_cpdir}" -y
mv "${nsis_cpdir}"/Plugin/nsProcess.dll "${nsis_cpdir}"/Plugins/x86-ansi/nsProcess.dll
mv "${nsis_cpdir}"/Plugin/nsProcessW.dll "${nsis_cpdir}"/Plugins/x86-unicode/nsProcess.dll
"${nsis_cpdir}"/makensis.exe -V4 \
-D"ExeFile=${version_name}-installer.exe" \
-D"FILES_FREECAD=${FILES_FREECAD}" \
-X'SetCompressor /FINAL lzma' \
../../WindowsInstaller/FreeCAD-installer.nsi
mv ../../WindowsInstaller/${version_name}-installer.exe .
sha256sum ${version_name}-installer.exe > ${version_name}-installer.exe-SHA256.txt
else
echo "Error: Failed to get NsProcess plugin. Aborting installer creation..."
fi
rm -rf "${nsis_cpdir}"
fi
if [ "${UPLOAD_RELEASE}" == "true" ]; then
gh release upload --clobber ${BUILD_TAG} "${version_name}.7z" "${version_name}.7z-SHA256.txt"
if [ "${MAKE_INSTALLER}" == "true" ]; then
gh release upload --clobber ${BUILD_TAG} "${version_name}-installer.exe" "${version_name}-installer.exe-SHA256.txt"
fi
fi
@@ -83,8 +83,11 @@ fi
# Check for dmgbuild executable
if ! command -v dmgbuild &> /dev/null; then
echo "Error: dmgbuild not installed. Please install it for example using pip:"
echo 'pip3 install "dmgbuild[badge_icons]>=1.6.0,<1.7.0"'
echo 'Error: dmgbuild not installed. Please install it'
echo '- using pixi:'
echo 'pixi g install dmgbuild --with pyobjc-framework-Quartz'
echo '- using pip:'
echo 'pip3 install dmgbuild pyobjc-framework-Quartz'
exit 1
fi
@@ -119,8 +122,96 @@ run_codesign "${CONTAINING_FOLDER}/${APP_NAME}"
echo "Creating disk image ${DMG_NAME}"
dmgbuild -s ${DMG_SETTINGS} -Dcontaining_folder="${CONTAINING_FOLDER}" -Dapp_name="${APP_NAME}" "${VOLUME_NAME}" "${DMG_NAME}"
# Submit it for notarization (requires that an App Store API Key has been set up in the notarytool)
time xcrun notarytool submit --wait --keychain-profile "${KEYCHAIN_PROFILE}" "${DMG_NAME}"
ID_FILE="${DMG_NAME}.notarization_id"
# Assuming that notarization succeeded, it's a good practice to staple that notarization to the DMG
xcrun stapler staple "${DMG_NAME}"
# Submit it for notarization (requires that an App Store API Key has been set up in the notarytool)
# This is a *very slow* process, and occasionally the GitHub runners lose the internet connection for a short time
# during the run. So in order to be fault-tolerant, this script polls, instead of using --wait
submit_notarization_request() {
if [[ -s "${ID_FILE}" ]]; then
cat "${ID_FILE}"
return
fi
local out
if ! out=$(xcrun notarytool submit --keychain-profile "${KEYCHAIN_PROFILE}" \
--output-format json --no-progress "${DMG_NAME}" 2>&1); then
print -r -- "$out" >&2
return 1
fi
# We asked for JSON output so we had something stable, but of course parsing JSON with ZSH is ugly, so a quick bit of
# Python does it instead...
local id
id=$(print -r -- "$out" |
/usr/bin/python3 -c 'import sys, json; print(json.load(sys.stdin).get("id",""))'
)
[[ -n "$id" ]] || { print -r -- "Could not parse submission id" >&2; return 1; }
print -r -- "$id" > "${ID_FILE}"
print -r -- "$id" # ID is a string here, not an integer, so I can't just return it
}
wait_for_notarization_result() {
local id="$1" attempt=0
while :; do
if xcrun notarytool wait "$id" --keychain-profile "${KEYCHAIN_PROFILE}" \
--timeout 10m --no-progress >/dev/null; then
return 0
fi
(( attempt++ ))
# If the failure was transient (timeout/HTTP/connection) just retry, but make sure to check to see if the problem
# was actually that the signing failed before retrying.
local tmp_json
tmp_json=$(mktemp)
trap 'rm -f "$tmp_json"' EXIT INT TERM
xcrun notarytool info "$id" --keychain-profile "${KEYCHAIN_PROFILE}" --output-format json 2>/dev/null > "$tmp_json"
/usr/bin/python3 - "$tmp_json" <<'PY'
import sys, json
try:
with open(sys.argv[1]) as f:
s = (json.load(f).get("status") or "").lower()
if s in ("invalid", "rejected"):
sys.exit(2)
else:
sys.exit(0)
except Exception:
sys.exit(1)
PY
rc=$?
rm -f "$tmp_json"
if [[ $rc == 2 ]]; then
print -r -- "Notarization was not accepted by Apple:" >&2
xcrun notarytool log "$id" --keychain-profile "${KEYCHAIN_PROFILE}" >&2
return 3
fi
if [[ $attempt -gt 120 ]]; then
print -r -- "🏳️ Notarization is taking too long, bailing out. 🏳️" >&2
return 4
fi
sleep $(( (attempt<6?2**attempt:60) + RANDOM%5 )) # Increasing timeout plus jitter for multi-run safety
done
}
if ! id="$(submit_notarization_request)"; then
print -r -- "❌ Failed to submit notarization request" >&2
exit 1
fi
if [[ -z "$id" ]]; then
print -r -- "❌ Submission succeeded but no ID was returned" >&2
exit 1
fi
print "Notarization submission ID: $id"
if wait_for_notarization_result "$id"; then
print "✅ Notarization succeeded. Stapling..."
xcrun stapler staple "${DMG_NAME}"
print "Stapled: ${DMG_NAME}"
rm -f "${ID_FILE}"
else
rc=$?
print "❌ Notarization failed (code $rc)." >&2
exit "$rc"
fi
@@ -11,12 +11,10 @@ import SubWCRev
gitInfo = SubWCRev.GitControl()
gitInfo.extractInfo("","")
gitDescription = os.environ['BUILD_TAG']
i = open("src/Build/Version.h.cmake")
content = []
for line in i.readlines():
line = line.replace("-${PACKAGE_VERSION_SUFFIX}",gitDescription)
line = line.replace("${PACKAGE_WCREF}",gitInfo.rev)
line = line.replace("${PACKAGE_WCDATE}",gitInfo.date)
line = line.replace("${PACKAGE_WCURL}",gitInfo.url)
@@ -28,6 +26,14 @@ with open("src/Build/Version.h.cmake", "w") as o:
content.append('#define FCRepositoryBranch "%s"\n' % (gitInfo.branch))
o.writelines(content)
with open("src/Tools/SubWCRev.py", "r") as f:
new_subwcrev = f.read()
new_subwcrev = new_subwcrev.replace("lines = i.writeVersion(lines)",
"#lines = i.writeVersion(lines) # this source package already has git info, we do nothing here")
with open("src/Tools/SubWCRev.py", "w") as f:
f.writelines(new_subwcrev)
with open(os.sys.argv[1], "w") as f:
f.write(f"rev_number: {gitInfo.rev}\n")
f.write(f"branch_name: {gitInfo.branch}\n")
@@ -35,8 +41,9 @@ with open(os.sys.argv[1], "w") as f:
f.write(f"commit_hash: {gitInfo.hash}\n")
f.write(f"remote_url: {gitInfo.url}\n")
p = subprocess.Popen(["git", "-c", "user.name='github-actions[bot]'", "-c", "user.email='41898282+github-actions[bot]@users.noreply.github.com'",
"commit", "-a", "-m", "add git information"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p = subprocess.Popen(["git", "-c", "user.name='github-actions[bot]'", "-c",
"user.email='41898282+github-actions[bot]@users.noreply.github.com'", "commit", "-a", "-m",
"add git version information"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate()
+10 -10
View File
@@ -152,24 +152,24 @@ freecad-stubs = "*"
## Qt 6 Configuration Presets
[target.linux-64.tasks]
configure-debug = { cmd = [ "cmake", "--preset", "conda-linux-debug", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
configure-release = { cmd = [ "cmake", "--preset", "conda-linux-release", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
configure-debug = { cmd = [ "cmake", "--preset", "conda-linux-debug" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
configure-release = { cmd = [ "cmake", "--preset", "conda-linux-release" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
[target.linux-aarch64.tasks]
configure-debug = { cmd = [ "cmake", "--preset", "conda-linux-debug", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on= ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
configure-release = { cmd = [ "cmake", "--preset", "conda-linux-release", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on= ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
configure-debug = { cmd = [ "cmake", "--preset", "conda-linux-debug" ], depends-on= ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
configure-release = { cmd = [ "cmake", "--preset", "conda-linux-release" ], depends-on= ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
[target.osx-64.tasks]
configure-debug = { cmd = [ "cmake", "--preset", "conda-macos-debug", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
configure-release = { cmd = [ "cmake", "--preset", "conda-macos-release", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
configure-debug = { cmd = [ "cmake", "--preset", "conda-macos-debug" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
configure-release = { cmd = [ "cmake", "--preset", "conda-macos-release" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
[target.osx-arm64.tasks]
configure-debug = { cmd = [ "cmake", "--preset", "conda-macos-debug", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
configure-release = { cmd = [ "cmake", "--preset", "conda-macos-release", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
configure-debug = { cmd = [ "cmake", "--preset", "conda-macos-debug" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
configure-release = { cmd = [ "cmake", "--preset", "conda-macos-release" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
[target.win-64.tasks]
configure-debug = { cmd = [ "cmake", "--preset", "conda-windows-debug", "-DBUILD_REVERSEENGINEERING=OFF", "-DCMAKE_GENERATOR_PLATFORM=", "-DCMAKE_GENERATOR_TOOLSET=" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
configure-release = { cmd = [ "cmake", "--preset", "conda-windows-release", "-DBUILD_REVERSEENGINEERING=OFF", "-DCMAKE_GENERATOR_PLATFORM=", "-DCMAKE_GENERATOR_TOOLSET=" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
configure-debug = { cmd = [ "cmake", "--preset", "conda-windows-debug", "-DCMAKE_GENERATOR_PLATFORM=", "-DCMAKE_GENERATOR_TOOLSET=" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
configure-release = { cmd = [ "cmake", "--preset", "conda-windows-release", "-DCMAKE_GENERATOR_PLATFORM=", "-DCMAKE_GENERATOR_TOOLSET=" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
freecad-debug = { cmd = [ ".pixi/envs/default/Library/bin/FreeCAD.exe" ], depends-on = ["install-debug"]}
freecad-release = { cmd = [ ".pixi/envs/default/Library/bin/FreeCAD.exe" ], depends-on = ["install-release"]}
+44 -23
View File
@@ -31,31 +31,38 @@ namespace App
class Application;
/// Helper class to manager transaction (i.e. undo/redo)
/**
* @brief A helper class to manage transactions (i.e. undo/redo).
*
* An AutoTransaction object is meant to be allocated on the stack and governs
* the transactions in that scope.
*/
class AppExport AutoTransaction
{
public:
/// Private new operator to prevent heap allocation
/// Delete the new operator to prevent heap allocation.
void* operator new(std::size_t) = delete;
public:
/** Constructor
/**
* @brief Construct an auto transaction.
*
* @param name: optional new transaction name on construction
* @param tmpName: if true and a new transaction is setup, the name given is
* @param[in] name: optional new transaction name on construction
* @param[in] tmpName: if true and a new transaction is setup, the name given is
* considered as temporary, and subsequent construction of this class (or
* calling Application::setActiveTransaction()) can override the transaction
* name.
*
* The constructor increments an internal counter
* (Application::_activeTransactionGuard). The counter prevents any new
* active transaction being setup. It also prevents close (i.e. commits) the
* current active transaction until it reaches zero. It does not have any
* effect on aborting transaction, though.
* active transactions being setup. It also prevents to close
* (i.e. commits) the current active transaction until it reaches zero. It
* does not have any effect on aborting transactions though.
*/
AutoTransaction(const char* name = nullptr, bool tmpName = false);
/** Destructor
/**
* @brief Destruct an auto transaction.
*
* This destructor decrease an internal counter
* (Application::_activeTransactionGuard), and will commit any current
@@ -63,15 +70,19 @@ public:
*/
~AutoTransaction();
/** Close or abort the transaction
/**
* @brief Close or abort the transaction.
*
* This function can be used to explicitly close (i.e. commit) the
* transaction, if the current transaction ID matches the one created inside
* the constructor. For aborting, it will abort any current transaction
* the constructor. For aborting, it will abort any current transaction.
*
* @param[in] abort: if true, abort the transaction; otherwise, commit it.
*/
void close(bool abort = false);
/** Enable/Disable any AutoTransaction instance in the current stack
/**
* @brief Enable/Disable any AutoTransaction instance on the current stack.
*
* Once disabled, any empty temporary named transaction is closed. If there
* are non-empty or non-temporary named active transaction, it will not be
@@ -79,6 +90,8 @@ public:
*
* This function may be used in, for example, Gui::Document::setEdit() to
* allow a transaction live past any command scope.
*
* @param[in] enable: if true, enable the AutoTransaction; otherwise, disable it.
*/
static void setEnable(bool enable);
@@ -87,47 +100,55 @@ private:
};
/** Helper class to lock a transaction from being closed or aborted.
/**
* @brief Helper class to lock a transaction from being closed or aborted.
*
* The helper class is used to protect some critical transaction from being
* The helper class is used to protect some critical transactions from being
* closed prematurely, e.g. when deleting some object.
*/
class AppExport TransactionLocker
{
public:
/** Constructor
* @param lock: whether to activate the lock
/**
* @brief Construct a transaction locker.
*
* @param[in] lock: whether to activate the lock
*/
TransactionLocker(bool lock = true);
/** Destructor
* Unlock the transaction is this locker is active
/**
* @brief Destruct a transaction locker.
*
* Unlock the transaction if this locker is active
*/
~TransactionLocker();
/** Activate or deactivate this locker
* @param enable: whether to activate the locker
/**
* @brief Activate or deactivate this locker.
*
* An internal counter is used to support recursive locker. When activated,
* the current active transaction cannot be closed or aborted. But the
* closing call (Application::closeActiveTransaction()) will be remembered,
* and performed when the internal lock counter reaches zero.
*
* @param enable: whether to activate the locker
*/
void activate(bool enable);
/// Check if the locker is active
/// Check if the locker is active.
bool isActive() const
{
return active;
}
/// Check if transaction is being locked
/// Check if transaction is being locked.
static bool isLocked();
friend class Application;
public:
/// Private new operator to prevent heap allocation
/// Delete the new operator to prevent heap allocation.
void* operator new(std::size_t) = delete;
private:
+16
View File
@@ -1792,6 +1792,12 @@ bool Document::saveToFile(const char* filename) const
// realpath is canonical filename i.e. without symlink
std::string nativePath = canonical_path(filename);
// check if file is writeable, then block the save if it is not.
Base::FileInfo originalFileInfo(nativePath);
if (originalFileInfo.exists() && !originalFileInfo.isWritable()) {
throw Base::FileException("Unable to save document because file is marked as read-only or write permission is not available.", originalFileInfo);
}
// make a tmp. file where to save the project data first and then rename to
// the actual file name. This may be useful if overwriting an existing file
// fails so that the data of the work up to now isn't lost.
@@ -2086,6 +2092,16 @@ bool Document::afterRestore(const std::vector<DocumentObject*>& objArray, bool c
FC_ERR("Failed to restore " << obj->getFullName() << ": " << e.what());
}
catch (...) {
// If a Python exception occurred, it must be cleared immediately.
// Otherwise, the interpreter remains in a dirty state, causing
// Segfaults later when FreeCAD interacts with Python.
if (PyErr_Occurred()) {
Base::Console().error("Python error during object restore:\n");
PyErr_Print(); // Print the traceback to stderr/Console
PyErr_Clear(); // Reset the interpreter state
}
d->addRecomputeLog("Unknown exception on restore", obj);
FC_ERR("Failed to restore " << obj->getFullName() << ": " << "unknown exception");
}
+1 -1
View File
@@ -1,5 +1,5 @@
import sys
from os import sys, path
from os import path
# Actual code generation is done in Base/param_utils.py.
+5 -4
View File
@@ -140,16 +140,17 @@ public:
bool operator==(const Material& m) const
{
// clang-format off
return _matType == m._matType
&& shininess == m.shininess
if (!uuid.empty() && uuid == m.uuid) {
return true;
}
return shininess == m.shininess
&& transparency == m.transparency
&& ambientColor == m.ambientColor
&& diffuseColor == m.diffuseColor
&& specularColor == m.specularColor
&& emissiveColor == m.emissiveColor
&& image == m.image
&& image == m.imagePath
&& uuid == m.uuid;
&& imagePath == m.imagePath;
// clang-format on
}
bool operator!=(const Material& m) const
+47
View File
@@ -35,6 +35,7 @@
#include <Base/Console.h>
#include <Base/Exception.h>
#include <Base/Interpreter.h>
#include <Base/ProgramVersion.h>
#include <Base/Reader.h>
#include <Base/Writer.h>
#include <Base/Quantity.h>
@@ -2226,6 +2227,25 @@ unsigned int PropertyBoolList::getMemSize() const
// PropertyColor
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
namespace
{
/// The definition of "alpha" was corrected in FreeCAD 1.1 -- returns true if the reader is working
/// on a file that pre-dates that correction.
bool readerRequiresAlphaConversion(const Base::XMLReader &reader)
{
return Base::getVersion(reader.ProgramVersion) < Base::Version::v1_1;
}
/// Given a material, invert the alpha channel of all of its colors.
void convertAlphaInMaterial(App::Material& material)
{
material.ambientColor.a = 1.0F - material.ambientColor.a;
material.diffuseColor.a = 1.0F - material.diffuseColor.a;
material.specularColor.a = 1.0F - material.specularColor.a;
material.emissiveColor.a = 1.0F - material.emissiveColor.a;
}
}
TYPESYSTEM_SOURCE(App::PropertyColor, App::Property)
//**************************************************************************
@@ -2367,6 +2387,12 @@ void PropertyColor::Restore(Base::XMLReader& reader)
reader.readElement("PropertyColor");
// get the value of my Attribute
unsigned long rgba = reader.getAttribute<unsigned long>("value");
if (readerRequiresAlphaConversion(reader)) {
// Convert transparency / alpha value
constexpr unsigned long alphaMax = 0xff;
unsigned long alpha = alphaMax - (rgba & alphaMax);
rgba = rgba - (rgba & alphaMax) + alpha;
}
setValue(rgba);
}
@@ -2448,6 +2474,8 @@ void PropertyColorList::Restore(Base::XMLReader& reader)
// initiate a file read
reader.addFile(file.c_str(), this);
}
requiresAlphaConversion = readerRequiresAlphaConversion(reader);
}
}
@@ -2472,6 +2500,11 @@ void PropertyColorList::RestoreDocFile(Base::Reader& reader)
str >> value;
it.setPackedValue(value);
}
if (requiresAlphaConversion) {
for (auto& it : values) {
it.a = 1.0F - it.a;
}
}
setValues(values);
}
@@ -2702,6 +2735,9 @@ void PropertyMaterial::Restore(Base::XMLReader& reader)
_cMat.emissiveColor.setPackedValue(reader.getAttribute<unsigned long>("emissiveColor"));
_cMat.shininess = (float)reader.getAttribute<double>("shininess");
_cMat.transparency = (float)reader.getAttribute<double>("transparency");
if (readerRequiresAlphaConversion(reader)) {
convertAlphaInMaterial(_cMat);
}
if (reader.hasAttribute("image")) {
_cMat.image = reader.getAttribute<const char*>("image");
}
@@ -3247,6 +3283,8 @@ void PropertyMaterialList::Restore(Base::XMLReader& reader)
// initiate a file read
reader.addFile(file.c_str(), this);
}
requiresAlphaConversion = readerRequiresAlphaConversion(reader);
}
}
@@ -3330,6 +3368,7 @@ void PropertyMaterialList::RestoreDocFileV0(uint32_t count, Base::Reader& reader
str >> valueF;
it.transparency = valueF;
}
convertAlpha(values);
setValues(values);
}
@@ -3360,9 +3399,17 @@ void PropertyMaterialList::RestoreDocFileV3(Base::Reader& reader)
readString(str, it.imagePath);
readString(str, it.uuid);
}
convertAlpha(values);
setValues(values);
}
void PropertyMaterialList::convertAlpha(std::vector<App::Material>& materials) const
{
if (requiresAlphaConversion) {
std::ranges::for_each(materials, convertAlphaInMaterial);
}
}
void PropertyMaterialList::readString(Base::InputStream& str, std::string& value)
{
uint32_t uCt {};
+5
View File
@@ -1091,6 +1091,9 @@ public:
protected:
Base::Color getPyValue(PyObject* py) const override;
private:
bool requiresAlphaConversion {false}; // In 1.1 the handling of alpha was inverted
};
@@ -1294,8 +1297,10 @@ private:
void verifyIndex(int index) const;
void setMinimumSizeOne();
int resizeByOneIfNeeded(int index);
void convertAlpha(std::vector<App::Material>& materials) const;
Format formatVersion {Version_0};
bool requiresAlphaConversion {false}; // In 1.1 the handling of alpha was inverted
};
+22 -2
View File
@@ -32,20 +32,40 @@ namespace App
class Document;
class TransactionObject;
/** Base class of transactional objects
/**
* @brief Base class of transactional objects.
*
* A transactional object provides functionality that is inherited by its
* children to ensure that these children objects can be targeted by a
* transaction.
*/
class AppExport TransactionalObject: public App::ExtensionContainer
{
PROPERTY_HEADER_WITH_OVERRIDE(App::TransactionalObject);
public:
/// Constructor
TransactionalObject();
~TransactionalObject() override;
/// Check if this object is attached to a document.
virtual bool isAttachedToDocument() const;
/**
* @brief Deteach this object from the document.
*
* @return the name of the document this object was attached to.
*/
virtual const char* detachFromDocument();
protected:
/**
* @brief Notify the document that a property is about to be changed.
*
* This function is called before the property is changed.
*
* @param[in,out] doc the document this object is attached to.
* @param[in] prop the property that is about to be changed.
*/
void onBeforeChangeProperty(Document* doc, const Property* prop);
};
+1 -13
View File
@@ -52,10 +52,6 @@ Transaction::Transaction(int id)
transID = id;
}
/**
* A destructor.
* A more elaborate description of the destructor.
*/
Transaction::~Transaction()
{
auto& index = _Objects.get<0>();
@@ -291,16 +287,8 @@ TYPESYSTEM_SOURCE_ABSTRACT(App::TransactionObject, Base::Persistence)
//**************************************************************************
// Construction/Destruction
/**
* A constructor.
* A more elaborate description of the constructor.
*/
TransactionObject::TransactionObject() = default;
/**
* A destructor.
* A more elaborate description of the destructor.
*/
TransactionObject::~TransactionObject()
{
for (auto& v : _PropChangeMap) {
@@ -344,7 +332,7 @@ void TransactionObject::applyChn(Document& /*Doc*/, TransactionalObject* pcObj,
}
// getPropertyName() is specially coded to be safe even if prop has
// been destroies. We must prepare for the case where user removed
// been destroyed. We must prepare for the case where user removed
// a dynamic property but does not recordered as transaction.
auto name = pcObj->getPropertyName(prop);
if (!name || (!data.name.empty() && data.name != name)
+164 -32
View File
@@ -40,52 +40,105 @@ class TransactionObject;
class TransactionalObject;
/** Represents a atomic transaction of the document
/**
* @brief A class that represents an atomic transaction of the document.
*
* A transaction can contain multiple actions. These actions are represented
* by TransactionObject objects. Actions can be on objects: adding one,
* removing one, or changing one. It can also be on properties: adding one or
* removing one.
*/
class AppExport Transaction: public Base::Persistence
{
TYPESYSTEM_HEADER_WITH_OVERRIDE();
public:
/** Construction
/**
* @brief Construct a transaction.
*
* @param id: transaction id. If zero, then it will be generated
* @param[in] id: The transaction id. If zero, then it will be generated
* automatically as a monotonically increasing index across the entire
* application. User can pass in a transaction id to group multiple
* transactions from different document, so that they can be undo/redo
* application. Users can pass in a transaction id to group multiple
* transactions from different document, so that they can be undone/redone
* together.
*/
explicit Transaction(int id = 0);
/// Construction
~Transaction() override;
/// apply the content to the document
/**
* @brief Apply the content of this transaction to the document.
*
* @param[in] Doc The document to apply the transaction to.
* @param[in] forward If true, apply the transaction; otherwise, undo it.
*/
void apply(Document& Doc, bool forward);
// the utf-8 name of the transaction
/// The UTF-8 name of the transaction
std::string Name;
unsigned int getMemSize() const override;
void Save(Base::Writer& writer) const override;
/// This method is used to restore properties from an XML document.
void Restore(Base::XMLReader& reader) override;
/// Return the transaction ID
/// Get the transaction ID of this transaction.
int getID() const;
/// Generate a new unique transaction ID
/// Generate a new unique transaction ID.
static int getNewID();
/// Get the last transaction ID.
static int getLastID();
/// Returns true if the transaction list is empty; otherwise returns false.
/// Check if the transaction list is empty.
bool isEmpty() const;
/// check if this object is used in a transaction
/**
* @brief Check if this object is used in a transaction.
*
* @param[in] Obj The object to check.
* @return true if the object is used in a transaction; otherwise false.
*/
bool hasObject(const TransactionalObject* Obj) const;
/**
* @brief Record renaming a property.
*
* @param[in] Obj The object of which the property to rename.
* @param[in] pcProp The property with a new name.
* @param[in] oldName The old name of the property.
*/
void renameProperty(TransactionalObject* Obj, const Property* pcProp, const char* oldName);
/**
* @brief Record adding or removing a property from an object.
*
* @param[in] Obj The object to add or remove the property from.
* @param[in] pcProp The property to add or remove.
* @param[in] add If true, add the property; otherwise, remove it.
*/
void addOrRemoveProperty(TransactionalObject* Obj, const Property* pcProp, bool add);
/**
* @brief Record adding a new object to the transaction.
*
* @param[in] Obj The object to add.
*/
void addObjectNew(TransactionalObject* Obj);
/**
* @brief Record removing an object from the transaction.
*
* @param[in] Obj The object to remove.
*/
void addObjectDel(const TransactionalObject* Obj);
/**
*@brief Record changing an object in the transaction.
*
* @param[in] Obj The object to change.
* @param[in] Prop The property that is changed.
*/
void addObjectChange(const TransactionalObject* Obj, const Property* Prop);
private:
@@ -103,41 +156,87 @@ private:
_Objects;
};
/** Represents an entry for an object in a Transaction
/**
* @brief Class that represents an entry for an object in a Transaction.
*
* This class is used to store the information about the object and its
* properties. It should not be confused with the TransactionalObject class
* that is a base class that contains functionality for a DocumentObject
* regarding transactions.
*/
class AppExport TransactionObject: public Base::Persistence
{
TYPESYSTEM_HEADER_WITH_OVERRIDE();
public:
/// Construction
TransactionObject();
/// Destruction
~TransactionObject() override;
virtual void applyNew(Document& Doc, TransactionalObject* pcObj);
virtual void applyDel(Document& Doc, TransactionalObject* pcObj);
virtual void applyChn(Document& Doc, TransactionalObject* pcObj, bool Forward);
/**
* @brief Apply the transaction that adds a new object to the document.
*
* @param[in,out] doc The document to apply the transaction to.
* @param[in,out] obj The object that is added in the transaction.
*/
virtual void applyNew(Document& doc, TransactionalObject* obj);
void setProperty(const Property* pcProp);
void renameProperty(const Property* pcProp, const char* newName);
void addOrRemoveProperty(const Property* pcProp, bool add);
/**
* @brief Apply the transaction that removes an object from the document.
*
* @param[in,out] doc The document to apply the transaction to.
* @param[in,out] obj The object that is removed in the transaction.
*/
virtual void applyDel(Document& doc, TransactionalObject* obj);
/**
* @brief Apply the transaction that changes an object in the document.
*
* @param[in,out] doc The document to apply the transaction to.
* @param[in,out] obj The object that is changed in the transaction.
* @param[in] forward If true, apply the transaction; otherwise, undo it.
*/
virtual void applyChn(Document& doc, TransactionalObject* obj, bool forward);
/**
* @brief Set the property of the object that is affected by the transaction.
*
* @param[in] prop The property that is affected by the transaction.
*/
void setProperty(const Property* prop);
/**
* @brief Rename a property.
*
* @param[in] pcProp The property with the new name.
* @param[in] oldName The old name of the property.
*/
void renameProperty(const Property* pcProp, const char* oldName);
/**
* @brief Add or remove a property from the object.
*
* @param[in] prop The property to add or remove.
* @param[in] add If true, add the property; otherwise, remove it.
*/
void addOrRemoveProperty(const Property* prop, bool add);
unsigned int getMemSize() const override;
void Save(Base::Writer& writer) const override;
/// This method is used to restore properties from an XML document.
void Restore(Base::XMLReader& reader) override;
friend class Transaction;
protected:
/// The status of the transaction object.
enum Status
{
New,
Del,
Chn
New, ///< A new object is added to the document.
Del, ///< An object is deleted from the document.
Chn ///< An object is changed in the document.
} status {New};
/// Struct to maintain property information.
struct PropData: DynamicProperty::PropData
{
Base::Type propertyType;
@@ -145,34 +244,59 @@ protected:
// for property renaming
std::string nameOrig;
};
/// A map to maintain the properties of the object.
std::unordered_map<int64_t, PropData> _PropChangeMap;
/// The name of the object in the document.
std::string _NameInDocument;
};
/** Represents an entry for a document object in a transaction
/**
* @brief Class that represents an entry for a document object in a transaction.
*/
class AppExport TransactionDocumentObject: public TransactionObject
{
TYPESYSTEM_HEADER_WITH_OVERRIDE();
public:
/// Construction
TransactionDocumentObject();
/// Destruction
~TransactionDocumentObject() override;
void applyNew(Document& Doc, TransactionalObject* pcObj) override;
void applyDel(Document& Doc, TransactionalObject* pcObj) override;
};
/**
* @brief Class that represents a factory for creating transaction objects.
*
* This class is used to create transaction objects for different types of
* transactions.
*/
class AppExport TransactionFactory
{
public:
/// Get the singleton instance of the TransactionFactory.
static TransactionFactory& instance();
/// Destruct the singleton instance of the TransactionFactory.
static void destruct();
/**
* @brief Create a transaction object for the given type.
*
* @param[in] type The type of the transaction object to create.
* @return A pointer to the created transaction object.
*/
TransactionObject* createTransaction(const Base::Type& type) const;
/**
* @brief Add a producer for a transaction object.
*
* @param[in] type The type id of the transaction object.
* @param[in,out] producer The producer to add.
*/
void addProducer(const Base::Type& type, Base::AbstractProducer* producer);
private:
@@ -183,10 +307,20 @@ private:
~TransactionFactory() = default;
};
/**
* @brief Class that represents a producer for creating transaction objects.
*
* @tparam CLASS The class of the transaction object to produce.
*/
template<class CLASS>
class TransactionProducer: public Base::AbstractProducer
{
public:
/**
* @brief Construct a transaction producer.
*
* @param[in] type The type of the transaction object.
*/
explicit TransactionProducer(const Base::Type& type)
{
TransactionFactory::instance().addProducer(type, this);
@@ -194,9 +328,7 @@ public:
~TransactionProducer() override = default;
/**
* Creates an instance of the specified transaction object.
*/
/// Creates an instance of the specified transaction object.
void* Produce() const override
{
return (new CLASS);
+38
View File
@@ -336,6 +336,44 @@ bool FileInfo::isWritable() const
if (fs::is_directory(path)) {
return directoryIsWritable(path);
}
#ifdef FC_OS_WIN32
// convert filename from UTF-8 to windows WSTRING
std::wstring fileNameWstring = toStdWString();
// requires import of <windows.h>
DWORD attributes = GetFileAttributes(fileNameWstring.c_str());
if (attributes == INVALID_FILE_ATTRIBUTES) {
// Log the error?
std::clog << "GetFileAttributes failed for file: " << FileName << '\n';
// usually indicates some kind of network file issue, so the file is probably not writable
return false;
}
if ((attributes & FILE_ATTRIBUTE_READONLY) != 0) {
return false;
}
// TEST if file is truly writable, because windows ACL does not map well to POSIX perms,
// and there are other potential blockers (app or shared file locks, etc)
HANDLE hFile = CreateFileW(
fileNameWstring.c_str(),
GENERIC_WRITE,
0, // ----> no sharing: fail if anyone else has it open
nullptr,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
nullptr
);
if (hFile == INVALID_HANDLE_VALUE) {
DWORD err = GetLastError();
if (err == ERROR_SHARING_VIOLATION || err == ERROR_LOCK_VIOLATION) {
return false;
}
return false;
}
if (!CloseHandle(hFile)) {
std::clog << "CloseHandle failed for file: " << FileName
<< " while checking for write access." << '\n';
}
#endif
fs::file_status stat = fs::status(path);
fs::perms perms = stat.permissions();
return (perms & fs::perms::owner_write) == fs::perms::owner_write;
+84
View File
@@ -0,0 +1,84 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/***************************************************************************
* Copyright (c) 2024 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD 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 *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#ifndef BASE_PROGRAM_VERSION_H
#define BASE_PROGRAM_VERSION_H
#include <algorithm>
#include <cstdint>
#include <string_view>
#include <FCGlobal.h>
namespace Base
{
enum class Version : std::uint8_t
{
v0_1x,
v0_16,
v0_17,
v0_18,
v0_19,
v0_20,
v0_21,
v0_22,
v1_0,
v1_1,
v1_x,
};
inline Version getVersion(std::string_view str)
{
// clang-format off
struct VersionItem
{
std::string_view name;
Version version;
};
static const std::initializer_list<VersionItem> items = {
{.name="0.16", .version=Version::v0_16},
{.name="0.17", .version=Version::v0_17},
{.name="0.18", .version=Version::v0_18},
{.name="0.19", .version=Version::v0_19},
{.name="0.20", .version=Version::v0_20},
{.name="0.21", .version=Version::v0_21},
{.name="0.22", .version=Version::v0_22},
{.name="1.0" , .version=Version::v1_0 },
{.name="1.1" , .version=Version::v1_1 },
};
// clang-format on
auto it = std::ranges::find_if(items, [str](const auto& item) {
return str.compare(0, item.name.size(), item.name) == 0;
});
if (it != items.end()) {
return it->version;
}
if (!str.empty() && str[0] == '0') {
return Version::v0_1x;
}
return Version::v1_x;
}
} // namespace Base
#endif // BASE_PROGRAM_VERSION_H
-4
View File
@@ -65,11 +65,7 @@ using namespace std;
Base::XMLReader::XMLReader(const char* FileName, std::istream& str)
: _File(FileName)
{
#ifdef _MSC_VER
str.imbue(std::locale::empty());
#else
str.imbue(std::locale::classic());
#endif
// create the parser
parser = XMLReaderFactory::createXMLReader(); // NOLINT
-8
View File
@@ -318,11 +318,7 @@ void Writer::putNextEntry(const char* file, const char* obj)
ZipWriter::ZipWriter(const char* FileName)
: ZipStream(FileName)
{
#ifdef _MSC_VER
ZipStream.imbue(std::locale::empty());
#else
ZipStream.imbue(std::locale::classic());
#endif
ZipStream.precision(std::numeric_limits<double>::digits10 + 1);
ZipStream.setf(std::ios::fixed, std::ios::floatfield);
}
@@ -330,11 +326,7 @@ ZipWriter::ZipWriter(const char* FileName)
ZipWriter::ZipWriter(std::ostream& os)
: ZipStream(os)
{
#ifdef _MSC_VER
ZipStream.imbue(std::locale::empty());
#else
ZipStream.imbue(std::locale::classic());
#endif
ZipStream.precision(std::numeric_limits<double>::digits10 + 1);
ZipStream.setf(std::ios::fixed, std::ios::floatfield);
}
+3 -1
View File
@@ -937,7 +937,9 @@ void RecentFilesAction::setFiles(const QStringList& files)
for (int index = 0; index < numRecentFiles; index++) {
QString numberLabel = numberToLabel(index + 1);
QFileInfo fi(files[index]);
recentFiles[index]->setText(QStringLiteral("%1 %2").arg(numberLabel).arg(fi.fileName()));
QString fileName {fi.fileName()};
fileName.replace(QLatin1Char('&'), QStringLiteral("&&"));
recentFiles[index]->setText(QStringLiteral("%1 %2").arg(numberLabel, fileName));
recentFiles[index]->setStatusTip(tr("Open file %1").arg(files[index]));
recentFiles[index]->setToolTip(files[index]); // set the full name that we need later for saving
recentFiles[index]->setData(QVariant(index));
+1 -1
View File
@@ -122,7 +122,7 @@ public:
const QString& header,
const QString& message,
bool check = false,
const QString& checkText = QStringLiteral("Don't show me again")
const QString& checkText = tr("Don't show me again")
);
/// Same as showMessage above, but it checks the specific preference path and parameter
+5 -5
View File
@@ -745,7 +745,7 @@ void DlgPropertyLink::onSelectionChanged(const Gui::SelectionChanges& msg)
ui->treeWidget->scrollToItem(item);
if (allowSubObject) {
QString element = QString::fromLatin1(msg.Object.getOldElementName().c_str());
QString element = QString::fromUtf8(msg.Object.getOldElementName().c_str());
if (element.size()) {
QStringList list;
QString text = item->text(1);
@@ -868,7 +868,7 @@ QString DlgPropertyLink::linksToPython(const QList<App::SubObjectT>& links)
}
if (links.size() == 1) {
return QString::fromLatin1(links.front().getSubObjectPython(false).c_str());
return QString::fromUtf8(links.front().getSubObjectPython(false).c_str());
}
std::ostringstream ss;
@@ -891,7 +891,7 @@ QString DlgPropertyLink::linksToPython(const QList<App::SubObjectT>& links)
ss << ']';
}
return QString::fromLatin1(ss.str().c_str());
return QString::fromUtf8(ss.str().c_str());
}
void DlgPropertyLink::filterObjects()
@@ -1114,7 +1114,7 @@ QTreeWidgetItem* DlgPropertyLink::createTypeItem(Base::Type type)
item = new QTreeWidgetItem(item);
}
item->setExpanded(true);
item->setText(0, QString::fromLatin1(type.getName()));
item->setText(0, QString::fromUtf8(type.getName()));
if (type == App::DocumentObject::getClassTypeId()) {
item->setFlags(Qt::ItemIsEnabled);
}
@@ -1129,7 +1129,7 @@ bool DlgPropertyLink::filterType(QTreeWidgetItem* item)
auto& pitem = typeItems[proxyType];
if (!pitem) {
pitem = new QTreeWidgetItem(ui->typeTree);
pitem->setText(0, QString::fromLatin1(proxyType));
pitem->setText(0, QString::fromUtf8(proxyType));
pitem->setIcon(0, item->icon(0));
pitem->setData(0, Qt::UserRole, proxyType);
}
+2 -2
View File
@@ -133,8 +133,8 @@ DlgVersionMigrator::DlgVersionMigrator(MainWindow* mw)
// NOTE: All rich-text strings are generated programmatically so that translators don't have to
// deal with the markup. The two strings in the middle of the dialog are set in the UI file.
auto programNameString = tr("Welcome to %1 %2.%3\n\n")
.arg(programName, QString::number(major), QString::number(minor));
auto programNameString
= tr("Welcome to %1 %2.%3").arg(programName, QString::number(major), QString::number(minor));
auto welcomeString = QStringLiteral("<b>") + programNameString + QStringLiteral("</b>");
auto calculatingSizeString = QStringLiteral("<b>") + tr("Calculating size…")
+3 -2
View File
@@ -1414,11 +1414,11 @@ static bool checkCanonicalPath(const std::map<App::Document*, bool>& docs)
auto docName = [](App::Document* doc) -> QString {
if (doc->Label.getStrValue() == doc->getName()) {
return QString::fromLatin1(doc->getName());
return QString::fromUtf8(doc->getName());
}
return QStringLiteral("%1 (%2)").arg(
QString::fromUtf8(doc->Label.getValue()),
QString::fromLatin1(doc->getName())
QString::fromUtf8(doc->getName())
);
};
int count = 0;
@@ -1823,6 +1823,7 @@ void Document::RestoreDocFile(Base::Reader& reader)
localreader->readElement("Document");
long scheme = localreader->getAttribute<long>("SchemaVersion");
localreader->DocumentSchema = scheme;
localreader->ProgramVersion = d->_pcDocument->getProgramVersion();
bool hasExpansion = localreader->hasAttribute("HasExpansion");
if (hasExpansion) {
+6 -7
View File
@@ -71,13 +71,12 @@ namespace sp = std::placeholders;
// taken from the script doctools.py
std::string DocumentRecovery::doctools
= "import os,sys,string\n"
"import xml.sax\n"
"import xml.sax.handler\n"
"import xml.sax.xmlreader\n"
"from defusedxml import sax as defused_sax\n"
"from xml.sax.handler import ContentHandler\n"
"import zipfile\n"
"\n"
"# SAX handler to parse the Document.xml\n"
"class DocumentHandler(xml.sax.handler.ContentHandler):\n"
"class DocumentHandler(ContentHandler):\n"
" def __init__(self, dirname):\n"
" self.files = []\n"
" self.dirname = dirname\n"
@@ -108,7 +107,7 @@ std::string DocumentRecovery::doctools
" for j in dirs:\n"
" curpath=curpath+\"/\"+j\n"
" os.mkdir(curpath)\n"
" output=open(outpath+\"/\"+i,\'wb\')\n"
" output=open(outpath+\"/\"+i,'wb')\n"
" output.write(data)\n"
" output.close()\n"
"\n"
@@ -118,7 +117,7 @@ std::string DocumentRecovery::doctools
" guixml=os.path.join(dirname,\"GuiDocument.xml\")\n"
" if os.path.exists(guixml):\n"
" files.extend(getFilesList(guixml))\n"
" compress=zipfile.ZipFile(outpath,\'w\',zipfile.ZIP_DEFLATED)\n"
" compress=zipfile.ZipFile(outpath,'w',zipfile.ZIP_DEFLATED)\n"
" for i in files:\n"
" dirs=os.path.split(i)\n"
" #print i, dirs[-1]\n"
@@ -128,7 +127,7 @@ std::string DocumentRecovery::doctools
"def getFilesList(filename):\n"
" dirname=os.path.dirname(filename)\n"
" handler=DocumentHandler(dirname)\n"
" parser=xml.sax.make_parser()\n"
" parser=defused_sax.make_parser()\n"
" parser.setContentHandler(handler)\n"
" parser.parse(filename)\n"
"\n"
+27 -14
View File
@@ -413,7 +413,7 @@ public:
res = QString::fromUtf8(quote(doc->Label.getStrValue()).c_str());
}
else {
res = QString::fromLatin1(doc->getName());
res = QString::fromUtf8(doc->getName());
}
if (sep) {
res += QLatin1Char('#');
@@ -1013,7 +1013,7 @@ void ExpressionLineEdit::slotTextChanged(const QString& text)
}
}
void ExpressionLineEdit::slotCompleteText(const QString& completionPrefix, bool isActivated)
void ExpressionLineEdit::slotCompleteText(const QString& completionPrefix, ActivationMode mode)
{
int start, end;
completer->getPrefixRange(start, end);
@@ -1030,7 +1030,7 @@ void ExpressionLineEdit::slotCompleteText(const QString& completionPrefix, bool
// chain completions if we select an entry from the completer drop down
// and that entry ends with '.' or '#'
if (isActivated) {
if (mode == ActivationMode::Activated) {
std::string textToComplete = completionPrefix.toUtf8().constData();
if (textToComplete.size()
&& (*textToComplete.crbegin() == '.' || *textToComplete.crbegin() == '#')) {
@@ -1042,12 +1042,12 @@ void ExpressionLineEdit::slotCompleteText(const QString& completionPrefix, bool
void ExpressionLineEdit::slotCompleteTextHighlighted(const QString& completionPrefix)
{
slotCompleteText(completionPrefix, false);
slotCompleteText(completionPrefix, ActivationMode::Highlighted);
}
void ExpressionLineEdit::slotCompleteTextSelected(const QString& completionPrefix)
{
slotCompleteText(completionPrefix, true);
slotCompleteText(completionPrefix, ActivationMode::Activated);
}
@@ -1116,13 +1116,13 @@ void ExpressionTextEdit::setDocumentObject(const App::DocumentObject* currentDoc
completer,
qOverload<const QString&>(&QCompleter::activated),
this,
&ExpressionTextEdit::slotCompleteText
&ExpressionTextEdit::slotCompleteTextSelected
);
connect(
completer,
qOverload<const QString&>(&QCompleter::highlighted),
this,
&ExpressionTextEdit::slotCompleteText
&ExpressionTextEdit::slotCompleteTextHighlighted
);
connect(this, &ExpressionTextEdit::textChanged2, completer, &ExpressionCompleter::slotUpdate);
connect(
@@ -1155,7 +1155,7 @@ void ExpressionTextEdit::slotTextChanged()
}
}
void ExpressionTextEdit::slotCompleteText(const QString& completionPrefix)
void ExpressionTextEdit::slotCompleteText(const QString& completionPrefix, ActivationMode mode)
{
QTextCursor cursor = textCursor();
int start, end;
@@ -1165,17 +1165,31 @@ void ExpressionTextEdit::slotCompleteText(const QString& completionPrefix)
cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, end - pos);
}
cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor, end - start);
Base::FlagToggler<bool> flag(block, false);
cursor.insertText(completionPrefix);
completer->updatePrefixEnd(cursor.positionInBlock());
std::string textToComplete = completionPrefix.toUtf8().constData();
if (textToComplete.size()
&& (*textToComplete.crbegin() == '.' || *textToComplete.crbegin() == '#')) {
completer->slotUpdate(cursor.block().text(), cursor.positionInBlock());
// chain completions only when activated (Enter/Click), not when highlighted (arrow keys)
if (mode == ActivationMode::Activated) {
std::string textToComplete = completionPrefix.toUtf8().constData();
if (!textToComplete.empty()
&& (*textToComplete.crbegin() == '.' || *textToComplete.crbegin() == '#')) {
completer->slotUpdate(cursor.block().text(), cursor.positionInBlock());
}
}
}
void ExpressionTextEdit::slotCompleteTextHighlighted(const QString& completionPrefix)
{
slotCompleteText(completionPrefix, ActivationMode::Highlighted);
}
void ExpressionTextEdit::slotCompleteTextSelected(const QString& completionPrefix)
{
slotCompleteText(completionPrefix, ActivationMode::Activated);
}
void ExpressionTextEdit::keyPressEvent(QKeyEvent* e)
{
Base::FlagToggler<bool> flag(block, true);
@@ -1208,9 +1222,8 @@ void ExpressionTextEdit::keyPressEvent(QKeyEvent* e)
if (!completer->popup()->currentIndex().isValid()) {
completer->popup()->setCurrentIndex(completer->popup()->model()->index(0, 0));
}
// insert completion
completer->setCurrentRow(completer->popup()->currentIndex().row());
slotCompleteText(completer->currentCompletion());
slotCompleteText(completer->currentCompletion(), ActivationMode::Highlighted);
// refresh completion list
completer->setCompletionPrefix(completer->currentCompletion());

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