Skip to content
15 changes: 10 additions & 5 deletions src/webots/nodes/utils/WbWorld.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -227,15 +227,20 @@ bool WbWorld::needSaving() const {
void WbWorld::setModifiedFromSceneTree() {
if (!mIsModifiedFromSceneTree) {
mIsModifiedFromSceneTree = true;
setModified();
}
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
7 changes: 6 additions & 1 deletion src/webots/scene_tree/WbFieldEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,12 @@ WbFieldEditor::WbFieldEditor(QWidget *parent) :
WbNodePane *const nodePane = new WbNodePane(this);
const WbNodeEditor *nodeEditor = nodePane->nodeEditor();
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);
connect(WbActionManager::instance()->action(WbAction::RELOAD_WORLD), &QAction::triggered, nodeEditor, &WbNodeEditor::startTimer);
// create editors
mEditors.insert(WB_NO_FIELD, new WbEmptyEditor(this));
mEditors.insert(WB_SF_BOOL, new WbBoolEditor(this));
Expand Down
105 changes: 105 additions & 0 deletions src/webots/scene_tree/WbNodeEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
#include "WbViewpoint.hpp"
#include "WbVrmlNodeUtilities.hpp"
#include "WbWorldInfo.hpp"
#include "WbWorld.hpp"
#include "WbActionManager.hpp"

#include <QtCore/QDir>
#include <QtWidgets/QCheckBox>
Expand All @@ -42,6 +44,7 @@
#include <QtWidgets/QLabel>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QStackedWidget>
#include <QtCore/QTimer>

WbNodeEditor::WbNodeEditor(QWidget *parent) :
WbValueEditor(parent),
Expand All @@ -52,6 +55,7 @@ WbNodeEditor::WbNodeEditor(QWidget *parent) :
mNbTriangles(new QLabel(this)),
mStackedWidget(new QStackedWidget(this)),
mMessageBox(false),
worldCheckTimer(new QTimer(this)),
mShowResizeHandlesLabel(new QLabel(tr("3D tools:"), this)),
mShowResizeHandlesCheckBox(new QCheckBox(tr("show resize handles"), this)) {
mShowResizeHandlesCheckBox->setChecked(false);
Expand Down Expand Up @@ -87,6 +91,25 @@ WbNodeEditor::WbNodeEditor(QWidget *parent) :
connect(mPrintUrl, &QPushButton::pressed, this, &WbNodeEditor::printUrl);
connect(mShowResizeHandlesCheckBox, &QAbstractButton::toggled, WbSelection::instance(),
&WbSelection::showResizeManipulatorFromSceneTree, Qt::UniqueConnection);
connect(worldCheckTimer, &QTimer::timeout, this, &WbNodeEditor::tryConnectToWorld);
worldCheckTimer->start(500);
}

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();
state = WbSimulationState::instance();
if (oldWorld != world) {
connect(world, &WbWorld::checkDefDiff, this, &WbNodeEditor::resetDefNamesToInitial);
connect(this, &WbNodeEditor::resetModifiedFromSceneTree, world, &WbWorld::resetModifiedFromSceneTree);
oldWorld = const_cast<WbWorld*>(world);
mInitialCurrentDefMap.clear();
worldCheckTimer->stop();
}
}

void WbNodeEditor::startTimer(){
disconnect(world, &WbWorld::checkDefDiff, this, &WbNodeEditor::resetDefNamesToInitial);
worldCheckTimer->start(500);
}

void WbNodeEditor::printUrl() {
Expand Down Expand Up @@ -131,6 +154,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 +224,13 @@ void WbNodeEditor::apply() {
QString newDef = mDefEdit->text();
const QString &previousDef = mNode->defName();

QString initialDef = mInitialCurrentDefMap.value(mNode).first; // Access the first QString (initial DEF)
mInitialCurrentDefMap[mNode].second = newDef;

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

if (newDef == previousDef)
return;

Expand Down Expand Up @@ -259,3 +292,75 @@ 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) {
WbNode *node = it.key();
QString &initialDef = it.value().first; // Reference to initial DEF name
const QString &currentDef = node->defName(); // Get the current DEF name of the node

// Switch the initial DEF to the current DEF
if (node && initialDef != currentDef)
initialDef = currentDef; // Update the initial DEF with the current one
}

update();

emit defNameChanged(false);
}
14 changes: 14 additions & 0 deletions src/webots/scene_tree/WbNodeEditor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
//

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

class WbFieldLineEdit;
class WbNode;
Expand Down Expand Up @@ -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 startTimer();

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

// actions buttons
QLabel *mShowResizeHandlesLabel;
Expand All @@ -71,6 +83,8 @@ public slots:
void setTransformActionVisibile(bool visible);
void takeKeyboardFocus() override {}
void printUrl();
void tryConnectToWorld();
void compareInitialCurrentDef();
};

#endif
Loading