diff --git a/ScreenPlay/CMakeLists.txt b/ScreenPlay/CMakeLists.txt index 004a00fe..f1007ed0 100644 --- a/ScreenPlay/CMakeLists.txt +++ b/ScreenPlay/CMakeLists.txt @@ -231,6 +231,7 @@ set(RESOURCES assets/icons/icon_sort-up-solid.svg assets/icons/icon_sort-down-solid.svg assets/icons/brand_reddit.svg + assets/icons/icon_import_export_.svg assets/icons/steam_default_avatar.png assets/macos/app.screenplay.plist assets/icons/item_banner_new.svg diff --git a/ScreenPlay/assets/icons/icon_import_export_.svg b/ScreenPlay/assets/icons/icon_import_export_.svg new file mode 100644 index 00000000..5db7d8f3 --- /dev/null +++ b/ScreenPlay/assets/icons/icon_import_export_.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ScreenPlay/inc/public/ScreenPlay/util.h b/ScreenPlay/inc/public/ScreenPlay/util.h index 5a5c1f70..59850a4e 100644 --- a/ScreenPlay/inc/public/ScreenPlay/util.h +++ b/ScreenPlay/inc/public/ScreenPlay/util.h @@ -88,14 +88,15 @@ class Util : public QObject { QML_ELEMENT Q_PROPERTY(QString debugMessages READ debugMessages NOTIFY debugMessagesChanged) + Q_PROPERTY(QArchive::DiskCompressor* compressor READ compressor NOTIFY compressorChanged) + Q_PROPERTY(QArchive::DiskExtractor* extractor READ extractor NOTIFY extractorChanged) public: explicit Util(QNetworkAccessManager* networkAccessManager, QObject* parent = nullptr); - QString debugMessages() const - { - return m_debugMessages; - } + QString debugMessages() const { return m_debugMessages; } + QArchive::DiskCompressor* compressor() const { return m_compressor.get(); } + QArchive::DiskExtractor* extractor() const { return m_extractor.get(); } signals: void requestNavigation(QString nav); @@ -105,13 +106,15 @@ signals: void allLicenseLoaded(QString licensesText); void allDataProtectionLoaded(QString dataProtectionText); void debugMessagesChanged(QString debugMessages); + void compressorChanged(QArchive::DiskCompressor* compressor); + void extractorChanged(QArchive::DiskExtractor* extractor); public slots: void copyToClipboard(const QString& text) const; void openFolderInExplorer(const QString& url) const; QString toLocal(const QString& url); - bool exportProject(QString& contentPath, QString& exportPath); - bool importProject(QString& archivePath, QString& extractionPath); + bool exportProject(QString& contentPath, QString& exportFileName); + bool importProject(QString& archivePath, QString extractionPath); void requestAllLicenses(); void requestDataProtection(); @@ -148,6 +151,22 @@ public slots: emit debugMessagesChanged(m_debugMessages); } + void setCompressor(QArchive::DiskCompressor* compressor) + { + if (m_compressor.get() == compressor) + return; + m_compressor.reset(compressor); + emit compressorChanged(m_compressor.get()); + } + + void setExtractor(QArchive::DiskExtractor* extractor) + { + if (m_extractor.get() == extractor) + return; + m_extractor.reset(extractor); + emit extractorChanged(m_extractor.get()); + } + private: QNetworkAccessManager* m_networkAccessManager { nullptr }; diff --git a/ScreenPlay/qml/Installed/Installed.qml b/ScreenPlay/qml/Installed/Installed.qml index c47607dc..65f691be 100644 --- a/ScreenPlay/qml/Installed/Installed.qml +++ b/ScreenPlay/qml/Installed/Installed.qml @@ -19,7 +19,7 @@ Item { property bool enabled: true property Sidebar sidebar - + property Item modalSource signal setNavigationItem(var pos) signal setSidebarActive(var active) @@ -284,7 +284,7 @@ Item { MenuItem { text: qsTr("Export") objectName: enabled ? "removeItem" : "removeWorkshopItem" - icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_download.svg" + icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_import_export_.svg" onClicked: { exportFileDialog.absoluteStoragePath = contextMenu.absoluteStoragePath let urlFileName = Labs.StandardPaths.writableLocation( @@ -301,6 +301,7 @@ Item { objectName: enabled ? "removeItem" : "removeWorkshopItem" icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_delete.svg" enabled: contextMenu.publishedFileID === 0 + || !App.settings.steamVersion onClicked: { deleteDialog.open() } @@ -309,6 +310,7 @@ Item { MenuItem { text: qsTr("Open Workshop Page") enabled: contextMenu.publishedFileID !== 0 + && App.settings.steamVersion icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_steam.svg" onClicked: { Qt.openUrlExternally( @@ -316,36 +318,13 @@ Item { } } } - Labs.FileDialog { - id: exportFileDialog - fileMode: FileDialog.SaveFile - property string absoluteStoragePath - onAccepted: { - const success = App.util.exportProject( - exportFileDialog.absoluteStoragePath, - exportFileDialog.currentFile) - } - - Dialog { - id: exportFileProgressDialog - modal: true - anchors.centerIn: Overlay.overlay - standardButtons: Dialog.Ok - onAccepted: errorDialog.close() - - ProgressBar { - id: exportFileProgressBar - anchors.centerIn: parent - } - } - } - - Dialog { + Util.Dialog { id: deleteDialog title: qsTr("Are you sure you want to delete this item?") standardButtons: Dialog.Ok | Dialog.Cancel modal: true dim: true + modalSource: root.modalSource anchors.centerIn: Overlay.overlay onAccepted: { root.sidebar.clear() @@ -354,6 +333,59 @@ Item { } } + Labs.FileDialog { + id: exportFileDialog + fileMode: FileDialog.SaveFile + property string absoluteStoragePath + onAccepted: { + exportFileProgressDialog.open() + } + } + + Util.Dialog { + id: exportFileProgressDialog + modal: true + anchors.centerIn: Overlay.overlay + width: 400 + focus: true + modalSource: root.modalSource + closePolicy: Popup.NoAutoClose + onOpened: { + const success = App.util.exportProject( + exportFileDialog.absoluteStoragePath, + exportFileDialog.currentFile) + } + onClosed: exportProgressBar.value = 0 + ColumnLayout { + width: parent.width + spacing: 20 + + Text { + text: qsTr("Export Content...") + color: Material.primaryTextColor + font.pointSize: 18 + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + } + ProgressBar { + id: exportProgressBar + from: 0 + to: 100 + Layout.alignment: Qt.AlignHCenter + } + } + Connections { + id: exportConnections + target: App.util.compressor + function onProgress(file, proc, total, br, bt) { + exportProgressBar.value = (br * 100 / bt) + } + function onFinished() { + exportFileProgressDialog.close() + } + } + } + InstalledNavigation { id: navWrapper @@ -376,17 +408,17 @@ Item { dropArea.enabled = false if (drop.urls.length > 1) { - errorDialog.title = qsTr( + importProjectErrorDialog.title = qsTr( "We only support adding one item at once.") - errorDialog.open() + importProjectErrorDialog.open() return } var file = "" // Convert url to string file = "" + drop.urls[0] if (!file.endsWith('.screenplay')) { - errorDialog.title = qsTr( + importProjectErrorDialog.title = qsTr( "File type not supported. We only support '.screenplay' files.") - errorDialog.open() + importProjectErrorDialog.open() return } importDialog.open() @@ -396,30 +428,54 @@ Item { dropPopup.close() } - Dialog { - id: errorDialog + Util.Dialog { + id: importProjectErrorDialog modal: true + modalSource: root.modalSource anchors.centerIn: Overlay.overlay standardButtons: Dialog.Ok - onAccepted: errorDialog.close() + onAccepted: importProjectErrorDialog.close() } - Dialog { + Util.Dialog { id: importDialog modal: true + modalSource: root.modalSource anchors.centerIn: Overlay.overlay - standardButtons: Dialog.Ok - RowLayout { - Text { - text: qsTr("Import Content...") - } - } + width: 400 + focus: true + closePolicy: Popup.NoAutoClose + onClosed: importProgressBar.value = 0 onOpened: { App.util.importProject(dropArea.filePath, App.globalVariables.localStoragePath) dropArea.filePath = "" } - onAccepted: { - importDialog.close() + ColumnLayout { + width: parent.width + spacing: 20 + Text { + text: qsTr("Import Content...") + color: Material.primaryTextColor + font.pointSize: 18 + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + } + ProgressBar { + id: importProgressBar + from: 0 + to: 100 + Layout.alignment: Qt.AlignHCenter + } + Connections { + id: importConnections + target: App.util.extractor + function onProgress(file, proc, total, br, bt) { + importProgressBar.value = (br * 100 / bt) + } + function onFinished() { + importDialog.close() + } + } } } } diff --git a/ScreenPlay/src/util.cpp b/ScreenPlay/src/util.cpp index 633ed3cf..4529c946 100644 --- a/ScreenPlay/src/util.cpp +++ b/ScreenPlay/src/util.cpp @@ -18,13 +18,13 @@ namespace ScreenPlay { Util::Util(QNetworkAccessManager* networkAccessManager, QObject* parent) : QObject(parent) , m_networkAccessManager { networkAccessManager } + , m_extractor { std::make_unique() } + , m_compressor { std::make_unique() } { utilPointer = this; // Fix log access vilation on quit QObject::connect(QGuiApplication::instance(), &QGuiApplication::aboutToQuit, this, []() { utilPointer = nullptr; }); - qmlRegisterUncreatableType("ScreenPlay.QMLUtilities", 1, 0, "QMLUtilities", "Error only for enums"); - // In release mode redirect messages to logging otherwhise we break the nice clickable output :( #ifdef QT_NO_DEBUG qInstallMessageHandler(Util::logToGui); @@ -108,10 +108,11 @@ QString Util::toLocal(const QString& url) /*! \brief Exports a given project into a .screenplay 7Zip file. */ -bool Util::exportProject(QString& contentPath, QString& exportPath) +bool Util::exportProject(QString& contentPath, QString& exportFileName) { + m_compressor->clear(); contentPath = ScreenPlayUtil::toLocal(contentPath); - exportPath = ScreenPlayUtil::toLocal(exportPath); + exportFileName = ScreenPlayUtil::toLocal(exportFileName); QDir dir(contentPath); bool success = true; @@ -123,38 +124,27 @@ bool Util::exportProject(QString& contentPath, QString& exportPath) for (auto& item : dir.entryInfoList(QDir::Files)) { files.append(item.absoluteFilePath()); } - m_compressor = std::make_unique(exportPath); + QFile exportFile(exportFileName); + if (exportFile.exists()) { + if (!exportFile.remove()) { + qWarning() << "Unable to delte file marked to override!" << dir; + return false; + } + } + m_compressor->setFileName(exportFileName); m_compressor->setArchiveFormat(QArchive::SevenZipFormat); m_compressor->addFiles(files); - /* Connect Signals with Slots (in this case lambda functions). */ - QObject::connect(m_compressor.get(), &QArchive::DiskCompressor::started, [&]() { - qInfo() << "[+] Starting Compressor... "; - }); - QObject::connect(m_compressor.get(), &QArchive::DiskCompressor::finished, [&]() { - qInfo() << "[+] Compressed File(s) Successfully!"; - - return; - }); - QObject::connect(m_compressor.get(), &QArchive::DiskCompressor::error, [&](short code, QString file) { - qInfo() << "[-] An error has occured :: " << QArchive::errorCodeToString(code) << " :: " << file; - - return; - }); - - QObject::connect(m_compressor.get(), &QArchive::DiskCompressor::progress, [&](QString file, int proc, int total, qint64 br, qint64 bt) { - qInfo() << "Progress::" << file << ":: Done ( " << proc << " / " << total << ") " << (br * 100 / bt) << "%."; - return; - }); - m_compressor->start(); return true; } /*! - \brief Imports a given project from a .screenplay zip file. + \brief Imports a given project from a .screenplay zip file. The argument extractionPath + must be copied otherwise it will get reset in qml before extracting. */ -bool Util::importProject(QString& archivePath, QString& extractionPath) +bool Util::importProject(QString& archivePath, QString extractionPath) { + m_extractor->clear(); archivePath = ScreenPlayUtil::toLocal(archivePath); extractionPath = ScreenPlayUtil::toLocal(extractionPath); @@ -179,33 +169,8 @@ bool Util::importProject(QString& archivePath, QString& extractionPath) return false; } - m_extractor = std::make_unique(archivePath, extractionPath); - - QObject::connect(m_extractor.get(), &QArchive::DiskExtractor::started, [&]() { - qInfo() << "[+] Starting Extractor... "; - }); - QObject::connect(m_extractor.get(), &QArchive::DiskExtractor::finished, [&]() { - qInfo() << "[+] Extracted File(s) Successfully!"; - return; - }); - QObject::connect(m_extractor.get(), &QArchive::DiskExtractor::error, [&](short code) { - if (code == QArchive::ArchivePasswordNeeded || code == QArchive::ArchivePasswordIncorrect) { - return; - } - qInfo() << "[-] An error has occured :: " << QArchive::errorCodeToString(code); - return; - }); - QObject::connect(m_extractor.get(), &QArchive::DiskExtractor::info, [&](QJsonObject info) { - qInfo() << "ARCHIVE CONTENTS:: " << info; - return; - }); - - QObject::connect(m_extractor.get(), &QArchive::DiskExtractor::progress, - [&](QString file, int proc, int total, qint64 br, qint64 bt) { - qInfo() << "Progress(" << proc << "/" << total << "): " - << file << " : " << (br * 100 / bt) << "% done."; - }); - + m_extractor->setArchive(archivePath); + m_extractor->setOutputDirectory(extractionPath); m_extractor->setCalculateProgress(true); m_extractor->getInfo(); m_extractor->start();