1700 lines
50 KiB
C++
Executable File
1700 lines
50 KiB
C++
Executable File
/**
|
|
* @RDE_HEADER@
|
|
* Show the exported scripts and allow them to upload them to the server
|
|
* $Id: tpm_exported_scripts_dialog.cpp 53111 2021-03-22 08:21:29Z wille_m $
|
|
* $HeadURL: svn://slssvn/TDS/branches/product/iterations/21_03_salted_caramel/Lts/Plugins/TakeHomePackageManager/tpm_exported_scripts_dialog.cpp $
|
|
* $Author: wille_m $
|
|
*
|
|
* Created by Gerlinde Rudolphus, NLE, 2019-03-25
|
|
* Rebuilt to use httq by Mike Wille, NLE, 2020-07-07
|
|
*/
|
|
|
|
/* TODO: FIXME: sme problem here!
|
|
* adjust data.json when uploading file, as data.json contains ALL
|
|
* scripts, but we only upload ONE script at a time - so remove all scripts but
|
|
* the one to be uploaded from data.json (but don't touch the main data.json!!)
|
|
*/
|
|
|
|
/*
|
|
*
|
|
* TODO:
|
|
* index.html that searches all exercises instead of stuff
|
|
* copy everything in exercise folder for now until above is done
|
|
* copy situation_map init into share folder
|
|
*
|
|
*/
|
|
|
|
#include "ui_tpm_usb_dialog.h"
|
|
#include "tpm_usb_dialog.h"
|
|
|
|
#include <Common/tpm_process_dialog.h>
|
|
#include <Common/tpm_exported_scripts_model.h>
|
|
#include <nglcompression/nglcomp_compress.h>
|
|
#include <nglsettings/nglsettings_connections.h>
|
|
#include <nglsettings/nglsettings_settings.h>
|
|
#include <LtsWidgets/lts_message_dialog.h>
|
|
#include <LtsWidgets/lts_global_wait_dialog.h>
|
|
#include <LtsSession/lts_session.h>
|
|
#include <LtsSession/lts_session_data.h>
|
|
#include <ngltools/ngltools_filesystem.h>
|
|
|
|
|
|
#include <QDir>
|
|
#include <QFile>
|
|
#include <QJsonArray>
|
|
#include <QJsonDocument>
|
|
#include <QJsonObject>
|
|
#include <QProcess>
|
|
#include <QTemporaryFile>
|
|
#include <QNetworkAccessManager>
|
|
#include <QNetworkRequest>
|
|
#include <QNetworkReply>
|
|
#include <QUrl>
|
|
#include <QWebSocket>
|
|
#include <QLocale>
|
|
#include <QTimer>
|
|
#include <QFileSystemWatcher>
|
|
#include <QTreeView>
|
|
#include <QFuture>
|
|
#include <QFutureWatcher>
|
|
#include <QtCore/qstring.h>
|
|
#include <QtCore/qobject.h>
|
|
#include <QtCore/qfile.h>
|
|
#include <QtCore/qfileinfo.h>
|
|
#include <QtCore/qvector.h>
|
|
#include <QtCore/qthread.h>
|
|
#include <QtConcurrent>
|
|
#include <QDirIterator>
|
|
#include <QFileDialog>
|
|
|
|
#ifdef Q_OS_UNIX
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include <QDebug>
|
|
|
|
#ifdef Q_OS_UNIX
|
|
#define _compiler_warning(x) _Pragma(#x)
|
|
#define compiler_warning(x) _compiler_warning(GCC warning x)
|
|
#define TMP_WARNING_LOG(x) compiler_warning("Temporary warning log active!") \
|
|
WARNING_LOG(x)
|
|
#else
|
|
#define compiler_warning(x)
|
|
#define TMP_WARNING_LOG(x) WARNING_LOG(x)
|
|
#endif
|
|
|
|
#ifndef ELOG
|
|
#define ELOG ERROR_LOG(m_logger) // polyfill
|
|
#endif
|
|
|
|
|
|
namespace TakeHomePackageManager
|
|
{
|
|
|
|
// TODO: mv to LtsCommon
|
|
class AsyncFileCopy : public QObject
|
|
{
|
|
Q_OBJECT
|
|
|
|
public:
|
|
static const int s_chunkSize = 1 * 1024 * 1024;
|
|
|
|
static AsyncFileCopy *createAsyncFileCopy(const QDir &src, const QDir &dst, const QFileInfoList &fil = {}, bool cdUp = true) // to prevent the user from thinking having to delete the class (which it does itself)
|
|
{
|
|
return new AsyncFileCopy(src, dst, fil, cdUp);
|
|
}
|
|
|
|
~AsyncFileCopy() = default;
|
|
|
|
private:
|
|
AsyncFileCopy(const QDir &src, const QDir &dst, const QFileInfoList &fil, bool cdUp)
|
|
|
|
{
|
|
QFutureWatcher<bool> *fw = new QFutureWatcher<bool>(this);
|
|
connect(fw, &QFutureWatcher<bool>::finished,
|
|
this, [this, fw]()
|
|
{
|
|
emit finished(fw->result());
|
|
});
|
|
connect(fw, &QFutureWatcher<bool>::finished,
|
|
this, &QObject::deleteLater); // self-deleting
|
|
fw->setFuture(QtConcurrent::run(this, &AsyncFileCopy::copy, src, dst, fil, cdUp));
|
|
}
|
|
|
|
private:
|
|
#define cancelationPoint if (m_wasCanceled) { qWarning() << "canceled"; return false; } (void)m_wasCanceled
|
|
|
|
bool copy(const QDir &src, const QDir &dst, const QFileInfoList &fil, bool cdUp)
|
|
{
|
|
qint64 totalAll = 0, writtenAll = 0;
|
|
int fileCount = 0;
|
|
|
|
//TODO
|
|
{
|
|
QDirIterator it(src.absolutePath(), QDir::Files | QDir::NoDotAndDotDot,
|
|
QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
|
|
while (it.hasNext())
|
|
{
|
|
const QFileInfo fi(it.next());
|
|
|
|
//qWarning() << "size fi 1" << fi;
|
|
|
|
// copy only files in fil (only if fil is not optional aka. empty)
|
|
|
|
// operator== doesn't work as intended, so no fil.contains(fi) :(
|
|
|
|
if (!fil.isEmpty())
|
|
{
|
|
auto it = std::find_if(fil.constBegin(), fil.constEnd(), [fi](const QFileInfo &_fi)
|
|
{
|
|
// qWarning() << "does" << fi.absoluteFilePath() << "and" << _fi.absoluteFilePath() << "match?";
|
|
return (fi.absoluteFilePath() == _fi.absoluteFilePath());
|
|
});
|
|
|
|
if (it == fil.constEnd())
|
|
continue;
|
|
}
|
|
|
|
//qWarning() << "size fi 2" << fi;
|
|
|
|
totalAll += fi.size();
|
|
fileCount++;
|
|
}
|
|
}
|
|
|
|
//qWarning() << "fileList size" << fileList.size() << fileList;
|
|
//qWarning() << "src for it" << src;
|
|
|
|
int currentFile = 0;
|
|
QDirIterator it(src.absolutePath(), QDir::Files | QDir::NoDotAndDotDot,
|
|
QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
|
|
while (it.hasNext())
|
|
{
|
|
cancelationPoint;
|
|
|
|
const QFileInfo fi(it.next());
|
|
|
|
|
|
|
|
//qWarning() << "copy fi 1" << fi;
|
|
|
|
// copy only files in fil (only if fil is not optional aka. empty)
|
|
|
|
// operator== doesn't work as intended, so no fil.contains(fi) :(
|
|
|
|
if (!fil.isEmpty())
|
|
{
|
|
auto it = std::find_if(fil.constBegin(), fil.constEnd(), [fi](const QFileInfo &_fi)
|
|
{
|
|
return (fi.absoluteFilePath() == _fi.absoluteFilePath());
|
|
});
|
|
|
|
if (it == fil.constEnd())
|
|
continue;
|
|
}
|
|
|
|
//qWarning() << "copy fi 2" << fi;
|
|
|
|
|
|
|
|
currentFile++;
|
|
qint64 totalFile = fi.size(), writtenFile = 0;
|
|
|
|
QDir x(src);
|
|
if (cdUp)
|
|
x.cdUp();
|
|
|
|
QString srcFilePath(fi.absolutePath());
|
|
QString dstFilePath(dst.absolutePath() + QDir::separator() + srcFilePath.remove(x.absolutePath()));
|
|
|
|
if (!QDir().mkpath(dstFilePath) /* why is this not static, Qt? Directory validation? */)
|
|
{
|
|
qWarning() << "cannot create path";
|
|
return false;
|
|
}
|
|
|
|
QFile srcFile(fi.absoluteFilePath());
|
|
QFile dstFile(dstFilePath + QDir::separator() + fi.fileName());
|
|
|
|
// qWarning() << "fi" << fi.absolutePath()
|
|
// << "src" << src.absolutePath()
|
|
// << "x" << x.absolutePath()
|
|
// << "srcFilePath" << srcFilePath
|
|
// << "dstFilePath" << dstFilePath
|
|
// << "srcFile" << fi.absoluteFilePath()
|
|
// << "dstFile" << dstFilePath + QDir::separator() + fi.fileName()
|
|
// ;
|
|
|
|
if (!srcFile.open(QIODevice::ReadOnly))
|
|
{
|
|
qWarning() << QStringLiteral("failed to open src '%1' (error: %2)").arg(srcFile.fileName(), srcFile.errorString());
|
|
continue;
|
|
}
|
|
|
|
if (!dstFile.open(QIODevice::ReadWrite | QIODevice::Truncate))
|
|
{
|
|
qWarning() << QStringLiteral("failed to open dst '%1' (error: %2)").arg(dstFile.fileName(), dstFile.errorString());
|
|
continue;
|
|
}
|
|
|
|
// not needed with mode truncate: dstFile.seek(0); // if file exists, overwrite
|
|
|
|
|
|
|
|
qint64 fileSize = srcFile.size();
|
|
while (fileSize)
|
|
{
|
|
cancelationPoint;
|
|
|
|
const auto data = srcFile.read(s_chunkSize);
|
|
|
|
cancelationPoint;
|
|
|
|
const auto _written = dstFile.write(data);
|
|
qWarning() << "progress" << writtenFile << totalFile << writtenAll << totalAll << currentFile << fileCount;
|
|
emit newAction(tr("Copying..."));
|
|
emit progress(writtenFile, totalFile, writtenAll, totalAll, currentFile, fileCount);
|
|
|
|
|
|
// TODO: windoofs
|
|
#ifdef Q_OS_UNIX
|
|
//qWarning() << "flushing...";
|
|
emit newAction(tr("Writing..."));
|
|
|
|
QElapsedTimer t;
|
|
t.start();
|
|
|
|
fsync(dstFile.handle()); // fsync(2): "The call blocks until the device reports that the transfer has completed."
|
|
|
|
//qWarning() << "took" << t.elapsed() << "ms";
|
|
#endif
|
|
|
|
|
|
if (data.size() == _written)
|
|
{
|
|
writtenFile += _written;
|
|
fileSize -= data.size();
|
|
}
|
|
else
|
|
{
|
|
qWarning() << "cannot write to" << dstFile.fileName() << ":" << dstFile.errorString();
|
|
fileSize = 0;
|
|
break; // skip this operation
|
|
}
|
|
}
|
|
|
|
writtenAll += writtenFile;
|
|
}
|
|
|
|
|
|
if (totalAll == writtenAll)
|
|
{
|
|
qWarning() << "done - " << writtenAll << "/" << totalAll << "bytes";
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
qWarning() << "failed.....";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool m_wasCanceled { false };
|
|
|
|
public slots:
|
|
void slotCancel()
|
|
{
|
|
m_wasCanceled = true;
|
|
}
|
|
|
|
signals:
|
|
void newAction(const QString &newAction);
|
|
void progress(qint64 bytesCopiedFile, qint64 bytesTotalFile, qint64 bytesCopiedAll, qint64 bytesTotalAll, int currentFile, int fileCount);
|
|
void finished(bool success);
|
|
};
|
|
|
|
|
|
|
|
class CopyProcess : public QObject
|
|
{
|
|
Q_OBJECT
|
|
|
|
public:
|
|
CopyProcess(const QDir &src, const QDir &dst, const QFileInfoList &fil = {}, bool cdUp = true, QObject *parent = nullptr)
|
|
: QObject(parent)
|
|
, m_process(new ProcessOverlay)
|
|
{
|
|
connect(m_process, &ProcessOverlay::signalCancel,
|
|
this, [this]()
|
|
{
|
|
//DEBUG_LOG(m_logger) << "m_wasCanceled!!";
|
|
m_wasCanceled = true;
|
|
m_process->setCancelEnabled(false);
|
|
});
|
|
connect(m_process, &ProcessOverlay::signalCancel,
|
|
this, &CopyProcess::signalCancel);
|
|
|
|
AsyncFileCopy *afc = AsyncFileCopy::createAsyncFileCopy(src, dst, fil, cdUp);
|
|
|
|
connect(m_process, &ProcessOverlay::signalCancel,
|
|
afc, &AsyncFileCopy::slotCancel);
|
|
|
|
connect(afc, &AsyncFileCopy::progress,
|
|
this, [this](qint64 bytesCopiedFile, qint64 bytesTotalFile,
|
|
qint64 bytesCopiedAll, qint64 bytesTotalAll,
|
|
int currentFile, int fileCount)
|
|
{
|
|
m_process->setStatus(m_currentAction, fileCount, currentFile, bytesTotalFile, bytesCopiedFile);
|
|
});
|
|
|
|
connect(afc, &AsyncFileCopy::newAction,
|
|
this, [this](const QString &newAction)
|
|
{
|
|
m_currentAction = newAction;
|
|
});
|
|
|
|
connect(afc, &AsyncFileCopy::finished,
|
|
this, &QObject::deleteLater);
|
|
|
|
connect(afc, &AsyncFileCopy::finished,
|
|
this, &CopyProcess::finished);
|
|
}
|
|
|
|
~CopyProcess()
|
|
{
|
|
//DEBUG_LOG(m_logger) << "~CopyProcess";
|
|
delete m_process;
|
|
}
|
|
|
|
QWidget *process() { return m_process; }
|
|
|
|
signals:
|
|
void signalCancel();
|
|
void finished(bool success);
|
|
|
|
private:
|
|
ProcessOverlay *m_process;
|
|
bool m_wasCanceled { false };
|
|
QString m_currentAction;
|
|
|
|
};
|
|
|
|
|
|
// USBDialog
|
|
USBDialog::USBDialog(LtsSession::LtsSession *session, QWidget *parent)
|
|
: LtsWidgets::LtsDialog(parent)
|
|
, m_localThpModel(new ExportedScriptsModel(this))
|
|
, m_usbThpModel(new ExportedScriptsModel(this))
|
|
, m_ui(new Ui::USBDialog)
|
|
, m_session(session)
|
|
//, m_fsWatcher(new QFileSystemWatcher(this))
|
|
, m_exerciseId(m_session->exerciseId())
|
|
, m_paths(0)
|
|
, m_logger(getLogger("USBDialog"))
|
|
{
|
|
m_ui->setupUi(this);
|
|
|
|
connect(m_ui->pbCHange, &QPushButton::clicked,
|
|
this, &USBDialog::slotChangePath);
|
|
|
|
connect(m_ui->pbCopy, &QPushButton::clicked,
|
|
this, &USBDialog::slotCopy);
|
|
connect(m_ui->pbRemove, &QPushButton::clicked,
|
|
this, &USBDialog::slotRemove);
|
|
connect(this, &USBDialog::nextMap,
|
|
this, &USBDialog::onNextMap);
|
|
|
|
connect(m_ui->pbCopyMaps, &QPushButton::clicked,
|
|
this, &USBDialog::slotCopyMaps);
|
|
connect(m_ui->pbRemoveMaps, &QPushButton::clicked,
|
|
this, &USBDialog::slotRemoveMaps);
|
|
connect(m_ui->tvLocal->selectionModel(), &QItemSelectionModel::selectionChanged,
|
|
this, &USBDialog::slotSelectionChanged);
|
|
|
|
// compiler_warning("TODO")
|
|
// auto initModelFn = [this]()
|
|
// {
|
|
// try
|
|
// {
|
|
// initModel();
|
|
// }
|
|
// catch()
|
|
// {
|
|
// ERROR_LOG(m_logger) << "initModel failed";
|
|
// }
|
|
// };
|
|
|
|
// connect(m_fsWatcher, &QFileSystemWatcher::directoryChanged,
|
|
// this, initModelFn);
|
|
// connect(m_fsWatcher, &QFileSystemWatcher::fileChanged,
|
|
// this, initModelFn);
|
|
|
|
initModel(); // throws!
|
|
|
|
setOnlineHelpFilename(QStringLiteral("Script_Editor-Export_Script_Administration"));
|
|
}
|
|
|
|
|
|
USBDialog::~USBDialog()
|
|
{
|
|
delete m_ui;
|
|
}
|
|
|
|
|
|
void USBDialog::initUsbModel()
|
|
{
|
|
m_usbThpModel->clear();
|
|
|
|
if (!m_usbThpModel->init(m_usbRoot))
|
|
{
|
|
new LtsWidgets::LtsMessageDialogNew(this, tr("The directory '%1' doesn't contain valid THP data.").arg(m_usbRoot));
|
|
ERROR_LOG(m_logger) << "cannot initialize usb thp model from usb root" << m_usbRoot;
|
|
return;
|
|
}
|
|
|
|
|
|
QDir tmp;
|
|
tmp.mkpath(getContentFolder(m_usbRoot) + QDir::separator() + QStringLiteral("Maps")); //getContentFolder
|
|
|
|
m_ui->tvUSB->setModel(m_usbThpModel);
|
|
|
|
m_ui->tvUSB->hideColumn(ExportedScriptsModel::ColId);
|
|
m_ui->tvUSB->hideColumn(ExportedScriptsModel::ColLocation);
|
|
m_ui->tvUSB->hideColumn(ExportedScriptsModel::ColUploaded);
|
|
m_ui->tvUSB->hideColumn(ExportedScriptsModel::ColUploadedTime);
|
|
|
|
m_ui->lblPath->setText(m_usbRoot);
|
|
|
|
|
|
// fill usb maps
|
|
{
|
|
m_ui->tvUSBMaps->setRowCount(0);
|
|
m_ui->tvUSBMaps->clearContents();
|
|
QDirIterator it(getContentFolder(m_usbRoot) + QDir::separator() + QStringLiteral("Maps"), QDir::Files | QDir::NoDotAndDotDot,
|
|
QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
|
|
while (it.hasNext())
|
|
{
|
|
QFileInfo fi(it.next());
|
|
m_ui->tvUSBMaps->insertRow(m_ui->tvUSBMaps->rowCount());
|
|
m_ui->tvUSBMaps->setItem(m_ui->tvUSBMaps->rowCount() - 1, 0, new QTableWidgetItem(fi.fileName()));
|
|
if (fi.fileName().endsWith(QStringLiteral("json")) ||
|
|
fi.fileName().endsWith(QStringLiteral("tif")))
|
|
m_ui->tvUSBMaps->hideRow(m_ui->tvUSBMaps->rowCount() -1);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void USBDialog::slotChangePath()
|
|
{
|
|
QFileDialog *dlg = new QFileDialog(this);
|
|
dlg->setFileMode(QFileDialog::Directory);
|
|
dlg->setOption(QFileDialog::ShowDirsOnly);
|
|
dlg->setAttribute(Qt::WA_DeleteOnClose);
|
|
dlg->show();
|
|
|
|
connect(dlg, &QFileDialog::accepted,
|
|
this, [this, dlg]()
|
|
{
|
|
QStringList selected(dlg->selectedFiles());
|
|
|
|
if (selected.count() > 1)
|
|
return;
|
|
|
|
m_usbRoot = selected.first();
|
|
|
|
|
|
if (!QFile::exists(m_usbRoot + QDir::separator() + QStringLiteral("exercises.json")))
|
|
{
|
|
auto m = new LtsWidgets::LtsMessageDialogNew(this, tr("The selected directory '%1' doesn't contain THPs yet. <br />Do you want to initialize the directory now?").arg(m_usbRoot),
|
|
{}, LtsWidgets::Behaviour::Modal, LtsWidgets::Button::Yes | LtsWidgets::Button::Cancel);
|
|
|
|
connect(m, &LtsWidgets::LtsMessageDialogNew::yes,
|
|
this, [this]()
|
|
{
|
|
|
|
QFile exerciseJsonFile(m_usbRoot + QDir::separator() + QStringLiteral("exercises.json"));
|
|
if (!exerciseJsonFile.open(QIODevice::WriteOnly))
|
|
{
|
|
new LtsWidgets::LtsMessageDialogNew(this, tr("The selected directory '%1' can't be written to and the initialization was aborted.").arg(m_usbRoot));
|
|
return;
|
|
}
|
|
|
|
exerciseJsonFile.write(QString(QStringLiteral("{ \"Exercises\": [ %1 ] }\n").arg(m_exerciseId)).toUtf8()); // meh... but quick
|
|
exerciseJsonFile.close();
|
|
|
|
slotCopyWinBin();
|
|
|
|
|
|
//create data.json if it doesn't exist
|
|
std::shared_ptr<ExerciseManagement::Interface::Dtc> exmDtc;
|
|
std::shared_ptr<AssetManagement::Interface::Dtc> asmDtc;
|
|
if (m_session->getDtc<AssetManagement::DataInterface>(asmDtc) &&
|
|
m_session->getDtc<ExerciseManagement::DataInterface>(exmDtc))
|
|
{
|
|
if (!m_usbThpModel->createDataJson(m_usbRoot, exmDtc, asmDtc, m_exerciseId))
|
|
{
|
|
// TODO: user msg
|
|
ERROR_LOG(m_logger) << "Failed to initialize data.json";
|
|
return;
|
|
}
|
|
|
|
TMP_WARNING_LOG(m_logger) << "DONE!";
|
|
}
|
|
else
|
|
TMP_WARNING_LOG(m_logger) << "Failed to get dtcs :( !";
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
initUsbModel();
|
|
generateLayerMappingXML();
|
|
});
|
|
|
|
|
|
}
|
|
|
|
void USBDialog::slotCopyMaps()
|
|
{
|
|
if (m_usbRoot.isEmpty())
|
|
return;
|
|
|
|
m_finished_mapCopys = 0;
|
|
|
|
QDir dstDir(getContentFolder(m_usbRoot) + QDir::separator() + QStringLiteral("Maps")); //getContentFolder for now
|
|
|
|
if (!dstDir.exists())
|
|
{
|
|
ERROR_LOG(m_logger) << "dst dir " << m_usbRoot << " is not accessible";
|
|
return;
|
|
}
|
|
|
|
const QModelIndexList mil = m_ui->tvLocalMaps->selectionModel()->selectedRows();
|
|
if (mil.size() < 1)
|
|
return;
|
|
|
|
|
|
DEBUG_LOG(m_logger) << "mil.size()" << mil.size();
|
|
|
|
|
|
|
|
for (const QModelIndex &idx : mil)
|
|
{
|
|
// qWarning() << "added fi" << idx.data().toString();
|
|
QString abs = m_ui->tvLocalMaps->item(idx.row(), 2)->text() + QDir::separator() + m_ui->tvLocalMaps->item(idx.row(), 1)->text();
|
|
m_files[m_ui->tvLocalMaps->item(idx.row(), 2)->text()] += abs;
|
|
// qWarning() << "the fi" << QFileInfo(idx.data().toString());
|
|
}
|
|
//add elevations
|
|
//Eleveation
|
|
// We will copy all elevations every time!
|
|
QDirIterator eleit(QStringLiteral("/usr/share/arcgis/resources/elevation/"), QDir::Files | QDir::NoDotAndDotDot,
|
|
QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
|
|
while (eleit.hasNext())
|
|
{
|
|
QString file = eleit.next();
|
|
QFileInfo eleFile(file);
|
|
if (!eleFile.fileName().toLower().endsWith(QStringLiteral(".tif")))
|
|
continue;
|
|
m_files[eleFile.absolutePath()] += eleFile.absoluteFilePath();
|
|
}
|
|
|
|
m_paths = m_files.count();
|
|
|
|
//ProcessOverlay* overlay = new ProcessOverlay();
|
|
//setOverlay(overlay);
|
|
|
|
//overlay->setStatus(QStringLiteral("Copy Maps"), paths, 1);
|
|
//overlay->setCancelEnabled(false);
|
|
|
|
|
|
emit nextMap();
|
|
|
|
}
|
|
|
|
void USBDialog::onNextMap()
|
|
{
|
|
auto path = m_files.firstKey();
|
|
{
|
|
QString src(path);
|
|
DEBUG_LOG(m_logger) << "src" << src;
|
|
|
|
QDir srcDir(src);
|
|
|
|
if (!srcDir.exists())
|
|
{
|
|
ERROR_LOG(m_logger) << "src dir " << src << " is not accessible";
|
|
return;
|
|
}
|
|
QDir dstDir(getContentFolder(m_usbRoot) + QDir::separator() + QStringLiteral("Maps")); //getContentFolder for now
|
|
|
|
auto cp = new CopyProcess(srcDir, dstDir, m_files[path], false, this);
|
|
setOverlay(cp->process());
|
|
connect(cp, &CopyProcess::finished,
|
|
this, [this, path](bool success)
|
|
{
|
|
if (!success)
|
|
new LtsWidgets::LtsMessageDialogNew(this, tr("The files could not be copied successfully from path! ") + path);
|
|
m_finished_mapCopys++;
|
|
m_files.remove(path);
|
|
// overlay->setStatus(QStringLiteral("Copy Maps"), paths, m_finished_mapCopys);
|
|
if(m_paths == m_finished_mapCopys)
|
|
{
|
|
// overlay->deleteLater();
|
|
new LtsWidgets::LtsMessageDialogNew(this, tr("The files were copied successfully from path: ") + path);
|
|
initUsbModel();
|
|
generateLayerMappingXML();
|
|
m_finished_mapCopys = 0;
|
|
}else
|
|
emit nextMap();
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
void USBDialog::generateLayerMappingXML()
|
|
{
|
|
QJsonObject layers;
|
|
|
|
QJsonArray elevationUrls;
|
|
QJsonArray mapUrls;
|
|
QJsonArray sceneObjectUrls;
|
|
QJsonArray sceneUrls;
|
|
|
|
|
|
for(int i = 0; i < m_ui->tvUSBMaps->rowCount(); ++i)
|
|
{
|
|
QFileInfo fileInfo = QFileInfo(m_usbRoot + QDir::separator() + m_ui->tvUSBMaps->item(i,0)->text());
|
|
|
|
if(fileInfo.suffix() == QStringLiteral("tif") || fileInfo.suffix() == QStringLiteral("dt2"))
|
|
{
|
|
QJsonObject elavation;
|
|
elavation.insert(QStringLiteral("description"), QJsonValue(QStringLiteral("Elevation")));
|
|
elavation.insert(QStringLiteral("maxScale"), QJsonValue(-1));
|
|
elavation.insert(QStringLiteral("minScale"), QJsonValue(-1));
|
|
elavation.insert(QStringLiteral("name"), QJsonValue(fileInfo.baseName()));
|
|
elavation.insert(QStringLiteral("url"), QJsonValue(fileInfo.fileName()));
|
|
elevationUrls.push_back(elavation);
|
|
}
|
|
else if (fileInfo.suffix() == QStringLiteral("slpk"))
|
|
{
|
|
QJsonObject objectScene;
|
|
objectScene.insert(QStringLiteral("description"), QJsonValue(QStringLiteral("ObjectScene")));
|
|
objectScene.insert(QStringLiteral("maxScale"), QJsonValue(125));
|
|
objectScene.insert(QStringLiteral("minScale"), QJsonValue(-1));
|
|
objectScene.insert(QStringLiteral("altitudeOffset"), QJsonValue(0.5));
|
|
objectScene.insert(QStringLiteral("name"), QJsonValue(fileInfo.baseName()));
|
|
objectScene.insert(QStringLiteral("url"), QJsonValue(fileInfo.fileName()));
|
|
sceneObjectUrls.push_back(objectScene);
|
|
}
|
|
else if(fileInfo.suffix() == QStringLiteral("tpk"))
|
|
{
|
|
//Maps when WebMerc dann Scene sonst map
|
|
if(fileInfo.fileName().contains(QStringLiteral("WebMerc")))
|
|
{
|
|
QJsonObject scene;
|
|
scene.insert(QStringLiteral("description"), QJsonValue(QStringLiteral("Scene")));
|
|
scene.insert(QStringLiteral("maxScale"), QJsonValue(125));
|
|
scene.insert(QStringLiteral("minScale"), QJsonValue(-1));
|
|
scene.insert(QStringLiteral("name"), QJsonValue(fileInfo.baseName()));
|
|
scene.insert(QStringLiteral("url"), QJsonValue(fileInfo.fileName()));
|
|
sceneUrls.push_back(scene);
|
|
|
|
}
|
|
else
|
|
{
|
|
QJsonObject map;
|
|
map.insert(QStringLiteral("description"), QJsonValue(QStringLiteral("Map")));
|
|
map.insert(QStringLiteral("maxScale"), QJsonValue(125));
|
|
map.insert(QStringLiteral("minScale"), QJsonValue(-1));
|
|
map.insert(QStringLiteral("name"), QJsonValue(fileInfo.baseName()));
|
|
map.insert(QStringLiteral("url"), QJsonValue(fileInfo.fileName()));
|
|
mapUrls.push_back(map);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
layers.insert(QStringLiteral("elevationUrls"), elevationUrls);
|
|
layers.insert(QStringLiteral("mapUrls"), mapUrls);
|
|
layers.insert(QStringLiteral("sceneObjectUrls"), sceneObjectUrls);
|
|
layers.insert(QStringLiteral("sceneUrls"), sceneUrls);
|
|
|
|
QFile layersMapping(getContentFolder(m_usbRoot) + QDir::separator() + QStringLiteral("layerMapping.json"));//getContentFolder
|
|
QFile layersMappingAtMaps(getContentFolder(m_usbRoot) + QDir::separator() + QStringLiteral("Maps") + QDir::separator() + QStringLiteral("layerMapping.json"));
|
|
|
|
if (!layersMapping.open(QIODevice::WriteOnly))
|
|
{
|
|
WARNING_LOG(m_logger) << "Couldn't open save file.";
|
|
return;
|
|
}
|
|
if (!layersMappingAtMaps.open(QIODevice::WriteOnly))
|
|
{
|
|
WARNING_LOG(m_logger) << "Couldn't open save file in folder Maps.";
|
|
return;
|
|
}
|
|
QJsonDocument doc(layers);
|
|
|
|
layersMapping.write(doc.toJson());
|
|
layersMappingAtMaps.write(doc.toJson());
|
|
}
|
|
|
|
QFileInfoList USBDialog::prepareFileInfoListForFolder(QString src, QFileInfoList fil)
|
|
{
|
|
|
|
QDir dir(src);
|
|
if (! dir.exists())
|
|
return fil;
|
|
|
|
for (QString d : dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot))
|
|
{
|
|
fil = prepareFileInfoListForFolder(src+ QDir::separator() + d, fil);
|
|
}
|
|
|
|
for (QString f : dir.entryList(QDir::Files))
|
|
{
|
|
fil += QString(src + QDir::separator() + f);
|
|
}
|
|
return fil;
|
|
}
|
|
|
|
void USBDialog::slotCopyWinBin()
|
|
{
|
|
if (m_usbRoot.isEmpty())
|
|
return;
|
|
|
|
QDir dstDir(getContentFolder(m_usbRoot) + QDir::separator()); //getContentFolder
|
|
|
|
if (!dstDir.exists())
|
|
{
|
|
dstDir.mkpath(QStringLiteral("."));
|
|
}
|
|
|
|
|
|
//TODO richtiger Pfad
|
|
QString src(QStringLiteral("/opt/THP/"));
|
|
|
|
QDir srcDir(src);
|
|
|
|
if (!srcDir.exists())
|
|
{
|
|
ERROR_LOG(m_logger) << "src dir " << src << " is not accessible";
|
|
return;
|
|
}
|
|
|
|
QFileInfoList fil;
|
|
fil = prepareFileInfoListForFolder(src, fil);
|
|
DEBUG_LOG(m_logger) << "XXXXXXXXXXX filsize is: " << fil.size();
|
|
|
|
auto cp = new CopyProcess(srcDir, dstDir, fil, false, this);
|
|
setOverlay(cp->process());
|
|
|
|
connect(cp, &CopyProcess::finished,
|
|
this, [this](bool success)
|
|
{
|
|
if (success)
|
|
{
|
|
// new LtsWidgets::LtsMessageDialogNew(this, tr("The files were copied successfully"));
|
|
slotCopyWinBinP2();
|
|
}
|
|
else
|
|
new LtsWidgets::LtsMessageDialogNew(this, tr("The files could not be copied successfully!"));
|
|
});
|
|
|
|
|
|
}
|
|
|
|
void USBDialog::slotCopyWinBinP2()
|
|
{
|
|
if (m_usbRoot.isEmpty())
|
|
return;
|
|
|
|
QDir dstDir(getContentFolder(m_usbRoot) + QDir::separator()); //getContentFolder
|
|
|
|
if (!dstDir.exists())
|
|
{
|
|
dstDir.mkpath(QStringLiteral("."));
|
|
}
|
|
const QString initFilePath = NGL::findFile(NGL::FileType::ConfigFile, QStringLiteral("situation_map_defaults.ini"));
|
|
|
|
if (initFilePath.isEmpty())
|
|
{
|
|
ERROR_LOG(m_logger) << "sitmap init is not accessible";
|
|
return;
|
|
}
|
|
|
|
QFileInfo fileInfo(initFilePath);
|
|
QFileInfoList fil2;
|
|
fil2.append(fileInfo);
|
|
DEBUG_LOG(m_logger) << "XXXXXXXXXXX sitmap is: " << fileInfo.absoluteFilePath();
|
|
ERROR_LOG(m_logger) << "XXXXXXXXXXX filsize is: " << fil2.size();
|
|
// QString x = QDir::separator() + QStringLiteral("thp") + QDir::separator() + QStringLiteral("share") + QDir::separator() + QStringLiteral("situation_Map");
|
|
|
|
const QDir dstDir2(dstDir.absolutePath() + QDir::separator() + QStringLiteral("thp") + QDir::separator() + QStringLiteral("share") + QDir::separator() + QStringLiteral("LTS") + QDir::separator() + QStringLiteral("situation_Map")) ;
|
|
DEBUG_LOG(m_logger) << "XXXXXXXXXXX sitmap dst is: " << dstDir2.absolutePath();
|
|
|
|
auto cp2 = new CopyProcess(fileInfo.absoluteDir(), dstDir2, fil2, false, this);
|
|
setOverlay(cp2->process());
|
|
|
|
connect(cp2, &CopyProcess::finished,
|
|
this, [this](bool success)
|
|
{
|
|
if (success)
|
|
{
|
|
// new LtsWidgets::LtsMessageDialogNew(this, tr("The files were copied successfully"));
|
|
slotCopyWinBinP3();
|
|
}
|
|
else
|
|
new LtsWidgets::LtsMessageDialogNew(this, tr("The files could not be copied successfully!"));
|
|
});
|
|
}
|
|
|
|
void USBDialog::slotCopyWinBinP3()
|
|
{
|
|
if (m_usbRoot.isEmpty())
|
|
return;
|
|
|
|
QDir dstDir(getContentFolder(m_usbRoot) + QDir::separator()); //getContentFolder
|
|
|
|
if (!dstDir.exists())
|
|
{
|
|
dstDir.mkpath(QStringLiteral("."));
|
|
}
|
|
|
|
const QString autorunFile = NGL::findFile(NGL::FileType::ConfigFile, QStringLiteral("autorun.inf"));
|
|
if (autorunFile.isEmpty())
|
|
{
|
|
ERROR_LOG(m_logger) << "autorunFile init is not accessible";
|
|
return;
|
|
}
|
|
|
|
QFileInfo fileInfo2(autorunFile);
|
|
QFileInfoList fil;
|
|
fil = prepareFileInfoListForFolder(fileInfo2.absoluteDir().path(), fil);
|
|
DEBUG_LOG(m_logger) << "XXXXXXXXXXX autofinFile is: " << fileInfo2.absoluteDir().path();
|
|
DEBUG_LOG(m_logger) << "XXXXXXXXXXX filsize is: " << fil.size();
|
|
|
|
auto cp3 = new CopyProcess(fileInfo2.absoluteDir(), dstDir, fil, false, this);
|
|
setOverlay(cp3->process());
|
|
|
|
connect(cp3, &CopyProcess::finished,
|
|
this, [this](bool success)
|
|
{
|
|
if (success)
|
|
{
|
|
// new LtsWidgets::LtsMessageDialogNew(this, tr("The files were copied successfully"));
|
|
initUsbModel();
|
|
}
|
|
else
|
|
new LtsWidgets::LtsMessageDialogNew(this, tr("The files could not be copied successfully!"));
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
class JsonHandler
|
|
{
|
|
public:
|
|
JsonHandler() // invalid
|
|
{}
|
|
|
|
|
|
// JsonHandler(JsonHandler &&o)
|
|
// {
|
|
// m_changesMade = o.m_changesMade;
|
|
// m_exists = o.m_exists;
|
|
// m_root = o.m_root;
|
|
// m_f = o.m_f;
|
|
// m_filepath = o.m_filepath;
|
|
// m_filename = o.m_filename;
|
|
// }
|
|
|
|
|
|
JsonHandler(const QString &filename, const QString &root, bool mustExist = true)
|
|
: m_f(new QFile)
|
|
, m_filename(filename)
|
|
{
|
|
m_filepath = QString(root + QDir::separator() + filename);
|
|
|
|
if (!QFile::exists(m_filepath) && mustExist)
|
|
throw QStringLiteral("%1 does not exist in given root").arg(filename);
|
|
|
|
m_f->setFileName(m_filepath);
|
|
|
|
if (!m_f->open(QIODevice::ReadWrite))
|
|
throw QStringLiteral("Cannot open %1").arg(filename);
|
|
|
|
qint64 s = m_f->size();
|
|
if (s == 0 && mustExist)
|
|
throw QStringLiteral("%1 is empty").arg(filename);
|
|
|
|
if (s == 0)
|
|
return;
|
|
|
|
QJsonParseError err;
|
|
QJsonDocument doc(QJsonDocument::fromJson(m_f->readAll(), &err));
|
|
|
|
if (err.error != QJsonParseError::NoError)
|
|
throw QStringLiteral("Json malformed: %1 - offset %2").arg(err.errorString(), err.offset);
|
|
|
|
m_root = doc.object();
|
|
|
|
if (m_root.isEmpty())
|
|
throw QStringLiteral("%1's object is empty").arg(filename);
|
|
|
|
m_exists = true;
|
|
// }
|
|
// else // since exericses.json is empty / did not exist, create the 'Exercises' array now
|
|
// m_root[s_exercisesObjectIdentifier] = QJsonObject();
|
|
|
|
// QJsonValue exercisesArrayValue = m_root[s_exercisesObjectIdentifier];
|
|
|
|
// if (s > 0 && exercisesArrayValue.isUndefined()) // aka. not found (even though the json file has valid json in it)
|
|
// throw "exercises.json's object is missing the 'Exercises' array";
|
|
|
|
// if (s == 0 && !mustExist)
|
|
// m_changesMade = true; // NOTE: can only be set if no throw might follow as the d_tor would write to the file is changesMade == true, despite an error
|
|
}
|
|
|
|
|
|
virtual ~JsonHandler()
|
|
{
|
|
std::shared_ptr<int> _(nullptr, [this](int *) { delete m_f; }); // TODO: QScopeGuard
|
|
|
|
if (m_f == nullptr || !m_f->isOpen() || !m_changesMade)
|
|
return;
|
|
|
|
//m_root[s_exercisesObjectIdentifier] = m_exercisesArray;
|
|
|
|
// move to start of file and set file size to zero
|
|
m_f->reset();
|
|
m_f->resize(0);
|
|
|
|
m_f->write(QJsonDocument(m_root).toJson(QJsonDocument::JsonFormat::Compact));
|
|
}
|
|
|
|
|
|
protected:
|
|
bool m_changesMade { false };
|
|
bool m_exists { false };
|
|
QJsonObject m_root;
|
|
|
|
|
|
private:
|
|
QFile *m_f { nullptr };
|
|
QString m_filepath;
|
|
QString m_filename;
|
|
};
|
|
|
|
|
|
class ExerciseJsonHandler : public JsonHandler
|
|
{
|
|
public:
|
|
ExerciseJsonHandler(const QString &root, bool mustExist = true)
|
|
: JsonHandler(QStringLiteral("exercises.json"), root, mustExist)
|
|
{
|
|
QJsonValue exercisesArrayValue(m_root[s_exercisesArrayIdentifier]);
|
|
|
|
if (m_exists && exercisesArrayValue.isUndefined()) // aka. not found (even though the json file has valid json in it)
|
|
throw "exercises.json's object is missing the 'Exercises' array";
|
|
|
|
m_exercisesArray = m_root[s_exercisesArrayIdentifier].toArray();
|
|
|
|
if (!m_exists)
|
|
m_changesMade = true; // NOTE: can only be set if no throw might follow as the d_tor would write to the file if changesMade == true, despite an error
|
|
}
|
|
|
|
|
|
~ExerciseJsonHandler()
|
|
{
|
|
m_root[s_exercisesArrayIdentifier] = m_exercisesArray;
|
|
}
|
|
|
|
|
|
void addExercise(int exerciseId)
|
|
{
|
|
m_exercisesArray.append(exerciseId);
|
|
m_changesMade = true;
|
|
}
|
|
|
|
|
|
bool removeExercise(int exerciseId)
|
|
{
|
|
auto it = std::find_if(m_exercisesArray.begin(), m_exercisesArray.end(),
|
|
[exerciseId](const QJsonValue &val)
|
|
{
|
|
bool k = false;
|
|
int v = val.toVariant().toInt(&k);
|
|
if (!k)
|
|
return false; // ouch
|
|
|
|
return (v == exerciseId);
|
|
});
|
|
|
|
bool ret = (it != m_exercisesArray.end());
|
|
if (ret)
|
|
{
|
|
m_exercisesArray.erase(it);
|
|
m_changesMade = true;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
private:
|
|
static const QString s_exercisesArrayIdentifier;
|
|
QJsonArray m_exercisesArray; // sadly a QJsonValueRef cannot be used here :(
|
|
};
|
|
|
|
|
|
const QString ExerciseJsonHandler::s_exercisesArrayIdentifier = QStringLiteral("Exercises");
|
|
|
|
|
|
class DataJsonHandler : public JsonHandler
|
|
{
|
|
public:
|
|
DataJsonHandler() // invalid
|
|
{}
|
|
|
|
|
|
// DataJsonHandler(DataJsonHandler &&o)
|
|
// {
|
|
// m_presentationsArray = o.m_presentationsArray;
|
|
// }
|
|
|
|
|
|
DataJsonHandler(const QString &root, int exerciseId/*, bool mustExist = true*/)
|
|
: JsonHandler(QStringLiteral("data.json"), QStringLiteral("%1/%2").arg(root).arg(USBDialog::exerciseDirectoryName(exerciseId)), true)
|
|
{
|
|
QJsonValue scriptsArrayValue(m_root[s_presentationsArrayIdentifier]);
|
|
|
|
if (m_exists && scriptsArrayValue.isUndefined()) // aka. not found (even though the json file has valid json in it)
|
|
throw "data.json's object is missing the 'presentations' array";
|
|
|
|
m_presentationsArray = m_root[s_presentationsArrayIdentifier].toArray();
|
|
|
|
if (!m_exists)
|
|
m_changesMade = true; // NOTE: can only be set if no throw might follow as the d_tor would write to the file if changesMade == true, despite an error
|
|
}
|
|
|
|
|
|
~DataJsonHandler()
|
|
{
|
|
m_root[s_presentationsArrayIdentifier] = m_presentationsArray;
|
|
}
|
|
|
|
|
|
// void addScript(int scriptId) nah... once deleted, data cannot be restored - re-copy data.json instead
|
|
// {
|
|
// m_presentationsArray.append(scriptId);
|
|
// m_changesMade = true;
|
|
// }
|
|
|
|
|
|
QVector<int> scripts()
|
|
{
|
|
QVector<int> ret;
|
|
|
|
for (const QJsonValue &val : qAsConst(m_presentationsArray))
|
|
{
|
|
QJsonObject scriptObject(val.toObject());
|
|
|
|
bool k = false;
|
|
int v = scriptObject[QStringLiteral("scriptId")].toVariant().toInt(&k);
|
|
if (!k)
|
|
continue; // ouch
|
|
|
|
ret += v;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
bool removeScript(int scriptId)
|
|
{
|
|
auto it = std::find_if(m_presentationsArray.begin(), m_presentationsArray.end(),
|
|
[scriptId](const QJsonValue &val)
|
|
{
|
|
QJsonObject scriptObject(val.toObject());
|
|
|
|
bool k = false;
|
|
int v = scriptObject[QStringLiteral("scriptId")].toVariant().toInt(&k);
|
|
if (!k)
|
|
return false; // ouch
|
|
|
|
return (v == scriptId);
|
|
});
|
|
|
|
bool ret = (it != m_presentationsArray.end());
|
|
if (ret)
|
|
{
|
|
m_presentationsArray.erase(it);
|
|
m_changesMade = true;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
private:
|
|
static const QString s_presentationsArrayIdentifier;
|
|
QJsonArray m_presentationsArray; // sadly a QJsonValueRef cannot be used here :(
|
|
};
|
|
|
|
|
|
const QString DataJsonHandler::s_presentationsArrayIdentifier = QStringLiteral("presentations");
|
|
|
|
|
|
void USBDialog::adjustJsonFiles(What what)
|
|
{
|
|
/* Problem: data.json has a weird location and has to be updated if a script was copied.
|
|
* As it has all presentations in it that were ever exported, data.json needs to be adjusted
|
|
* if a script was deleted or copied.
|
|
*
|
|
* Use-cases:
|
|
*
|
|
* Script was deleted from exercise:
|
|
* Check if other scripts exists in exercise, otherwise delete exercise directory, delete exercise from exercises.json
|
|
*
|
|
* Exercise diretory was deleted (by removing the last existing script from an exericse):
|
|
* New exercise directory was created (by copying a script):
|
|
* Rewrite exercises.json by checking what exercise directories exist
|
|
*
|
|
* Script was copied to an existing exercise:
|
|
* re-copy exercise's data.json and remove all scripts that don't exist in the exercise's directory
|
|
*
|
|
*/
|
|
|
|
switch (what)
|
|
{
|
|
case What::ScriptWasDeletedFromExercise:
|
|
{
|
|
// // look into exercise folder and check if there are script dirs left
|
|
// QDir dir(getContentFolder(m_usbRoot));
|
|
// dir.setFilter(QDir::Filter::Dirs | QDir::Filter::NoDotAndDotDot);
|
|
|
|
// const QFileInfoList fil(dir.entryInfoList());
|
|
// if (fil.isEmpty())
|
|
// {
|
|
// DEBUG_LOG(m_logger) << "no scripts left";
|
|
// /* ... determined by checking if there are directories, as scripts are
|
|
// * in their own directories and scripts are the only directories in an
|
|
// * exercise directory */
|
|
|
|
// // delete exercise directory
|
|
// dir.removeRecursively(); // TODO: hangs.. should be async?? + error handling
|
|
|
|
// // finally remove exercise from exercise.json
|
|
// try
|
|
// {
|
|
// ExerciseJsonHandler(m_usbRoot).removeExercise(m_exerciseId);
|
|
// }
|
|
// catch (const QString &str)
|
|
// {
|
|
// ERROR_LOG(m_logger) << "ExerciseJsonHandler failed: " << str;
|
|
// break;
|
|
// }
|
|
|
|
// break;
|
|
// }
|
|
|
|
// // there are scripts left, only remove script from exercise dir's data.json
|
|
// try
|
|
// {
|
|
// DataJsonHandler(m_usbRoot, m_exerciseId).removeScript(m_exerciseId);
|
|
// }
|
|
// catch (const QString &str)
|
|
// {
|
|
// ERROR_LOG(m_logger) << "ExerciseJsonHandler failed: " << str;
|
|
// break;
|
|
// }
|
|
|
|
// break;
|
|
}
|
|
|
|
case What::ExerciseDirectoryWasDeleted:
|
|
case What::NewExerciseDirectoryWasCreated:
|
|
{
|
|
|
|
|
|
break;
|
|
}
|
|
|
|
case What::ScriptWasCopiedToExercise:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void USBDialog::scriptDeleted(int scriptId)
|
|
{
|
|
// look into exercise folder and check if there are script dirs left
|
|
QDir dir(m_usbRoot);
|
|
dir.setFilter(QDir::Filter::Dirs | QDir::Filter::NoDotAndDotDot);
|
|
|
|
const QFileInfoList fil(dir.entryInfoList());
|
|
if (fil.isEmpty())
|
|
{
|
|
DEBUG_LOG(m_logger) << "no scripts left";
|
|
/* ... determined by checking if there are directories, as scripts are
|
|
* in their own directories and scripts are the only directories in an
|
|
* exercise directory */
|
|
|
|
// delete exercise directory
|
|
dir.removeRecursively(); // TODO: hangs.. should be async?? + error handling
|
|
|
|
// finally remove exercise from exercise.json
|
|
try
|
|
{
|
|
ExerciseJsonHandler(m_usbRoot).removeExercise(m_exerciseId);
|
|
}
|
|
catch (const QString &str)
|
|
{
|
|
ERROR_LOG(m_logger) << "ExerciseJsonHandler failed: " << str;
|
|
return;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// there are scripts left, only remove script from exercise dir's data.json
|
|
try
|
|
{
|
|
DataJsonHandler(m_usbRoot, m_exerciseId).removeScript(scriptId);
|
|
}
|
|
catch (const QString &str)
|
|
{
|
|
ERROR_LOG(m_logger) << "DataJsonHandler failed: " << str;
|
|
}
|
|
}
|
|
|
|
|
|
void USBDialog::scriptCopied(int /*scriptId*/)
|
|
{
|
|
compiler_warning("TODO: re-copy data.json ???")
|
|
// open data.json and remove all 'presentation's that do not exist on the USB
|
|
|
|
// get list of scripts that exist on the USB
|
|
QDir dir(getContentFolder(m_usbRoot));
|
|
dir.setFilter(QDir::Filter::Dirs | QDir::Filter::NoDotAndDotDot);
|
|
|
|
const QFileInfoList fil(dir.entryInfoList());
|
|
Q_ASSERT_X(!fil.isEmpty(), "", "no scripts on USB"); // Logic failure: No scripts on the USB, but missing presentations in data.json isn't possible as a deleted script would have triggered a deletion of the whole exercise directory
|
|
|
|
|
|
QVector<int> existingScripts;
|
|
|
|
for (const QFileInfo &fi : fil)
|
|
{
|
|
bool k = false;
|
|
int val = fi.fileName().toInt(&k);
|
|
|
|
if (!k)
|
|
continue; // hmm.. the only allowed directories are script directories, which have integer-convertible filenames
|
|
|
|
existingScripts += val;
|
|
}
|
|
|
|
|
|
QVector<int> usbScripts;
|
|
DataJsonHandler djh; // invalid
|
|
|
|
try
|
|
{
|
|
// get list of scripts on usb
|
|
djh = DataJsonHandler(m_usbRoot, m_exerciseId);
|
|
}
|
|
catch (const QString &str)
|
|
{
|
|
ERROR_LOG(m_logger) << "DataJsonHandler failed: " << str;
|
|
return;
|
|
}
|
|
|
|
usbScripts = djh.scripts();
|
|
|
|
// delete all scripts from data.json that do not exist on USB
|
|
// prepare usbScripts list to only have the script ids now that DON'T exist on usb
|
|
usbScripts.erase(std::remove_if(usbScripts.begin(), usbScripts.end(),
|
|
[existingScripts](int id)
|
|
{
|
|
return (std::find(existingScripts.begin(), existingScripts.end(), id) != existingScripts.end());
|
|
}), usbScripts.end());
|
|
|
|
|
|
// now really delete scripts from data.json
|
|
for (const int scriptId : qAsConst(usbScripts))
|
|
djh.removeScript(scriptId);
|
|
|
|
|
|
// delete all script directories of scripts that do not exist on USB
|
|
for (const QFileInfo &fi : fil)
|
|
{
|
|
bool k = false;
|
|
int val = fi.fileName().toInt(&k);
|
|
|
|
if (!k || !usbScripts.contains(val))
|
|
continue;
|
|
|
|
fi.absoluteDir().removeRecursively();
|
|
}
|
|
}
|
|
|
|
|
|
void USBDialog::slotCopy()
|
|
{
|
|
if (m_usbRoot.isEmpty())
|
|
return;
|
|
|
|
QDir dstDir(m_usbRoot);
|
|
|
|
if (!dstDir.exists())
|
|
{
|
|
ERROR_LOG(m_logger) << "dst dir " << m_usbRoot << " is not accessible";
|
|
return;
|
|
}
|
|
|
|
dstDir = QDir(getContentFolder(m_usbRoot));
|
|
|
|
if (!dstDir.mkpath(dstDir.absolutePath())) // NOTE: returns true if path already exists
|
|
{
|
|
ERROR_LOG(m_logger) << "Could not create destination path " << dstDir.absolutePath();
|
|
return;
|
|
}
|
|
|
|
qWarning() << "dst" << dstDir.absolutePath();
|
|
|
|
const QModelIndexList mil = m_ui->tvLocal->selectionModel()->selectedRows();
|
|
if (mil.size() != 1)
|
|
return;
|
|
|
|
ExportedScriptInformation esi = m_localThpModel->getRowData(mil.first().row());
|
|
|
|
if (!QDir(esi.rootDirectory).exists())
|
|
return;
|
|
|
|
QString src(getScriptFolder(esi.rootDirectory, esi.exerciseId, esi.scriptId));
|
|
DEBUG_LOG(m_logger) << "src" << src;
|
|
|
|
QDir srcDir(src);
|
|
QDir bldgDir( QStringLiteral("%1/BuildingModels").arg(getContentFolder(esi.rootDirectory, esi.exerciseId)));
|
|
|
|
if (!srcDir.exists())
|
|
{
|
|
ERROR_LOG(m_logger) << "src dir " << src << " is not accessible";
|
|
return;
|
|
}
|
|
|
|
auto cp = new CopyProcess(srcDir, dstDir, {}, true, this);
|
|
//copy buildings secretly
|
|
auto cpBuildings = new CopyProcess(bldgDir, dstDir, {}, true, this);
|
|
cpBuildings->process();
|
|
|
|
setOverlay(cp->process());
|
|
|
|
connect(cp, &CopyProcess::finished,
|
|
this, [this, esi](bool success)
|
|
{
|
|
if (!success)
|
|
{
|
|
new LtsWidgets::LtsMessageDialogNew(this, tr("The files could not be copied successfully!"));
|
|
return;
|
|
}
|
|
|
|
new LtsWidgets::LtsMessageDialogNew(this, tr("The files were copied successfully"));
|
|
|
|
TMP_WARNING_LOG(m_logger) << "GO GO GO";
|
|
ExportedScriptInformation esi2(esi);
|
|
esi2.rootDirectory = m_usbRoot; // edit esi to point to the new rootdir (the usb)
|
|
m_localThpModel->addDownloadData(esi2);
|
|
initUsbModel(); // fill !
|
|
|
|
// scriptCopied(esi.scriptId);
|
|
});
|
|
}
|
|
|
|
|
|
void USBDialog::slotRemove()
|
|
{
|
|
// this operation is fast enough to NOT do it async
|
|
|
|
if (m_usbRoot.isEmpty())
|
|
return;
|
|
|
|
const QModelIndexList mil = m_ui->tvUSB->selectionModel()->selectedRows();
|
|
if (mil.size() < 1)
|
|
return;
|
|
|
|
|
|
DEBUG_LOG(m_logger) << "mil.size()" << mil.size();
|
|
|
|
|
|
for (const QModelIndex &idx : mil)
|
|
{
|
|
int scriptId = idx.data(Qt::UserRole).toInt();
|
|
// qWarning() << "rm" << scriptId;
|
|
// qWarning() << "rm'd?" << QDir(getScriptFolder(m_usbRoot, m_exerciseId, scriptId)).removeRecursively();
|
|
scriptDeleted(scriptId);
|
|
}
|
|
|
|
initUsbModel(); // instead of deleting the rows, just reinit the whole model... ugh, but quickly done
|
|
}
|
|
|
|
|
|
void USBDialog::slotRemoveMaps()
|
|
{
|
|
// this operation is fast enough to NOT do it async
|
|
|
|
if (m_usbRoot.isEmpty())
|
|
return;
|
|
|
|
QDir dir(getContentFolder(m_usbRoot) + QDir::separator() + QStringLiteral("Maps")); //getContentFolder
|
|
|
|
if (!dir.exists())
|
|
{
|
|
ERROR_LOG(m_logger) << "dir " << m_usbRoot << " is not accessible";
|
|
return;
|
|
}
|
|
|
|
const QModelIndexList mil = m_ui->tvUSBMaps->selectionModel()->selectedRows();
|
|
if (mil.size() < 1)
|
|
return;
|
|
|
|
|
|
DEBUG_LOG(m_logger) << "mil.size()" << mil.size();
|
|
|
|
|
|
for (const QModelIndex &idx : mil)
|
|
{
|
|
DEBUG_LOG(m_logger) << "rm" << idx.data().toString();
|
|
QFile::remove(dir.absolutePath() + QDir::separator() + idx.data().toString());
|
|
}
|
|
|
|
|
|
|
|
initUsbModel(); // instead of deleting the rows, just reinit the whole model... ugh, but quickly done
|
|
generateLayerMappingXML();
|
|
}
|
|
|
|
|
|
void USBDialog::slotSelectionChanged()
|
|
{
|
|
const QModelIndexList mil = m_ui->tvLocal->selectionModel()->selectedRows();
|
|
|
|
// compiler_warning("TODO")
|
|
// if (mil.size() != 1)
|
|
// {
|
|
// m_buttonBox->getButton(LtsWidgets::DialogButton::Show)->setEnabled(false); // Show presentation Button
|
|
// m_buttonBox->getButton(LtsWidgets::DialogButton::Remove)->setEnabled(false); // local remove Button
|
|
// m_buttonBox->getButton(LtsWidgets::DialogButton::Save)->setEnabled(false); // download from Server
|
|
// m_buttonBox->getButton(LtsWidgets::DialogButton::Import)->setEnabled(false); // Remove from Server Button
|
|
// return;
|
|
// }
|
|
|
|
// const ExportedScriptInformation esi = m_localThpModel->getRowData(mil.first().row());
|
|
// if (m_session->app() != LtsAppDescriptor::App::OC)
|
|
// {
|
|
// if (esi.local)
|
|
// {
|
|
// m_buttonBox->getButton(LtsWidgets::DialogButton::Remove)->setEnabled(true); // local remove Button
|
|
// m_buttonBox->getButton(LtsWidgets::DialogButton::Save)->setEnabled(true); // upload to Server
|
|
// }
|
|
// else
|
|
// {
|
|
// m_buttonBox->getButton(LtsWidgets::DialogButton::Remove)->setEnabled(false); // local remove Button
|
|
// m_buttonBox->getButton(LtsWidgets::DialogButton::Save)->setEnabled(false); // upload to Server
|
|
// }
|
|
// if (esi.uploaded == ThpAlreadyUploaded)
|
|
// {
|
|
// m_buttonBox->getButton(LtsWidgets::DialogButton::Import)->setEnabled(true); // Remove from Server Button
|
|
// }
|
|
// else
|
|
// {
|
|
// m_buttonBox->getButton(LtsWidgets::DialogButton::Import)->setEnabled(false); // Remove from Server Button
|
|
// }
|
|
// }
|
|
// else // OC
|
|
// {
|
|
// if (esi.uploaded == ThpAlreadyUploaded)
|
|
// {
|
|
// m_buttonBox->getButton(LtsWidgets::DialogButton::Save)->setEnabled(true); // download from Server
|
|
// m_buttonBox->getButton(LtsWidgets::DialogButton::Import)->setEnabled(true); // Remove from Server Button
|
|
// }
|
|
// else
|
|
// {
|
|
// m_buttonBox->getButton(LtsWidgets::DialogButton::Import)->setEnabled(false); // Remove from Server Button
|
|
// }
|
|
// if (esi.local)
|
|
// {
|
|
// m_buttonBox->getButton(LtsWidgets::DialogButton::Show)->setEnabled(true); // Show presentation Button
|
|
// m_buttonBox->getButton(LtsWidgets::DialogButton::Remove)->setEnabled(true); // local remove Button
|
|
// }
|
|
// else
|
|
// {
|
|
// m_buttonBox->getButton(LtsWidgets::DialogButton::Show)->setEnabled(false); // Show presentation Button
|
|
// m_buttonBox->getButton(LtsWidgets::DialogButton::Remove)->setEnabled(false); // local remove Button
|
|
// }
|
|
// }
|
|
}
|
|
|
|
|
|
void USBDialog::retranslate()
|
|
{
|
|
translate(m_ui, tr("Manage exported scripts"));
|
|
}
|
|
|
|
|
|
void USBDialog::initModel()
|
|
{
|
|
DEBUG_LOG(m_logger) << "(re)initModel !!";
|
|
|
|
// TODO: move to model
|
|
// compiler_warning("TODO")
|
|
// m_fsWatcher->addPath(getRootFolder());
|
|
// m_fsWatcher->addPath(getContentFolder(m_session->exerciseId()));
|
|
|
|
if (!m_localThpModel->init(getRootFolder()))
|
|
{
|
|
throw "init model failed for Export";
|
|
if (!m_localThpModel->init(getRootFolder(true))) // Download directory
|
|
throw "init model failed for Download";
|
|
}
|
|
|
|
m_ui->tvLocal->setModel(m_localThpModel); // important to set model before setting hidden columns
|
|
|
|
m_ui->tvLocal->hideColumn(ExportedScriptsModel::ColId);
|
|
m_ui->tvLocal->hideColumn(ExportedScriptsModel::ColLocation);
|
|
m_ui->tvLocal->hideColumn(ExportedScriptsModel::ColUploaded);
|
|
m_ui->tvLocal->hideColumn(ExportedScriptsModel::ColUploadedTime);
|
|
|
|
|
|
|
|
// fill local maps
|
|
{
|
|
m_ui->tvLocalMaps->setRowCount(0);
|
|
m_ui->tvLocalMaps->clearContents();
|
|
|
|
|
|
//Maps when WebMerc dann Scene sonst map
|
|
QDirIterator tpit(QStringLiteral("/usr/share/arcgis/resources/TilePackage/"), QDir::Files | QDir::NoDotAndDotDot,
|
|
QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
|
|
while (tpit.hasNext())
|
|
{
|
|
QString file = tpit.next();
|
|
m_ui->tvLocalMaps->insertRow(m_ui->tvLocalMaps->rowCount());
|
|
QFileInfo mapFile(file);
|
|
m_ui->tvLocalMaps->setItem(m_ui->tvLocalMaps->rowCount() - 1, 0, mapFile.fileName().contains(QStringLiteral("WebMerc")) ? new QTableWidgetItem(tr("3D Map")) : new QTableWidgetItem(tr("2D Map")) );
|
|
m_ui->tvLocalMaps->setItem(m_ui->tvLocalMaps->rowCount() - 1, 1, new QTableWidgetItem(mapFile.fileName()));
|
|
m_ui->tvLocalMaps->setItem(m_ui->tvLocalMaps->rowCount() - 1, 2, new QTableWidgetItem(QFileInfo(file).absolutePath()));
|
|
}
|
|
|
|
//ObjectScene
|
|
QDirIterator slpkit(QStringLiteral("/usr/share/arcgis/resources/slpk/"), QDir::Files | QDir::NoDotAndDotDot,
|
|
QDirIterator::Subdirectories);
|
|
while (slpkit.hasNext())
|
|
{
|
|
QString file = slpkit.next();
|
|
if (!file.endsWith(QStringLiteral(".slpk")))
|
|
continue;
|
|
QFileInfo fi(file);
|
|
if(fi.isSymLink())
|
|
continue;
|
|
|
|
m_ui->tvLocalMaps->insertRow(m_ui->tvLocalMaps->rowCount());
|
|
m_ui->tvLocalMaps->setItem(m_ui->tvLocalMaps->rowCount() - 1, 0, new QTableWidgetItem(tr("3D Object")));
|
|
m_ui->tvLocalMaps->setItem(m_ui->tvLocalMaps->rowCount() - 1, 1, new QTableWidgetItem(QFileInfo(file).fileName()));
|
|
m_ui->tvLocalMaps->setItem(m_ui->tvLocalMaps->rowCount() - 1, 2, new QTableWidgetItem(QFileInfo(file).absolutePath()));
|
|
}
|
|
|
|
//Eleveation
|
|
// We will copy all elevations every time!
|
|
// QDirIterator eleit(QStringLiteral("/usr/share/arcgis/resources/elevation/"), QDir::Files | QDir::NoDotAndDotDot,
|
|
// QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
|
|
// while (eleit.hasNext())
|
|
// {
|
|
// QString file = eleit.next();
|
|
// m_ui->tvLocalMaps->insertRow(m_ui->tvLocalMaps->rowCount());
|
|
// m_ui->tvLocalMaps->setItem(m_ui->tvLocalMaps->rowCount() - 1, 0, new QTableWidgetItem(QStringLiteral("Eleveation")));
|
|
// m_ui->tvLocalMaps->setItem(m_ui->tvLocalMaps->rowCount() - 1, 1, new QTableWidgetItem(QFileInfo(file).fileName()));
|
|
// // m_ui->tvLocalMaps->setItem(m_ui->tvLocalMaps->rowCount() - 1, 2, new QTableWidgetItem(QFileInfo(file).absolutePath()));
|
|
// }
|
|
|
|
|
|
}
|
|
m_ui->tvLocalMaps->sortItems(0);
|
|
|
|
m_ui->tvLocalMaps->hideColumn(2);
|
|
slotSelectionChanged(); // aka. enaDisa
|
|
}
|
|
|
|
|
|
QString USBDialog::getContentFolder(const QString &rootFolder) const
|
|
{
|
|
return QStringLiteral("%1/%2").arg(rootFolder, exerciseDirectoryName());
|
|
}
|
|
|
|
|
|
QString USBDialog::getContentFolder() const
|
|
{
|
|
return getContentFolder(getRootFolder(), m_exerciseId);
|
|
}
|
|
|
|
|
|
/*static*/ QString USBDialog::exerciseDirectoryName(int exerciseId)
|
|
{
|
|
return QStringLiteral("Exercise_%1").arg(exerciseId);
|
|
}
|
|
|
|
|
|
QString USBDialog::exerciseDirectoryName() const
|
|
{
|
|
return exerciseDirectoryName(m_exerciseId);
|
|
}
|
|
|
|
|
|
/*static*/ QString USBDialog::getContentFolder(const QString &rootFolder, int exerciseId)
|
|
{
|
|
return QStringLiteral("%1/%2").arg(rootFolder, exerciseDirectoryName(exerciseId));
|
|
}
|
|
|
|
|
|
QString USBDialog::getContentFolder(int exerciseId) const
|
|
{
|
|
return getContentFolder(getRootFolder(), exerciseId);
|
|
}
|
|
|
|
|
|
/* static */ QString USBDialog::getScriptFolder(const QString &rootFolder, int exerciseId, int scriptId)
|
|
{
|
|
return QStringLiteral("%1/%2").arg(getContentFolder(rootFolder, exerciseId)).arg(scriptId);
|
|
}
|
|
|
|
|
|
QString USBDialog::getScriptFolder(const QString &rootFolder, int scriptId) const
|
|
{
|
|
return getScriptFolder(rootFolder, m_exerciseId, scriptId);
|
|
}
|
|
|
|
|
|
QString USBDialog::getScriptFolder(int scriptId) const
|
|
{
|
|
return getScriptFolder(getRootFolder(), m_exerciseId, scriptId);
|
|
}
|
|
|
|
|
|
QString USBDialog::getRootFolder(bool forceDownloadDir) const
|
|
{
|
|
if (m_session->app() == LtsAppDescriptor::App::OC || forceDownloadDir)
|
|
return QDir::tempPath() + QStringLiteral("/THP-Download/");
|
|
|
|
return QDir::tempPath() + QStringLiteral("/THP-Export/");
|
|
}
|
|
|
|
|
|
} // namespace TakeHomePackageManager
|
|
|
|
|
|
#include "tpm_usb_dialog.moc"
|