diff --git a/Desktop/components/JASP/Widgets/MainWindow.qml b/Desktop/components/JASP/Widgets/MainWindow.qml index 784ebe7560..45f6242bd2 100644 --- a/Desktop/components/JASP/Widgets/MainWindow.qml +++ b/Desktop/components/JASP/Widgets/MainWindow.qml @@ -87,25 +87,58 @@ Window return (a + n) % n; } - DropArea - { - id: drop - enabled: true - anchors.fill: parent - onDropped: (drop) => mainWindow.openURLFile(drop.text) - } - Item { anchors.fill: parent Rectangle { + id: warningRect z: 1 - visible: mainWindow.hadFatalError - color: jaspTheme.red + color: mainWindow.hadFatalError ? jaspTheme.red : "transparent" opacity: 0.75 anchors.fill: parent + + DropArea + { + enabled: true + anchors.fill: parent + onDropped: (drop) => + { + if (mainWindow.openURLFile(drop.text)) + drop.accepted = true + parent.state = "" + } + + onExited: parent.state = "" + onEntered: (drag) => + { + if (drag.hasText) + parent.state = "active" + } + } + + states: [ + State { + name: "active" + PropertyChanges { + warningRect { + color: jaspTheme.blueLighter + } + } + } + ] + + transitions: [ + Transition { + from: "" + to: "active" + reversible: true + ColorAnimation { properties: "color"; duration: 150; easing.type: Easing.InOutQuad } + } + ] + + } Shortcut { onActivated: mainWindow.showEnginesWindow(); sequences: ["Ctrl+Alt+Shift+E"]; context: Qt.ApplicationShortcut; } diff --git a/Desktop/data/datasetpackage.cpp b/Desktop/data/datasetpackage.cpp index 59e03e744f..98bbbd7538 100644 --- a/Desktop/data/datasetpackage.cpp +++ b/Desktop/data/datasetpackage.cpp @@ -2523,7 +2523,7 @@ bool DataSetPackage::filePathIsNonSaveable(const QString & path) const { QFileInfo fileDir(path); - return fileDir.dir() == QDir(AppDirs::examples()) || fileDir.dir() == QDir(AppDirs::autoSaveDir()); + return path.startsWith(AppDirs::examples()) || fileDir.dir() == QDir(AppDirs::autoSaveDir()); } void DataSetPackage::setAnalysesData(const Json::Value &analysesData) diff --git a/Desktop/data/fileevent.cpp b/Desktop/data/fileevent.cpp index 5d605d494e..ce3187a9eb 100644 --- a/Desktop/data/fileevent.cpp +++ b/Desktop/data/fileevent.cpp @@ -23,9 +23,10 @@ #include "exporters/dataexporter.h" #include "exporters/jaspexporter.h" #include "exporters/resultexporter.h" +#include "widgets/filemenu/filemenu.h" +#include "log.h" - -FileEvent::FileEvent(QObject *parent, FileEvent::FileMode fileMode) +FileEvent::FileEvent(FileMenu *parent, FileEvent::FileMode fileMode) : QObject(parent), _operation(fileMode) { switch (_operation) @@ -36,6 +37,9 @@ FileEvent::FileEvent(QObject *parent, FileEvent::FileMode fileMode) case FileEvent::FileSave: _exporter = new JASPExporter(); break; default: _exporter = nullptr; break; } + + // The FileMenu handles the started signal + connect(this, &FileEvent::started, parent, &FileMenu::fileEventRequestDispatcher); } FileEvent::~FileEvent() @@ -93,19 +97,59 @@ bool FileEvent::setPath(const QString & path) } +void FileEvent::setStarted() +{ + if (isStarted()) + { + Log::log() << "Try to start event '" << getProgressMsg().toStdString() << "', but it was already started!" << std::endl; + return; + } + + _status = EventStatus::EventStarted; + + emit started(); +} + void FileEvent::setComplete(bool success, const QString & message) { - _completed = true; + if (isCompleted()) + { + Log::log() << "Try to set complete event '" << getProgressMsg().toStdString() << "', but it was already completed!" << std::endl; + return; + } + + _status = EventStatus::EventCompleted; _success = success; _message = message; - emit completed(this); + emit completed(); +} + +void FileEvent::setFinalized() +{ + if (isFinalized()) + { + Log::log() << "Try to set finalize event '" << getProgressMsg().toStdString() << "', but it was already finalized!" << std::endl; + return; + } + + _status = EventStatus::EventFinalized; + emit finalized(); + deleteLater(); } + void FileEvent::chain(FileEvent *event) { - _chainedTo = event; - connect(event, &FileEvent::completed, this, &FileEvent::chainedComplete); + if (isStarted()) + { + if (isCompleted()) + Log::log() << "Event " << getProgressMsg().toStdString() << " is completed before Event " << event->getProgressMsg().toStdString() << " was completed" << std::endl; + else // The `this` event is already started, but it should be set complete (and start the finalize process) only when `event` is finalized + connect(event, &FileEvent::finalized, this, [this, event]() { setComplete(event->isSuccessful(), event->message()); }); + } + else + connect(event, &FileEvent::finalized, this, [this, event]() { if (event->isSuccessful()) emit started(); } ); } bool FileEvent::isExample() const diff --git a/Desktop/data/fileevent.h b/Desktop/data/fileevent.h index 03e617c9e3..847fe99611 100644 --- a/Desktop/data/fileevent.h +++ b/Desktop/data/fileevent.h @@ -25,6 +25,8 @@ #include "exporters/exporter.h" #include "utilenums.h" +class FileMenu; + /// /// This class is used to handle the communication to and from the asynchronous loading/synching/saving file processes. /// These can be chained to have a Close come after a save once the latter is done. @@ -34,8 +36,10 @@ class FileEvent : public QObject public: enum FileMode { FileSave, FileNew, FileOpen, FileExportResults, FileExportData, FileGenerateData, FileSyncData, FileClose }; + enum EventStatus { EventInitialized, EventStarted, EventCompleted, EventFinalized }; + - FileEvent(QObject *parent = nullptr, FileMode fileMode = FileEvent::FileOpen); + FileEvent(FileMenu *parent, FileMode fileMode = FileEvent::FileOpen); virtual ~FileEvent(); bool setPath( const QString & path); @@ -45,14 +49,18 @@ class FileEvent : public QObject void setFileType( Utils::FileType type) { _type = type; } void setTmp( bool saveTmp) { _tmp = saveTmp; } + void setStarted(); void setComplete(bool success = true, const QString &message = ""); + void setFinalized(); void chain(FileEvent *event); bool isDatabase() const { return _database != Json::nullValue; } bool isOnlineNode() const { return _path.startsWith("http"); } bool isExample() const; bool isReadOnly() const { return isExample() || isDatabase(); } - bool isCompleted() const { return _completed; } + bool isStarted() const { return _status != EventStatus::EventInitialized; } + bool isCompleted() const { return _status == EventStatus::EventCompleted || _status == EventStatus::EventFinalized; } + bool isFinalized() const { return _status == EventStatus::EventFinalized; } bool isSuccessful() const { return _success; } bool isTmp() const { return _tmp; } static bool autoSaveExists(); @@ -74,10 +82,9 @@ class FileEvent : public QObject QString getProgressMsg() const; signals: - void completed(FileEvent *event); - -private slots: - void chainedComplete(FileEvent *event) { setComplete(event->isSuccessful(), event->message()); } + void started(); + void completed(); + void finalized(); private: FileMode _operation; @@ -87,10 +94,9 @@ private slots: _dataFilePath, _last_error = "Unknown error", _message; - bool _completed = false, - _success = false, + EventStatus _status = EventStatus::EventInitialized; + bool _success = false, _tmp = false; - FileEvent * _chainedTo = nullptr; Exporter * _exporter = nullptr; Json::Value _database = Json::nullValue; }; diff --git a/Desktop/main.cpp b/Desktop/main.cpp index 61b785a4bb..9ec57567e7 100644 --- a/Desktop/main.cpp +++ b/Desktop/main.cpp @@ -31,18 +31,12 @@ #include "utilities/imgschemehandler.h" #include #include "utilities/appdirs.h" +#include "parsedarguments.h" #ifdef linux #include "utilities/qmlutils.h" #endif -const std::string jaspExtension = ".jasp", - unitTestArg = "--unitTest", - saveArg = "--save", - timeOutArg = "--timeOut=", - junctionArg = "--junctions", - removeJunctionsArg = "--removeJunctions"; - #ifdef _WIN32 #include "utilities/dynamicruntimeinfo.h" #include "utilities/processhelper.h" @@ -114,230 +108,12 @@ bool runJaspEngineJunctionFixer(int argc, char *argv[], bool removeJunctions = f #endif -#ifdef _WIN32 -#define QSTRING_FILE_ARG QString::fromLocal8Bit -#else -#define QSTRING_FILE_ARG QString::fromStdString -#endif - - -void parseArguments(int argc, char *argv[], std::string & filePath, bool & newData, bool & unitTest, bool & dirTest, int & timeOut, bool & save, bool & logToFile, bool & hideJASP, bool & safeGraphics, bool & containerSettingForced, bool & container, Json::Value & dbJson, QString & reportingDir) -{ - filePath = ""; - unitTest = false; - dirTest = false; - save = false; - logToFile = false; - hideJASP = false; - safeGraphics = false; - newData = false; - containerSettingForced = false; - container = false; - reportingDir = ""; - timeOut = 10; - dbJson = Json::nullValue; - - bool letsExplainSomeThings = false; - - std::vector args(argv + 1, argv + argc); // make the arguments a little less annoying to work with - - for(int arg = 0; arg < args.size(); arg++) - { - if(args[arg] == saveArg) save = true; - else if(args[arg] == "--help" || args[arg] == "-h") letsExplainSomeThings = true; - else if(args[arg] == "--logToFile") logToFile = true; - else if(args[arg] == "--hide") hideJASP = true; - else if(args[arg] == "--safeGraphics") safeGraphics = true; - else if(args[arg] == "--newData") newData = true; -#ifdef _WIN32 - else if(args[arg] == "--sandbox") { containerSettingForced = true; container = true; } - else if(args[arg] == "--noSandbox") { containerSettingForced = true; container = false; } - else if(args[arg] == junctionArg) runJaspEngineJunctionFixer(argc, argv, false); //Run the junctionfixer, it will exit the application btw! - else if(args[arg] == removeJunctionsArg) runJaspEngineJunctionFixer(argc, argv, true); //Remove the junctions -#endif - else if(args[arg] == "--unitTestRecursive") - { - if(arg >= args.size() - 1) - letsExplainSomeThings = true; - else - { - QDir folder(QSTRING_FILE_ARG(args[arg + 1].c_str())); - - if(!folder.exists()) - { - std::cerr << "Folder for unitTestRecursive " << folder.absolutePath().toStdString() << " does not exist!" << std::endl; - exit(1); - } - - dirTest = true; - filePath = args[arg + 1]; - } - } - else if(args[arg] == unitTestArg) - { - if(arg >= args.size() - 1) - { - std::cerr << "Argument for unit test file missing!" << std::endl; - letsExplainSomeThings = true; - } - else - { - filePath = args[arg + 1]; - - QFileInfo testMe(QSTRING_FILE_ARG(filePath.c_str())); - - if(!testMe.exists()) - { - std::cerr << "File for unitTest " << testMe.absoluteFilePath().toStdString() << " does not exist!" << std::endl; - letsExplainSomeThings = true; - } - - bool isJaspFile = filePath.size() >= jaspExtension.size() && filePath.substr(filePath.size() - jaspExtension.size()) == jaspExtension; - - if(!isJaspFile && !letsExplainSomeThings) - { - std::cerr << "File for unitTest " << filePath << " is not a JASP file!" << std::endl; - letsExplainSomeThings = true; - } - - unitTest = true; - } - } - else if(args[arg] == "--report") - { - if(arg >= args.size() - 1) - { - std::cerr << "Argument for reporting directory missing!" << std::endl; - letsExplainSomeThings = true; - } - else - { - arg++; - std::string dirPath = args[arg]; - - QDir testMe(QSTRING_FILE_ARG(dirPath.c_str())); - testMe.mkpath("."); - - if(!testMe.exists()) - { - std::cerr << "Directory to write reports to " << testMe.absolutePath().toStdString() << " does not exist and cannot be created!" << std::endl; - letsExplainSomeThings = true; - } - else - reportingDir = testMe.absolutePath(); - } - } - else if(args[arg].size() > timeOutArg.size() && args[arg].substr(0, timeOutArg.size()) == timeOutArg) - { - std::string time = timeOutArg.substr(timeOutArg.size()); - size_t convertedChars = 0; - int convertedTime = 0; - try { convertedTime = std::stoi(time, &convertedChars); } - catch(std::invalid_argument &) {} - catch(std::out_of_range &) {} - - if(convertedChars > 0) - timeOut = convertedTime; - } - else - { - const std::string remoteDebuggingPort = "--remote-debugging-port=", - webEngineArgs = "--webEngineArgs", // This is apparently necessary to use in front of --remote-debugging-port nowadays, see: https://doc.qt.io/qt-6/qtwebengine-debugging.html - qmlJsDebug = "-qmljsdebugger", - dashing = "--", - psn = "-psn"; - - static bool foundWebEngineArgs = false; - - auto startsWith = [&](const std::string checkThis) - { - return args[arg].size() >= checkThis.size() && args[arg].substr(0, checkThis.size()) == checkThis; - }; - - if(args[arg] == "-platform") - arg++; // because it is always followed by the actual platform one wants to use (minimal for example) - else if(startsWith(webEngineArgs)) - foundWebEngineArgs = true; - else if(startsWith(remoteDebuggingPort) && !foundWebEngineArgs) - { - std::cerr << "If you want to use a remote debugging port enter the following: '--webEngineArgs --remote-debugging-port=12345' otherwise webengine will ignore it." << std::endl; - exit(2); - } - else if(!(startsWith(remoteDebuggingPort) || startsWith(qmlJsDebug))) //Just making sure it isnt something else that should be allowed. - { - if(startsWith(dashing)) - { - //So, it looks like an option, but not one we recognize, maybe it is meant for qt/chromium - std::cout << "Argument '" << args[arg] << "' was not recognized by JASP, but it might be recognized by one of it's components in Qt (such as chromium), it will be passed on. If you really expected JASP to do something with it check '--help' again." << std::endl; - } -#ifdef __APPLE__ - else if(startsWith(psn)) // https://github.com/jasp-stats/jasp-test-release/issues/1945 - { - //this is one of those arguments to ignore... - std::cout << "Ignoring " << args[arg] << std::endl; - } -#endif - else - { - //if it isn't anything else it must be a file to open right? - // Well yes, but it might also be the url of an OSF file, then we do not need to check if it exists. - // And also, it might be a "database json" which needs to be handled a bit more involved - - QFileInfo openMe(QSTRING_FILE_ARG(args[arg].c_str())); - - if(startsWith("https:") || startsWith("http:") || openMe.exists()) - filePath = args[arg]; - else - { - //Check whether it can be parsed as a json and if so assume it is a database connection json as returned by DatabaseConnectionInfo - - Json::Reader jsonReader; - - if(!jsonReader.parse(args[arg], dbJson)) - { - std::cerr << "File to open " << args[arg] << " does not exist (and also is not a (database) json)!" << std::endl; - letsExplainSomeThings = true; - } - } - } - } - } - } - - if(filePath == "" && reportingDir != "") - { - std::cerr << "If you want JASP to run in reportingmode you should also give it a jaspfile to run off." << std::endl; - letsExplainSomeThings = true; - } - if(letsExplainSomeThings) - { - std::cerr << "JASP can be started without arguments, or the following: { --help | -h | filename | --unitTest filename | --unitTestRecursive folder | --save | --timeOut=10 | --logToFile | --hide } \n" - << "If a filename is supplied JASP will try to load it. \nIf --unitTest is specified JASP will refresh all analyses in \"filename\" (which must be a JASP file) and see if the output remains the same and will then exit with an errorcode indicating succes or failure.\n" - << "If --unitTestRecursive is specified JASP will go through specified \"folder\" and perform a --unitTest on each JASP file. After it has done this it will exit with an errorcode indication succes or failure.\n" - << "For both testing arguments there is the optional --save argument, which specifies that JASP should save the file after refreshing it.\n" - << "For both testing arguments there is the optional --timeout argument, which specifies how many minutes JASP will wait for the analyses-refresh to take. Default is 10 minutes.\n" - << "If --logToFile is specified then JASP will try it's utmost to write logging to a file, this might come in handy if you want to figure out why JASP does not start in case of a bug.\n" - << "If --hide is specified then JASP will not be shown during recursive testing or reporting.\n" - << "If --safeGraphics is specified then JASP will be started with software rendering enabled, this will be saved to your settings.\n" - << "If --report is specified then JASP will be started in reporting mode, which requires a path to where you would like to store the results. This is usually used in conjunction with a service/daemon and in that case it might make sense to also pass --hide. Don't forget to also pass a jasp filename otherwise it won't have anything to run...\n" - #ifdef _WIN32 - << "If --junctions is specified JASP will recreate the junctions in Modules/ to renv-cache/, this needs to be done at least once after install, but is usually triggered automatically." - << "In case one really wants the engines to be sandboxed specify --sandbox, otherwise use --noSandbox." - #endif - << "This text will be shown when either --help or -h is specified or something else that JASP does not understand is given as argument.\n" - << std::flush; - - exit(1); - } -} #define SEPARATE_PROCESS -void recursiveFileOpener(QFileInfo file, int & failures, int & total, int & timeOut, int argc, char *argv[], bool save, bool hideJASP) +void recursiveFileOpener(const QFileInfo & file, int & failures, int & total, const ParsedArguments& arguments, char * JASPName) { - const QString jaspExtension(".jasp"); - //std::cout << "recursiveFileOpener in " << file.absoluteFilePath().toStdString() << std::endl; if(file.isDir()) @@ -349,14 +125,14 @@ void recursiveFileOpener(QFileInfo file, int & failures, int & total, int & time //std::cout << "QDir dir: " << dir.path().toStdString() << " has " << files.size() << " subfiles!" << std::endl; for(QFileInfo & subFile : dir.entryInfoList(QDir::Filter::NoDotAndDotDot | QDir::Files | QDir::Dirs)) - recursiveFileOpener(subFile, failures, total, timeOut, argc, argv, save, hideJASP); + recursiveFileOpener(subFile, failures, total, arguments, JASPName); } else if(file.isFile()) { //std::cout << "it is a file" << std::endl; - if(file.absoluteFilePath().indexOf(jaspExtension) == file.absoluteFilePath().size() - jaspExtension.size()) + if (Utils::getTypeFromFileName(file.absoluteFilePath().toStdString()) == Utils::FileType::jasp) { //std::cout << "it has the .jasp extension so we will start JASP" << std::endl; #ifndef SEPARATE_PROCESS @@ -368,22 +144,22 @@ void recursiveFileOpener(QFileInfo file, int & failures, int & total, int & time try{ #ifdef SEPARATE_PROCESS QProcess subJasp; - subJasp.setProgram(argv[0]); - QStringList arguments({"--unitTest", file.absoluteFilePath()}); + subJasp.setProgram(JASPName); + QStringList processArguments({tq(arguments.unitTestArg), file.absoluteFilePath()}); - if(save) - arguments << "--save"; + if(arguments.save) + processArguments << tq(arguments.saveArg); - arguments << QString::fromStdString("--timeOut="+std::to_string(timeOut)); + processArguments << tq(arguments.timeOutArg) + QString::number(arguments.timeOut); - if(hideJASP) - arguments << "-platform" << "minimal"; + if(arguments.hideJASP) + processArguments << tq(arguments.hideArg); - std::cout << "Starting subJASP with args: " << arguments.join(' ').toStdString() << std::endl; - subJasp.setArguments(arguments); + std::cout << "Starting subJASP with args: " << fq(processArguments.join(' ')) << std::endl; + subJasp.setArguments(processArguments); subJasp.start(); - subJasp.waitForFinished((timeOut * 60000) + 10000); + subJasp.waitForFinished((arguments.timeOut * 60000) + 10000); std::cerr << subJasp.readAllStandardError().toStdString() << std::endl; @@ -406,6 +182,62 @@ void recursiveFileOpener(QFileInfo file, int & failures, int & total, int & time } } +void recursiveSyncDataFile(const QFileInfo& fileInfo, std::vector& dataFiles) +{ + if (fileInfo.exists() && fileInfo.isDir()) + { + QDir dir(fileInfo.absoluteFilePath()); + for(QFileInfo & subFile : dir.entryInfoList(QDir::Filter::NoDotAndDotDot | QDir::Files | QDir::Dirs)) + { + if (subFile.isDir()) + recursiveSyncDataFile(subFile, dataFiles); + else if (ParsedArguments::isDataFileType(subFile.fileName())) + dataFiles.push_back(subFile); + } + } +} + +void syncDataFiles(const ParsedArguments& arguments, char* jaspName) +{ + std::vector dataFiles = arguments.dataFiles; + + recursiveSyncDataFile(arguments.inputDataDir, dataFiles); + + for (const QFileInfo& dataFile : dataFiles) + { + try{ + QProcess subJasp; + subJasp.setProgram(jaspName); + QStringList subArguments({arguments.mainFilePath.absoluteFilePath(), dataFile.absoluteFilePath()}); + + if(arguments.outputDir.exists()) + subArguments << tq(arguments.outputDirArg) << arguments.outputDir.absolutePath(); + + if (arguments.outputPdf) + subArguments << tq(arguments.outputPdfArg); + + if(arguments.save) + subArguments << tq(arguments.saveArg); + + if(arguments.hideJASP) + subArguments << tq(arguments.hideArg); + + subArguments << tq(arguments.timeOutArg) + QString::number(arguments.timeOut); + + + std::cout << "Starting subJASP with args: " << fq(subArguments.join(' ')) << std::endl; + subJasp.setArguments(subArguments); + subJasp.start(); + + subJasp.waitForFinished((arguments.timeOut * 60000) + 10000); + + std::cerr << subJasp.readAllStandardError().toStdString() << std::endl; + } + catch(...) { } + } + +} + void qtMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { const char *file = context.file ? context.file : ""; @@ -429,20 +261,6 @@ void qtMessageHandler(QtMsgType type, const QMessageLogContext &context, const Q int main(int argc, char *argv[]) { - std::string filePath; - QString reportingDir; - bool unitTest, - dirTest, - save, - logToFile, - hideJASP, - safeGraphics, - containForce, - contain, - newData; - int timeOut; - Json::Value dbJson; - qInstallMessageHandler(qtMessageHandler); #ifdef _WIN32 @@ -463,152 +281,151 @@ int main(int argc, char *argv[]) #endif Dirs::setLocalAppdataDir(AppDirs::appData(false).toStdString()); - parseArguments(argc, argv, filePath, newData, unitTest, dirTest, timeOut, save, logToFile, hideJASP, safeGraphics, containForce, contain, dbJson, reportingDir); + ParsedArguments arguments(argc, argv); - if(safeGraphics) Settings::setValue(Settings::SAFE_GRAPHICS_MODE, true); - else safeGraphics = Settings::value(Settings::SAFE_GRAPHICS_MODE).toBool(); + if(arguments.safeGraphics) Settings::setValue(Settings::SAFE_GRAPHICS_MODE, true); + else arguments.safeGraphics = Settings::value(Settings::SAFE_GRAPHICS_MODE).toBool(); - if(containForce) Settings::setValue(Settings::ENGINE_SANDBOX, contain); - else contain = Settings::value(Settings::ENGINE_SANDBOX).toBool(); + if(arguments.containerSettingForced) Settings::setValue(Settings::ENGINE_SANDBOX, arguments.container); + else arguments.container = Settings::value(Settings::ENGINE_SANDBOX).toBool(); - if(reportingDir!="") Settings::setValue(Settings::REPORT_SHOW, true); + if(arguments.reportingDir.exists()) Settings::setValue(Settings::REPORT_SHOW, true); - QString filePathQ(QSTRING_FILE_ARG(filePath.c_str())); + if(arguments.unitTestRecursive) + { + int failures = 0, + total = 0; - //Now, to allow us to add some arguments we store the ones we got in a vector - std::vector args(argv, argv + argc); + recursiveFileOpener(arguments.mainFilePath, failures, total, arguments, argv[0]); -// std::filesystem::path::imbue(std::locale( std::locale(), new std::codecvt_utf8_utf16() ) ); This is not needed anymore since we set the locale to UTF8 + if(total == 0) + { + std::cerr << "Couldn't find any jasp-files in specified directory " << arguments.mainFilePath.absoluteFilePath() << ", it is be treated as a failure to notify you of this!" << std::endl; + exit(2); + } - if(!dirTest) - //try + if(failures > 0) { - if(safeGraphics) - { - std::cout << "Setting special options for software rendering (aka safe graphics)." << std::endl; - QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL); - args.push_back("--disable-gpu"); - char dst[] = "LIBGL_ALWAYS_SOFTWARE=1"; - putenv(dst); - } + std::cerr << "Finished running test, " << failures << " out of " << total << " jasp files FAILED!" << std::endl; + exit(1); + } - if(hideJASP) - { - args.push_back("-platform"); - args.push_back("minimal"); - } + std::cout << "All " << total << " jasp files succeeded in refreshing and displaying the same data afterwards!" << std::endl; + exit(0); + } + else if (arguments.syncDataFileRecursive) + { + syncDataFiles(arguments, argv[0]); + exit(0); + } + else + { + //Now, to allow us to add some arguments we store the ones we got in a vector + std::vector args(argv, argv + argc); - PlotSchemeHandler::createUrlScheme(); //Needs to be done *before* creating PlotSchemeHandler instance and also before QApplication is instantiated - ImgSchemeHandler::createUrlScheme(); + if(arguments.safeGraphics) + { + std::cout << "Setting special options for software rendering (aka safe graphics)." << std::endl; + QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL); + args.push_back("--disable-gpu"); + char dst[] = "LIBGL_ALWAYS_SOFTWARE=1"; + putenv(dst); + } - QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); - QCoreApplication::setAttribute(Qt::AA_SynthesizeTouchForUnhandledMouseEvents, false); //To avoid weird splitterbehaviour with QML and a touchscreen + if(arguments.hideJASP) + { + args.push_back("-platform"); + args.push_back("minimal"); + } - #ifdef linux - QmlUtils::configureQMLCacheDir(); - #endif + PlotSchemeHandler::createUrlScheme(); //Needs to be done *before* creating PlotSchemeHandler instance and also before QApplication is instantiated + ImgSchemeHandler::createUrlScheme(); + QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); + QCoreApplication::setAttribute(Qt::AA_SynthesizeTouchForUnhandledMouseEvents, false); //To avoid weird splitterbehaviour with QML and a touchscreen - QLocale::setDefault(QLocale(QLocale::English)); // make decimal points == . in at least R? Anyway, this has been here forever, ill just leave it. + #ifdef linux + QmlUtils::configureQMLCacheDir(); + #endif - //Now we convert all these strings in args back to an int and a char * array. - //But to keep things easy, we are going to copy the old argv to avoid duplication (or messing up the executable name) - char** argvs = new char*[args.size()]; + QLocale::setDefault(QLocale(QLocale::English)); // make decimal points == . in at least R? Anyway, this has been here forever, ill just leave it. - std::cout << "Making new argument list for Application startup:"; + //Now we convert all these strings in args back to an int and a char * array. + //But to keep things easy, we are going to copy the old argv to avoid duplication (or messing up the executable name) + char** argvs = new char*[args.size()]; - for(size_t i = 0; i< args.size(); i++) - { - argvs[i] = new char[ args[i].size() + 1]; // +1 for null delimiter - memset(argvs[i], '\0', args[i].size() + 1); - memcpy(argvs[i], args[i].data(), args[i].size()); - argvs[i][ args[i].size()] = '\0'; - std::cout << " " << argvs[i]; - } - std::cout << "\nStarting JASP " << AppInfo::version.asString() << " from commit " << AppInfo::gitCommit << " and branch " << AppInfo::gitBranch << std::endl; + std::cout << "Making new argument list for Application startup:"; - int argvsize = args.size(); - //To be all neat we should clean up all this stuff after we are done running JASP, but on the other hand the memory will be thrown out anyway after exit so why bother. + for(size_t i = 0; i< args.size(); i++) + { + argvs[i] = new char[ args[i].size() + 1]; // +1 for null delimiter + memset(argvs[i], '\0', args[i].size() + 1); + memcpy(argvs[i], args[i].data(), args[i].size()); + argvs[i][ args[i].size()] = '\0'; + std::cout << " " << argvs[i]; + } - JASPTIMER_START("JASP"); + std::cout << "\nStarting JASP " << AppInfo::version.asString() << " from commit " << AppInfo::gitCommit << " and branch " << AppInfo::gitBranch << std::endl; - QtWebEngineQuick::initialize(); // We can do this here and not in MainWindow::loadQML() (before QQmlApplicationEngine is instantiated) because that is called from a singleshot timer. And will only be executed once we enter a.exec() below! - std::cout << "QtWebEngineQuick initialized" << std::endl; + int argvsize = args.size(); + //To be all neat we should clean up all this stuff after we are done running JASP, but on the other hand the memory will be thrown out anyway after exit so why bother. - Application a(argvsize, argvs); + JASPTIMER_START("JASP"); - std::cout << "Application initialized" << std::endl; + QtWebEngineQuick::initialize(); // We can do this here and not in MainWindow::loadQML() (before QQmlApplicationEngine is instantiated) because that is called from a singleshot timer. And will only be executed once we enter a.exec() below! + std::cout << "QtWebEngineQuick initialized" << std::endl; - PlotSchemeHandler plotSchemeHandler; //Makes sure plots can still be loaded in webengine with Qt6 - ImgSchemeHandler imgSchemeHandler; + Application a(argvsize, argvs); -#ifdef _WIN32 - auto runtimeEnv = DynamicRuntimeInfo::getInstance()->getRuntimeEnvironmentAsString(); - Log::log() << "Runtime Environment: " << runtimeEnv << std::endl; + std::cout << "Application initialized" << std::endl; - // Since we introduced renv to JASP, we need to recreate the junctions from Modules -> renv-cache on first run. Because windows does not support proper symlinks on user perms - // For this JASP has the --junctions argument, and is run on first execution of a specific jasp version on a system. - Log::log() << "Checking if we need to recreate junctions or not" << std::endl; - if(!DynamicRuntimeInfo::getInstance()->bundledModulesInitialized()) - { - Log::log() << "We need to recreate junctions!" << std::endl; + PlotSchemeHandler plotSchemeHandler; //Makes sure plots can still be loaded in webengine with Qt6 + ImgSchemeHandler imgSchemeHandler; - QMessageBox *msgBox = MessageForwarder::getInfoBox("Creating Junctions, one moment please", "Creating Junctions, one moment please"); - msgBox->show(); +#ifdef _WIN32 + auto runtimeEnv = DynamicRuntimeInfo::getInstance()->getRuntimeEnvironmentAsString(); + Log::log() << "Runtime Environment: " << runtimeEnv << std::endl; - if(!runJaspEngineJunctionFixer(argc, argv, false, false)) - { - std::cerr << "Modules folder missing and couldn't be created!\nContact the JASP team for support." << std::endl; - exit(254); - } - msgBox->close(); - } -#endif - a.init(filePathQ, newData, unitTest, timeOut, save, logToFile, dbJson, reportingDir); + // Since we introduced renv to JASP, we need to recreate the junctions from Modules -> renv-cache on first run. Because windows does not support proper symlinks on user perms + // For this JASP has the --junctions argument, and is run on first execution of a specific jasp version on a system. + Log::log() << "Checking if we need to recreate junctions or not" << std::endl; + if(!DynamicRuntimeInfo::getInstance()->bundledModulesInitialized()) + { + Log::log() << "We need to recreate junctions!" << std::endl; - try - { - std::cout << "Entering eventloop" << std::endl; + QMessageBox *msgBox = MessageForwarder::getInfoBox("Creating Junctions, one moment please", "Creating Junctions, one moment please"); + msgBox->show(); - int exitCode = a.exec(); - JASPTIMER_STOP("JASP"); - JASPTIMER_PRINTALL(); - return exitCode; - } - catch(std::exception & e) - { - std::cerr << "Uncaught std::exception! Was: " << e.what() << "\n"; - return -1; - } - catch(...) + if(!runJaspEngineJunctionFixer(argc, argv, false, false)) { - std::cerr << "Uncaught ???\n"; - return -1; + std::cerr << "Modules folder missing and couldn't be created!\nContact the JASP team for support." << std::endl; + exit(254); } + msgBox->close(); } - else - { - int failures = 0, - total = 0; +#endif + a.init(arguments); - recursiveFileOpener(QFileInfo(filePathQ), failures, total, timeOut, argc, argv, save, hideJASP); + try + { + std::cout << "Entering eventloop" << std::endl; - if(total == 0) + int exitCode = a.exec(); + JASPTIMER_STOP("JASP"); + JASPTIMER_PRINTALL(); + return exitCode; + } + catch(std::exception & e) { - std::cerr << "Couldn't find any jasp-files in specified directory " << filePath << ", it is be treated as a failure to notify you of this!" << std::endl; - exit(2); + std::cerr << "Uncaught std::exception! Was: " << e.what() << "\n"; + return -1; } - - if(failures > 0) + catch(...) { - std::cerr << "Finished running test, " << failures << " out of " << total << " jasp files FAILED!" << std::endl; - exit(1); + std::cerr << "Uncaught ???\n"; + return -1; } - - std::cout << "All " << total << " jasp files succeeded in refreshing and displaying the same data afterwards!" << std::endl; - exit(0); } - } diff --git a/Desktop/mainwindow.cpp b/Desktop/mainwindow.cpp index 54d4904bdb..a7bda64349 100644 --- a/Desktop/mainwindow.cpp +++ b/Desktop/mainwindow.cpp @@ -441,7 +441,6 @@ void MainWindow::makeConnections() connect(_resultsJsInterface, &ResultsJsInterface::saveTextToFile, this, &MainWindow::saveTextToFileHandler ); connect(_resultsJsInterface, &ResultsJsInterface::analysisSaveImage, this, &MainWindow::analysisSaveImageHandler ); connect(_resultsJsInterface, &ResultsJsInterface::analysisResizeImage, this, &MainWindow::analysisEditImageHandler ); - connect(_resultsJsInterface, &ResultsJsInterface::resultsPageLoadedSignal, this, &MainWindow::resultsPageLoaded ); connect(_resultsJsInterface, &ResultsJsInterface::refreshAllAnalyses, this, &MainWindow::refreshKeyPressed ); connect(_resultsJsInterface, &ResultsJsInterface::removeAllAnalyses, this, &MainWindow::removeAllAnalyses ); connect(_resultsJsInterface, &ResultsJsInterface::openFileTab, _fileMenu, &FileMenu::showFileOpenMenu ); @@ -459,6 +458,7 @@ void MainWindow::makeConnections() connect(_analyses, &Analyses::countChanged, this, &MainWindow::analysesCountChangedHandler ); connect(_analyses, &Analyses::analysisResultsChanged, this, &MainWindow::analysisResultsChangedHandler ); + connect(_analyses, &Analyses::analysisResultsChanged, this, &MainWindow::waitForAllAnalysesFinishedBeforeStartingEvent ); connect(_analyses, &Analyses::analysisImageSaved, this, &MainWindow::analysisImageSavedHandler ); connect(_analyses, &Analyses::emptyQMLCache, this, &MainWindow::resetQmlCache ); connect(_analyses, &Analyses::analysisAdded, this, &MainWindow::analysisAdded ); @@ -478,7 +478,6 @@ void MainWindow::makeConnections() connect(_analyses, &Analyses::analysisImageEdited, _plotEditorModel, &PlotEditorModel::updateOptions ); connect(_fileMenu, &FileMenu::exportSelected, _resultsJsInterface, &ResultsJsInterface::exportSelected ); - connect(_fileMenu, &FileMenu::dataSetIORequest, this, &MainWindow::dataSetIORequestHandler ); connect(_fileMenu, &FileMenu::showAbout, this, &MainWindow::showAbout ); connect(_fileMenu, &FileMenu::showContact, this, &MainWindow::showContact ); connect(_fileMenu, &FileMenu::showCommunity, this, &MainWindow::showCommunity ); @@ -853,13 +852,13 @@ void MainWindow::showLogFolder() const openFolderExternally(AppDirs::logDir()); } -void MainWindow::openURLFile(QString fileURLPath) +bool MainWindow::openURLFile(QString fileURLPath) { QUrl fileUrl = fileURLPath.startsWith("file:") ? QUrl(fileURLPath) : QUrl::fromLocalFile(fileURLPath); if (!fileUrl.isLocalFile()) { MessageForwarder::showWarning(tr("Open file"), tr("Cannot access file %1").arg(fileURLPath)); - return; + return false; } QString filePath = fileUrl.toLocalFile(); @@ -868,26 +867,74 @@ void MainWindow::openURLFile(QString fileURLPath) if (!fileInfo.exists()) { MessageForwarder::showWarning(tr("Open file"), tr("File %1 is not found.").arg(filePath)); - return; + return false; } - if (!FileTypeBaseValidName(fileInfo.suffix().toLower().toStdString())) + Utils::FileType fileType = Utils::getTypeFromFileName(fq(filePath)); + + if (fileType == Utils::FileType::empty || fileType == Utils::FileType::unknown || fileType == Utils::FileType::html || fileType == Utils::FileType::pdf) { MessageForwarder::showWarning(tr("Open file"), tr("JASP does not support this file type %1.").arg(filePath)); - return; + return false; } - open(filePath); + if (_package->hasDataSet() && fileType != Utils::FileType::jasp) + { + FileEvent * syncEvent = new FileEvent(_fileMenu, FileEvent::FileSyncData); + syncEvent->setPath(filePath); + syncEvent->setStarted(); + } + else + open(filePath); + + return true; } -void MainWindow::open(QString filepath) +void MainWindow::open(const QString & mainFilePath, const QString & inputDataFile, const QString & outputFile, bool keepJASPOpen) { if(resultXmlCompare::compareResults::theOne()->testMode()) - resultXmlCompare::compareResults::theOne()->setFilePath(filepath); + resultXmlCompare::compareResults::theOne()->setFilePath(mainFilePath); _openedUsingArgs = true; - if (_resultsPageLoaded) _fileMenu->open(filepath); - else _openOnLoadFilename = filepath; + if (_resultsJsInterface->resultsLoaded()) + _open(mainFilePath, inputDataFile, outputFile, keepJASPOpen); + else + connect(_resultsJsInterface, &ResultsJsInterface::resultsPageLoadedSignal, this, [this, mainFilePath, inputDataFile, outputFile, keepJASPOpen](){_open(mainFilePath, inputDataFile, outputFile, keepJASPOpen); }, Qt::SingleShotConnection); +} + +void MainWindow::_open(const QString & mainFilePath, const QString & inputDataFile, const QString & outputFile, bool keepJASPOpen) +{ + FileEvent * openEvent = _fileMenu->open(mainFilePath); + if (!inputDataFile.isEmpty()) + { + FileEvent * syncEvent = new FileEvent(_fileMenu, FileEvent::FileSyncData); + syncEvent->setPath(inputDataFile); + syncEvent->chain(openEvent); + + if (!outputFile.isEmpty()) + { + FileEvent * exportEvent = new FileEvent(_fileMenu, FileEvent::FileExportResults); + exportEvent->setPath(outputFile); + + if (!keepJASPOpen) + connect(exportEvent, &FileEvent::finalized, this, [this]() { emit exitSignal(0); }); + + // To export the result, we need to wait for all the analyses to be refreshed after the synchronization + connect(syncEvent, &FileEvent::finalized, this, [this, exportEvent]() { _waitingEvent = exportEvent; waitForAllAnalysesFinishedBeforeStartingEvent(); } ); + } + } +} + +void MainWindow::waitForAllAnalysesFinishedBeforeStartingEvent() +{ + if (!_waitingEvent || _waitingEvent->isStarted()) + return; + + if (_analyses->allFinished()) + { + _waitingEvent->setStarted(); + _waitingEvent = nullptr; + } } void MainWindow::showNewData() @@ -899,8 +946,8 @@ void MainWindow::showNewData() void MainWindow::open(const Json::Value & dbJson) { _openedUsingArgs = true; - if (_resultsPageLoaded) _fileMenu->open(dbJson); - else _openOnLoadDbJson = dbJson; + if (_resultsJsInterface->resultsLoaded()) _fileMenu->open(dbJson); + else _openOnLoadDbJson = dbJson; } /* @@ -1173,11 +1220,6 @@ void MainWindow::analysisEditImageHandler(int id, QString options) } } -void MainWindow::connectFileEventCompleted(FileEvent * event) -{ - connect(event, &FileEvent::completed, this, &MainWindow::dataSetIOCompleted, Qt::QueuedConnection); -} - bool MainWindow::startDetached(const QString & applicationPath, const QStringList & args) const { QProcess detachMe; @@ -1200,7 +1242,7 @@ bool MainWindow::startDetached(const QString & applicationPath, const QStringLis return worked; } -void MainWindow::dataSetIORequestHandler(FileEvent *event) +void MainWindow::fileEventRequestHandler(FileEvent *event) { if (event->operation() == FileEvent::FileNew) { @@ -1223,8 +1265,6 @@ void MainWindow::dataSetIORequestHandler(FileEvent *event) } else { - connectFileEventCompleted(event); - setWelcomePageVisible(false); _loader->io(event); @@ -1233,8 +1273,6 @@ void MainWindow::dataSetIORequestHandler(FileEvent *event) } else if (event->operation() == FileEvent::FileSave) { - connectFileEventCompleted(event); - _resultsJsInterface->exportPreviewHTML(); _package->setAnalysesData(_analyses->asJson()); @@ -1243,13 +1281,11 @@ void MainWindow::dataSetIORequestHandler(FileEvent *event) } else if (event->operation() == FileEvent::FileExportResults) { - connectFileEventCompleted(event); _loader->io(event); showProgress(); } else if (event->operation() == FileEvent::FileExportData || event->operation() == FileEvent::FileGenerateData) { - connectFileEventCompleted(event); _loader->io(event); showProgress(); } @@ -1258,14 +1294,11 @@ void MainWindow::dataSetIORequestHandler(FileEvent *event) if (!_package->hasDataSet()) return; - connectFileEventCompleted(event); _loader->io(event); showProgress(); } else if (event->operation() == FileEvent::FileClose) { - connectFileEventCompleted(event); - if (_package->isModified() && (dataAvailable() || analysesAvailable())) { QString title = windowTitle(); @@ -1334,7 +1367,7 @@ void MainWindow::closeVariablesPage() _columnModel->setVisible(false); } -void MainWindow::dataSetIOCompleted(FileEvent *event) +void MainWindow::fileEventRequestFinalize(FileEvent *event) { hideProgress(); @@ -1536,32 +1569,15 @@ void MainWindow::qmlLoaded() handleDeferredFileLoad(); } -void MainWindow::resultsPageLoaded() -{ - Log::log() << "MainWindow::resultsPageLoaded()" << std::endl; - _resultsPageLoaded = true; - - handleDeferredFileLoad(); -} - void MainWindow::handleDeferredFileLoad() { - if( !(_qmlLoaded && _resultsPageLoaded)) + if( !(_qmlLoaded)) return; - if (_openOnLoadFilename != "") - QTimer::singleShot(0, this, &MainWindow::_openFile); // this timer solves a resizing issue with the webengineview (https://github.com/jasp-stats/jasp-test-release/issues/70) - if(!_openOnLoadDbJson.isNull()) QTimer::singleShot(0, this, &MainWindow::_openDbJson); } -void MainWindow::_openFile() -{ - _fileMenu->open(_openOnLoadFilename); - _openOnLoadFilename = ""; -} - void MainWindow::_openDbJson() { _fileMenu->open(_openOnLoadDbJson); @@ -1815,7 +1831,7 @@ bool MainWindow::startDataEditorHandler() if (!dataFilePath.endsWith(".csv", Qt::CaseInsensitive)) dataFilePath.append(".csv"); - event = new FileEvent(this, FileEvent::FileGenerateData); + event = new FileEvent(_fileMenu, FileEvent::FileGenerateData); break; } @@ -1832,7 +1848,7 @@ bool MainWindow::startDataEditorHandler() if (dataFilePath == "") return false; - event = new FileEvent(this, FileEvent::FileSyncData); + event = new FileEvent(_fileMenu, FileEvent::FileSyncData); } break; @@ -1842,11 +1858,9 @@ bool MainWindow::startDataEditorHandler() if(!justOpenItAlready) { - connect(event, &FileEvent::completed, this, &MainWindow::startDataEditorEventCompleted); - connect(event, &FileEvent::completed, _fileMenu, &FileMenu::setSyncFile); event->setPath(dataFilePath); - _loader->io(event); - showProgress(); + connect(event, &FileEvent::completed, this, [this, event]() { startDataEditorEventCompleted(event); }); + event->setStarted(); } else { @@ -1909,6 +1923,7 @@ void MainWindow::startDataEditorEventCompleted(FileEvent* event) if (event->isSuccessful()) { + _fileMenu->setSyncFile(event); _package->setDataFilePath(event->path().toStdString()); _package->setDataFileReadOnly(false); _package->setModified(true); @@ -1966,12 +1981,10 @@ void MainWindow::startDataEditor(QString path) if (!path.endsWith(".csv", Qt::CaseInsensitive)) path.append(".csv"); - FileEvent *event = new FileEvent(this, FileEvent::FileGenerateData); - connect(event, &FileEvent::completed, this, &MainWindow::startDataEditorEventCompleted); - connect(event, &FileEvent::completed, _fileMenu, &FileMenu::setSyncFile); - event->setPath(path); - _loader->io(event); - showProgress(); + FileEvent *event = new FileEvent(_fileMenu, FileEvent::FileGenerateData); + event->setPath(path); + connect(event, &FileEvent::completed, this, [this, event]() { startDataEditorEventCompleted(event); }); + event->setStarted(); } } } @@ -2077,20 +2090,20 @@ void MainWindow::finishSavingComparedResults() void MainWindow::saveJaspFileHandler() { - FileEvent * saveEvent = new FileEvent(this, FileEvent::FileSave); + FileEvent * saveEvent = new FileEvent(_fileMenu, FileEvent::FileSave); saveEvent->setPath(resultXmlCompare::compareResults::theOne()->filePath()); - dataSetIORequestHandler(saveEvent); + saveEvent->setStarted(); } void MainWindow::saveTmpFileHandler() { - FileEvent * saveEvent = new FileEvent(this, FileEvent::FileSave); + FileEvent * saveEvent = new FileEvent(_fileMenu, FileEvent::FileSave); saveEvent->setTmp(true); - dataSetIORequestHandler(saveEvent); + saveEvent->setStarted(); } bool MainWindow::enginesInitializing() diff --git a/Desktop/mainwindow.h b/Desktop/mainwindow.h index 43745f2d21..b6c412c9fe 100644 --- a/Desktop/mainwindow.h +++ b/Desktop/mainwindow.h @@ -104,7 +104,8 @@ class MainWindow : public QObject static MainWindow * singleton() { return _singleton; } void showNewData(); - void open(QString filepath); + + void open(const QString & mainFilePath, const QString & inputDataFile = "", const QString & outputFile = "", bool keepJASPOpen = false); void open(const Json::Value & dbJson); void testLoadedJaspFile(int timeOut, bool save); void reportHere(QString dir); @@ -170,7 +171,7 @@ public slots: void zoomResetKeyPressed(); void undo(); void redo(); - void openURLFile(QString fileURLPath); + bool openURLFile(QString fileURLPath); QObject * loadQmlData(QString data, QUrl url); @@ -187,6 +188,8 @@ public slots: void setCheckAutomaticSync(bool check) { _checkAutomaticSync = check; } void openGitHubBugReport() const; void reloadResults() const; + void _open(const QString & mainFilePath, const QString & inputDataFile, const QString & outputFile, bool keepJASPOpen); + void waitForAllAnalysesFinishedBeforeStartingEvent(); private: @@ -226,7 +229,6 @@ public slots: void _openFile(); void _openDbJson(); - void connectFileEventCompleted(FileEvent * event); void refreshPlotsHandler(bool askUserForRefresh = true); void checkEmptyWorkspace(); @@ -259,13 +261,12 @@ public slots: void hadFatalErrorChanged(); private slots: - void resultsPageLoaded(); void analysisResultsChangedHandler(Analysis* analysis); void analysisImageSavedHandler(Analysis* analysis); void removeAllAnalyses(); - void dataSetIORequestHandler(FileEvent *event); - void dataSetIOCompleted(FileEvent *event); + void fileEventRequestHandler(FileEvent *event); + void fileEventRequestFinalize(FileEvent *event); void populateUIfromDataSet(); void startDataEditorEventCompleted(FileEvent *event); void analysisAdded(Analysis *analysis); @@ -342,8 +343,7 @@ private slots: int _progressBarProgress, //Runs from 0 to 100 _screenPPI = 1; - QString _openOnLoadFilename, - _fatalError = "The engine crashed...", + QString _fatalError = "The engine crashed...", _progressBarStatus, _downloadNewJASPUrl = ""; Json::Value _openOnLoadDbJson = Json::nullValue; @@ -352,7 +352,6 @@ private slots: AsyncLoaderThread _loaderThread; bool _applicationExiting = false, - _resultsPageLoaded = false, _qmlLoaded = false, _openedUsingArgs = false, _runButtonEnabled = false, @@ -367,6 +366,7 @@ private slots: _hadFatalError = false; QFont _defaultFont; + FileEvent * _waitingEvent = nullptr; }; #endif // MAINWIDGET_H diff --git a/Desktop/parsedarguments.cpp b/Desktop/parsedarguments.cpp new file mode 100644 index 0000000000..3ba671c0c5 --- /dev/null +++ b/Desktop/parsedarguments.cpp @@ -0,0 +1,285 @@ +// +// Copyright (C) 2013-2025 University of Amsterdam +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public +// License along with this program. If not, see +// . +// + +#include "parsedarguments.h" +#include +#include +#include +#include "utilenums.h" +#include "utilities/qutils.h" + +ParsedArguments::ParsedArguments(int argc, char *argv[]) +{ + bool letsExplainSomeThings = false; + + std::vector args(argv + 1, argv + argc); // make the arguments a little less annoying to work with + + for(int argNr = 0; argNr < args.size() && !letsExplainSomeThings; argNr++) + { + std::string arg = args[argNr]; + + if(arg == saveArg) save = true; + else if(arg == helpArg || arg == helpShortArg) letsExplainSomeThings = true; + else if(arg == logToFileArg) logToFile = true; + else if(arg == hideArg) hideJASP = true; + else if(arg == safeGraphicsArg) safeGraphics = true; + else if(arg == newDataArg) newData = true; + else if(arg == outputPdfArg) outputPdf = true; + else if(arg == keepJASPOpenArg) keepJASPOpenAfterExporting = true; + else if(arg == dontExportResultArg) dontExportResult = true; +#ifdef _WIN32 + else if(args[arg] == sandboxArg) { containerSettingForced = true; container = true; } + else if(args[arg] == noSandboxArg) { containerSettingForced = true; container = false; } + else if(args[arg] == junctionArg) runJaspEngineJunctionFixer(argc, argv, false); //Run the junctionfixer, it will exit the application btw! + else if(args[arg] == removeJunctionsArg) runJaspEngineJunctionFixer(argc, argv, true); //Remove the junctions +#endif + else if(arg == unitTestRecursiveArg) + { + argNr++; + if (checkFolder(args, argNr, mainFilePath)) + unitTestRecursive = true; + else + letsExplainSomeThings = true; + } + else if(arg == unitTestArg) + { + argNr++; + if (checkFile(args, argNr, mainFilePath, true, true)) + unitTest = true; + else + letsExplainSomeThings = true; + } + else if(arg == reportArg) + { + argNr++; + if (!checkFolder(args, argNr, reportingDir, true)) + letsExplainSomeThings = true; + } + else if(arg.size() > timeOutArg.size() && arg.substr(0, timeOutArg.size()) == timeOutArg) + { + std::string time = timeOutArg.substr(timeOutArg.size()); + size_t convertedChars = 0; + int convertedTime = 0; + try { convertedTime = std::stoi(time, &convertedChars); } + catch(std::invalid_argument &) {} + catch(std::out_of_range &) {} + + if(convertedChars > 0) + timeOut = convertedTime; + } + else if (arg == inputDataDirArg) + { + argNr++; + if (checkFolder(args, argNr, inputDataDir)) + syncDataFileRecursive = true; + else + letsExplainSomeThings = true; + + } + else if (arg == outputDirArg) + { + argNr++; + if (!checkFolder(args, argNr, outputDir)) + letsExplainSomeThings = true; + } + else + { + QString qarg = tq(arg); + if(arg == platformQtArg) + argNr++; // because it is always followed by the actual platform one wants to use (minimal for example) + else if(qarg.startsWith(tq(webEngineArgs))) + foundWebEngineArgs = true; + else if(qarg.startsWith(tq(remoteDebuggingPortArg)) && !foundWebEngineArgs) + { + std::cerr << "If you want to use a remote debugging port enter the following: '--webEngineArgs --remote-debugging-port=12345' otherwise webengine will ignore it." << std::endl; + exit(2); + } + else if(!(qarg.startsWith(tq(remoteDebuggingPortArg)) || qarg.startsWith(tq(qmlJsDebugArg)))) //Just making sure it isnt something else that should be allowed. + { + if(qarg.startsWith(tq(dashing))) + { + //So, it looks like an option, but not one we recognize, maybe it is meant for qt/chromium + std::cout << "Argument '" << arg << "' was not recognized by JASP, but it might be recognized by one of it's components in Qt (such as chromium), it will be passed on. If you really expected JASP to do something with it check '--help' again." << std::endl; + } +#ifdef __APPLE__ + else if(qarg.startsWith(tq(psnArg))) // https://github.com/jasp-stats/jasp-test-release/issues/1945 + { + //this is one of those arguments to ignore... + std::cout << "Ignoring " << arg << std::endl; + } +#endif + else + { + //if it isn't anything else it must be a file to open right? + if (!mainFilePath.exists()) + { + if (checkFile(args, argNr, mainFilePath)) + mainFileIsJaspFile = Utils::getTypeFromFileName(fq(mainFilePath.fileName())) == Utils::FileType::jasp; + else + letsExplainSomeThings = true; + } + else if (mainFileIsJaspFile) + { + QFileInfo file; + if (checkFile(args, argNr, file, true, false)) + { + dataFiles.push_back(file); + if (dataFiles.size() > 1) + syncDataFileRecursive = true; + } + else + letsExplainSomeThings = true; + } + else + { + //Check whether it can be parsed as a json and if so assume it is a database connection json as returned by DatabaseConnectionInfo + + Json::Reader jsonReader; + + if(!jsonReader.parse(arg, dbJson)) + { + std::cerr << "File to open " << arg << " does not exist (and also is not a (database) json)!" << std::endl; + letsExplainSomeThings = true; + } + } + } + } + } + } + + if(!mainFilePath.exists() && reportingDir.exists()) + { + std::cerr << "If you want JASP to run in reportingmode you should also give it a jaspfile to run off." << std::endl; + letsExplainSomeThings = true; + } + + if(letsExplainSomeThings) + { + std::cerr << "JASP can be started without arguments, or the following: { --help | -h | filename (filedata1 filedata2 ...) | --unitTest filename | --unitTestRecursive folder | --save | --timeOut=10 | --logToFile | --hide | --outputDir | --outputPdf | --inputDataDir | --dontExportResult | --keepJASPOpen } \n" + << "If a filename is supplied JASP will try to load it. \n" + << "If a filedata or several filedata are supplied, then JASP will synchronize the JASP file with the new data. In this case it will per default export the results in HTML format (in PDF format if --outputPdf is set)\n" + << "If --outputDir is specified, then the results are exported in this folder, if not it wlll be exported in the same folder as the data file.\n" + << "if --inputDataDir is specified, all the data files in this folder (and subfolders) will be used for the synchronization.\n" + << "Per default after synchronizing with a data file, it will export the result, except if --dontExportResult is specified.\n" + << "Also per default JASP will be automatically closed after synchronizing (and exporting the result), except if only one data file is used and --keepJASPOpen is specified.\n" + << "\n" + << "If --unitTest is specified JASP will refresh all analyses in \"filename\" (which must be a JASP file) and see if the output remains the same and will then exit with an errorcode indicating succes or failure.\n" + << "If --unitTestRecursive is specified JASP will go through specified \"folder\" and perform a --unitTest on each JASP file. After it has done this it will exit with an errorcode indication succes or failure.\n" + << "For both testing arguments there is the optional --save argument, which specifies that JASP should save the file after refreshing it.\n" + << "For both testing arguments there is the optional --timeout argument, which specifies how many minutes JASP will wait for the analyses-refresh to take. Default is 10 minutes.\n" + << "If --logToFile is specified then JASP will try it's utmost to write logging to a file, this might come in handy if you want to figure out why JASP does not start in case of a bug.\n" + << "If --hide is specified then JASP will not be shown during recursive testing or reporting.\n" + << "If --safeGraphics is specified then JASP will be started with software rendering enabled, this will be saved to your settings.\n" + << "If --report is specified then JASP will be started in reporting mode, which requires a path to where you would like to store the results. This is usually used in conjunction with a service/daemon and in that case it might make sense to also pass --hide. Don't forget to also pass a jasp filename otherwise it won't have anything to run...\n" +#ifdef _WIN32 + << "If --junctions is specified JASP will recreate the junctions in Modules/ to renv-cache/, this needs to be done at least once after install, but is usually triggered automatically." + << "In case one really wants the engines to be sandboxed specify --sandbox, otherwise use --noSandbox." +#endif + << "This text will be shown when either --help or -h is specified or something else that JASP does not understand is given as argument.\n" + << std::flush; + + exit(1); + } +} + +bool ParsedArguments::checkFile(const std::vector & args, int arg, QFileInfo & filePath, bool checkFileType, bool checkJASPType) +{ + if (arg >= args.size()) + return false; + + QString path = tq(args[arg]); + filePath = QFileInfo(path); + + if (!path.startsWith("https:") && !path.startsWith("http:") && !filePath.exists()) + { + std::cerr << "File " << filePath.absoluteFilePath().toStdString() << " does not exist!" << std::endl; + return false; + } + + Utils::FileType fileType = Utils::getTypeFromFileName(args[arg]); + + if (fileType == Utils::FileType::unknown || fileType == Utils::FileType::empty) + { + std::cerr << "Unknown file type: " << path << std::endl; + return false; + } + + if (checkFileType) + { + if (checkJASPType) + { + if (fileType != Utils::FileType::jasp) + { + std::cerr << "File " << path << " is not a JASP file!" << std::endl; + return false; + } + } + else if (!isDataFileType(fileType)) + { + std::cerr << "File " << path << " is not a data file!" << std::endl; + return false; + } + } + + return true; +} + +bool ParsedArguments::checkFolder(const std::vector & args, int arg, QFileInfo &folderPath, bool createIt) +{ + if(arg >= args.size()) + return false; + + QString path = tq(args[arg]); + folderPath = QFileInfo(path); + + if (createIt && !folderPath.exists()) + { + QDir folder(path); + folder.mkpath("."); + } + + if(!folderPath.exists()) + { + std::cerr << "Folder for " << folderPath.absolutePath().toStdString() << " does not exist!" << (createIt ? " and cannot be created" : "") << std::endl; + return false; + } + + return true; +} + +bool ParsedArguments::isDataFileType(Utils::FileType type) +{ + switch(type) + { + case Utils::FileType::empty: + case Utils::FileType::unknown: + case Utils::FileType::jasp: + case Utils::FileType::html: + case Utils::FileType::pdf: + case Utils::FileType::database: + return false; + default: + return true; + } +} + +bool ParsedArguments::isDataFileType(const QString & filePath) +{ + return isDataFileType(Utils::getTypeFromFileName(fq(filePath))); +} + diff --git a/Desktop/parsedarguments.h b/Desktop/parsedarguments.h new file mode 100644 index 0000000000..1d3d1dcfbd --- /dev/null +++ b/Desktop/parsedarguments.h @@ -0,0 +1,96 @@ +// +// Copyright (C) 2013-2025 University of Amsterdam +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public +// License along with this program. If not, see +// . +// + +#ifndef PARSEDARGUMENTS_H +#define PARSEDARGUMENTS_H + +#include +#include "utils.h" +#include +#include +#include + +struct ParsedArguments +{ +public: + + ParsedArguments(int argc, char *argv[]); + + bool mainFileIsJaspFile = false, + newData = false, + unitTest = false, + unitTestRecursive = false, + syncDataFileRecursive = false, + save = false, + logToFile = false, + hideJASP = false, + safeGraphics = false, + containerSettingForced = false, + container = false, + outputPdf = false, + keepJASPOpenAfterExporting = false, + dontExportResult = false, + foundWebEngineArgs = false; + int timeOut = 10; + Json::Value dbJson; + QFileInfo mainFilePath, + reportingDir, + inputDataDir, + outputDir; + std::vector dataFiles; + + static bool isDataFileType( Utils::FileType type); + static bool isDataFileType( const QString &path); + + + const std::string unitTestArg = "--unitTest", + saveArg = "--save", + timeOutArg = "--timeOut=", + junctionArg = "--junctions", + removeJunctionsArg = "--removeJunctions", + helpArg = "--help", + helpShortArg = "-h", + logToFileArg = "--logToFile", + hideArg = "--hide", + safeGraphicsArg = "--safeGraphics", + newDataArg = "--newData", + sandboxArg = "--sandbox", + noSandboxArg = "--noSandbox", + unitTestRecursiveArg = "--unitTestRecursive", + reportArg = "--report", + inputDataDirArg = "--inputDataDir", + outputDirArg = "--outputDir", + outputPdfArg = "--outputPdf", + keepJASPOpenArg = "--keepJASPOpen", + dontExportResultArg = "--dontExportResult", + platformQtArg = "-platform", + remoteDebuggingPortArg = "--remote-debugging-port=", + webEngineArgs = "--webEngineArgs", // This is apparently necessary to use in front of --remote-debugging-port nowadays, see: https://doc.qt.io/qt-6/qtwebengine-debugging.html + qmlJsDebugArg = "-qmljsdebugger", + dashing = "--", + psnArg = "-psn"; + +private: + bool checkFile(const std::vector & args, int arg, QFileInfo & filePath, bool checkFileType = false, bool checkJASPType = false); + bool checkFolder(const std::vector & args, int arg, QFileInfo & folderPath, bool createIt = false); + + + +}; + +#endif // PARSEDARGUMENTS_H diff --git a/Desktop/utilities/application.cpp b/Desktop/utilities/application.cpp index a4f31a2a3e..a15203a54b 100644 --- a/Desktop/utilities/application.cpp +++ b/Desktop/utilities/application.cpp @@ -25,16 +25,16 @@ #include #include "log.h" -void Application::init(QString filePath, bool newData, bool unitTest, int timeOut, bool save, bool logToFile, const Json::Value & dbJson, QString reportingPath) +void Application::init(const ParsedArguments& arguments) { std::cout << "Application init entered" << std::endl; - if(logToFile) + if(arguments.logToFile) Settings::setValue(Settings::LOG_TO_FILE, true); - Dirs::setReportingDir(fq(reportingPath)); + Dirs::setReportingDir(fq(arguments.reportingDir.absolutePath())); - if(unitTest) + if(arguments.unitTest) resultXmlCompare::compareResults::theOne()->enableTestMode(); //So languagemodel can be aware _mainWindow = new MainWindow(this); @@ -42,24 +42,46 @@ void Application::init(QString filePath, bool newData, bool unitTest, int timeOu connect(_mainWindow, &MainWindow::qmlLoadedChanged, _mainWindow, [=,this]() { // The QML files are not yet laoded when MainWindow is just created (loadQML is called via a QTmer::singleShot) // But to correctly work, the following calls need the QML files to be loaded. - if (newData) + if (arguments.newData) _mainWindow->showNewData(); else { - if(unitTest) - _mainWindow->testLoadedJaspFile(timeOut, save); - - if(filePath.size() > 0) - _mainWindow->open(filePath); - - if(!dbJson.isNull()) - _mainWindow->open(dbJson); + if(arguments.unitTest) + _mainWindow->testLoadedJaspFile(arguments.timeOut, arguments.save); + + if(arguments.mainFilePath.exists()) + { + QFileInfo inputDataFile; + QString outputFile; + + if (arguments.dataFiles.size() > 0) + { + inputDataFile = arguments.dataFiles.front(); + + if (!inputDataFile.exists()) + { + std::cerr << "File " << inputDataFile.absoluteFilePath() << " does not exist!" << std::endl; + exit(-1); + } + + if (!arguments.dontExportResult) + { + QString outputDir = arguments.outputDir.exists() ? arguments.outputDir.absolutePath() : inputDataFile.absoluteDir().absolutePath(); + outputFile = outputDir + "/" + inputDataFile.baseName() + (arguments.outputPdf ? ".pdf" : ".html"); + } + } + + _mainWindow->open(arguments.mainFilePath.absoluteFilePath(), inputDataFile.absoluteFilePath(), outputFile, arguments.keepJASPOpenAfterExporting); + } + + if(!arguments.dbJson.isNull()) + _mainWindow->open(arguments.dbJson); } }); - if(reportingPath != "") - _mainWindow->reportHere(reportingPath); + if(arguments.reportingDir.exists()) + _mainWindow->reportHere(arguments.reportingDir.absolutePath()); } Application::~Application() diff --git a/Desktop/utilities/application.h b/Desktop/utilities/application.h index dc57f34e0e..c71caf5f18 100644 --- a/Desktop/utilities/application.h +++ b/Desktop/utilities/application.h @@ -22,6 +22,7 @@ #include #include "mainwindow.h" #include "common.h" +#include "parsedarguments.h" /// /// Our override of QApplication, basically instantiates MainWindow and cleans it up at the end @@ -34,7 +35,7 @@ class Application : public QApplication virtual bool notify(QObject *receiver, QEvent *event) OVERRIDE; virtual bool event(QEvent *event) OVERRIDE; - void init(QString filePath, bool newData, bool unitTest, int timeOut, bool save, bool logToFile, const Json::Value & dbJson, QString reportingPath); + void init(const ParsedArguments& arguments); signals: diff --git a/Desktop/widgets/filemenu/autosavefilelistmodel.cpp b/Desktop/widgets/filemenu/autosavefilelistmodel.cpp index e99754d3e6..e4cf5956a4 100644 --- a/Desktop/widgets/filemenu/autosavefilelistmodel.cpp +++ b/Desktop/widgets/filemenu/autosavefilelistmodel.cpp @@ -1,7 +1,7 @@ #include "autosavefilelistmodel.h" #include "autosavefilesystem.h" -AutoSaveFileListModel::AutoSaveFileListModel(QObject *parent) +AutoSaveFileListModel::AutoSaveFileListModel(FileMenuObject *parent) : FileMenuBasicListModel{parent, new AutoSaveFileSystem(parent)} { @@ -13,14 +13,3 @@ void AutoSaveFileListModel::refresh() _model->refresh(); endResetModel(); } - -void AutoSaveFileListModel::openFile(const QString &path) -{ - if (path.isEmpty()) - return; - - FileEvent *event = new FileEvent(this->parent(), FileEvent::FileOpen); - event->setPath(path); - - emit dataSetIORequest(event); -} diff --git a/Desktop/widgets/filemenu/autosavefilelistmodel.h b/Desktop/widgets/filemenu/autosavefilelistmodel.h index 96018ea368..97066b2744 100644 --- a/Desktop/widgets/filemenu/autosavefilelistmodel.h +++ b/Desktop/widgets/filemenu/autosavefilelistmodel.h @@ -7,13 +7,9 @@ class AutoSaveFileListModel : public FileMenuBasicListModel { Q_OBJECT public: - explicit AutoSaveFileListModel(QObject *parent = nullptr); + explicit AutoSaveFileListModel(FileMenuObject *parent); Q_INVOKABLE void refresh(); - void openFile(const QString &path) override; - -signals: - void dataSetIORequest(FileEvent *event); }; #endif // AUTOSAVEFILELISTMODEL_H diff --git a/Desktop/widgets/filemenu/autosaves.cpp b/Desktop/widgets/filemenu/autosaves.cpp index 92698a367b..c2ad816974 100644 --- a/Desktop/widgets/filemenu/autosaves.cpp +++ b/Desktop/widgets/filemenu/autosaves.cpp @@ -5,5 +5,4 @@ AutoSaves::AutoSaves(FileMenu *parent) : FileMenuObject(parent) , _listModel(new AutoSaveFileListModel(this)) { - connect(_listModel, &AutoSaveFileListModel::dataSetIORequest, this, &AutoSaves::dataSetIORequest); } diff --git a/Desktop/widgets/filemenu/computer.cpp b/Desktop/widgets/filemenu/computer.cpp index c5b647cbc3..c6cdfdc5c3 100644 --- a/Desktop/widgets/filemenu/computer.cpp +++ b/Desktop/widgets/filemenu/computer.cpp @@ -55,12 +55,12 @@ FileEvent *Computer::browseOpen(const QString &path) QString finalPath = MessageForwarder::browseOpenFile("Open", browsePath, filter); Log::log() << "Chosen path: \"" << finalPath.toStdString() << "\"" << std::endl; - FileEvent *event = new FileEvent(this, mode()); + FileEvent *event = new FileEvent(_filemenu, mode()); if (finalPath != "") { event->setPath(finalPath); - emit dataSetIORequest(event); + event->setStarted(); } else { @@ -116,7 +116,7 @@ FileEvent *Computer::browseSave(const QString &path, FileEvent::FileMode mode) QString extension, finalPath = MessageForwarder::browseSaveFile(caption, browsePath, filter, &extension); - FileEvent *event = new FileEvent(this, mode); + FileEvent *event = new FileEvent(_filemenu, mode); if (finalPath != "") { @@ -129,7 +129,7 @@ FileEvent *Computer::browseSave(const QString &path, FileEvent::FileMode mode) !finalPath.endsWith(".tsv", Qt::CaseInsensitive)) ) finalPath.append(QString(".csv")); event->setPath(finalPath); - emit dataSetIORequest(event); + event->setStarted(); } else event->setComplete(false); diff --git a/Desktop/widgets/filemenu/computer.h b/Desktop/widgets/filemenu/computer.h index 594a625918..1bb1ebe774 100644 --- a/Desktop/widgets/filemenu/computer.h +++ b/Desktop/widgets/filemenu/computer.h @@ -28,7 +28,7 @@ class Computer : public FileMenuObject Q_PROPERTY(ComputerListModel * listModel READ listModel WRITE setListModel NOTIFY listModelChanged) public: - explicit Computer(FileMenu *parent = nullptr); + explicit Computer(FileMenu *parent); void setFileName(const QString &filename); void clearFileName(); diff --git a/Desktop/widgets/filemenu/computerlistmodel.cpp b/Desktop/widgets/filemenu/computerlistmodel.cpp index 5be5ea664c..7f7b18b6b9 100644 --- a/Desktop/widgets/filemenu/computerlistmodel.cpp +++ b/Desktop/widgets/filemenu/computerlistmodel.cpp @@ -3,7 +3,7 @@ #include #include -ComputerListModel::ComputerListModel(QObject *parent) +ComputerListModel::ComputerListModel(FileMenuObject *parent) : FileMenuBasicListModel(parent, new ComputerFileSystem()) { _fsbmRecentFolders = static_cast(_model); diff --git a/Desktop/widgets/filemenu/computerlistmodel.h b/Desktop/widgets/filemenu/computerlistmodel.h index 219dcc3c31..b0448f30cb 100644 --- a/Desktop/widgets/filemenu/computerlistmodel.h +++ b/Desktop/widgets/filemenu/computerlistmodel.h @@ -12,7 +12,7 @@ class ComputerListModel : public FileMenuBasicListModel Q_OBJECT public: - explicit ComputerListModel(QObject *parent = nullptr); + explicit ComputerListModel(FileMenuObject *parent); QString getMostRecent(); void addRecentFolder(const QString &newpath); diff --git a/Desktop/widgets/filemenu/currentdatafile.cpp b/Desktop/widgets/filemenu/currentdatafile.cpp index 1881c859c2..c2a9ad56ee 100644 --- a/Desktop/widgets/filemenu/currentdatafile.cpp +++ b/Desktop/widgets/filemenu/currentdatafile.cpp @@ -47,10 +47,12 @@ QString CurrentDataFile::getHeaderText() } -void CurrentDataFile::syncFile(FileEvent *event) +void CurrentDataFile::syncFile(const QString & path) { emit setCheckAutomaticSync(false); - emit dataSetIORequest(event); + FileEvent *event = new FileEvent(_filemenu, FileEvent::FileSyncData); + event->setPath(path); + event->setStarted(); } diff --git a/Desktop/widgets/filemenu/currentdatafile.h b/Desktop/widgets/filemenu/currentdatafile.h index 65fb01c23b..2a68ff6559 100644 --- a/Desktop/widgets/filemenu/currentdatafile.h +++ b/Desktop/widgets/filemenu/currentdatafile.h @@ -29,7 +29,7 @@ class CurrentDataFile : public FileMenuObject Q_PROPERTY(CurrentFileListModel * listModel READ listModel WRITE setListModel NOTIFY listModelChanged) public: - explicit CurrentDataFile(FileMenu *parent = nullptr); + explicit CurrentDataFile(FileMenu *parent); ~CurrentDataFile(); void setCurrentFilePath(const QString &path); @@ -41,8 +41,8 @@ class CurrentDataFile : public FileMenuObject public slots: QString getCurrentFilePath(); - QString getHeaderText(); - void syncFile(FileEvent *event); + QString getHeaderText(); + void syncFile(const QString& path); void setListModel(CurrentFileListModel * listModel); signals: diff --git a/Desktop/widgets/filemenu/currentfilelistmodel.cpp b/Desktop/widgets/filemenu/currentfilelistmodel.cpp index 1ba98efe49..6c5b2a634a 100644 --- a/Desktop/widgets/filemenu/currentfilelistmodel.cpp +++ b/Desktop/widgets/filemenu/currentfilelistmodel.cpp @@ -1,9 +1,8 @@ #include "currentfilelistmodel.h" -#include "filesystementry.h" #include #include -CurrentFileListModel::CurrentFileListModel(QObject *parent) +CurrentFileListModel::CurrentFileListModel(FileMenuObject *parent) : FileMenuBasicListModel(parent, new CurrentFileFileSystem(parent)) { _openFileWhenClicked = false; @@ -28,11 +27,9 @@ void CurrentFileListModel::setCurrentFilePath(const QString &newcurrent) void CurrentFileListModel::openFile(const QString &path) { - if (path.isEmpty()) - return; + if (path.isEmpty()) + return; - FileEvent *event = new FileEvent(this->parent(), FileEvent::FileSyncData); - event->setPath(path); + emit syncCurrentFile(path); - emit syncCurrentFile(event); } diff --git a/Desktop/widgets/filemenu/currentfilelistmodel.h b/Desktop/widgets/filemenu/currentfilelistmodel.h index dd8a0e40df..d068e0beb5 100644 --- a/Desktop/widgets/filemenu/currentfilelistmodel.h +++ b/Desktop/widgets/filemenu/currentfilelistmodel.h @@ -3,8 +3,6 @@ #include #include "currentfilefilesystem.h" -#include "data/fileevent.h" -#include "filemenulistitem.h" #include "filemenubasiclistmodel.h" class CurrentFileListModel : public FileMenuBasicListModel @@ -12,19 +10,17 @@ class CurrentFileListModel : public FileMenuBasicListModel Q_OBJECT public: - explicit CurrentFileListModel(QObject *parent = nullptr); + explicit CurrentFileListModel(FileMenuObject *parent); CurrentFileFileSystem* getCurrentFileFSBModel(); - void setCurrentFilePath(const QString &newcurrent); + void setCurrentFilePath(const QString &newcurrent); signals: - void syncCurrentFile(FileEvent *event); + void syncCurrentFile(const QString& currentFile); public slots: - void openFile(const QString& path) override; + void openFile(const QString& path) override; - - private: CurrentFileFileSystem *_fsbmCurrentFile; }; diff --git a/Desktop/widgets/filemenu/databasefilemenu.cpp b/Desktop/widgets/filemenu/databasefilemenu.cpp index 477de4e53c..d6d5c6ee6e 100644 --- a/Desktop/widgets/filemenu/databasefilemenu.cpp +++ b/Desktop/widgets/filemenu/databasefilemenu.cpp @@ -94,13 +94,11 @@ void DatabaseFileMenu::importResults() _info.close(); - FileEvent *event = new FileEvent(this, mode()); - + FileEvent *event = new FileEvent(_filemenu, mode()); event->setFileType(FileTypeBase::database); - event->setDatabase(_info.toJson()); - emit dataSetIORequest(event); + event->setStarted(); resetEphemeralFields(); } diff --git a/Desktop/widgets/filemenu/databasefilemenu.h b/Desktop/widgets/filemenu/databasefilemenu.h index bf3804203e..b2889a3280 100644 --- a/Desktop/widgets/filemenu/databasefilemenu.h +++ b/Desktop/widgets/filemenu/databasefilemenu.h @@ -28,7 +28,7 @@ class DatabaseFileMenu : public FileMenuObject Q_PROPERTY(bool rememberMe READ rememberMe WRITE setRememberMe NOTIFY rememberMeChanged ) public: - explicit DatabaseFileMenu(FileMenu *parent = nullptr); + explicit DatabaseFileMenu(FileMenu *parent); Q_INVOKABLE void connect(); Q_INVOKABLE void runQuery(); diff --git a/Desktop/widgets/filemenu/datalibrary.cpp b/Desktop/widgets/filemenu/datalibrary.cpp index 2d9de3301c..a0bfce15a2 100644 --- a/Desktop/widgets/filemenu/datalibrary.cpp +++ b/Desktop/widgets/filemenu/datalibrary.cpp @@ -36,11 +36,6 @@ void DataLibrary::refres() _dataLibraryListModel->refresh(); } -void DataLibrary::openFile(FileEvent *event) -{ - emit dataSetIORequest(event); -} - void DataLibrary::setListModel(DataLibraryListModel * listModel) { if (_dataLibraryListModel == listModel) diff --git a/Desktop/widgets/filemenu/datalibrary.h b/Desktop/widgets/filemenu/datalibrary.h index 75c8eebec1..c2dd0fcf21 100644 --- a/Desktop/widgets/filemenu/datalibrary.h +++ b/Desktop/widgets/filemenu/datalibrary.h @@ -32,7 +32,7 @@ class DataLibrary : public FileMenuObject Q_PROPERTY(DataLibraryBreadCrumbsListModel * breadcrumbsmodel READ breadcrumbsmodel WRITE setBreadcrumbsmodel NOTIFY breadcrumbsmodelChanged) public: - explicit DataLibrary(FileMenu *parent = nullptr); + explicit DataLibrary(FileMenu *parent); ~DataLibrary() {} DataLibraryListModel * listModel() const { return _dataLibraryListModel; } @@ -45,8 +45,6 @@ class DataLibrary : public FileMenuObject void resetPath(); public slots: - void openFile(FileEvent *event); - void setListModel(DataLibraryListModel * listModel); void setBreadcrumbsmodel(DataLibraryBreadCrumbsListModel * breadcrumbsmodel); diff --git a/Desktop/widgets/filemenu/datalibrarylistmodel.cpp b/Desktop/widgets/filemenu/datalibrarylistmodel.cpp index 10a1746233..2d865c9604 100644 --- a/Desktop/widgets/filemenu/datalibrarylistmodel.cpp +++ b/Desktop/widgets/filemenu/datalibrarylistmodel.cpp @@ -5,12 +5,10 @@ #include "log.h" #include "datalibrary.h" -DataLibraryListModel::DataLibraryListModel(QObject *parent, DataLibraryBreadCrumbsListModel* crumbs) : FileMenuBasicListModel(parent, new DataLibraryFileSystem(parent, DataLibraryFileSystem::rootelementname )), _dataLibraryBreadCrumbsListModel(crumbs) +DataLibraryListModel::DataLibraryListModel(FileMenuObject *parent, DataLibraryBreadCrumbsListModel* crumbs) : FileMenuBasicListModel(parent, new DataLibraryFileSystem(parent, DataLibraryFileSystem::rootelementname )), _dataLibraryBreadCrumbsListModel(crumbs) { _fsbmDataLibrary = static_cast(_model);; _fsbmDataLibrary->refresh(); - - connect(this, &DataLibraryListModel::openFileEvent, dynamic_cast(parent), &DataLibrary::openFile); } void DataLibraryListModel::refresh() @@ -50,10 +48,7 @@ void DataLibraryListModel::changePathCrumbIndex(const int &index) void DataLibraryListModel::openFile(const QString &path) { - FileEvent *event = new FileEvent(this->parent(), FileEvent::FileOpen); - event->setPath(path); - - emit openFileEvent(event); + FileMenuBasicListModel::openFile(path); changePathCrumbIndex(0); //Reset begin screen datalibrary (the same as with key navigation). } diff --git a/Desktop/widgets/filemenu/datalibrarylistmodel.h b/Desktop/widgets/filemenu/datalibrarylistmodel.h index 998b50b306..bef8bbed97 100644 --- a/Desktop/widgets/filemenu/datalibrarylistmodel.h +++ b/Desktop/widgets/filemenu/datalibrarylistmodel.h @@ -13,13 +13,10 @@ class DataLibraryListModel : public FileMenuBasicListModel Q_OBJECT public: - explicit DataLibraryListModel(QObject *parent, DataLibraryBreadCrumbsListModel* crumbs); + explicit DataLibraryListModel(FileMenuObject *parent, DataLibraryBreadCrumbsListModel* crumbs); void setBreadCrumbsListModel (DataLibraryBreadCrumbsListModel *dataLibraryBreadCrumbsModel); void refresh(); -signals: - void openFileEvent(FileEvent *event); - public slots: void changePath(const QString& name, const QString& path) override; void changePathCrumbIndex(const int& index) override; diff --git a/Desktop/widgets/filemenu/filemenu.cpp b/Desktop/widgets/filemenu/filemenu.cpp index 0b2488a38c..39cce29068 100644 --- a/Desktop/widgets/filemenu/filemenu.cpp +++ b/Desktop/widgets/filemenu/filemenu.cpp @@ -39,9 +39,7 @@ FileMenu::FileMenu(QObject *parent) : QObject(parent) _actionButtons = new ActionButtons (this); _resourceButtons = new ResourceButtons (this); _resourceButtonsVisible = new ResourceButtonsVisible(this, _resourceButtons); - - connect(&_watcher, &QFileSystemWatcher::fileChanged, this, &FileMenu::dataFileModifiedHandler ); connect(_actionButtons, &ActionButtons::buttonClicked, this, &FileMenu::actionButtonClicked ); connect(_actionButtons, &ActionButtons::selectedActionChanged, this, &FileMenu::setFileoperation ); @@ -80,6 +78,7 @@ void FileMenu::setResourceButtonsVisibleFor(ActionButtons::FileOperation fo) _resourceButtons->setOnlyTheseButtonsVisible(_actionButtons->resourceButtonsForButton(fo)); } + void FileMenu::setMode(FileEvent::FileMode mode) { _mode = mode; @@ -98,7 +97,7 @@ FileEvent *FileMenu::open(const QString &path) { FileEvent *event = new FileEvent(this, FileEvent::FileOpen); event->setPath(path); - dataSetIORequestHandler(event); + event->setStarted(); return event; } @@ -108,7 +107,7 @@ FileEvent *FileMenu::open(const Json::Value & dbJson) FileEvent *event = new FileEvent(this, FileEvent::FileOpen); event->setDatabase(dbJson); event->setFileType(Utils::FileType::database); - dataSetIORequestHandler(event); + event->setStarted(); return event; } @@ -122,7 +121,7 @@ FileEvent *FileMenu::newData() { FileEvent *event = new FileEvent(this, FileEvent::FileNew); - dataSetIORequestHandler(event); + event->setStarted(); return event; } @@ -140,7 +139,7 @@ FileEvent *FileMenu::save() return event; } - dataSetIORequestHandler(event); + event->setStarted(); return event; } @@ -152,7 +151,7 @@ void FileMenu::sync() FileEvent *event = new FileEvent(this, FileEvent::FileSyncData); event->setDatabase(DataSetPackage::pkg()->databaseJson()); - dataSetIORequestHandler(event); + event->setStarted(); } else { @@ -175,7 +174,7 @@ void FileMenu::sync() void FileMenu::close() { FileEvent *event = new FileEvent(this, FileEvent::FileClose); - dataSetIORequestHandler(event); + event->setStarted(); setMode(FileEvent::FileOpen); _actionButtons->setSelectedAction(ActionButtons::FileOperation::Open); @@ -262,8 +261,28 @@ void FileMenu::buttonsForEmptyWorkspace() _actionButtons->setEnabled(ActionButtons::Close, false); } -void FileMenu::dataSetIOCompleted(FileEvent *event) +void FileMenu::fileEventRequestDispatcher() +{ + FileEvent* event = qobject_cast(sender()); + + // When an event starts, many objects can be involved. It's important to be sure that when an event is set completed, that all computing is effectly done. + // The event is set finalized only here, when the MainWindow has finished the finalizing work for the event: all other objects connected with the completed signal should use direct connection. + // This is to ensure that when the event is set finalized, no other objects are still using the event. + // The finalize signal can be used to compute other stuff (like in chaining event), but the event self should not be used anymore since it is then deleted. + connect(event, &FileEvent::completed, this, &FileMenu::fileEventRequestFinalize); + connect(event, &FileEvent::completed, _mainWindow, [this, event]() { _mainWindow->fileEventRequestFinalize(event); event->setFinalized(); }, Qt::QueuedConnection); + + _mainWindow->fileEventRequestHandler(event); + +} + +void FileMenu::fileEventRequestFinalize() { + FileEvent* event = qobject_cast(sender()); + + if(event->operation() != FileEvent::FileClose) + setVisible(false); //If we just did something we are now done with the filemenu right? Except if we just closed a file + if (event->operation() == FileEvent::FileSave || event->operation() == FileEvent::FileOpen) { if (event->isSuccessful() && !event->isTmp()) @@ -363,16 +382,6 @@ void FileMenu::dataFileModifiedHandler(QString path) syncDataFile(path, true); } -void FileMenu::dataSetIORequestHandler(FileEvent *event) -{ - connect(event, &FileEvent::completed, this, &FileMenu::dataSetIOCompleted ); - - emit dataSetIORequest(event); - - if(event->operation() != FileEvent::FileClose) - setVisible(false); //If we just did something we are now done with the filemenu right? Except if we just closed a file -} - void FileMenu::analysisAdded(Analysis *analysis) { _actionButtons->setEnabled(ActionButtons::Close, true); @@ -400,10 +409,10 @@ void FileMenu::dataColumnAdded(QString columnName) FileEvent * event = new FileEvent(this, FileEvent::FileGenerateData); - connect(event, &FileEvent::completed, this, &FileMenu::setSyncFile); + connect(event, &FileEvent::completed, this, [this, event]() { setSyncFile(event); }); event->setPath(_currentDataFile->getCurrentFilePath()); - dataSetIORequestHandler(event); + event->setStarted(); } } @@ -488,7 +497,7 @@ void FileMenu::setSyncRequest(const QString& path, bool waitForExistence) FileEvent *event = new FileEvent(this, FileEvent::FileSyncData); event->setPath(path); - dataSetIORequestHandler(event); + event->setStarted(); } } diff --git a/Desktop/widgets/filemenu/filemenu.h b/Desktop/widgets/filemenu/filemenu.h index 40428f3616..e270b37a4f 100644 --- a/Desktop/widgets/filemenu/filemenu.h +++ b/Desktop/widgets/filemenu/filemenu.h @@ -43,7 +43,7 @@ class DataSetPackage; class FileMenu : public QObject { friend FileMenuObject; - + typedef ActionButtons::FileOperation FileOperation; Q_OBJECT @@ -82,7 +82,7 @@ class FileMenu : public QObject void setCurrentDataFile(const QString & path); void setDataFileWatcher(bool watch); - + void setMode(FileEvent::FileMode mode); Utils::FileType getCurrentFileType() const { return _currentFileType; } QString getCurrentFilePath() const { return _currentFilePath; } @@ -111,7 +111,6 @@ class FileMenu : public QObject signals: void fileoperationChanged(); - void dataSetIORequest(FileEvent *event); void exportSelected(QString filename); void visibleChanged(bool visible); void dummyChangedNotifier(); @@ -125,7 +124,8 @@ public slots: void workspaceModified(); void setSyncFile(FileEvent *event); void dataAutoSynchronizationChanged(bool on) { setDataFileWatcher(on); } - void dataSetIOCompleted(FileEvent *event); + void fileEventRequestDispatcher(); + void fileEventRequestFinalize(); void dataFileModifiedHandler(QString path); void setFileoperation(const ActionButtons::FileOperation fo); void actionButtonClicked(const ActionButtons::FileOperation action); @@ -141,10 +141,6 @@ public slots: void enableButtonsForOpenedWorkspace(bool enableSaveButton = false); void buttonsForEmptyWorkspace(); - -private slots: - void dataSetIORequestHandler(FileEvent *event); - private: bool checkSyncFileExists(const QString &path, bool waitForExistence = false); void clearSyncData(); diff --git a/Desktop/widgets/filemenu/filemenubasiclistmodel.cpp b/Desktop/widgets/filemenu/filemenubasiclistmodel.cpp index a6a606ee2c..fe0f399654 100644 --- a/Desktop/widgets/filemenu/filemenubasiclistmodel.cpp +++ b/Desktop/widgets/filemenu/filemenubasiclistmodel.cpp @@ -6,9 +6,9 @@ #include "jasptheme.h" #include "utilities/appdirs.h" -FileMenuBasicListModel::FileMenuBasicListModel(QObject *parent, FileSystem * model) : QAbstractListModel(parent), _model(model) +FileMenuBasicListModel::FileMenuBasicListModel(FileMenuObject *parent, FileSystem * model) : QAbstractListModel(parent), _model(model) { - + connect(this, &FileMenuBasicListModel::openFileEvent, parent, &FileMenuObject::openFile); } int FileMenuBasicListModel::rowCount(const QModelIndex &parent) const @@ -119,7 +119,10 @@ void FileMenuBasicListModel::changePathCrumbIndex(const int& index) void FileMenuBasicListModel::openFile(const QString& path) { - assert(false); + if (path.isEmpty()) + return; + + emit openFileEvent(path); } void FileMenuBasicListModel::saveFile(const QString& path) diff --git a/Desktop/widgets/filemenu/filemenubasiclistmodel.h b/Desktop/widgets/filemenu/filemenubasiclistmodel.h index 6f06b22576..7536a217ab 100644 --- a/Desktop/widgets/filemenu/filemenubasiclistmodel.h +++ b/Desktop/widgets/filemenu/filemenubasiclistmodel.h @@ -8,13 +8,14 @@ #include "datalibrarybreadcrumbsmodel.h" #include "data/fileevent.h" #include "filemenulistitem.h" +#include "filemenuobject.h" class FileMenuBasicListModel : public QAbstractListModel { Q_OBJECT public: - explicit FileMenuBasicListModel(QObject *parent, FileSystem * model); + explicit FileMenuBasicListModel(FileMenuObject *parent, FileSystem * model); virtual ~FileMenuBasicListModel() {} int rowCount(const QModelIndex &parent = QModelIndex()) const override; @@ -33,6 +34,9 @@ public slots: void resetPath() { changePathCrumbIndex(0); } bool mayOpen(); +signals: + void openFileEvent(const QString &path); + protected: FileSystem * _model = nullptr; bool _openFileWhenClicked = true; diff --git a/Desktop/widgets/filemenu/filemenuobject.cpp b/Desktop/widgets/filemenu/filemenuobject.cpp index c01d0cc9b2..23727fa59f 100644 --- a/Desktop/widgets/filemenu/filemenuobject.cpp +++ b/Desktop/widgets/filemenu/filemenuobject.cpp @@ -22,7 +22,6 @@ FileMenuObject::FileMenuObject(FileMenu * parent) : QObject(parent) { _filemenu = parent; - connect(this, &FileMenuObject::dataSetIORequest, parent, &FileMenu::dataSetIORequestHandler); } FileEvent::FileMode FileMenuObject::mode() @@ -34,3 +33,10 @@ void FileMenuObject::setMode(FileEvent::FileMode mode) { _filemenu->_mode = mode; } + +void FileMenuObject::openFile(const QString & path) +{ + FileEvent *event = new FileEvent(_filemenu, FileEvent::FileOpen); + event->setPath(path); + event->setStarted(); +} diff --git a/Desktop/widgets/filemenu/filemenuobject.h b/Desktop/widgets/filemenu/filemenuobject.h index 6ae60b6114..57839b77f6 100644 --- a/Desktop/widgets/filemenu/filemenuobject.h +++ b/Desktop/widgets/filemenu/filemenuobject.h @@ -31,8 +31,8 @@ class FileMenuObject : public QObject public: explicit FileMenuObject(FileMenu *parent); -signals: - void dataSetIORequest(FileEvent *event); +public slots: + virtual void openFile(const QString & path); protected: FileEvent::FileMode mode(); diff --git a/Desktop/widgets/filemenu/osf.cpp b/Desktop/widgets/filemenu/osf.cpp index 58a6ca5d86..45e1c4d177 100644 --- a/Desktop/widgets/filemenu/osf.cpp +++ b/Desktop/widgets/filemenu/osf.cpp @@ -275,7 +275,7 @@ void OSF::openSaveFile(const QString & nodePath, const QString & filename, const { bool storedata = (mode() == FileEvent::FileSave || mode() == FileEvent::FileExportResults || mode() == FileEvent::FileExportData); - FileEvent *event = new FileEvent(this, mode()); + FileEvent *event = new FileEvent(_filemenu, mode()); event->setOsfPath(osfPath); @@ -287,7 +287,7 @@ void OSF::openSaveFile(const QString & nodePath, const QString & filename, const { setSavefilename(filename); - connect(event, SIGNAL(completed(FileEvent*)), this, SLOT(openSaveCompleted(FileEvent*))); + connect(event, &FileEvent::completed, this, &OSF::openSaveCompleted); } } else @@ -297,7 +297,7 @@ void OSF::openSaveFile(const QString & nodePath, const QString & filename, const return; } - emit dataSetIORequest(event); + event->setStarted(); } void OSF::userDetailsReceived() @@ -307,13 +307,12 @@ void OSF::userDetailsReceived() userNode->deleteLater(); } -void OSF::openSaveCompleted(FileEvent* event) +void OSF::openSaveCompleted() { + FileEvent* event = qobject_cast(sender()); if (event->isSuccessful()) - { _osfFileSystem->refresh(); - } setProcessing(false); } diff --git a/Desktop/widgets/filemenu/osf.h b/Desktop/widgets/filemenu/osf.h index 4bf4215951..808e64d629 100644 --- a/Desktop/widgets/filemenu/osf.h +++ b/Desktop/widgets/filemenu/osf.h @@ -44,7 +44,7 @@ class OSF: public FileMenuObject Q_PROPERTY(SortMenuModel * sortedMenuModel READ sortedMenuModel NOTIFY sortedMenuModelChanged ) public: - explicit OSF(FileMenu *parent = nullptr); + explicit OSF(FileMenu *parent); bool loggedin() const { return _mLoggedin; } bool rememberme() const { return _mRememberMe; } @@ -96,7 +96,7 @@ private slots: void saveClicked(); void openSaveFile(const QString & nodePath, const QString & filename, const QString & osfpath = ""); void userDetailsReceived(); - void openSaveCompleted(FileEvent * event); + void openSaveCompleted(); void updateUserDetails(); void newFolderCreated(); void resetOSFListModel(); @@ -105,7 +105,7 @@ private slots: public slots: void logoutClicked(); void loginRequested(const QString &username, const QString &password); - void openFile(const QString &name); + void openFile(const QString &name) override; void saveFile(const QString &name); void saveFolder(const QString &name); void startProcessing(); diff --git a/Desktop/widgets/filemenu/osflistmodel.cpp b/Desktop/widgets/filemenu/osflistmodel.cpp index 820492d772..71a8f3cfac 100644 --- a/Desktop/widgets/filemenu/osflistmodel.cpp +++ b/Desktop/widgets/filemenu/osflistmodel.cpp @@ -3,7 +3,7 @@ #include #include -OSFListModel::OSFListModel(QObject *parent, OSFFileSystem * fsbMod, OSFBreadCrumbsListModel * crummyList) +OSFListModel::OSFListModel(FileMenuObject *parent, OSFFileSystem * fsbMod, OSFBreadCrumbsListModel * crummyList) : FileMenuBasicListModel(parent, nullptr) { diff --git a/Desktop/widgets/filemenu/osflistmodel.h b/Desktop/widgets/filemenu/osflistmodel.h index 947cac34f9..88cd54c2da 100644 --- a/Desktop/widgets/filemenu/osflistmodel.h +++ b/Desktop/widgets/filemenu/osflistmodel.h @@ -11,7 +11,7 @@ class OSFListModel : public FileMenuBasicListModel Q_OBJECT public: - explicit OSFListModel(QObject *parent, OSFFileSystem * fsbMod, OSFBreadCrumbsListModel * crummyList); + explicit OSFListModel(FileMenuObject *parent, OSFFileSystem * fsbMod, OSFBreadCrumbsListModel * crummyList); void setFSBModel(OSFFileSystem *model); @@ -21,7 +21,6 @@ class OSFListModel : public FileMenuBasicListModel public slots: void changePath(const QString& name, const QString& path) override; void changePathCrumbIndex(const int& index) override; - void openFile(const QString& path) override { emit openFileRequest(path); } signals: void startProcessing(); diff --git a/Desktop/widgets/filemenu/recentfiles.cpp b/Desktop/widgets/filemenu/recentfiles.cpp index 7237684786..81cfa8f456 100644 --- a/Desktop/widgets/filemenu/recentfiles.cpp +++ b/Desktop/widgets/filemenu/recentfiles.cpp @@ -12,11 +12,6 @@ void RecentFiles::pushRecentFilePath(const QString &newrecent) _recentFilesListModel->addRecentFilePath(newrecent); } -void RecentFiles::openFile(FileEvent *event) -{ - emit dataSetIORequest(event); -} - void RecentFiles::setListModel(RecentFilesListModel * listModel) { if (_recentFilesListModel == listModel) diff --git a/Desktop/widgets/filemenu/recentfiles.h b/Desktop/widgets/filemenu/recentfiles.h index 7f22813cc1..0b461c74ce 100644 --- a/Desktop/widgets/filemenu/recentfiles.h +++ b/Desktop/widgets/filemenu/recentfiles.h @@ -28,15 +28,13 @@ class RecentFiles : public FileMenuObject Q_PROPERTY(RecentFilesListModel * listModel READ listModel WRITE setListModel NOTIFY listModelChanged) public: - explicit RecentFiles(FileMenu *parent = nullptr); + explicit RecentFiles(FileMenu *parent); void pushRecentFilePath(const QString & newrecent); RecentFilesListModel * listModel() const { return _recentFilesListModel; } -public slots: - void openFile(FileEvent *event); - +public slots: void setListModel(RecentFilesListModel * listModel); signals: diff --git a/Desktop/widgets/filemenu/recentfileslistmodel.cpp b/Desktop/widgets/filemenu/recentfileslistmodel.cpp index 7b1a0b21a2..094e98b573 100644 --- a/Desktop/widgets/filemenu/recentfileslistmodel.cpp +++ b/Desktop/widgets/filemenu/recentfileslistmodel.cpp @@ -4,12 +4,10 @@ #include #include -RecentFilesListModel::RecentFilesListModel(QObject *parent) : FileMenuBasicListModel(parent, new RecentFilesFileSystem(parent)) +RecentFilesListModel::RecentFilesListModel(FileMenuObject *parent) : FileMenuBasicListModel(parent, new RecentFilesFileSystem(parent)) { _fsbmRecentFiles = static_cast(_model); _fsbmRecentFiles->refresh(); - - connect(this, &RecentFilesListModel::openFileEvent, dynamic_cast (parent), &RecentFiles::openFile); } void RecentFilesListModel::addRecentFilePath(const QString &newpath) @@ -28,12 +26,3 @@ void RecentFilesListModel::addRecentFilePath(const QString &newpath) endResetModel(); } - -//Slots -void RecentFilesListModel::openFile(const QString &path) -{ - FileEvent *event = new FileEvent(this->parent(), FileEvent::FileOpen); - event->setPath(path); - - emit openFileEvent(event); -} diff --git a/Desktop/widgets/filemenu/recentfileslistmodel.h b/Desktop/widgets/filemenu/recentfileslistmodel.h index a4d4134d60..7ed9f3d369 100644 --- a/Desktop/widgets/filemenu/recentfileslistmodel.h +++ b/Desktop/widgets/filemenu/recentfileslistmodel.h @@ -12,16 +12,10 @@ class RecentFilesListModel : public FileMenuBasicListModel Q_OBJECT public: - explicit RecentFilesListModel(QObject *parent = nullptr); + explicit RecentFilesListModel(FileMenuObject *parent); void addRecentFilePath(const QString &newpath); -signals: - void openFileEvent(FileEvent *event); - -public slots: - void openFile(const QString& path) override; - private: RecentFilesFileSystem *_fsbmRecentFiles; };