Merge remote-tracking branch 'origin/bstoeger-divesites'

This commit is contained in:
Dirk Hohndel 2022-09-23 08:02:54 -07:00
commit 884dbc9d5e
10 changed files with 110 additions and 66 deletions

View file

@ -1,3 +1,4 @@
desktop: remove divesite list from tab-widgets
infobox: show an icon for warnings
import: allow import of divesites without UUID
profile: implement panning of the profile

View file

@ -27,6 +27,7 @@ set (SUBSURFACE_UI
divelogexportdialog.ui
divelogimportdialog.ui
divesiteimportdialog.ui
divesitelistview.ui
diveplanner.ui
diveshareexportdialog.ui
downloadfromdivecomputer.ui
@ -57,7 +58,6 @@ set (SUBSURFACE_UI
tab-widgets/TabDivePhotos.ui
tab-widgets/TabDiveExtraInfo.ui
tab-widgets/TabDiveEquipment.ui
tab-widgets/TabDiveSite.ui
)
# the interface, in C++
@ -80,6 +80,8 @@ set(SUBSURFACE_INTERFACE
diveshareexportdialog.h
divesiteimportdialog.cpp
divesiteimportdialog.h
divesitelistview.cpp
divesitelistview.h
downloadfromdivecomputer.cpp
downloadfromdivecomputer.h
filterconstraintwidget.cpp
@ -128,8 +130,6 @@ set(SUBSURFACE_INTERFACE
tab-widgets/TabDivePhotos.h
tab-widgets/TabDiveStatistics.cpp
tab-widgets/TabDiveStatistics.h
tab-widgets/TabDiveSite.cpp
tab-widgets/TabDiveSite.h
tab-widgets/maintab.cpp
tab-widgets/maintab.h
tableview.cpp

View file

@ -1,18 +1,28 @@
// SPDX-License-Identifier: GPL-2.0
#include "TabDiveSite.h"
#include "divesitelistview.h"
#include "core/subsurface-qt/divelistnotifier.h"
#include "core/divesite.h"
#include "core/divefilter.h"
#include "qt-models/divelocationmodel.h"
#include "desktop-widgets/mainwindow.h" // to place message box
#include "desktop-widgets/mainwindow.h"
#include "commands/command.h"
#include <QMessageBox>
TabDiveSite::TabDiveSite(QWidget *parent) : TabBase(parent)
DiveSiteListView::DiveSiteListView(QWidget *parent) : QWidget(parent)
{
ui.setupUi(this);
// What follows is duplicate code with locationinformation.cpp.
// We might want to unify this.
ui.diveSiteMessage->setCloseButtonVisible(false);
QAction *acceptAction = new QAction(tr("Done"), this);
connect(acceptAction, &QAction::triggered, this, &DiveSiteListView::done);
ui.diveSiteMessage->setText(tr("Dive site management"));
ui.diveSiteMessage->addAction(acceptAction);
model = new DiveSiteSortedModel(this);
ui.diveSites->setTitle(tr("Dive sites"));
ui.diveSites->setModel(model);
@ -27,24 +37,21 @@ TabDiveSite::TabDiveSite(QWidget *parent) : TabBase(parent)
for (int i = LocationInformationModel::LOCATION; i < LocationInformationModel::COLUMNS; ++i)
ui.diveSites->view()->setColumnHidden(i, true);
connect(ui.diveSites, &TableView::addButtonClicked, this, &TabDiveSite::add);
connect(ui.diveSites, &TableView::itemClicked, this, &TabDiveSite::diveSiteClicked);
connect(ui.diveSites->view()->selectionModel(), &QItemSelectionModel::selectionChanged, this, &TabDiveSite::selectionChanged);
connect(ui.diveSites, &TableView::addButtonClicked, this, &DiveSiteListView::add);
connect(ui.diveSites, &TableView::itemClicked, this, &DiveSiteListView::diveSiteClicked);
connect(ui.diveSites->view()->selectionModel(), &QItemSelectionModel::selectionChanged, this, &DiveSiteListView::selectionChanged);
// Subtle: We depend on this slot being executed after the slot in the model.
// This is realized because the model was constructed as a member object and connects in the constructor.
connect(&diveListNotifier, &DiveListNotifier::diveSiteChanged, this, &TabDiveSite::diveSiteChanged);
connect(&diveListNotifier, &DiveListNotifier::diveSiteChanged, this, &DiveSiteListView::diveSiteChanged);
}
void TabDiveSite::updateData()
void DiveSiteListView::done()
{
MainWindow::instance()->enterPreviousState();
}
void TabDiveSite::clear()
{
}
void TabDiveSite::diveSiteClicked(const QModelIndex &index)
void DiveSiteListView::diveSiteClicked(const QModelIndex &index)
{
struct dive_site *ds = model->getDiveSite(index);
if (!ds)
@ -55,7 +62,7 @@ void TabDiveSite::diveSiteClicked(const QModelIndex &index)
break;
case LocationInformationModel::REMOVE:
if (ds->dives.nr > 0 &&
QMessageBox::warning(MainWindow::instance(), tr("Delete dive site?"),
QMessageBox::warning(this, tr("Delete dive site?"),
tr("This dive site has %n dive(s). Do you really want to delete it?\n", "", ds->dives.nr),
QMessageBox::Yes|QMessageBox::No) == QMessageBox::No)
return;
@ -64,7 +71,7 @@ void TabDiveSite::diveSiteClicked(const QModelIndex &index)
}
}
void TabDiveSite::add()
void DiveSiteListView::add()
{
// This is mighty dirty: We hook into the "dive site added" signal and
// select the name field of the added dive site when the command sends
@ -72,12 +79,12 @@ void TabDiveSite::add()
// connection first. Very subtle!
// After the command has finished, the signal is disconnected so that dive
// site names are not selected on regular redo / undo.
connect(&diveListNotifier, &DiveListNotifier::diveSiteAdded, this, &TabDiveSite::diveSiteAdded);
connect(&diveListNotifier, &DiveListNotifier::diveSiteAdded, this, &DiveSiteListView::diveSiteAdded);
Command::addDiveSite(tr("New dive site"));
disconnect(&diveListNotifier, &DiveListNotifier::diveSiteAdded, this, &TabDiveSite::diveSiteAdded);
disconnect(&diveListNotifier, &DiveListNotifier::diveSiteAdded, this, &DiveSiteListView::diveSiteAdded);
}
void TabDiveSite::diveSiteAdded(struct dive_site *, int idx)
void DiveSiteListView::diveSiteAdded(struct dive_site *, int idx)
{
if (idx < 0)
return;
@ -87,7 +94,7 @@ void TabDiveSite::diveSiteAdded(struct dive_site *, int idx)
ui.diveSites->view()->edit(localIdx);
}
void TabDiveSite::diveSiteChanged(struct dive_site *ds, int field)
void DiveSiteListView::diveSiteChanged(struct dive_site *ds, int field)
{
int idx = get_divesite_idx(ds, &dive_site_table);
if (idx < 0)
@ -97,17 +104,17 @@ void TabDiveSite::diveSiteChanged(struct dive_site *ds, int field)
ui.diveSites->view()->scrollTo(localIdx);
}
void TabDiveSite::on_purgeUnused_clicked()
void DiveSiteListView::on_purgeUnused_clicked()
{
Command::purgeUnusedDiveSites();
}
void TabDiveSite::on_filterText_textChanged(const QString &text)
void DiveSiteListView::on_filterText_textChanged(const QString &text)
{
model->setFilter(text);
}
QVector<dive_site *> TabDiveSite::selectedDiveSites()
QVector<dive_site *> DiveSiteListView::selectedDiveSites()
{
const QModelIndexList indices = ui.diveSites->view()->selectionModel()->selectedRows();
QVector<dive_site *> sites;
@ -119,20 +126,21 @@ QVector<dive_site *> TabDiveSite::selectedDiveSites()
return sites;
}
void TabDiveSite::selectionChanged(const QItemSelection &, const QItemSelection &)
void DiveSiteListView::selectionChanged(const QItemSelection &, const QItemSelection &)
{
DiveFilter::instance()->setFilterDiveSite(selectedDiveSites());
}
void TabDiveSite::showEvent(QShowEvent *)
void DiveSiteListView::hideEvent(QHideEvent *)
{
// If the user switches to the dive site tab and there was already a selection,
// filter on that selection.
DiveFilter::instance()->stopFilterDiveSites();
}
void DiveSiteListView::showEvent(QShowEvent *)
{
// If the user switches to the dive site tab and there was already a selection,
// filter on that selection.
DiveFilter::instance()->startFilterDiveSites(selectedDiveSites());
}
void TabDiveSite::hideEvent(QHideEvent *)
{
// If the user switches to a different tab, stop the dive site filtering
DiveFilter::instance()->stopFilterDiveSites();
}

View file

@ -1,20 +1,18 @@
// SPDX-License-Identifier: GPL-2.0
#ifndef TAB_DIVE_SITE_H
#define TAB_DIVE_SITE_H
#ifndef DIVE_SITE_LIST_VIEW_H
#define DIVE_SITE_LIST_VIEW_H
#include "TabBase.h"
#include "ui_TabDiveSite.h"
#include "ui_divesitelistview.h"
class DiveSiteSortedModel;
class TabDiveSite : public TabBase {
class DiveSiteListView : public QWidget {
Q_OBJECT
public:
TabDiveSite(QWidget *parent = 0);
void updateData() override;
void clear() override;
DiveSiteListView(QWidget *parent = 0);
private slots:
void add();
void done();
void diveSiteAdded(struct dive_site *, int idx);
void diveSiteChanged(struct dive_site *ds, int field);
void diveSiteClicked(const QModelIndex &);
@ -22,7 +20,7 @@ private slots:
void on_filterText_textChanged(const QString &text);
void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
private:
Ui::TabDiveSite ui;
Ui::DiveSiteListView ui;
DiveSiteSortedModel *model;
QVector<dive_site *> selectedDiveSites();
void hideEvent(QHideEvent *) override;

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TabDiveSite</class>
<widget class="QWidget" name="TabDiveSite">
<class>DiveSiteListView</class>
<widget class="QWidget" name="DiveSiteListView">
<property name="geometry">
<rect>
<x>0</x>
@ -14,6 +14,16 @@
<string>Dive sites</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item row="0" column="0" colspan="5">
<widget class="KMessageWidget" name="diveSiteMessage">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="layout">
<item>

View file

@ -220,7 +220,7 @@ void LocationInformationWidget::acceptChanges()
MainWindow::instance()->diveList->setEnabled(true);
MainWindow::instance()->setEnabledToolbar(true);
MainWindow::instance()->setApplicationState(MainWindow::ApplicationState::Default);
MainWindow::instance()->enterPreviousState();
DiveFilter::instance()->stopFilterDiveSites();
// Subtlety alert: diveSite must be cleared *after* exiting the dive-site mode.
@ -246,7 +246,6 @@ void LocationInformationWidget::initFields(dive_site *ds)
}
}
void LocationInformationWidget::on_GPSbutton_clicked()
{
QFileInfo finfo(system_default_directory());

View file

@ -39,8 +39,9 @@
#include "desktop-widgets/divelistview.h"
#include "desktop-widgets/divelogexportdialog.h"
#include "desktop-widgets/divelogimportdialog.h"
#include "desktop-widgets/divesiteimportdialog.h"
#include "desktop-widgets/diveplanner.h"
#include "desktop-widgets/divesiteimportdialog.h"
#include "desktop-widgets/divesitelistview.h"
#include "desktop-widgets/downloadfromdivecomputer.h"
#include "desktop-widgets/findmovedimagesdialog.h"
#include "desktop-widgets/locationinformation.h"
@ -139,6 +140,7 @@ MainWindow::MainWindow() :
#endif
plannerWidgets.reset(new PlannerWidgets);
statistics.reset(new StatsWidget);
diveSites.reset(new DiveSiteListView);
profile.reset(new ProfileWidget);
diveSiteEdit.reset(new LocationInformationWidget);
@ -155,6 +157,8 @@ MainWindow::MainWindow() :
{ diveList.get(), FLAG_NONE }, { &filterWidget, FLAG_NONE } });
registerApplicationState(ApplicationState::Statistics, { true, { statistics.get(), FLAG_NONE }, { nullptr, FLAG_NONE },
{ diveList.get(), FLAG_DISABLED }, { &filterWidget, FLAG_NONE } });
registerApplicationState(ApplicationState::DiveSites, { false, { diveSites.get(), FLAG_NONE }, { profile.get(), FLAG_NONE },
{ diveList.get(), FLAG_NONE }, { mapWidget.get(), FLAG_NONE } });
registerApplicationState(ApplicationState::MapMaximized, { true, { nullptr, FLAG_NONE }, { nullptr, FLAG_NONE },
{ nullptr, FLAG_NONE }, { mapWidget.get(), FLAG_NONE } });
registerApplicationState(ApplicationState::ProfileMaximized, { true, { nullptr, FLAG_NONE }, { profile.get(), FLAG_NONE },
@ -278,6 +282,7 @@ void MainWindow::editDiveSite(dive_site *ds)
if (!ds)
return;
diveSiteEdit->initFields(ds);
state_stack.push_back(appState);
setApplicationState(ApplicationState::EditDiveSite);
}
@ -757,6 +762,14 @@ void MainWindow::on_actionViewMap_triggered()
setApplicationState(ApplicationState::MapMaximized);
}
void MainWindow::on_actionViewDiveSites_triggered()
{
if (!userMayChangeAppState())
return;
state_stack.push_back(appState);
setApplicationState(ApplicationState::DiveSites);
}
void MainWindow::on_actionViewAll_triggered()
{
if (!userMayChangeAppState())
@ -1461,6 +1474,17 @@ bool MainWindow::userMayChangeAppState() const
return applicationState[(int)appState].allowUserChange;
}
// For the dive-site list view and the dive-site edit states,
// we remember the previous state and then switch back to that.
void MainWindow::enterPreviousState()
{
if (state_stack.empty())
setApplicationState(ApplicationState::Default);
ApplicationState prev = state_stack.back();
state_stack.pop_back();
setApplicationState(prev);
}
void MainWindow::setApplicationState(ApplicationState state)
{
if (appState == state)
@ -1508,6 +1532,7 @@ void MainWindow::setApplicationState(ApplicationState state)
ui.actionViewProfile->setEnabled(allowChange);
ui.actionViewInfo->setEnabled(allowChange);
ui.actionViewMap->setEnabled(allowChange);
ui.actionViewDiveSites->setEnabled(allowChange);
ui.actionFilterTags->setEnabled(allowChange);
}

View file

@ -28,6 +28,7 @@ class QSortFilterProxyModel;
class DiveTripModel;
class QItemSelection;
class DiveListView;
class DiveSiteListView;
class MainTab;
class MapWidget;
class QWebView;
@ -59,6 +60,7 @@ public:
EditDiveSite,
FilterDive,
Statistics,
DiveSites,
MapMaximized,
ProfileMaximized,
ListMaximized,
@ -70,6 +72,7 @@ public:
void importFiles(const QStringList importFiles);
void setToolButtonsEnabled(bool enabled);
void setApplicationState(ApplicationState state);
void enterPreviousState();
NotificationWidget *getNotificationWidget();
void enableDisableCloudActions();
void enableDisableOtherDCsActions();
@ -79,6 +82,7 @@ public:
std::unique_ptr<MainTab> mainTab;
std::unique_ptr<PlannerWidgets> plannerWidgets;
std::unique_ptr<StatsWidget> statistics;
std::unique_ptr<DiveSiteListView> diveSites;
std::unique_ptr<DiveListView> diveList;
std::unique_ptr<ProfileWidget> profile;
std::unique_ptr<MapWidget> mapWidget;
@ -112,6 +116,7 @@ slots:
void on_actionViewProfile_triggered();
void on_actionViewInfo_triggered();
void on_actionViewMap_triggered();
void on_actionViewDiveSites_triggered();
void on_actionViewAll_triggered();
void on_actionPreviousDC_triggered();
void on_actionNextDC_triggered();
@ -158,6 +163,7 @@ slots:
private:
ApplicationState appState;
std::vector<ApplicationState> state_stack;
Ui::MainWindow ui;
FilterWidget filterWidget;
std::unique_ptr<QSplitter> topSplitter;

View file

@ -93,6 +93,7 @@
<addaction name="actionViewProfile"/>
<addaction name="actionViewInfo"/>
<addaction name="actionViewMap"/>
<addaction name="actionViewDiveSites"/>
<addaction name="separator"/>
<addaction name="actionStats"/>
<addaction name="actionYearlyStatistics"/>
@ -343,6 +344,14 @@
<string notr="true">Ctrl+5</string>
</property>
</action>
<action name="actionViewDiveSites">
<property name="text">
<string>Dive sites</string>
</property>
<property name="shortcut">
<string notr="true">Ctrl+6</string>
</property>
</action>
<action name="actionDivePlanner">
<property name="text">
<string>P&amp;lan dive</string>

View file

@ -13,7 +13,6 @@
#include "TabDiveNotes.h"
#include "TabDivePhotos.h"
#include "TabDiveStatistics.h"
#include "TabDiveSite.h"
#include "core/selection.h"
#include "qt-models/diveplannermodel.h"
@ -43,8 +42,6 @@ MainTab::MainTab(QWidget *parent) : QTabWidget(parent),
addTab(extraWidgets.last(), tr("Media"));
extraWidgets << new TabDiveExtraInfo(this);
addTab(extraWidgets.last(), tr("Extra Info"));
extraWidgets << new TabDiveSite(this);
addTab(extraWidgets.last(), tr("Dive sites"));
// make sure we know if this is a light or dark mode
isDark = paletteIsDark(palette());
@ -79,39 +76,30 @@ void MainTab::updateDiveInfo()
if (DivePlannerPointsModel::instance()->isPlanner())
return;
// If there is no current dive, disable all widgets except the last one,
// which is the dive site tab
// TODO: Conceptually, this shouldn't even be a tab here!
// If there is no current dive, disable all widgets.
bool enabled = current_dive != nullptr;
for (int i = 0; i < extraWidgets.size() - 1; ++i)
extraWidgets[i]->setEnabled(enabled);
for (TabBase *widget: extraWidgets)
widget->setEnabled(enabled);
if (current_dive) {
for (TabBase *widget: extraWidgets)
widget->updateData();
// If we're on the dive-site tab, we don't want to switch tab when entering / exiting
// trip mode. The reason is that
// 1) this disrupts the user-experience and
// 2) the filter is reset, potentially erasing the current trip under our feet.
// TODO: Don't hard code tab location!
bool onDiveSiteTab = currentIndex() == 6;
if (single_selected_trip()) {
// Remember the tab selected for last dive but only if we're not on the dive site tab
if (lastSelectedDive && !onDiveSiteTab)
if (lastSelectedDive)
lastTabSelectedDive = currentIndex();
setTabText(0, tr("Trip notes"));
// Recover the tab selected for last dive trip but only if we're not on the dive site tab
if (lastSelectedDive && !onDiveSiteTab)
if (lastSelectedDive)
setCurrentIndex(lastTabSelectedDiveTrip);
lastSelectedDive = false;
} else {
// Remember the tab selected for last dive trip but only if we're not on the dive site tab
if (!lastSelectedDive && !onDiveSiteTab)
if (!lastSelectedDive)
lastTabSelectedDiveTrip = currentIndex();
setTabText(0, tr("Notes"));
// Recover the tab selected for last dive but only if we're not on the dive site tab
if (!lastSelectedDive && !onDiveSiteTab)
if (!lastSelectedDive)
setCurrentIndex(lastTabSelectedDive);
lastSelectedDive = true;
}