From 420730c2be2dfb66971e1cac7855ced3bdb36976 Mon Sep 17 00:00:00 2001 From: Stefania Pedrazzi Date: Mon, 7 Nov 2022 12:47:01 +0100 Subject: [PATCH] Copy icon when editing cached remote PROTO (#5414) * Copy icon when editing web PROTO * Fix cppcheck error --- src/webots/Makefile | 1 + src/webots/gui/WbMainWindow.cpp | 12 ++++ src/webots/scene_tree/WbAddNodeDialog.cpp | 69 ++++++----------------- src/webots/scene_tree/WbAddNodeDialog.hpp | 11 ++-- src/webots/scene_tree/WbProtoIcon.cpp | 65 +++++++++++++++++++++ src/webots/scene_tree/WbProtoIcon.hpp | 52 +++++++++++++++++ 6 files changed, 152 insertions(+), 58 deletions(-) create mode 100644 src/webots/scene_tree/WbProtoIcon.cpp create mode 100644 src/webots/scene_tree/WbProtoIcon.hpp diff --git a/src/webots/Makefile b/src/webots/Makefile index bdbfce51311..74142eebb9f 100644 --- a/src/webots/Makefile +++ b/src/webots/Makefile @@ -377,6 +377,7 @@ QT_SOURCES = WbAboutBox.cpp \ WbPropeller.cpp \ WbProject.cpp \ WbProjectRelocationDialog.cpp \ + WbProtoIcon.cpp \ WbProtoManager.cpp \ WbProtoModel.cpp \ WbProtoTemplateEngine.cpp \ diff --git a/src/webots/gui/WbMainWindow.cpp b/src/webots/gui/WbMainWindow.cpp index 4bc30900130..c5b47ff269e 100644 --- a/src/webots/gui/WbMainWindow.cpp +++ b/src/webots/gui/WbMainWindow.cpp @@ -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" @@ -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); diff --git a/src/webots/scene_tree/WbAddNodeDialog.cpp b/src/webots/scene_tree/WbAddNodeDialog.cpp index 5561bb089d3..edc54a8583b 100644 --- a/src/webots/scene_tree/WbAddNodeDialog.cpp +++ b/src/webots/scene_tree/WbAddNodeDialog.cpp @@ -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" @@ -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" @@ -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()); @@ -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(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)) { @@ -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(sender()); + assert(protoIcon); + protoIcon->deleteLater(); } QString WbAddNodeDialog::modelName() const { @@ -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(); @@ -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 { diff --git a/src/webots/scene_tree/WbAddNodeDialog.hpp b/src/webots/scene_tree/WbAddNodeDialog.hpp index 1647d1d1b5d..8d4bbae888d 100644 --- a/src/webots/scene_tree/WbAddNodeDialog.hpp +++ b/src/webots/scene_tree/WbAddNodeDialog.hpp @@ -24,7 +24,7 @@ class WbField; class WbNode; -class WbDownloader; +class WbProtoIcon; class QGroupBox; class QLabel; @@ -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; @@ -78,14 +77,10 @@ private slots: bool mHasRobotTopNode; QString mSelectionPath; - - QVector mIconDownloaders; bool mRetrievalTriggered; QMap mUniqueLocalProto; - void downloadIcon(const QString &url); - int addProtosFromProtoList(QTreeWidgetItem *parentItem, int type, const QRegularExpression ®exp, bool regenerate); int addProtosFromDirectory(QTreeWidgetItem *parentItem, const QString &dirPath, const QRegularExpression ®exp, const QDir &rootDirectory, bool recurse = true, bool inProtos = false); @@ -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 diff --git a/src/webots/scene_tree/WbProtoIcon.cpp b/src/webots/scene_tree/WbProtoIcon.cpp new file mode 100644 index 00000000000..c748f6866a5 --- /dev/null +++ b/src/webots/scene_tree/WbProtoIcon.cpp @@ -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 +#include + +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)); +} diff --git a/src/webots/scene_tree/WbProtoIcon.hpp b/src/webots/scene_tree/WbProtoIcon.hpp new file mode 100644 index 00000000000..2a35d62e73c --- /dev/null +++ b/src/webots/scene_tree/WbProtoIcon.hpp @@ -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 + +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