From dee8f0d218e4d86780989236085877dee9dde35b Mon Sep 17 00:00:00 2001 From: Kalila L Date: Tue, 15 Dec 2020 02:22:56 -0500 Subject: [PATCH] Add initial data export API. --- interface/src/Application.cpp | 35 +++++++++++++++++-- interface/src/Application.h | 5 +++ .../scripting/ClipboardScriptingInterface.cpp | 9 +++++ .../scripting/ClipboardScriptingInterface.h | 10 ++++++ .../scripting/WindowScriptingInterface.cpp | 15 +++++++- .../src/scripting/WindowScriptingInterface.h | 18 ++++++++++ 6 files changed, 89 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index cc46d8591be..68466cddf5b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -51,7 +52,7 @@ #include #include #include - +#include #include #include @@ -5388,6 +5389,36 @@ ivec2 Application::getMouse() const { return getApplicationCompositor().getReticlePosition(); } +void Application::setInterfaceScriptDataPath(const QString& dataPath) { + interfaceScriptDataPath = dataPath; +} + +bool Application::exportData(const QString& dataString) { + qCDebug(interfaceapp, "Saving data to file %s...", interfaceScriptDataPath); + + QSaveFile persistFile(interfaceScriptDataPath); + bool success = false; + if (persistFile.open(QIODevice::WriteOnly)) { + if (persistFile.write(dataString.toUtf8()) != -1) { + success = persistFile.commit(); + if (!success) { + qCritical() << "Failed to commit to text save file:" << persistFile.errorString(); + } + } else { + qCritical("Failed to write to text file."); + } + } else { + qCritical("Failed to open text file for writing."); + } + + if (success) { + // restore the main window's active state + _window->activateWindow(); + } + + return success; +} + bool Application::exportEntities(const QString& filename, const QVector& entityIDs, const glm::vec3* givenOffset) { @@ -8712,7 +8743,7 @@ void Application::notifyPacketVersionMismatch() { } void Application::checkSkeleton() const { - if (getMyAvatar()->getSkeletonModel()->isLoaded() && !getMyAvatar()->getSkeletonModel()->hasSkeleton()) { + if (getMyAvatar()->getSkeletonModel()->isActive() && !getMyAvatar()->getSkeletonModel()->hasSkeleton()) { qCDebug(interfaceapp) << "MyAvatar model has no skeleton"; QString message = "Your selected avatar body has no skeleton.\n\nThe default body will be loaded..."; diff --git a/interface/src/Application.h b/interface/src/Application.h index 5cb5fdd5c0e..98db03c15f6 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -251,6 +251,8 @@ class Application : public QApplication, void setActiveDisplayPlugin(const QString& pluginName); + void setInterfaceScriptDataPath(const QString& dataPath); + #ifndef Q_OS_ANDROID FileLogger* getLogger() const { return _logger; } #endif @@ -377,6 +379,7 @@ class Application : public QApplication, public slots: QVector pasteEntities(const QString& entityHostType, float x, float y, float z); + bool exportData(const QString& dataString); bool exportEntities(const QString& filename, const QVector& entityIDs, const glm::vec3* givenOffset = nullptr); bool exportEntities(const QString& filename, float x, float y, float z, float scale); bool importEntities(const QString& url, const bool isObservable = true, const qint64 callerId = -1); @@ -708,6 +711,8 @@ private slots: bool _notifiedPacketVersionMismatchThisDomain; + QString interfaceScriptDataPath; + ConditionalGuard _settingsGuard; GLCanvas* _glWidget{ nullptr }; diff --git a/interface/src/scripting/ClipboardScriptingInterface.cpp b/interface/src/scripting/ClipboardScriptingInterface.cpp index af7ac8165b9..1eac0712558 100644 --- a/interface/src/scripting/ClipboardScriptingInterface.cpp +++ b/interface/src/scripting/ClipboardScriptingInterface.cpp @@ -3,6 +3,7 @@ // interface/src/scripting // // Copyright 2014 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -25,6 +26,14 @@ float ClipboardScriptingInterface::getClipboardContentsLargestDimension() { return qApp->getEntityClipboard()->getContentsLargestDimension(); } +bool ClipboardScriptingInterface::exportData(const QString& dataString) { + bool retVal; + BLOCKING_INVOKE_METHOD(qApp, "exportData", + Q_RETURN_ARG(bool, retVal), + Q_ARG(const QString&, dataString)); + return retVal; +} + bool ClipboardScriptingInterface::exportEntities(const QString& filename, const QVector& entityIDs) { bool retVal; BLOCKING_INVOKE_METHOD(qApp, "exportEntities", diff --git a/interface/src/scripting/ClipboardScriptingInterface.h b/interface/src/scripting/ClipboardScriptingInterface.h index 9660b2158b8..d36cea8a564 100644 --- a/interface/src/scripting/ClipboardScriptingInterface.h +++ b/interface/src/scripting/ClipboardScriptingInterface.h @@ -3,6 +3,7 @@ // interface/src/scripting // // Copyright 2014 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -112,6 +113,15 @@ class ClipboardScriptingInterface : public QObject { * @returns {boolean} true if entities were found and the file was written, otherwise false. */ Q_INVOKABLE bool exportEntities(const QString& filename, float x, float y, float z, float scale); + + /**jsdoc + * Export a string of data to a file path previously set by Window.setInterfaceD + * @function Clipboard.exportData + * @variation 0 + * @param {string} data - The string of data to export. (If exporting JSON, use JSON.stringify() first.) + * @returns {boolean} true if the file was written, otherwise false. + */ + Q_INVOKABLE bool exportData(const QString& dataString); /**jsdoc * Pastes the contents of the clipboard into the domain. diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 14a0d040233..670cb9b7b1a 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -325,6 +325,19 @@ QScriptValue WindowScriptingInterface::save(const QString& title, const QString& return result.isEmpty() ? QScriptValue::NullValue : QScriptValue(result); } +/// Display a save file dialog. If `directory` is an invalid file or directory the browser will start at the current +/// working directory. +/// \param const QString& title title of the window +/// \param const QString& directory directory to start the file browser at +/// \param const QString& nameFilter filter to filter filenames by - see `QFileDialog` +/// \return QScriptValue file path as a string if one was selected, otherwise `QScriptValue::NullValue` +QScriptValue WindowScriptingInterface::setInterfaceScriptDataPath(const QString& title, const QString& directory, const QString& nameFilter) { + QScriptValue result = WindowScriptingInterface::save(title, directory, nameFilter); + QString resultString = result.toString(); + qApp->setInterfaceScriptDataPath(resultString); + return resultString.isEmpty() ? QScriptValue::NullValue : result; +} + /// Display a save file dialog. If `directory` is an invalid file or directory the browser will start at the current /// working directory. /// \param const QString& title title of the window @@ -467,7 +480,7 @@ void WindowScriptingInterface::copyToClipboard(const QString& text) { bool WindowScriptingInterface::setDisplayTexture(const QString& name) { - return qApp->getActiveDisplayPlugin()->setDisplayTexture(name); // Plugins that don't know how, answer false. + return qApp->getActiveDisplayPlugin()->setDisplayTexture(name); // Plugins that don't know how, answer false. } void WindowScriptingInterface::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio, const QString& filename) { diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 86d0f400eab..b9c5f286399 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -221,6 +221,24 @@ public slots: */ QScriptValue save(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); + /**jsdoc + * Prompts the user to specify the path and name of a file to use as the script data path. Displays a modal dialog that navigates the directory + * tree and allows the user to type in a file name. + * @function Window.setInterfaceScriptDataPath + * @param {string} [title=""] - The title to display at the top of the dialog. + * @param {string} [directory=""] - The initial directory to start browsing at. + * @param {string} [nameFilter=""] - The types of files to display. Examples: "*.json" and + * "Images (*.png *.jpg *.svg)". All files are displayed if a filter isn't specified. + * @returns {string} The path and name of the file if one is specified, otherwise null. If a single file type + * is specified in the nameFilter, that file type extension is automatically appended to the result when appropriate. + * @example Ask the user to specify a file to save to. + * var filename = Window.setInterfaceScriptDataPath("Save to JSON file", Paths.resources, "*.json"); + * print("File: " + filename); + */ + QScriptValue setInterfaceScriptDataPath(const QString& title = "", + const QString& directory = "", + const QString& nameFilter = ""); + /**jsdoc * Prompts the user to specify the path and name of a file to save to. Displays a non-modal dialog that navigates the * directory tree and allows the user to type in a file name. A {@link Window.saveFileChanged|saveFileChanged} signal is