diff --git a/src/Gui/Application.cpp b/src/Gui/Application.cpp
index ac7eda9c4d..3cb80e658f 100644
--- a/src/Gui/Application.cpp
+++ b/src/Gui/Application.cpp
@@ -1022,6 +1022,7 @@ void Application::createStandardOperations()
Gui::CreateStructureCommands();
Gui::CreateTestCommands();
Gui::CreateLinkCommands();
+ Gui::CreateOriginCommands();
}
void Application::slotNewDocument(const App::Document& Doc, bool isMainDoc)
diff --git a/src/Gui/CMakeLists.txt b/src/Gui/CMakeLists.txt
index c9519eed18..dd4641361c 100644
--- a/src/Gui/CMakeLists.txt
+++ b/src/Gui/CMakeLists.txt
@@ -529,6 +529,7 @@ SET(Command_CPP_SRCS
CommandFeat.cpp
CommandMacro.cpp
CommandStd.cpp
+ CommandOrigin.cpp
CommandWindow.cpp
CommandTest.cpp
CommandView.cpp
diff --git a/src/Gui/Command.h b/src/Gui/Command.h
index f060a3dd86..d0d6e7a0a8 100644
--- a/src/Gui/Command.h
+++ b/src/Gui/Command.h
@@ -247,6 +247,7 @@ void CreateWindowStdCommands();
void CreateStructureCommands();
void CreateTestCommands();
void CreateLinkCommands();
+void CreateOriginCommands();
/** The CommandBase class
diff --git a/src/Gui/CommandOrigin.cpp b/src/Gui/CommandOrigin.cpp
new file mode 100644
index 0000000000..dca6b9764a
--- /dev/null
+++ b/src/Gui/CommandOrigin.cpp
@@ -0,0 +1,277 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
+/***************************************************************************
+ * Copyright (c) 2025 Kindred Systems *
+ * *
+ * 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 *
+ * . *
+ * *
+ ***************************************************************************/
+
+/**
+ * @file CommandOrigin.cpp
+ * @brief Unified origin commands that work with the current origin
+ *
+ * These commands delegate to the current FileOrigin's extended operations.
+ * They are only active when the current origin supports the required capability.
+ */
+
+#include
+#include
+
+#include "Application.h"
+#include "BitmapFactory.h"
+#include "Command.h"
+#include "Document.h"
+#include "FileOrigin.h"
+#include "MainWindow.h"
+#include "OriginManager.h"
+
+
+using namespace Gui;
+
+//===========================================================================
+// Origin_Commit
+//===========================================================================
+
+DEF_STD_CMD_A(OriginCmdCommit)
+
+OriginCmdCommit::OriginCmdCommit()
+ : Command("Origin_Commit")
+{
+ sGroup = "File";
+ sMenuText = QT_TR_NOOP("&Commit");
+ sToolTipText = QT_TR_NOOP("Commit changes as a new revision");
+ sWhatsThis = "Origin_Commit";
+ sStatusTip = sToolTipText;
+ sPixmap = "silo-commit";
+ sAccel = "Ctrl+Shift+C";
+ eType = AlterDoc;
+}
+
+void OriginCmdCommit::activated(int /*iMsg*/)
+{
+ App::Document* doc = App::GetApplication().getActiveDocument();
+ if (!doc) {
+ return;
+ }
+
+ FileOrigin* origin = OriginManager::instance()->findOwningOrigin(doc);
+ if (origin && origin->supportsRevisions()) {
+ origin->commitDocument(doc);
+ }
+}
+
+bool OriginCmdCommit::isActive()
+{
+ App::Document* doc = App::GetApplication().getActiveDocument();
+ if (!doc) {
+ return false;
+ }
+
+ FileOrigin* origin = OriginManager::instance()->findOwningOrigin(doc);
+ return origin && origin->supportsRevisions();
+}
+
+//===========================================================================
+// Origin_Pull
+//===========================================================================
+
+DEF_STD_CMD_A(OriginCmdPull)
+
+OriginCmdPull::OriginCmdPull()
+ : Command("Origin_Pull")
+{
+ sGroup = "File";
+ sMenuText = QT_TR_NOOP("&Pull");
+ sToolTipText = QT_TR_NOOP("Pull a specific revision from the origin");
+ sWhatsThis = "Origin_Pull";
+ sStatusTip = sToolTipText;
+ sPixmap = "silo-pull";
+ sAccel = "Ctrl+Shift+P";
+ eType = AlterDoc;
+}
+
+void OriginCmdPull::activated(int /*iMsg*/)
+{
+ App::Document* doc = App::GetApplication().getActiveDocument();
+ if (!doc) {
+ return;
+ }
+
+ FileOrigin* origin = OriginManager::instance()->findOwningOrigin(doc);
+ if (origin && origin->supportsRevisions()) {
+ origin->pullDocument(doc);
+ }
+}
+
+bool OriginCmdPull::isActive()
+{
+ App::Document* doc = App::GetApplication().getActiveDocument();
+ if (!doc) {
+ return false;
+ }
+
+ FileOrigin* origin = OriginManager::instance()->findOwningOrigin(doc);
+ return origin && origin->supportsRevisions();
+}
+
+//===========================================================================
+// Origin_Push
+//===========================================================================
+
+DEF_STD_CMD_A(OriginCmdPush)
+
+OriginCmdPush::OriginCmdPush()
+ : Command("Origin_Push")
+{
+ sGroup = "File";
+ sMenuText = QT_TR_NOOP("Pu&sh");
+ sToolTipText = QT_TR_NOOP("Push local changes to the origin");
+ sWhatsThis = "Origin_Push";
+ sStatusTip = sToolTipText;
+ sPixmap = "silo-push";
+ sAccel = "Ctrl+Shift+U";
+ eType = AlterDoc;
+}
+
+void OriginCmdPush::activated(int /*iMsg*/)
+{
+ App::Document* doc = App::GetApplication().getActiveDocument();
+ if (!doc) {
+ return;
+ }
+
+ FileOrigin* origin = OriginManager::instance()->findOwningOrigin(doc);
+ if (origin && origin->supportsRevisions()) {
+ origin->pushDocument(doc);
+ }
+}
+
+bool OriginCmdPush::isActive()
+{
+ App::Document* doc = App::GetApplication().getActiveDocument();
+ if (!doc) {
+ return false;
+ }
+
+ FileOrigin* origin = OriginManager::instance()->findOwningOrigin(doc);
+ return origin && origin->supportsRevisions();
+}
+
+//===========================================================================
+// Origin_Info
+//===========================================================================
+
+DEF_STD_CMD_A(OriginCmdInfo)
+
+OriginCmdInfo::OriginCmdInfo()
+ : Command("Origin_Info")
+{
+ sGroup = "File";
+ sMenuText = QT_TR_NOOP("&Info");
+ sToolTipText = QT_TR_NOOP("Show document information from origin");
+ sWhatsThis = "Origin_Info";
+ sStatusTip = sToolTipText;
+ sPixmap = "silo-info";
+ eType = 0;
+}
+
+void OriginCmdInfo::activated(int /*iMsg*/)
+{
+ App::Document* doc = App::GetApplication().getActiveDocument();
+ if (!doc) {
+ return;
+ }
+
+ FileOrigin* origin = OriginManager::instance()->findOwningOrigin(doc);
+ if (origin && origin->supportsPartNumbers()) {
+ origin->showInfo(doc);
+ }
+}
+
+bool OriginCmdInfo::isActive()
+{
+ App::Document* doc = App::GetApplication().getActiveDocument();
+ if (!doc) {
+ return false;
+ }
+
+ FileOrigin* origin = OriginManager::instance()->findOwningOrigin(doc);
+ return origin && origin->supportsPartNumbers();
+}
+
+//===========================================================================
+// Origin_BOM
+//===========================================================================
+
+DEF_STD_CMD_A(OriginCmdBOM)
+
+OriginCmdBOM::OriginCmdBOM()
+ : Command("Origin_BOM")
+{
+ sGroup = "File";
+ sMenuText = QT_TR_NOOP("&Bill of Materials");
+ sToolTipText = QT_TR_NOOP("Show Bill of Materials for this document");
+ sWhatsThis = "Origin_BOM";
+ sStatusTip = sToolTipText;
+ sPixmap = "silo-bom";
+ eType = 0;
+}
+
+void OriginCmdBOM::activated(int /*iMsg*/)
+{
+ App::Document* doc = App::GetApplication().getActiveDocument();
+ if (!doc) {
+ return;
+ }
+
+ FileOrigin* origin = OriginManager::instance()->findOwningOrigin(doc);
+ if (origin && origin->supportsBOM()) {
+ origin->showBOM(doc);
+ }
+}
+
+bool OriginCmdBOM::isActive()
+{
+ App::Document* doc = App::GetApplication().getActiveDocument();
+ if (!doc) {
+ return false;
+ }
+
+ FileOrigin* origin = OriginManager::instance()->findOwningOrigin(doc);
+ return origin && origin->supportsBOM();
+}
+
+
+//===========================================================================
+// Command Registration
+//===========================================================================
+
+namespace Gui {
+
+void CreateOriginCommands()
+{
+ CommandManager& rcCmdMgr = Application::Instance->commandManager();
+
+ rcCmdMgr.addCommand(new OriginCmdCommit());
+ rcCmdMgr.addCommand(new OriginCmdPull());
+ rcCmdMgr.addCommand(new OriginCmdPush());
+ rcCmdMgr.addCommand(new OriginCmdInfo());
+ rcCmdMgr.addCommand(new OriginCmdBOM());
+}
+
+} // namespace Gui
diff --git a/src/Gui/Workbench.cpp b/src/Gui/Workbench.cpp
index 96b393b58c..93e500fd50 100644
--- a/src/Gui/Workbench.cpp
+++ b/src/Gui/Workbench.cpp
@@ -682,7 +682,10 @@ MenuItem* StdWorkbench::setupMenuBar() const
file->setCommand("&File");
*file << "Std_New" << "Std_Open" << "Std_RecentFiles" << "Separator" << "Std_CloseActiveWindow"
<< "Std_CloseAllWindows" << "Separator" << "Std_Save" << "Std_SaveAs"
- << "Std_SaveCopy" << "Std_SaveAll" << "Std_Revert" << "Separator" << "Std_Import"
+ << "Std_SaveCopy" << "Std_SaveAll" << "Std_Revert"
+ << "Separator" << "Origin_Commit" << "Origin_Pull" << "Origin_Push"
+ << "Origin_Info" << "Origin_BOM"
+ << "Separator" << "Std_Import"
<< "Std_Export" << "Std_MergeProjects" << "Std_ProjectInfo"
<< "Separator" << "Std_Print" << "Std_PrintPreview" << "Std_PrintPdf"
<< "Separator" << "Std_Quit";
@@ -836,6 +839,12 @@ ToolBarItem* StdWorkbench::setupToolBars() const
file->setCommand("File");
*file << "Std_Origin" << "Std_New" << "Std_Open" << "Std_Save";
+ // Origin Tools (PLM operations - commands auto-disable when not applicable)
+ auto originTools = new ToolBarItem(root);
+ originTools->setCommand("Origin Tools");
+ *originTools << "Origin_Commit" << "Origin_Pull" << "Origin_Push"
+ << "Separator" << "Origin_Info" << "Origin_BOM";
+
// Edit
auto edit = new ToolBarItem(root);
edit->setCommand("Edit");