Skip to content
5 changes: 5 additions & 0 deletions src/webots/gui/WbMainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include "WbNewProjectWizard.hpp"
#include "WbNewProtoWizard.hpp"
#include "WbNewWorldWizard.hpp"
#include "WbNodeEditor.hpp"
#include "WbNodeOperations.hpp"
#include "WbNodeUtilities.hpp"
#include "WbOdeDebugger.hpp"
Expand Down Expand Up @@ -1343,6 +1344,10 @@ void WbMainWindow::updateAfterWorldLoading(bool reloading, bool firstLoad) {
if (world->fileName() != WbProject::newWorldPath())
mRecentFiles->makeRecent(world->fileName());

connect(this, &WbMainWindow::worldCreated, WbNodeEditor::instance(), &WbNodeEditor::tryConnectToWorld);
emit worldCreated();
disconnect(this, &WbMainWindow::worldCreated, WbNodeEditor::instance(), &WbNodeEditor::tryConnectToWorld);

mSimulationView->setWorld(WbSimulationWorld::instance());

// update 'view' menu
Expand Down
1 change: 1 addition & 0 deletions src/webots/gui/WbMainWindow.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ class WbMainWindow : public QMainWindow {
signals:
void restartRequested();
void splashScreenCloseRequested();
void worldCreated();

public slots:
void loadDifferentWorld(const QString &fileName);
Expand Down
18 changes: 11 additions & 7 deletions src/webots/nodes/utils/WbWorld.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,17 +225,21 @@ bool WbWorld::needSaving() const {
}

void WbWorld::setModifiedFromSceneTree() {
if (!mIsModifiedFromSceneTree) {
mIsModifiedFromSceneTree = true;
setModified();
mIsModifiedFromSceneTree = true;

if (isModifiedFromSceneTreeRestarted) {
mIsModifiedFromSceneTree = false;
isModifiedFromSceneTreeRestarted = false;
}
}

void WbWorld::resetModifiedFromSceneTree() {
mIsModifiedFromSceneTree = false;
isModifiedFromSceneTreeRestarted = true;
}

void WbWorld::setModified(bool isModified) {
if (mIsModified != isModified) {
mIsModified = isModified;
emit modificationChanged(isModified);
}
emit checkDefDiff();
}

bool WbWorld::saveAs(const QString &fileName) {
Expand Down
3 changes: 3 additions & 0 deletions src/webots/nodes/utils/WbWorld.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,12 +158,14 @@ class WbWorld : public QObject {
void robotAdded(WbRobot *robot);
void robotRemoved(WbRobot *robot);
void resetRequested(bool restartControllers);
void checkDefDiff();

public slots:
void awake();
void updateVideoRecordingStatus(int status) {
mIsVideoRecording = (status == WB_SUPERVISOR_MOVIE_RECORDING || status == WB_SUPERVISOR_MOVIE_SAVING);
}
void resetModifiedFromSceneTree();

protected:
// collecting contact and immersion geometries
Expand Down Expand Up @@ -199,6 +201,7 @@ protected slots:
bool mIsLoading;
bool mIsCleaning;
bool mIsVideoRecording;
bool isModifiedFromSceneTreeRestarted = false;

void checkPresenceOfMandatoryNodes();
WbNode *findTopLevelNode(const QString &modelName, int preferredPosition) const;
Expand Down
13 changes: 11 additions & 2 deletions src/webots/scene_tree/WbFieldEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,18 @@ WbFieldEditor::WbFieldEditor(QWidget *parent) :
connect(stringEditor, &WbExtendedStringEditor::editRequested, this, &WbFieldEditor::editRequested);

WbNodePane *const nodePane = new WbNodePane(this);
const WbNodeEditor *nodeEditor = nodePane->nodeEditor();
const WbNodeEditor *nodeEditor = WbNodeEditor::instance(nodePane);
connect(nodeEditor, &WbNodeEditor::dictionaryUpdateRequested, this, &WbFieldEditor::dictionaryUpdateRequested);

connect(nodeEditor, &WbNodeEditor::defNameChanged, WbActionManager::instance()->action(WbAction::SAVE_WORLD),
&QAction::setEnabled);
connect(WbActionManager::instance()->action(WbAction::SAVE_WORLD), &QAction::setEnabled, nodeEditor,
&WbNodeEditor::resetDefNamesToInitial);
connect(WbActionManager::instance()->action(WbAction::SAVE_WORLD_AS), &QAction::setEnabled, nodeEditor,
&WbNodeEditor::resetDefNamesToInitial);
connect(WbActionManager::instance()->action(WbAction::SAVE_WORLD), &QAction::triggered, nodeEditor,
&WbNodeEditor::switchInitialCurrentDef);
connect(WbActionManager::instance()->action(WbAction::SAVE_WORLD_AS), &QAction::triggered, nodeEditor,
&WbNodeEditor::switchInitialCurrentDef);
// create editors
mEditors.insert(WB_NO_FIELD, new WbEmptyEditor(this));
mEditors.insert(WB_SF_BOOL, new WbBoolEditor(this));
Expand Down
99 changes: 99 additions & 0 deletions src/webots/scene_tree/WbNodeEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#include "WbNodeEditor.hpp"

#include "WbActionManager.hpp"
#include "WbBaseNode.hpp"
#include "WbField.hpp"
#include "WbFieldLineEdit.hpp"
Expand All @@ -32,9 +33,11 @@
#include "WbTransform.hpp"
#include "WbViewpoint.hpp"
#include "WbVrmlNodeUtilities.hpp"
#include "WbWorld.hpp"
#include "WbWorldInfo.hpp"

#include <QtCore/QDir>
#include <QtCore/QTimer>
#include <QtWidgets/QCheckBox>
#include <QtWidgets/QComboBox>
#include <QtWidgets/QFileDialog>
Expand All @@ -43,6 +46,14 @@
#include <QtWidgets/QPushButton>
#include <QtWidgets/QStackedWidget>

WbNodeEditor *WbNodeEditor::cInstance = nullptr;

WbNodeEditor *WbNodeEditor::instance(QWidget *parent) {
if (!cInstance)
cInstance = new WbNodeEditor(parent);
return cInstance;
}

WbNodeEditor::WbNodeEditor(QWidget *parent) :
WbValueEditor(parent),
mNode(NULL),
Expand Down Expand Up @@ -87,6 +98,14 @@ WbNodeEditor::WbNodeEditor(QWidget *parent) :
connect(mPrintUrl, &QPushButton::pressed, this, &WbNodeEditor::printUrl);
connect(mShowResizeHandlesCheckBox, &QAbstractButton::toggled, WbSelection::instance(),
&WbSelection::showResizeManipulatorFromSceneTree, Qt::UniqueConnection);
state = WbSimulationState::instance();
}

void WbNodeEditor::tryConnectToWorld() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We cannot have a timer here, we need a cleaner solution

world = WbWorld::instance();
connect(world, &WbWorld::checkDefDiff, this, &WbNodeEditor::resetDefNamesToInitial);
connect(this, &WbNodeEditor::resetModifiedFromSceneTree, world, &WbWorld::resetModifiedFromSceneTree);
mInitialCurrentDefMap.clear();
}

void WbNodeEditor::printUrl() {
Expand Down Expand Up @@ -131,6 +150,9 @@ void WbNodeEditor::edit(bool copyOriginalValue) {
mShowResizeHandlesCheckBox->setChecked(g->isResizeManipulatorAttached());
}
}

if (mNode && !mInitialCurrentDefMap.contains(mNode))
mInitialCurrentDefMap[mNode] = QPair<QString, QString>(mNode->defName(), QString());
}

update();
Expand Down Expand Up @@ -198,6 +220,12 @@ void WbNodeEditor::apply() {
QString newDef = mDefEdit->text();
const QString &previousDef = mNode->defName();

mInitialCurrentDefMap[mNode].second = newDef;

bool hasStarted = state->hasStarted();
if (!hasStarted)
this->compareInitialCurrentDef();

if (newDef == previousDef)
return;

Expand Down Expand Up @@ -259,3 +287,74 @@ void WbNodeEditor::apply() {
if (dictionaryUpdateRequest)
emit dictionaryUpdateRequested();
}

void WbNodeEditor::compareInitialCurrentDef() {
if (!mInitialCurrentDefMap.isEmpty()) {
bool foundDifference = false;
// Iterate through the QMap
for (auto it = mInitialCurrentDefMap.constBegin(); it != mInitialCurrentDefMap.constEnd(); ++it) {
const QString &initialDef = it.value().first; // First QString (initial)
const QString &currentDef = it.value().second; // Second QString (current)

// Compare the two QStrings
if (initialDef != currentDef) {
foundDifference = true; // Mark that a difference is found
break;
}
}
if (foundDifference)
emit defNameChanged(true); // Emit true if any difference is found
else {
emit resetModifiedFromSceneTree();
emit defNameChanged(false); // Emit false if no differences were found
}
} else
emit defNameChanged(false); // If all QStrings are the same, return false
}

void WbNodeEditor::resetDefNamesToInitial() {
// Check if the map is empty
if (mInitialCurrentDefMap.isEmpty()) {
emit defNameChanged(false);
return;
}

// Iterate through the map and reset each node's DEF name to its initial value
for (auto it = mInitialCurrentDefMap.begin(); it != mInitialCurrentDefMap.end(); ++it) {
WbNode *node = it.key();
const QString &initialDef = it.value().first; // Access initial DEF name

// Only reset if node exists and the current DEF differs from the initial one
if (node && node->defName() != initialDef)
node->setDefName(initialDef); // Set the DEF name back to the initial one
}

update();

emit defNameChanged(false);
emit resetModifiedFromSceneTree();
}

void WbNodeEditor::switchInitialCurrentDef() {
// Check if the map is empty
if (mInitialCurrentDefMap.isEmpty()) {
emit defNameChanged(false);
return;
}

// Iterate through the map and switch the initial DEF to the current one
for (auto it = mInitialCurrentDefMap.begin(); it != mInitialCurrentDefMap.end(); ++it) {
const WbNode *node = it.key();

// Switch the initial DEF to the current DEF
if (node) {
QString &initialDef = it.value().first; // Reference to initial DEF name
const QString &currentDef = node->defName(); // Get the current DEF name of the node
initialDef = currentDef; // Update the initial DEF with the current one
}
}

update();

emit defNameChanged(false);
}
15 changes: 14 additions & 1 deletion src/webots/scene_tree/WbNodeEditor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
// Description: editor for editing a WbSFNode or a WbMFNode item
//

#include "WbSimulationState.hpp"
#include "WbValueEditor.hpp"
#include "WbWorld.hpp"

class WbFieldLineEdit;
class WbNode;
Expand All @@ -33,7 +35,7 @@ class WbNodeEditor : public WbValueEditor {
Q_OBJECT

public:
explicit WbNodeEditor(QWidget *parent = NULL);
static WbNodeEditor *instance(QWidget *parent = nullptr);

void recursiveBlockSignals(bool block) override;

Expand All @@ -47,10 +49,15 @@ class WbNodeEditor : public WbValueEditor {

signals:
void dictionaryUpdateRequested();
void defNameChanged(bool changed);
void resetModifiedFromSceneTree();

public slots:
void apply() override;
void cleanValue() override;
void resetDefNamesToInitial();
void switchInitialCurrentDef();
void tryConnectToWorld();

protected:
enum PaneType { DEF_PANE, EMPTY_PANE };
Expand All @@ -63,14 +70,20 @@ public slots:
QLabel *mNbTriangles;
QStackedWidget *mStackedWidget;
bool mMessageBox;
QMap<WbNode *, QPair<QString, QString>> mInitialCurrentDefMap;
const WbWorld *world = nullptr;
WbSimulationState *state = nullptr;
static WbNodeEditor *cInstance;

// actions buttons
QLabel *mShowResizeHandlesLabel;
QCheckBox *mShowResizeHandlesCheckBox;

explicit WbNodeEditor(QWidget *parent = nullptr);
void setTransformActionVisibile(bool visible);
void takeKeyboardFocus() override {}
void printUrl();
void compareInitialCurrentDef();
};

#endif
2 changes: 1 addition & 1 deletion src/webots/scene_tree/WbNodePane.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ static const QStringList cTabNames = QStringList() << "Node"
WbNodePane::WbNodePane(QWidget *parent) :
WbValueEditor(parent),
mTabs(new QTabWidget(this)),
mNodeEditor(new WbNodeEditor()),
mNodeEditor(WbNodeEditor::instance()),
mPhysicsViewer(new WbPhysicsViewer()),
mPositionViewer(new WbPositionViewer()),
mVelocityViewer(new WbVelocityViewer()),
Expand Down
Loading