Skip to content

Commit

Permalink
Copy icon when editing cached remote PROTO (#5414)
Browse files Browse the repository at this point in the history
* Copy icon when editing web PROTO

* Fix cppcheck error
  • Loading branch information
stefaniapedrazzi authored Nov 7, 2022
1 parent 9a55b85 commit 420730c
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 58 deletions.
1 change: 1 addition & 0 deletions src/webots/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ QT_SOURCES = WbAboutBox.cpp \
WbPropeller.cpp \
WbProject.cpp \
WbProjectRelocationDialog.cpp \
WbProtoIcon.cpp \
WbProtoManager.cpp \
WbProtoModel.cpp \
WbProtoTemplateEngine.cpp \
Expand Down
12 changes: 12 additions & 0 deletions src/webots/gui/WbMainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
#include "WbPreferencesDialog.hpp"
#include "WbProject.hpp"
#include "WbProjectRelocationDialog.hpp"
#include "WbProtoIcon.hpp"
#include "WbProtoManager.hpp"
#include "WbRecentFilesList.hpp"
#include "WbRenderingDevice.hpp"
Expand Down Expand Up @@ -2358,6 +2359,17 @@ void WbMainWindow::openFileInTextEditor(const QString &fileName, bool modify, bo
return;
}

// copy icon
WbProtoIcon *protoIcon = new WbProtoIcon(protoModelName, fileName, this);
auto copyIcon = [protoIcon, destDir]() {
protoIcon->duplicate(destDir);
protoIcon->deleteLater();
};
if (protoIcon->isReady())
copyIcon();
else
connect(protoIcon, &WbProtoIcon::iconReady, copyIcon);

// adjust all the urls referenced by the PROTO
// note: this won't work well if a URL is forged with Javascript code
const QString repo = fileName.mid(0, fileName.lastIndexOf('/') + 1);
Expand Down
69 changes: 18 additions & 51 deletions src/webots/scene_tree/WbAddNodeDialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
#include "WbClipboard.hpp"
#include "WbDesktopServices.hpp"
#include "WbDictionary.hpp"
#include "WbDownloadManager.hpp"
#include "WbDownloader.hpp"
#include "WbField.hpp"
#include "WbFileUtil.hpp"
#include "WbLog.hpp"
Expand All @@ -32,6 +30,7 @@
#include "WbPreferences.hpp"
#include "WbProject.hpp"
#include "WbProjectRelocationDialog.hpp"
#include "WbProtoIcon.hpp"
#include "WbProtoManager.hpp"
#include "WbProtoModel.hpp"
#include "WbSFNode.hpp"
Expand Down Expand Up @@ -69,8 +68,6 @@ WbAddNodeDialog::WbAddNodeDialog(WbNode *currentNode, WbField *field, int index,
mRetrievalTriggered(false) {
assert(mCurrentNode && mField);

mIconDownloaders.clear();

// check if top node is a robot node
const WbNode *const topNode =
field ? WbVrmlNodeUtilities::findTopNode(mCurrentNode) : WbVrmlNodeUtilities::findTopNode(mCurrentNode->parentNode());
Expand Down Expand Up @@ -182,26 +179,7 @@ WbAddNodeDialog::WbAddNodeDialog(WbNode *currentNode, WbField *field, int index,
WbProtoManager::instance()->retrieveLocalProtoDependencies();
}

WbAddNodeDialog::~WbAddNodeDialog() {
}

void WbAddNodeDialog::downloadIcon(const QString &url) {
WbDownloader *const downloader = WbDownloadManager::instance()->createDownloader(QUrl(url), this);
mIconDownloaders.push_back(downloader);
connect(downloader, &WbDownloader::complete, this, &WbAddNodeDialog::iconUpdate);
downloader->download();
}

void WbAddNodeDialog::iconUpdate() {
const WbDownloader *const source = dynamic_cast<WbDownloader *>(sender());
QString pixmapPath;
if (source && !source->error().isEmpty()) {
// failure downloading or file does not exist (404)
pixmapPath = WbUrl::missingProtoIcon();
} else {
pixmapPath = WbNetwork::instance()->get(source->url().toString());
}

void WbAddNodeDialog::setPixmap(const QString &pixmapPath) {
QPixmap pixmap(pixmapPath);
if (!pixmap.isNull()) {
if (pixmap.size() != QSize(128, 128)) {
Expand All @@ -211,12 +189,14 @@ void WbAddNodeDialog::iconUpdate() {
mPixmapLabel->show();
mPixmapLabel->setPixmap(pixmap);
}
}

// purge completed downloaders
for (int i = mIconDownloaders.size() - 1; i >= 0; --i) {
if (mIconDownloaders[i] && mIconDownloaders[i]->hasFinished())
mIconDownloaders.remove(i);
}
void WbAddNodeDialog::updateIcon(const QString &path) {
setPixmap(path.isEmpty() ? WbUrl::missingProtoIcon() : path);

WbProtoIcon *protoIcon = dynamic_cast<WbProtoIcon *>(sender());
assert(protoIcon);
protoIcon->deleteLater();
}

QString WbAddNodeDialog::modelName() const {
Expand Down Expand Up @@ -373,7 +353,6 @@ void WbAddNodeDialog::showNodeInfo(const QString &nodeFileName, NodeType nodeTyp
}

description = info->description();
pixmapPath = QString("%1icons/%2.png").arg(QUrl(path).adjusted(QUrl::RemoveFilename).toString()).arg(modelName);
}

mInfoText->clear();
Expand Down Expand Up @@ -405,27 +384,15 @@ void WbAddNodeDialog::showNodeInfo(const QString &nodeFileName, NodeType nodeTyp
mInfoText->moveCursor(QTextCursor::Start);

mPixmapLabel->hide();
if (!pixmapPath.isEmpty()) {
if (WbUrl::isWeb(pixmapPath)) {
if (WbNetwork::instance()->isCachedWithMapUpdate(pixmapPath))
pixmapPath = WbNetwork::instance()->get(pixmapPath);
else {
downloadIcon(pixmapPath);
return;
}
} else if (WbUrl::isLocalUrl(pixmapPath))
pixmapPath = QDir::cleanPath(pixmapPath.replace("webots://", WbStandardPaths::webotsHomePath()));

QPixmap pixmap(pixmapPath);
if (!pixmap.isNull()) {
if (pixmap.size() != QSize(128, 128)) {
WbLog::warning(tr("The \"%1\" icon should have a dimension of 128x128 pixels.").arg(pixmapPath));
pixmap = pixmap.scaled(128, 128);
}
mPixmapLabel->show();
mPixmapLabel->setPixmap(pixmap);
}
}
if (pixmapPath.isEmpty()) {
WbProtoIcon *icon = new WbProtoIcon(modelName, path, this);
if (icon->isReady()) {
setPixmap(icon->path());
delete icon;
} else
connect(icon, &WbProtoIcon::iconReady, this, &WbAddNodeDialog::updateIcon);
} else
setPixmap(pixmapPath);
}

bool WbAddNodeDialog::doFieldRestrictionsAllowNode(const QString &nodeName) const {
Expand Down
11 changes: 4 additions & 7 deletions src/webots/scene_tree/WbAddNodeDialog.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

class WbField;
class WbNode;
class WbDownloader;
class WbProtoIcon;

class QGroupBox;
class QLabel;
Expand All @@ -40,7 +40,6 @@ class WbAddNodeDialog : public QDialog {

public:
explicit WbAddNodeDialog(WbNode *currentNode, WbField *field, int index, QWidget *parent = NULL);
virtual ~WbAddNodeDialog();

QString modelName() const;
QString protoUrl() const;
Expand Down Expand Up @@ -78,14 +77,10 @@ private slots:
bool mHasRobotTopNode;

QString mSelectionPath;

QVector<WbDownloader *> mIconDownloaders;
bool mRetrievalTriggered;

QMap<QString, QString> mUniqueLocalProto;

void downloadIcon(const QString &url);

int addProtosFromProtoList(QTreeWidgetItem *parentItem, int type, const QRegularExpression &regexp, bool regenerate);
int addProtosFromDirectory(QTreeWidgetItem *parentItem, const QString &dirPath, const QRegularExpression &regexp,
const QDir &rootDirectory, bool recurse = true, bool inProtos = false);
Expand All @@ -96,8 +91,10 @@ private slots:

bool isDeclarationConflicting(const QString &protoName, const QString &url);

void setPixmap(const QString &pixmapPath);

private slots:
void iconUpdate();
void updateIcon(const QString &path);
};

#endif
65 changes: 65 additions & 0 deletions src/webots/scene_tree/WbProtoIcon.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright 1996-2022 Cyberbotics Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "WbProtoIcon.hpp"

#include "WbDownloadManager.hpp"
#include "WbDownloader.hpp"
#include "WbFileUtil.hpp"
#include "WbNetwork.hpp"
#include "WbStandardPaths.hpp"
#include "WbUrl.hpp"

#include <QtCore/QDir>
#include <QtCore/QUrl>

WbProtoIcon::WbProtoIcon(const QString &modelName, const QString &protoPath, QObject *parent) :
QObject(parent),
mPath(QString("%1icons/%2.png").arg(QUrl(protoPath).adjusted(QUrl::RemoveFilename).toString()).arg(modelName)),
mModelName(modelName),
mDownloader(NULL),
mReady(true) {
if (WbUrl::isWeb(mPath)) {
if (WbNetwork::instance()->isCachedWithMapUpdate(mPath))
mPath = WbNetwork::instance()->get(mPath);
else {
mReady = false;
mDownloader = WbDownloadManager::instance()->createDownloader(QUrl(mPath), this);
connect(mDownloader, &WbDownloader::complete, this, &WbProtoIcon::updateIcon);
mDownloader->download();
}
} else if (WbUrl::isLocalUrl(mPath))
mPath = QDir::cleanPath(mPath.replace("webots://", WbStandardPaths::webotsHomePath()));
}

void WbProtoIcon::updateIcon() {
assert(mDownloader);
if (mDownloader->error().isEmpty())
mPath = WbNetwork::instance()->get(mDownloader->url().toString());
else
mPath = QString();
// else failure downloading or file does not exist (404)

mReady = true;
emit iconReady(mPath);
}

void WbProtoIcon::duplicate(QDir destinationDir) {
assert(mReady);
if (!QFile::exists(mPath))
return;

if (destinationDir.exists("icons") || destinationDir.mkdir("icons"))
WbFileUtil::forceCopy(mPath, QString("%1/icons/%2.png").arg(destinationDir.absolutePath()).arg(mModelName));
}
52 changes: 52 additions & 0 deletions src/webots/scene_tree/WbProtoIcon.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright 1996-2022 Cyberbotics Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef WB_PROTO_ICON_HPP
#define WB_PROTO_ICON_HPP

//
// Description: object representing the PROTO icon and handling its download
//

#include <QtCore/QObject>

class WbDownloader;

class QDir;

class WbProtoIcon : public QObject {
Q_OBJECT

public:
explicit WbProtoIcon(const QString &modelName, const QString &protoPath, QObject *parent = NULL);

const QString &path() const { return mPath; }
bool isReady() const { return mReady; }

void duplicate(QDir destinationDir);

signals:
void iconReady(const QString &path);

private:
QString mPath;
const QString mModelName;
WbDownloader *mDownloader;
bool mReady;

private slots:
void updateIcon();
};

#endif

0 comments on commit 420730c

Please sign in to comment.