Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 14 additions & 19 deletions src/appproject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,12 @@ void AppProject::closeProject()
// Close any remaining windows (for example split auto-clones).
_mdiArea->closeAllSubWindows();

// Force immediate destruction of MDI subwindows (WA_DeleteOnClose uses
// deleteLater, so without this their destructors run after new forms are
// already created, causing deviceIdAdded/unitMapAdded signals to be
// suppressed for the newly opened project).
QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);

const auto deleteClosedForms = [this](auto&& shouldDelete) {
const auto snapshot = _closedForms;
for (auto* frm : snapshot) {
Expand All @@ -628,6 +634,8 @@ void AppProject::closeProject()

_closedForms.clear();
_mbServer.clearAddressSpace();
_mbServer.clearDescriptions();
_mbServer.clearTimestamps();
_dataCounter = 0;
_trafficCounter = 0;
_scriptCounter = 0;
Expand Down Expand Up @@ -1687,11 +1695,11 @@ void AppProject::loadProject(const QString& filename)
if(!file.open(QFile::ReadOnly))
return;

_projectFilename = QFileInfo(filename).absoluteFilePath();
emit projectOpened(_projectFilename);

_mbServer.clearDescriptions();
_mbServer.clearTimestamps();
if (_projectFilename.isEmpty()) {
setSavePath(QFileInfo(filename).absoluteDir().absolutePath());
_projectFilename = QFileInfo(filename).absoluteFilePath();
emit projectOpened(_projectFilename);
}

ModbusDefinitions defs;
QList<ConnectionDetails> conns;
Expand Down Expand Up @@ -1773,20 +1781,6 @@ void AppProject::loadProject(const QString& filename)
_mdiArea->setSplitViewEnabled(splitView);
viewPreparedForForms = true;
}

_mdiArea->closeAllSubWindows();
// Force immediate destruction of MDI subwindows (WA_DeleteOnClose uses
// deleteLater, so without this their destructors run after new forms are
// already created, causing deviceIdAdded/unitMapAdded signals to be
// suppressed for the newly opened project).
QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
// Clean up forms that were already closed (hidden)
const auto closed = _closedForms;
for (auto&& frm : closed) {
_projectTree->removeForm(frm);
delete frm;
}
_closedForms.clear();
while (xml.readNextStartElement()) {
ProjectFormKind kind;
bool isForm = true;
Expand Down Expand Up @@ -2103,6 +2097,7 @@ bool AppProject::saveProject(const QString& filename)
return false;
}

setSavePath(QFileInfo(filename).absoluteDir().absolutePath());
_projectFilename = absoluteFilename;

QXmlStreamWriter w(&file);
Expand Down
1 change: 1 addition & 0 deletions src/appproject.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ class AppProject : public QObject
void loadProject(const QString& filename);
bool saveProject(const QString& filename);
void restoreActiveWindows();
const QString & filePath() const noexcept { return _projectFilename; }

// Called from MainWindow::~MainWindow() before delete ui.
// Closes MDI windows and deletes forms/scripts without touching the project tree UI
Expand Down
161 changes: 104 additions & 57 deletions src/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ MainWindow::MainWindow(const QString& profile, bool useSession, const QString& s
setWindowTitle(APP_PRODUCT_NAME);
setUnifiedTitleAndToolBarOnMac(true);
setStatusBar(new MainStatusBar(_mbMultiServer, this));
setAcceptDrops(true);

if (auto* newButton = qobject_cast<QToolButton*>(ui->toolBarMain->widgetForAction(ui->actionNew))) {
newButton->setMenu(ui->menuNew);
Expand Down Expand Up @@ -522,9 +523,6 @@ void MainWindow::on_outputDockVisibilityChanged(bool visible)
///
void MainWindow::on_mdiSubWindowActivated(QMdiSubWindow* wnd)
{
if(wnd)
markModified();

QMdiSubWindow* stableWnd = ui->mdiArea->activeSubWindow();
if(!stableWnd)
stableWnd = ui->mdiArea->currentSubWindow();
Expand Down Expand Up @@ -693,12 +691,10 @@ void MainWindow::changeEvent(QEvent* event)
///
void MainWindow::closeEvent(QCloseEvent *event)
{
const bool shouldAskToSave = hasProjectContext() && (_isModified || _projectFilePath.isEmpty());
if(shouldAskToSave) {
if(!confirmSaveOnClose()) {
event->ignore();
return;
}
if (!confirmSaveOnClose()) {
// User canceled
event->ignore();
return;
}

saveAppSettings();
Expand Down Expand Up @@ -748,6 +744,47 @@ bool MainWindow::eventFilter(QObject* obj, QEvent* e)
return QObject::eventFilter(obj, e);
}

void MainWindow::dragEnterEvent(QDragEnterEvent *event)
{
const auto urls = event->mimeData()->urls();
if (!urls.empty()) {
if (QFile::exists(urls.front().toLocalFile())) {
event->acceptProposedAction();
}
}
}

void MainWindow::dropEvent(QDropEvent *event)
{
const auto urls = event->mimeData()->urls();
if (!urls.empty()) {
auto replace = true;
if (hasProjectContext()) {
switch (QMessageBox::question(this, APP_PRODUCT_NAME,
tr("Would you like to combine the file(s) with the project?"),
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::No)) {
case QMessageBox::Yes:
replace = false;
break;
case QMessageBox::No:
replace = true;
break;
default:
// User canceled
return;
}
}
for (const auto& url : urls) {
if (!loadProject(url.toLocalFile(), replace)) {
// User canceled
return;
}
replace = false;
}
event->acceptProposedAction();
}
}

///
/// \brief MainWindow::on_awake
///
Expand Down Expand Up @@ -931,25 +968,27 @@ void MainWindow::on_actionOpenProject_triggered()
filters << tr("Project files (*.omsim)");
filters << tr("All files (*)");

const auto filename = QFileDialog::getOpenFileName(this, QString(), _project->savePath(), filters.join(";;"));
if(filename.isEmpty()) return;

_project->setSavePath(QFileInfo(filename).absoluteDir().absolutePath());
loadProject(filename);
const auto filenames = QFileDialog::getOpenFileNames(this, QString(), _project->savePath(), filters.join(";;"));
auto replace = true;
for (const auto& filename : filenames) {
if (!loadProject(filename, replace)) {
return;
}
replace = false;
}
}

///
/// \brief MainWindow::on_actionSaveProject_triggered
///
void MainWindow::on_actionSaveProject_triggered()
{
if(_projectFilePath.isEmpty()) {
if (_project->filePath().isEmpty()) {
on_actionSaveProjectAs_triggered();
return;
}

if(saveProject(_projectFilePath)) {
addRecentProject(_projectFilePath);
if (saveProject(_project->filePath())) {
return;
}

Expand All @@ -969,18 +1008,7 @@ void MainWindow::on_actionSaveProjectAs_triggered()
///
void MainWindow::on_actionCloseProject_triggered()
{
const bool shouldAskToSave = hasProjectContext() && (_isModified || _projectFilePath.isEmpty());
if(shouldAskToSave) {
if(!confirmSaveOnClose())
return;
}

_project->closeProject();
_projectFilePath.clear();
_lastProjectPath.clear();
_isModified = false;
updateProjectWindowTitle();
updateMainToolbarState();
closeProject();
}

///
Expand Down Expand Up @@ -1524,29 +1552,29 @@ void MainWindow::presetRegs(QModbusDataUnit::RegisterType type)
///
/// \brief MainWindow::loadProject
/// \param filename
/// \param replace
/// \return
///
void MainWindow::loadProject(const QString& filename)
bool MainWindow::loadProject(const QString& filename, bool replace)
{
if (hasProjectContext()) {
_project->closeProject();
_projectFilePath.clear();
_isModified = false;
updateProjectWindowTitle();
if (replace) {
if (!closeProject()) {
// User canceled
return false;
}
AppLogger::clear();
}

AppLogger::clear();

_project->loadProject(filename);
applyGlobalAddressBase(AppPreferences::instance().globalAddressBase(), false);
applyGlobalHexView(AppPreferences::instance().globalHexView(), false);
syncGlobalViewControls();
_projectFilePath = QFileInfo(filename).absoluteFilePath();
_lastProjectPath = _projectFilePath;
_project->setSavePath(QFileInfo(filename).absoluteDir().absolutePath());
_isModified = false;
_isModified = !replace;
updateProjectWindowTitle();
updateMainToolbarState();
addRecentProject(_projectFilePath);
addRecentProject(_project->filePath());

return true;
}

///
Expand All @@ -1558,11 +1586,28 @@ bool MainWindow::saveProject(const QString& filename)
if (!_project->saveProject(filename))
return false;

_projectFilePath = QFileInfo(filename).absoluteFilePath();
_lastProjectPath = _projectFilePath;
_project->setSavePath(QFileInfo(filename).absoluteDir().absolutePath());
_isModified = false;
updateProjectWindowTitle();
addRecentProject(_project->filePath());
return true;
}

///
/// \brief MainWindow::closeProject
/// \return
///
bool MainWindow::closeProject()
{
if (!confirmSaveOnClose()) {
// User canceled
return false;
}

_project->closeProject();
_isModified = false;
updateProjectWindowTitle();
updateMainToolbarState();

return true;
}

Expand All @@ -1572,8 +1617,8 @@ bool MainWindow::saveProject(const QString& filename)
///
QString MainWindow::projectName() const
{
if(!_projectFilePath.isEmpty())
return QFileInfo(_projectFilePath).completeBaseName();
if (!_project->filePath().isEmpty())
return QFileInfo(_project->filePath()).completeBaseName();

if(hasProjectContext())
return tr("Untitled");
Expand All @@ -1592,7 +1637,7 @@ void MainWindow::updateProjectWindowTitle()
if(name.isEmpty())
setWindowTitle(modifiedMark + APP_PRODUCT_NAME);
else
setWindowTitle(QString("%1%2 - %3").arg(modifiedMark, APP_PRODUCT_NAME, name));
setWindowTitle(QString("%1%2 - %3").arg(modifiedMark, name, APP_PRODUCT_NAME));
}

///
Expand Down Expand Up @@ -1750,7 +1795,7 @@ void MainWindow::saveAppSettings()
m.setValue("SavePath", _project->savePath());
m.setValue(kNewFormKindKey, newFormKindToSetting(_newFormKind));
m.setValue(kRecentProjectsKey, _recentProjects);
m.setValue(kLastProjectPathKey, _lastProjectPath);
m.setValue(kLastProjectPathKey, _project->filePath());
}

///
Expand Down Expand Up @@ -1921,6 +1966,11 @@ void MainWindow::applyGlobalHexView(bool enabled, bool persist)
///
bool MainWindow::confirmSaveOnClose()
{
const auto shouldAskToSave = hasProjectContext() && (_isModified || _project->filePath().isEmpty());
if (!shouldAskToSave) {
return true;
}

const auto button = QMessageBox::question(this,
tr("Save Project"),
tr("Save project before closing?"),
Expand All @@ -1932,9 +1982,8 @@ bool MainWindow::confirmSaveOnClose()
if(button != QMessageBox::Save)
return true;

if(!_projectFilePath.isEmpty()) {
if(saveProject(_projectFilePath)) {
addRecentProject(_projectFilePath);
if (!_project->filePath().isEmpty()) {
if (saveProject(_project->filePath())) {
return true;
}

Expand All @@ -1954,9 +2003,9 @@ bool MainWindow::promptSaveProjectAs(const QString& initialPath)
QStringList filters;
filters << tr("Project files (*.omsim)");

const QString defaultPath = _projectFilePath.isEmpty()
const QString defaultPath = _project->filePath().isEmpty()
? _project->savePath() + "/" + projectName() + ".omsim"
: _projectFilePath;
: _project->filePath();
const QString dialogPath = initialPath.isEmpty() ? defaultPath : initialPath;
auto filename = QFileDialog::getSaveFileName(this, QString(), dialogPath, filters.join(";;"));

Expand All @@ -1966,11 +2015,9 @@ bool MainWindow::promptSaveProjectAs(const QString& initialPath)
if(!filename.endsWith(".omsim", Qt::CaseInsensitive))
filename.append(".omsim");

_project->setSavePath(QFileInfo(filename).absoluteDir().absolutePath());
if(!saveProject(filename))
return false;

addRecentProject(QFileInfo(filename).absoluteFilePath());
return true;
}

Expand All @@ -1997,7 +2044,7 @@ QString MainWindow::projectSavePathInProfileDir() const
///
bool MainWindow::hasProjectContext() const
{
return !_projectFilePath.isEmpty()
return !_project->filePath().isEmpty()
|| _project->firstMdiChild() != nullptr
|| !_project->closedForms().isEmpty();
}
Expand Down
Loading
Loading