diff --git a/pkg/freecad/InitGui.py b/pkg/freecad/InitGui.py
index 293569a..3a95a64 100644
--- a/pkg/freecad/InitGui.py
+++ b/pkg/freecad/InitGui.py
@@ -28,6 +28,8 @@ class SiloWorkbench(FreeCADGui.Workbench):
import silo_commands
self.toolbar_commands = [
+ "Silo_ToggleMode",
+ "Separator",
"Silo_Open",
"Silo_New",
"Silo_Save",
diff --git a/pkg/freecad/silo_commands.py b/pkg/freecad/silo_commands.py
index e27297a..1c18809 100644
--- a/pkg/freecad/silo_commands.py
+++ b/pkg/freecad/silo_commands.py
@@ -50,6 +50,16 @@ def _get_ssl_context() -> ssl.SSLContext:
"""Build an SSL context based on the current SSL verification preference."""
if _get_ssl_verify():
ctx = ssl.create_default_context()
+ # Load custom CA certificate if configured (for internal CAs)
+ param = FreeCAD.ParamGet(_PREF_GROUP)
+ custom_cert = param.GetString("SslCertPath", "")
+ if custom_cert and os.path.isfile(custom_cert):
+ try:
+ ctx.load_verify_locations(custom_cert)
+ except Exception as e:
+ FreeCAD.Console.PrintWarning(
+ f"Silo: Failed to load custom cert {custom_cert}: {e}\n"
+ )
# The bundled Python may not find the system CA store automatically
# (its compiled-in path points to the build environment). Load the
# system CA bundle explicitly so internal CAs (e.g. FreeIPA) are trusted.
@@ -1970,12 +1980,51 @@ class Silo_Settings:
ssl_hint.setStyleSheet("color: #888; font-size: 11px;")
layout.addWidget(ssl_hint)
+ layout.addSpacing(5)
+
+ # Custom CA certificate
+ cert_label = QtGui.QLabel("Custom CA certificate file:")
+ layout.addWidget(cert_label)
+
+ cert_row = QtGui.QHBoxLayout()
+ cert_input = QtGui.QLineEdit()
+ cert_input.setPlaceholderText("(Use system CA certificates)")
+ current_cert = param.GetString("SslCertPath", "")
+ if current_cert:
+ cert_input.setText(current_cert)
+ cert_browse = QtGui.QPushButton("Browse...")
+ cert_row.addWidget(cert_input)
+ cert_row.addWidget(cert_browse)
+ layout.addLayout(cert_row)
+
+ cert_hint = QtGui.QLabel(
+ "Path to a PEM/CRT file for internal CAs. "
+ "Leave empty for system certificates only."
+ )
+ cert_hint.setWordWrap(True)
+ cert_hint.setStyleSheet("color: #888; font-size: 11px;")
+ layout.addWidget(cert_hint)
+
+ def on_browse_cert():
+ path, _ = QtGui.QFileDialog.getOpenFileName(
+ dialog,
+ "Select CA Certificate",
+ os.path.dirname(cert_input.text()) or "/etc/ssl/certs",
+ "Certificates (*.pem *.crt *.cer);;All Files (*)",
+ )
+ if path:
+ cert_input.setText(path)
+
+ cert_browse.clicked.connect(on_browse_cert)
+
layout.addSpacing(10)
# Current effective values (read-only)
+ cert_display = param.GetString("SslCertPath", "") or "(system defaults)"
status_label = QtGui.QLabel(
f"Active URL: {_get_api_url()}
"
- f"SSL verification: {'enabled' if _get_ssl_verify() else 'disabled'}"
+ f"SSL verification: {'enabled' if _get_ssl_verify() else 'disabled'}
"
+ f"CA certificate: {cert_display}"
)
status_label.setTextFormat(QtCore.Qt.RichText)
layout.addWidget(status_label)
@@ -1995,9 +2044,12 @@ class Silo_Settings:
url = url_input.text().strip()
param.SetString("ApiUrl", url)
param.SetBool("SslVerify", ssl_checkbox.isChecked())
+ cert_path = cert_input.text().strip()
+ param.SetString("SslCertPath", cert_path)
FreeCAD.Console.PrintMessage(
f"Silo settings saved. URL: {_get_api_url()}, "
- f"SSL verify: {_get_ssl_verify()}\n"
+ f"SSL verify: {_get_ssl_verify()}, "
+ f"Cert: {cert_path or '(system)'}\n"
)
dialog.accept()
@@ -2383,6 +2435,85 @@ class Silo_BOM:
return FreeCAD.ActiveDocument is not None
+# ---------------------------------------------------------------------------
+# Silo Mode toggle - swap Ctrl+O/S/N between standard and Silo commands
+# ---------------------------------------------------------------------------
+
+# Stored original shortcuts so they can be restored on toggle-off
+_original_shortcuts: Dict[str, Any] = {}
+
+
+def _swap_shortcuts(mapping, enable_silo):
+ """Swap keyboard shortcuts between standard and Silo commands.
+
+ mapping: list of (std_cmd, silo_cmd, shortcut) tuples
+ enable_silo: True to assign shortcuts to Silo commands, False to restore.
+ """
+ from PySide import QtGui
+
+ mw = FreeCADGui.getMainWindow()
+ if mw is None:
+ return
+
+ for std_cmd, silo_cmd, shortcut in mapping:
+ if enable_silo:
+ # Save and clear the standard command's shortcut
+ std_action = mw.findChild(QtGui.QAction, std_cmd)
+ if std_action:
+ _original_shortcuts[std_cmd] = std_action.shortcut().toString()
+ std_action.setShortcut("")
+ # Assign the shortcut to the Silo command
+ silo_action = mw.findChild(QtGui.QAction, silo_cmd)
+ if silo_action:
+ silo_action.setShortcut(shortcut)
+ else:
+ # Clear the Silo command's shortcut
+ silo_action = mw.findChild(QtGui.QAction, silo_cmd)
+ if silo_action:
+ silo_action.setShortcut("")
+ # Restore the standard command's original shortcut
+ std_action = mw.findChild(QtGui.QAction, std_cmd)
+ if std_action and std_cmd in _original_shortcuts:
+ std_action.setShortcut(_original_shortcuts.pop(std_cmd))
+
+
+_SHORTCUT_MAP = [
+ ("Std_Open", "Silo_Open", "Ctrl+O"),
+ ("Std_Save", "Silo_Save", "Ctrl+S"),
+ ("Std_New", "Silo_New", "Ctrl+N"),
+]
+
+
+class Silo_ToggleMode:
+ """Toggle between standard file operations and Silo equivalents."""
+
+ def GetResources(self):
+ return {
+ "MenuText": "Silo Mode",
+ "ToolTip": (
+ "Toggle between standard file operations and Silo equivalents.\n"
+ "When ON: Ctrl+O/S/N use Silo Open/Save/New.\n"
+ "When OFF: Standard FreeCAD file operations."
+ ),
+ "Pixmap": _icon("silo"),
+ "Checkable": True,
+ }
+
+ def Activated(self, checked):
+ param = FreeCAD.ParamGet(_PREF_GROUP)
+ if checked:
+ _swap_shortcuts(_SHORTCUT_MAP, enable_silo=True)
+ param.SetBool("SiloMode", True)
+ FreeCAD.Console.PrintMessage("Silo mode enabled\n")
+ else:
+ _swap_shortcuts(_SHORTCUT_MAP, enable_silo=False)
+ param.SetBool("SiloMode", False)
+ FreeCAD.Console.PrintMessage("Silo mode disabled\n")
+
+ def IsActive(self):
+ return True
+
+
# Register commands
FreeCADGui.addCommand("Silo_Open", Silo_Open())
FreeCADGui.addCommand("Silo_New", Silo_New())
@@ -2396,3 +2527,4 @@ FreeCADGui.addCommand("Silo_TagProjects", Silo_TagProjects())
FreeCADGui.addCommand("Silo_Rollback", Silo_Rollback())
FreeCADGui.addCommand("Silo_SetStatus", Silo_SetStatus())
FreeCADGui.addCommand("Silo_Settings", Silo_Settings())
+FreeCADGui.addCommand("Silo_ToggleMode", Silo_ToggleMode())