Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Copy icon when editing cached remote PROTO #5414

Merged
merged 5 commits into from
Nov 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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