2017-04-27 18:26:05 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2016-04-05 05:02:03 +00:00
|
|
|
#include "desktop-widgets/locationinformation.h"
|
2020-01-19 20:06:50 +00:00
|
|
|
#include "desktop-widgets/importgps.h"
|
2018-05-11 15:25:41 +00:00
|
|
|
#include "core/subsurface-string.h"
|
2016-04-05 05:02:03 +00:00
|
|
|
#include "desktop-widgets/mainwindow.h"
|
|
|
|
#include "desktop-widgets/divelistview.h"
|
|
|
|
#include "core/qthelper.h"
|
2017-07-15 20:52:59 +00:00
|
|
|
#include "desktop-widgets/mapwidget.h"
|
2020-02-04 09:03:41 +00:00
|
|
|
#include "core/color.h"
|
2019-11-17 17:13:55 +00:00
|
|
|
#include "core/divefilter.h"
|
core: introduce divelog structure
The parser API was very annoying, as a number of tables
to-be-filled were passed in as pointers. The goal of this
commit is to collect all these tables in a single struct.
This should make it (more or less) clear what is actually
written into the divelog files.
Moreover, it should now be rather easy to search for
instances, where the global logfile is accessed (and it
turns out that there are many!).
The divelog struct does not contain the tables as substructs,
but only collects pointers. The idea is that the "divelog.h"
file can be included without all the other files describing
the numerous tables.
To make it easier to use from C++ parts of the code, the
struct implements a constructor and a destructor. Sadly,
we can't use smart pointers, since the pointers are accessed
from C code. Therfore the constructor and destructor are
quite complex.
The whole commit is large, but was mostly an automatic
conversion.
One oddity of note: the divelog structure also contains
the "autogroup" flag, since that is saved in the divelog.
This actually fixes a bug: Before, when importing dives
from a different log, the autogroup flag was overwritten.
This was probably not intended and does not happen anymore.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2022-11-08 20:31:08 +00:00
|
|
|
#include "core/divelog.h"
|
2020-05-01 11:51:29 +00:00
|
|
|
#include "core/divesite.h"
|
2016-04-05 05:02:03 +00:00
|
|
|
#include "core/divesitehelpers.h"
|
|
|
|
#include "desktop-widgets/modeldelegates.h"
|
2020-02-03 18:33:06 +00:00
|
|
|
#include "core/subsurface-qt/divelistnotifier.h"
|
2019-03-15 13:32:55 +00:00
|
|
|
#include "core/taxonomy.h"
|
2022-04-04 16:57:28 +00:00
|
|
|
#include "core/selection.h"
|
2019-03-25 21:18:32 +00:00
|
|
|
#include "core/settings/qPrefUnit.h"
|
2019-11-13 14:08:40 +00:00
|
|
|
#include "commands/command.h"
|
2015-08-25 21:45:29 +00:00
|
|
|
|
2015-05-17 19:13:41 +00:00
|
|
|
#include <QShowEvent>
|
2015-09-01 00:11:28 +00:00
|
|
|
#include <QItemSelectionModel>
|
2015-09-01 01:01:25 +00:00
|
|
|
#include <qmessagebox.h>
|
2015-09-01 00:35:17 +00:00
|
|
|
#include <cstdlib>
|
2022-02-10 01:00:48 +00:00
|
|
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
2015-09-21 19:08:58 +00:00
|
|
|
#include <QDesktopWidget>
|
2022-02-10 01:00:48 +00:00
|
|
|
#endif
|
2020-01-19 20:06:50 +00:00
|
|
|
#include <QFileDialog>
|
2015-09-21 19:08:58 +00:00
|
|
|
#include <QScrollBar>
|
2015-05-17 19:13:41 +00:00
|
|
|
|
2019-03-25 21:18:32 +00:00
|
|
|
LocationInformationWidget::LocationInformationWidget(QWidget *parent) : QGroupBox(parent), diveSite(nullptr), closeDistance(0)
|
2015-05-17 19:13:41 +00:00
|
|
|
{
|
|
|
|
ui.setupUi(this);
|
|
|
|
ui.diveSiteMessage->setCloseButtonVisible(false);
|
|
|
|
|
2019-03-14 22:28:45 +00:00
|
|
|
QAction *acceptAction = new QAction(tr("Done"), this);
|
|
|
|
connect(acceptAction, &QAction::triggered, this, &LocationInformationWidget::acceptChanges);
|
2015-05-17 19:13:41 +00:00
|
|
|
|
|
|
|
ui.diveSiteMessage->setText(tr("Dive site management"));
|
2015-06-04 03:18:25 +00:00
|
|
|
ui.diveSiteMessage->addAction(acceptAction);
|
2015-05-17 19:13:41 +00:00
|
|
|
|
2022-08-13 18:19:01 +00:00
|
|
|
connect(ui.geoCodeButton, &QPushButton::clicked, this, &LocationInformationWidget::reverseGeocode);
|
2017-11-26 22:26:46 +00:00
|
|
|
ui.diveSiteCoordinates->installEventFilter(this);
|
2015-07-31 01:10:01 +00:00
|
|
|
|
2019-03-12 22:51:39 +00:00
|
|
|
connect(&diveListNotifier, &DiveListNotifier::diveSiteChanged, this, &LocationInformationWidget::diveSiteChanged);
|
2019-09-06 20:01:59 +00:00
|
|
|
connect(&diveListNotifier, &DiveListNotifier::diveSiteDeleted, this, &LocationInformationWidget::diveSiteDeleted);
|
2019-03-25 21:18:32 +00:00
|
|
|
connect(qPrefUnits::instance(), &qPrefUnits::unit_systemChanged, this, &LocationInformationWidget::unitsChanged);
|
2019-03-12 22:51:39 +00:00
|
|
|
|
2018-10-08 17:01:45 +00:00
|
|
|
ui.diveSiteListView->setModel(&filter_model);
|
2015-09-01 00:03:31 +00:00
|
|
|
ui.diveSiteListView->setModelColumn(LocationInformationModel::NAME);
|
2015-09-01 00:11:28 +00:00
|
|
|
ui.diveSiteListView->installEventFilter(this);
|
2015-05-17 20:14:23 +00:00
|
|
|
}
|
|
|
|
|
2019-03-16 09:44:39 +00:00
|
|
|
void LocationInformationWidget::keyPressEvent(QKeyEvent *e)
|
|
|
|
{
|
|
|
|
if (e->key() == Qt::Key_Escape)
|
|
|
|
MainWindow::instance()->setFocus();
|
|
|
|
return QGroupBox::keyPressEvent(e);
|
|
|
|
}
|
|
|
|
|
2022-08-13 18:19:01 +00:00
|
|
|
bool LocationInformationWidget::eventFilter(QObject *, QEvent *ev)
|
2015-09-01 00:11:28 +00:00
|
|
|
{
|
2015-09-29 23:57:53 +00:00
|
|
|
if (ev->type() == QEvent::ContextMenu) {
|
|
|
|
QContextMenuEvent *ctx = (QContextMenuEvent *)ev;
|
2015-09-04 10:23:19 +00:00
|
|
|
QMenu contextMenu;
|
2019-10-14 18:57:13 +00:00
|
|
|
contextMenu.addAction(tr("Merge into current site"), this, &LocationInformationWidget::mergeSelectedDiveSites);
|
2023-04-21 12:18:32 +00:00
|
|
|
const QModelIndexList selection = ui.diveSiteListView->selectionModel()->selectedIndexes();
|
|
|
|
if (selection.count() == 1)
|
|
|
|
contextMenu.addAction(tr("Merge current site into this site"), this, &LocationInformationWidget::mergeIntoSelectedDiveSite);
|
2015-09-04 10:23:19 +00:00
|
|
|
contextMenu.exec(ctx->globalPos());
|
|
|
|
return true;
|
2015-09-01 00:11:28 +00:00
|
|
|
}
|
2015-09-01 02:24:36 +00:00
|
|
|
return false;
|
2015-09-01 00:11:28 +00:00
|
|
|
}
|
|
|
|
|
2017-11-26 22:26:46 +00:00
|
|
|
void LocationInformationWidget::enableLocationButtons(bool enable)
|
|
|
|
{
|
|
|
|
ui.geoCodeButton->setEnabled(enable);
|
|
|
|
}
|
|
|
|
|
2015-09-29 23:57:53 +00:00
|
|
|
void LocationInformationWidget::mergeSelectedDiveSites()
|
|
|
|
{
|
2018-10-13 11:14:48 +00:00
|
|
|
if (!diveSite)
|
|
|
|
return;
|
2015-09-01 01:01:25 +00:00
|
|
|
|
2019-04-01 20:15:19 +00:00
|
|
|
const QModelIndexList selection = ui.diveSiteListView->selectionModel()->selectedIndexes();
|
2019-03-15 16:41:31 +00:00
|
|
|
QVector<dive_site *> selected_dive_sites;
|
2018-10-23 17:40:41 +00:00
|
|
|
selected_dive_sites.reserve(selection.count());
|
2019-04-01 20:15:19 +00:00
|
|
|
for (const QModelIndex &idx: selection) {
|
2018-10-28 20:16:42 +00:00
|
|
|
dive_site *ds = idx.data(LocationInformationModel::DIVESITE_ROLE).value<dive_site *>();
|
2018-10-23 17:40:41 +00:00
|
|
|
if (ds)
|
|
|
|
selected_dive_sites.push_back(ds);
|
2015-09-01 00:35:17 +00:00
|
|
|
}
|
2019-03-15 16:41:31 +00:00
|
|
|
Command::mergeDiveSites(diveSite, selected_dive_sites);
|
2015-09-01 00:35:17 +00:00
|
|
|
}
|
|
|
|
|
2023-04-21 12:18:32 +00:00
|
|
|
void LocationInformationWidget::mergeIntoSelectedDiveSite()
|
|
|
|
{
|
|
|
|
if (!diveSite)
|
|
|
|
return;
|
|
|
|
|
|
|
|
const QModelIndexList selection = ui.diveSiteListView->selectionModel()->selectedIndexes();
|
|
|
|
if (selection.count() != 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
dive_site *selected_dive_site = selection[0].data(LocationInformationModel::DIVESITE_ROLE).value<dive_site *>();
|
|
|
|
if (!selected_dive_site)
|
|
|
|
return;
|
|
|
|
|
|
|
|
QVector<dive_site *> dive_sites;
|
|
|
|
dive_sites.push_back(diveSite);
|
|
|
|
Command::mergeDiveSites(selected_dive_site, dive_sites);
|
|
|
|
}
|
|
|
|
|
2020-02-04 09:03:41 +00:00
|
|
|
// If we can't parse the coordinates, inform the user with a visual clue
|
|
|
|
void LocationInformationWidget::coordinatesSetWarning(bool warn)
|
|
|
|
{
|
|
|
|
QPalette palette;
|
|
|
|
if (warn) {
|
|
|
|
palette.setColor(QPalette::Base, REDORANGE1_MED_TRANS);
|
|
|
|
palette.setColor(QPalette::Text, WHITE1);
|
|
|
|
}
|
|
|
|
ui.diveSiteCoordinates->setPalette(palette);
|
|
|
|
}
|
|
|
|
|
2015-07-25 16:03:14 +00:00
|
|
|
void LocationInformationWidget::updateLabels()
|
2015-05-17 19:13:41 +00:00
|
|
|
{
|
2018-10-13 11:14:48 +00:00
|
|
|
if (!diveSite) {
|
|
|
|
clearLabels();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (diveSite->name)
|
|
|
|
ui.diveSiteName->setText(diveSite->name);
|
2015-05-17 19:13:41 +00:00
|
|
|
else
|
|
|
|
ui.diveSiteName->clear();
|
2019-03-15 13:32:55 +00:00
|
|
|
const char *country = taxonomy_get_country(&diveSite->taxonomy);
|
2017-10-03 06:03:44 +00:00
|
|
|
if (country)
|
|
|
|
ui.diveSiteCountry->setText(country);
|
2017-10-02 15:51:20 +00:00
|
|
|
else
|
|
|
|
ui.diveSiteCountry->clear();
|
2018-10-13 11:14:48 +00:00
|
|
|
if (diveSite->description)
|
|
|
|
ui.diveSiteDescription->setText(diveSite->description);
|
2015-05-17 19:13:41 +00:00
|
|
|
else
|
|
|
|
ui.diveSiteDescription->clear();
|
2018-10-13 11:14:48 +00:00
|
|
|
if (diveSite->notes)
|
|
|
|
ui.diveSiteNotes->setPlainText(diveSite->notes);
|
2015-05-17 19:13:41 +00:00
|
|
|
else
|
|
|
|
ui.diveSiteNotes->clear();
|
2019-03-25 08:05:47 +00:00
|
|
|
if (has_location(&diveSite->location))
|
|
|
|
ui.diveSiteCoordinates->setText(printGPSCoords(&diveSite->location));
|
|
|
|
else
|
2015-05-17 19:13:41 +00:00
|
|
|
ui.diveSiteCoordinates->clear();
|
2020-02-04 09:03:41 +00:00
|
|
|
coordinatesSetWarning(false);
|
2015-10-07 23:00:22 +00:00
|
|
|
|
2019-03-15 13:32:55 +00:00
|
|
|
ui.locationTags->setText(constructLocationTags(&diveSite->taxonomy, false));
|
2015-05-17 19:13:41 +00:00
|
|
|
}
|
|
|
|
|
2019-03-25 21:18:32 +00:00
|
|
|
void LocationInformationWidget::unitsChanged()
|
|
|
|
{
|
|
|
|
if (prefs.units.length == units::METERS) {
|
|
|
|
ui.diveSiteDistanceUnits->setText("m");
|
|
|
|
ui.diveSiteDistance->setText(QString::number(lrint(closeDistance / 1000.0)));
|
|
|
|
} else {
|
|
|
|
ui.diveSiteDistanceUnits->setText("ft");
|
|
|
|
ui.diveSiteDistance->setText(QString::number(lrint(mm_to_feet(closeDistance))));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-12 22:51:39 +00:00
|
|
|
void LocationInformationWidget::diveSiteChanged(struct dive_site *ds, int field)
|
|
|
|
{
|
|
|
|
if (diveSite != ds)
|
|
|
|
return; // A different dive site was changed -> do nothing.
|
|
|
|
switch (field) {
|
|
|
|
case LocationInformationModel::NAME:
|
|
|
|
ui.diveSiteName->setText(diveSite->name);
|
2019-03-13 19:10:22 +00:00
|
|
|
return;
|
|
|
|
case LocationInformationModel::DESCRIPTION:
|
|
|
|
ui.diveSiteDescription->setText(diveSite->description);
|
|
|
|
return;
|
2019-03-13 23:00:54 +00:00
|
|
|
case LocationInformationModel::NOTES:
|
|
|
|
ui.diveSiteNotes->setText(diveSite->notes);
|
|
|
|
return;
|
2019-03-14 07:26:50 +00:00
|
|
|
case LocationInformationModel::TAXONOMY:
|
|
|
|
ui.diveSiteCountry->setText(taxonomy_get_country(&diveSite->taxonomy));
|
2019-03-15 13:32:55 +00:00
|
|
|
ui.locationTags->setText(constructLocationTags(&diveSite->taxonomy, false));
|
2019-03-14 07:26:50 +00:00
|
|
|
return;
|
2019-03-14 21:07:48 +00:00
|
|
|
case LocationInformationModel::LOCATION:
|
|
|
|
filter_model.setCoordinates(diveSite->location);
|
|
|
|
if (has_location(&diveSite->location)) {
|
|
|
|
enableLocationButtons(true);
|
|
|
|
ui.diveSiteCoordinates->setText(printGPSCoords(&diveSite->location));
|
|
|
|
} else {
|
|
|
|
enableLocationButtons(false);
|
|
|
|
ui.diveSiteCoordinates->clear();
|
|
|
|
}
|
2020-02-04 09:03:41 +00:00
|
|
|
coordinatesSetWarning(false);
|
2019-09-03 05:11:43 +00:00
|
|
|
return;
|
2019-03-12 22:51:39 +00:00
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-06 19:06:13 +00:00
|
|
|
void LocationInformationWidget::clearLabels()
|
|
|
|
{
|
|
|
|
ui.diveSiteName->clear();
|
|
|
|
ui.diveSiteCountry->clear();
|
|
|
|
ui.diveSiteDescription->clear();
|
|
|
|
ui.diveSiteNotes->clear();
|
|
|
|
ui.diveSiteCoordinates->clear();
|
2020-02-04 09:03:41 +00:00
|
|
|
coordinatesSetWarning(false);
|
2017-10-06 19:06:13 +00:00
|
|
|
ui.locationTags->clear();
|
|
|
|
}
|
|
|
|
|
2019-03-14 22:28:45 +00:00
|
|
|
// Parse GPS text into location_t
|
|
|
|
static location_t parseGpsText(const QString &text)
|
2018-10-13 10:30:32 +00:00
|
|
|
{
|
|
|
|
double lat, lon;
|
2020-02-04 09:03:41 +00:00
|
|
|
if (parseGpsText(text.trimmed(), &lat, &lon))
|
2019-03-14 22:28:45 +00:00
|
|
|
return create_location(lat, lon);
|
2020-01-09 04:55:29 +00:00
|
|
|
return zero_location;
|
2018-10-13 10:30:32 +00:00
|
|
|
}
|
|
|
|
|
2020-02-04 09:03:41 +00:00
|
|
|
// Check if GPS text is parseable
|
|
|
|
static bool validateGpsText(const QString &textIn)
|
|
|
|
{
|
|
|
|
double lat, lon;
|
|
|
|
QString text = textIn.trimmed();
|
|
|
|
return text.isEmpty() || parseGpsText(text.trimmed(), &lat, &lon);
|
|
|
|
}
|
|
|
|
|
2019-09-06 20:01:59 +00:00
|
|
|
void LocationInformationWidget::diveSiteDeleted(struct dive_site *ds, int)
|
|
|
|
{
|
|
|
|
// If the currently edited dive site was removed under our feet, close the widget.
|
|
|
|
// This will reset the dangling pointer.
|
|
|
|
if (ds && ds == diveSite)
|
|
|
|
acceptChanges();
|
|
|
|
}
|
|
|
|
|
2015-05-17 19:13:41 +00:00
|
|
|
void LocationInformationWidget::acceptChanges()
|
|
|
|
{
|
2019-09-06 20:01:59 +00:00
|
|
|
closeDistance = 0;
|
|
|
|
|
2019-03-14 22:28:45 +00:00
|
|
|
MainWindow::instance()->diveList->setEnabled(true);
|
|
|
|
MainWindow::instance()->setEnabledToolbar(true);
|
2022-09-18 13:25:41 +00:00
|
|
|
MainWindow::instance()->enterPreviousState();
|
2019-11-17 17:13:55 +00:00
|
|
|
DiveFilter::instance()->stopFilterDiveSites();
|
2019-10-28 05:57:54 +00:00
|
|
|
|
|
|
|
// Subtlety alert: diveSite must be cleared *after* exiting the dive-site mode.
|
|
|
|
// Exiting dive-site mode removes the focus from the active widget and
|
|
|
|
// thus fires the corresponding editingFinished signal, which in turn creates
|
|
|
|
// an undo-command. To set an undo-command, the widget has to know the
|
|
|
|
// currently edited dive-site.
|
|
|
|
diveSite = nullptr;
|
2015-05-17 19:13:41 +00:00
|
|
|
}
|
|
|
|
|
2018-10-13 06:57:46 +00:00
|
|
|
void LocationInformationWidget::initFields(dive_site *ds)
|
2015-05-17 20:14:23 +00:00
|
|
|
{
|
2018-10-13 11:14:48 +00:00
|
|
|
diveSite = ds;
|
2018-10-13 06:57:46 +00:00
|
|
|
if (ds) {
|
2018-10-25 06:02:06 +00:00
|
|
|
filter_model.set(ds, ds->location);
|
2015-07-25 16:03:14 +00:00
|
|
|
updateLabels();
|
2018-10-13 11:14:48 +00:00
|
|
|
enableLocationButtons(dive_site_has_gps_location(ds));
|
2019-11-17 17:13:55 +00:00
|
|
|
DiveFilter::instance()->startFilterDiveSites(QVector<dive_site *>{ ds });
|
2019-04-15 19:47:08 +00:00
|
|
|
filter_model.invalidate();
|
2017-10-06 19:06:13 +00:00
|
|
|
} else {
|
2020-01-09 04:55:29 +00:00
|
|
|
filter_model.set(0, zero_location);
|
2017-10-06 19:06:13 +00:00
|
|
|
clearLabels();
|
2015-07-31 01:10:01 +00:00
|
|
|
}
|
2023-02-24 10:46:23 +00:00
|
|
|
|
|
|
|
unitsChanged();
|
2015-05-17 19:13:41 +00:00
|
|
|
}
|
|
|
|
|
2020-01-19 20:06:50 +00:00
|
|
|
void LocationInformationWidget::on_GPSbutton_clicked()
|
|
|
|
{
|
|
|
|
QFileInfo finfo(system_default_directory());
|
|
|
|
QString fileName = QFileDialog::getOpenFileName(this,
|
|
|
|
tr("Select GPS file to open"),
|
|
|
|
finfo.absolutePath(),
|
|
|
|
tr("GPS files (*.gpx *.GPX)"));
|
|
|
|
if (fileName.isEmpty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
ImportGPS GPSDialog(this, fileName, &ui); // Create a GPS import QDialog
|
|
|
|
GPSDialog.coords.start_dive = current_dive->when; // initialise
|
|
|
|
GPSDialog.coords.end_dive = dive_endtime(current_dive);
|
2020-01-22 21:25:58 +00:00
|
|
|
if (getCoordsFromGPXFile(&GPSDialog.coords, fileName) == 0) { // Get coordinates from GPS file
|
2020-01-19 20:06:50 +00:00
|
|
|
GPSDialog.updateUI(); // If successful, put results in Dialog
|
|
|
|
if (!GPSDialog.exec()) // and show QDialog
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-14 21:07:48 +00:00
|
|
|
void LocationInformationWidget::on_diveSiteCoordinates_editingFinished()
|
2015-05-17 19:13:41 +00:00
|
|
|
{
|
2020-02-04 09:15:39 +00:00
|
|
|
if (diveSite && validateGpsText(ui.diveSiteCoordinates->text()))
|
2019-06-24 05:02:05 +00:00
|
|
|
Command::editDiveSiteLocation(diveSite, parseGpsText(ui.diveSiteCoordinates->text()));
|
2015-05-17 19:13:41 +00:00
|
|
|
}
|
|
|
|
|
2020-02-04 09:03:41 +00:00
|
|
|
void LocationInformationWidget::on_diveSiteCoordinates_textEdited(const QString &s)
|
|
|
|
{
|
|
|
|
coordinatesSetWarning(!validateGpsText(s));
|
|
|
|
}
|
|
|
|
|
2019-03-14 07:26:50 +00:00
|
|
|
void LocationInformationWidget::on_diveSiteCountry_editingFinished()
|
2017-10-02 15:51:20 +00:00
|
|
|
{
|
2019-03-14 07:26:50 +00:00
|
|
|
if (diveSite)
|
|
|
|
Command::editDiveSiteCountry(diveSite, ui.diveSiteCountry->text());
|
2017-10-02 15:51:20 +00:00
|
|
|
}
|
|
|
|
|
2019-03-13 21:49:34 +00:00
|
|
|
void LocationInformationWidget::on_diveSiteDescription_editingFinished()
|
2015-05-17 19:13:41 +00:00
|
|
|
{
|
2019-03-13 21:49:34 +00:00
|
|
|
if (diveSite)
|
|
|
|
Command::editDiveSiteDescription(diveSite, ui.diveSiteDescription->text());
|
2015-05-17 19:13:41 +00:00
|
|
|
}
|
|
|
|
|
2019-03-13 21:49:34 +00:00
|
|
|
void LocationInformationWidget::on_diveSiteName_editingFinished()
|
2015-05-17 19:13:41 +00:00
|
|
|
{
|
2019-03-13 21:49:34 +00:00
|
|
|
if (diveSite)
|
|
|
|
Command::editDiveSiteName(diveSite, ui.diveSiteName->text());
|
2015-05-17 19:13:41 +00:00
|
|
|
}
|
|
|
|
|
2019-03-13 23:00:54 +00:00
|
|
|
void LocationInformationWidget::on_diveSiteNotes_editingFinished()
|
2015-05-17 19:13:41 +00:00
|
|
|
{
|
2019-03-13 23:00:54 +00:00
|
|
|
if (diveSite)
|
|
|
|
Command::editDiveSiteNotes(diveSite, ui.diveSiteNotes->toPlainText());
|
2015-05-17 19:13:41 +00:00
|
|
|
}
|
|
|
|
|
2019-03-25 21:18:32 +00:00
|
|
|
void LocationInformationWidget::on_diveSiteDistance_textChanged(const QString &s)
|
|
|
|
{
|
|
|
|
bool ok;
|
|
|
|
uint64_t d = s.toLongLong(&ok);
|
|
|
|
if (!ok)
|
|
|
|
d = 0;
|
|
|
|
closeDistance = prefs.units.length == units::METERS ? d * 1000 : feet_to_mm(d);
|
|
|
|
filter_model.setDistance(closeDistance);
|
|
|
|
}
|
|
|
|
|
2015-08-25 21:45:29 +00:00
|
|
|
void LocationInformationWidget::reverseGeocode()
|
|
|
|
{
|
2020-09-02 07:01:34 +00:00
|
|
|
dive_site *ds = diveSite; /* Save local copy; possibility of user closing the widget while reverseGeoLookup is running (see #2930) */
|
2019-03-14 22:28:45 +00:00
|
|
|
location_t location = parseGpsText(ui.diveSiteCoordinates->text());
|
2020-09-02 07:01:34 +00:00
|
|
|
if (!ds || !has_location(&location))
|
2018-10-13 10:30:32 +00:00
|
|
|
return;
|
2020-09-03 03:04:05 +00:00
|
|
|
taxonomy_data taxonomy = reverseGeoLookup(location.lat, location.lon);
|
|
|
|
if (ds != diveSite) {
|
|
|
|
free_taxonomy(&taxonomy);
|
2020-09-02 07:01:34 +00:00
|
|
|
return;
|
2020-09-03 03:04:05 +00:00
|
|
|
}
|
|
|
|
// This call transfers ownership of the taxonomy memory into an EditDiveSiteTaxonomy object
|
2020-09-02 07:01:34 +00:00
|
|
|
Command::editDiveSiteTaxonomy(ds, taxonomy);
|
2015-08-25 21:45:29 +00:00
|
|
|
}
|
2015-09-21 17:01:58 +00:00
|
|
|
|
2020-01-09 04:55:29 +00:00
|
|
|
DiveLocationFilterProxyModel::DiveLocationFilterProxyModel(QObject *) : currentLocation(zero_location)
|
2015-09-21 17:01:58 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2019-04-15 18:15:40 +00:00
|
|
|
void DiveLocationFilterProxyModel::setFilter(const QString &filterIn)
|
|
|
|
{
|
|
|
|
filter = filterIn;
|
|
|
|
invalidate();
|
2019-04-15 20:16:33 +00:00
|
|
|
sort(LocationInformationModel::NAME);
|
2019-04-15 18:15:40 +00:00
|
|
|
}
|
2015-09-21 17:01:58 +00:00
|
|
|
|
2019-04-24 22:26:48 +00:00
|
|
|
void DiveLocationFilterProxyModel::setCurrentLocation(location_t loc)
|
|
|
|
{
|
|
|
|
currentLocation = loc;
|
|
|
|
sort(LocationInformationModel::NAME);
|
|
|
|
}
|
|
|
|
|
2018-05-21 16:09:09 +00:00
|
|
|
bool DiveLocationFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex&) const
|
2015-09-21 17:01:58 +00:00
|
|
|
{
|
2019-04-15 20:26:11 +00:00
|
|
|
// We don't want to show the first two entries (add dive site with that name)
|
|
|
|
// if there is no filter text.
|
|
|
|
if (filter.isEmpty() && source_row <= 1)
|
|
|
|
return false;
|
|
|
|
|
2015-09-29 23:57:53 +00:00
|
|
|
if (source_row == 0)
|
2015-09-21 17:01:58 +00:00
|
|
|
return true;
|
|
|
|
|
2018-10-09 10:26:58 +00:00
|
|
|
QString sourceString = sourceModel()->index(source_row, LocationInformationModel::NAME).data(Qt::DisplayRole).toString();
|
2019-04-15 18:15:40 +00:00
|
|
|
return sourceString.contains(filter, Qt::CaseInsensitive);
|
2015-09-21 17:01:58 +00:00
|
|
|
}
|
|
|
|
|
2015-09-29 23:57:53 +00:00
|
|
|
bool DiveLocationFilterProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
|
2015-09-21 18:04:52 +00:00
|
|
|
{
|
2019-04-15 20:16:33 +00:00
|
|
|
// The first two entries are special - we never want to change their order
|
|
|
|
if (source_left.row() <= 1 || source_right.row() <= 1)
|
|
|
|
return source_left.row() < source_right.row();
|
2019-04-24 22:26:48 +00:00
|
|
|
|
|
|
|
// If there is a current location, sort by that - otherwise use the provided column
|
|
|
|
if (has_location(¤tLocation)) {
|
|
|
|
// The dive sites are -2 because of the first two items.
|
core: introduce divelog structure
The parser API was very annoying, as a number of tables
to-be-filled were passed in as pointers. The goal of this
commit is to collect all these tables in a single struct.
This should make it (more or less) clear what is actually
written into the divelog files.
Moreover, it should now be rather easy to search for
instances, where the global logfile is accessed (and it
turns out that there are many!).
The divelog struct does not contain the tables as substructs,
but only collects pointers. The idea is that the "divelog.h"
file can be included without all the other files describing
the numerous tables.
To make it easier to use from C++ parts of the code, the
struct implements a constructor and a destructor. Sadly,
we can't use smart pointers, since the pointers are accessed
from C code. Therfore the constructor and destructor are
quite complex.
The whole commit is large, but was mostly an automatic
conversion.
One oddity of note: the divelog structure also contains
the "autogroup" flag, since that is saved in the divelog.
This actually fixes a bug: Before, when importing dives
from a different log, the autogroup flag was overwritten.
This was probably not intended and does not happen anymore.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2022-11-08 20:31:08 +00:00
|
|
|
struct dive_site *ds1 = get_dive_site(source_left.row() - 2, divelog.sites);
|
|
|
|
struct dive_site *ds2 = get_dive_site(source_right.row() - 2, divelog.sites);
|
2019-04-24 22:26:48 +00:00
|
|
|
return get_distance(&ds1->location, ¤tLocation) < get_distance(&ds2->location, ¤tLocation);
|
|
|
|
}
|
2019-04-15 20:16:33 +00:00
|
|
|
return source_left.data().toString().compare(source_right.data().toString(), Qt::CaseInsensitive) < 0;
|
2015-09-21 18:04:52 +00:00
|
|
|
}
|
|
|
|
|
2019-04-15 18:15:40 +00:00
|
|
|
DiveLocationModel::DiveLocationModel(QObject *)
|
2015-09-21 17:01:58 +00:00
|
|
|
{
|
|
|
|
resetModel();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DiveLocationModel::resetModel()
|
|
|
|
{
|
|
|
|
beginResetModel();
|
|
|
|
endResetModel();
|
|
|
|
}
|
|
|
|
|
2015-09-29 23:57:53 +00:00
|
|
|
QVariant DiveLocationModel::data(const QModelIndex &index, int role) const
|
2015-09-21 17:01:58 +00:00
|
|
|
{
|
2017-11-29 09:57:08 +00:00
|
|
|
static const QIcon plusIcon(":list-add-icon");
|
|
|
|
static const QIcon geoCode(":geotag-icon");
|
2015-09-25 15:19:41 +00:00
|
|
|
|
2015-09-29 23:57:53 +00:00
|
|
|
if (index.row() <= 1) { // two special cases.
|
2018-10-25 06:02:06 +00:00
|
|
|
if (index.column() == LocationInformationModel::DIVESITE)
|
2018-10-30 17:34:36 +00:00
|
|
|
return QVariant::fromValue<dive_site *>(RECENTLY_ADDED_DIVESITE);
|
2015-09-29 23:57:53 +00:00
|
|
|
switch (role) {
|
|
|
|
case Qt::DisplayRole:
|
|
|
|
return new_ds_value[index.row()];
|
|
|
|
case Qt::ToolTipRole:
|
2019-04-13 16:54:41 +00:00
|
|
|
return current_dive && current_dive->dive_site ?
|
2015-09-30 22:33:33 +00:00
|
|
|
tr("Create a new dive site, copying relevant information from the current dive.") :
|
|
|
|
tr("Create a new dive site with this name");
|
2015-09-29 23:57:53 +00:00
|
|
|
case Qt::DecorationRole:
|
|
|
|
return plusIcon;
|
2015-09-21 17:01:58 +00:00
|
|
|
}
|
2019-04-13 16:54:41 +00:00
|
|
|
return QVariant();
|
2015-09-21 17:01:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// The dive sites are -2 because of the first two items.
|
core: introduce divelog structure
The parser API was very annoying, as a number of tables
to-be-filled were passed in as pointers. The goal of this
commit is to collect all these tables in a single struct.
This should make it (more or less) clear what is actually
written into the divelog files.
Moreover, it should now be rather easy to search for
instances, where the global logfile is accessed (and it
turns out that there are many!).
The divelog struct does not contain the tables as substructs,
but only collects pointers. The idea is that the "divelog.h"
file can be included without all the other files describing
the numerous tables.
To make it easier to use from C++ parts of the code, the
struct implements a constructor and a destructor. Sadly,
we can't use smart pointers, since the pointers are accessed
from C code. Therfore the constructor and destructor are
quite complex.
The whole commit is large, but was mostly an automatic
conversion.
One oddity of note: the divelog structure also contains
the "autogroup" flag, since that is saved in the divelog.
This actually fixes a bug: Before, when importing dives
from a different log, the autogroup flag was overwritten.
This was probably not intended and does not happen anymore.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2022-11-08 20:31:08 +00:00
|
|
|
struct dive_site *ds = get_dive_site(index.row() - 2, divelog.sites);
|
2018-10-09 10:26:58 +00:00
|
|
|
return LocationInformationModel::getDiveSiteData(ds, index.column(), role);
|
2015-09-21 17:01:58 +00:00
|
|
|
}
|
|
|
|
|
2018-05-21 16:09:09 +00:00
|
|
|
int DiveLocationModel::columnCount(const QModelIndex&) const
|
2015-09-21 17:01:58 +00:00
|
|
|
{
|
2018-10-09 10:26:58 +00:00
|
|
|
return LocationInformationModel::COLUMNS;
|
2015-09-21 17:01:58 +00:00
|
|
|
}
|
|
|
|
|
2018-05-21 16:09:09 +00:00
|
|
|
int DiveLocationModel::rowCount(const QModelIndex&) const
|
2015-09-21 17:01:58 +00:00
|
|
|
{
|
core: introduce divelog structure
The parser API was very annoying, as a number of tables
to-be-filled were passed in as pointers. The goal of this
commit is to collect all these tables in a single struct.
This should make it (more or less) clear what is actually
written into the divelog files.
Moreover, it should now be rather easy to search for
instances, where the global logfile is accessed (and it
turns out that there are many!).
The divelog struct does not contain the tables as substructs,
but only collects pointers. The idea is that the "divelog.h"
file can be included without all the other files describing
the numerous tables.
To make it easier to use from C++ parts of the code, the
struct implements a constructor and a destructor. Sadly,
we can't use smart pointers, since the pointers are accessed
from C code. Therfore the constructor and destructor are
quite complex.
The whole commit is large, but was mostly an automatic
conversion.
One oddity of note: the divelog structure also contains
the "autogroup" flag, since that is saved in the divelog.
This actually fixes a bug: Before, when importing dives
from a different log, the autogroup flag was overwritten.
This was probably not intended and does not happen anymore.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2022-11-08 20:31:08 +00:00
|
|
|
return divelog.sites->nr + 2;
|
2015-09-21 17:01:58 +00:00
|
|
|
}
|
|
|
|
|
2022-08-14 07:43:58 +00:00
|
|
|
Qt::ItemFlags DiveLocationModel::flags(const QModelIndex &index) const
|
|
|
|
{
|
|
|
|
// This is crazy: If an entry is not marked as editable, the QListView
|
|
|
|
// (or rather the QAbstractItemView base class) clears the WA_InputMethod
|
|
|
|
// flag, which means that key-composition events are disabled. This
|
|
|
|
// breaks composition as long as the popup is openen. Therefore,
|
|
|
|
// make all items editable.
|
|
|
|
return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
|
|
|
|
}
|
|
|
|
|
2018-05-21 16:09:09 +00:00
|
|
|
bool DiveLocationModel::setData(const QModelIndex &index, const QVariant &value, int)
|
2015-09-21 17:01:58 +00:00
|
|
|
{
|
2015-09-29 23:57:53 +00:00
|
|
|
if (!index.isValid())
|
2015-09-21 17:01:58 +00:00
|
|
|
return false;
|
|
|
|
if (index.row() > 1)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
new_ds_value[index.row()] = value.toString();
|
|
|
|
|
|
|
|
dataChanged(index, index);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-09-22 17:31:56 +00:00
|
|
|
DiveLocationLineEdit::DiveLocationLineEdit(QWidget *parent) : QLineEdit(parent),
|
2022-08-13 18:19:01 +00:00
|
|
|
proxy(new DiveLocationFilterProxyModel),
|
|
|
|
model(new DiveLocationModel),
|
|
|
|
view(new DiveLocationListView),
|
2018-10-25 06:02:06 +00:00
|
|
|
currDs(nullptr)
|
2015-09-21 17:01:58 +00:00
|
|
|
{
|
|
|
|
proxy->setSourceModel(model);
|
2018-10-09 10:26:58 +00:00
|
|
|
proxy->setFilterKeyColumn(LocationInformationModel::NAME);
|
2015-09-21 19:51:39 +00:00
|
|
|
|
2015-09-21 18:04:52 +00:00
|
|
|
view->setModel(proxy);
|
2018-10-09 10:26:58 +00:00
|
|
|
view->setModelColumn(LocationInformationModel::NAME);
|
2019-04-25 07:35:46 +00:00
|
|
|
view->setItemDelegate(&delegate);
|
2015-09-21 19:51:39 +00:00
|
|
|
view->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
|
|
|
view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
|
|
view->setSelectionBehavior(QAbstractItemView::SelectRows);
|
|
|
|
view->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
|
|
view->setParent(0, Qt::Popup);
|
|
|
|
view->installEventFilter(this);
|
2015-09-22 17:31:56 +00:00
|
|
|
view->setFocusPolicy(Qt::NoFocus);
|
|
|
|
view->setFocusProxy(this);
|
2015-09-25 16:11:44 +00:00
|
|
|
view->setMouseTracking(true);
|
2015-09-21 19:51:39 +00:00
|
|
|
|
2015-09-21 17:01:58 +00:00
|
|
|
connect(this, &QLineEdit::textEdited, this, &DiveLocationLineEdit::setTemporaryDiveSiteName);
|
2015-09-21 20:18:52 +00:00
|
|
|
connect(view, &QAbstractItemView::activated, this, &DiveLocationLineEdit::itemActivated);
|
2015-09-25 16:11:44 +00:00
|
|
|
connect(view, &QAbstractItemView::entered, this, &DiveLocationLineEdit::entered);
|
2015-09-25 16:21:23 +00:00
|
|
|
connect(view, &DiveLocationListView::currentIndexChanged, this, &DiveLocationLineEdit::currentChanged);
|
2015-09-21 17:01:58 +00:00
|
|
|
}
|
|
|
|
|
2019-04-15 20:16:33 +00:00
|
|
|
bool DiveLocationLineEdit::eventFilter(QObject *, QEvent *e)
|
2015-09-21 19:51:39 +00:00
|
|
|
{
|
2015-09-29 23:57:53 +00:00
|
|
|
if (e->type() == QEvent::KeyPress) {
|
|
|
|
QKeyEvent *keyEv = (QKeyEvent *)e;
|
2015-09-21 19:51:39 +00:00
|
|
|
|
|
|
|
if (keyEv->key() == Qt::Key_Escape) {
|
|
|
|
view->hide();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-09-29 23:57:53 +00:00
|
|
|
if (keyEv->key() == Qt::Key_Return ||
|
|
|
|
keyEv->key() == Qt::Key_Enter) {
|
2015-10-25 01:38:16 +00:00
|
|
|
#if __APPLE__
|
|
|
|
// for some reason it seems like on a Mac hitting return/enter
|
|
|
|
// doesn't call 'activated' for that index. so let's do it manually
|
|
|
|
if (view->currentIndex().isValid())
|
|
|
|
itemActivated(view->currentIndex());
|
|
|
|
#endif
|
2015-09-21 19:51:39 +00:00
|
|
|
view->hide();
|
|
|
|
return false;
|
|
|
|
}
|
2015-09-22 19:27:07 +00:00
|
|
|
|
2015-09-29 23:57:53 +00:00
|
|
|
if (keyEv->key() == Qt::Key_Tab) {
|
|
|
|
itemActivated(view->currentIndex());
|
|
|
|
view->hide();
|
|
|
|
return false;
|
|
|
|
}
|
2015-09-21 20:11:59 +00:00
|
|
|
event(e);
|
2015-09-29 23:57:53 +00:00
|
|
|
} else if (e->type() == QEvent::MouseButtonPress) {
|
2015-09-22 17:31:56 +00:00
|
|
|
if (!view->underMouse()) {
|
|
|
|
view->hide();
|
|
|
|
return true;
|
|
|
|
}
|
2022-08-13 18:19:01 +00:00
|
|
|
} else if (e->type() == QEvent::InputMethod) {
|
|
|
|
inputMethodEvent(static_cast<QInputMethodEvent *>(e));
|
2018-06-26 04:17:00 +00:00
|
|
|
}
|
2015-09-21 20:11:59 +00:00
|
|
|
|
2015-09-21 19:51:39 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-09-29 23:57:53 +00:00
|
|
|
void DiveLocationLineEdit::focusOutEvent(QFocusEvent *ev)
|
2015-09-22 18:23:38 +00:00
|
|
|
{
|
2019-06-30 21:08:02 +00:00
|
|
|
if (!view->isVisible())
|
2015-09-22 18:23:38 +00:00
|
|
|
QLineEdit::focusOutEvent(ev);
|
|
|
|
}
|
|
|
|
|
2015-09-29 23:57:53 +00:00
|
|
|
void DiveLocationLineEdit::itemActivated(const QModelIndex &index)
|
2015-09-21 20:18:52 +00:00
|
|
|
{
|
2015-10-05 21:01:28 +00:00
|
|
|
QModelIndex idx = index;
|
2018-10-25 06:02:06 +00:00
|
|
|
if (index.column() == LocationInformationModel::DIVESITE)
|
2018-10-09 10:26:58 +00:00
|
|
|
idx = index.model()->index(index.row(), LocationInformationModel::NAME);
|
2015-10-05 21:01:28 +00:00
|
|
|
|
2018-10-28 20:16:42 +00:00
|
|
|
dive_site *ds = index.model()->index(index.row(), LocationInformationModel::DIVESITE).data().value<dive_site *>();
|
2018-10-25 06:02:06 +00:00
|
|
|
currDs = ds;
|
2015-10-05 21:01:28 +00:00
|
|
|
setText(idx.data().toString());
|
2015-09-29 23:57:53 +00:00
|
|
|
if (view->isVisible())
|
2015-09-23 16:56:49 +00:00
|
|
|
view->hide();
|
2018-10-24 19:14:57 +00:00
|
|
|
emit diveSiteSelected();
|
2015-09-21 20:18:52 +00:00
|
|
|
}
|
|
|
|
|
2015-09-21 17:01:58 +00:00
|
|
|
void DiveLocationLineEdit::refreshDiveSiteCache()
|
|
|
|
{
|
|
|
|
model->resetModel();
|
|
|
|
}
|
|
|
|
|
2015-09-29 23:57:53 +00:00
|
|
|
static struct dive_site *get_dive_site_name_start_which_str(const QString &str)
|
|
|
|
{
|
2015-09-21 17:01:58 +00:00
|
|
|
struct dive_site *ds;
|
|
|
|
int i;
|
core: introduce divelog structure
The parser API was very annoying, as a number of tables
to-be-filled were passed in as pointers. The goal of this
commit is to collect all these tables in a single struct.
This should make it (more or less) clear what is actually
written into the divelog files.
Moreover, it should now be rather easy to search for
instances, where the global logfile is accessed (and it
turns out that there are many!).
The divelog struct does not contain the tables as substructs,
but only collects pointers. The idea is that the "divelog.h"
file can be included without all the other files describing
the numerous tables.
To make it easier to use from C++ parts of the code, the
struct implements a constructor and a destructor. Sadly,
we can't use smart pointers, since the pointers are accessed
from C code. Therfore the constructor and destructor are
quite complex.
The whole commit is large, but was mostly an automatic
conversion.
One oddity of note: the divelog structure also contains
the "autogroup" flag, since that is saved in the divelog.
This actually fixes a bug: Before, when importing dives
from a different log, the autogroup flag was overwritten.
This was probably not intended and does not happen anymore.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2022-11-08 20:31:08 +00:00
|
|
|
for_each_dive_site (i, ds, divelog.sites) {
|
2015-09-21 17:01:58 +00:00
|
|
|
QString dsName(ds->name);
|
core: introduce divelog structure
The parser API was very annoying, as a number of tables
to-be-filled were passed in as pointers. The goal of this
commit is to collect all these tables in a single struct.
This should make it (more or less) clear what is actually
written into the divelog files.
Moreover, it should now be rather easy to search for
instances, where the global logfile is accessed (and it
turns out that there are many!).
The divelog struct does not contain the tables as substructs,
but only collects pointers. The idea is that the "divelog.h"
file can be included without all the other files describing
the numerous tables.
To make it easier to use from C++ parts of the code, the
struct implements a constructor and a destructor. Sadly,
we can't use smart pointers, since the pointers are accessed
from C code. Therfore the constructor and destructor are
quite complex.
The whole commit is large, but was mostly an automatic
conversion.
One oddity of note: the divelog structure also contains
the "autogroup" flag, since that is saved in the divelog.
This actually fixes a bug: Before, when importing dives
from a different log, the autogroup flag was overwritten.
This was probably not intended and does not happen anymore.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2022-11-08 20:31:08 +00:00
|
|
|
if (dsName.toLower().startsWith(str.toLower()))
|
2015-09-21 17:01:58 +00:00
|
|
|
return ds;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-04-15 18:15:40 +00:00
|
|
|
void DiveLocationLineEdit::setTemporaryDiveSiteName(const QString &name)
|
2015-09-21 17:01:58 +00:00
|
|
|
{
|
2018-10-08 20:23:47 +00:00
|
|
|
// This function fills the first two entries with potential names of
|
|
|
|
// a dive site to be generated. The first entry is simply the entered
|
|
|
|
// text. The second entry is the first known dive site name starting
|
|
|
|
// with the entered text.
|
2018-10-09 10:26:58 +00:00
|
|
|
QModelIndex i0 = model->index(0, LocationInformationModel::NAME);
|
|
|
|
QModelIndex i1 = model->index(1, LocationInformationModel::NAME);
|
2019-04-15 18:15:40 +00:00
|
|
|
model->setData(i0, name);
|
2015-09-21 17:01:58 +00:00
|
|
|
|
2018-10-08 20:23:47 +00:00
|
|
|
// Note: if i1_name stays empty, the line will automatically
|
|
|
|
// be filtered out by the proxy filter, as it does not contain
|
|
|
|
// the user entered text.
|
|
|
|
QString i1_name;
|
2019-04-15 18:15:40 +00:00
|
|
|
if (struct dive_site *ds = get_dive_site_name_start_which_str(name)) {
|
2015-09-21 18:04:52 +00:00
|
|
|
const QString orig_name = QString(ds->name).toLower();
|
2019-04-15 18:15:40 +00:00
|
|
|
const QString new_name = name.toLower();
|
2015-09-21 18:04:52 +00:00
|
|
|
if (new_name != orig_name)
|
|
|
|
i1_name = QString(ds->name);
|
|
|
|
}
|
|
|
|
|
2015-09-29 23:57:53 +00:00
|
|
|
model->setData(i1, i1_name);
|
2019-04-15 18:15:40 +00:00
|
|
|
proxy->setFilter(name);
|
2015-09-23 19:27:44 +00:00
|
|
|
fixPopupPosition();
|
2022-08-13 18:19:01 +00:00
|
|
|
if (!view->isVisible()) {
|
2015-09-23 19:27:44 +00:00
|
|
|
view->show();
|
2022-08-13 18:19:01 +00:00
|
|
|
// TODO: For some reason the show() call clears this flag,
|
|
|
|
// which breaks key composition. Find the real cause for
|
|
|
|
// this strange behavior!
|
|
|
|
view->setAttribute(Qt::WA_InputMethodEnabled);
|
|
|
|
}
|
2015-09-21 17:01:58 +00:00
|
|
|
}
|
|
|
|
|
2015-09-21 19:08:58 +00:00
|
|
|
void DiveLocationLineEdit::keyPressEvent(QKeyEvent *ev)
|
|
|
|
{
|
2015-09-21 20:22:31 +00:00
|
|
|
QLineEdit::keyPressEvent(ev);
|
2015-09-29 23:57:53 +00:00
|
|
|
if (ev->key() != Qt::Key_Left &&
|
|
|
|
ev->key() != Qt::Key_Right &&
|
|
|
|
ev->key() != Qt::Key_Escape &&
|
|
|
|
ev->key() != Qt::Key_Return) {
|
2015-09-22 20:20:55 +00:00
|
|
|
|
2019-06-30 21:18:12 +00:00
|
|
|
if (ev->key() != Qt::Key_Up && ev->key() != Qt::Key_Down)
|
2018-10-25 06:02:06 +00:00
|
|
|
currDs = RECENTLY_ADDED_DIVESITE;
|
2019-06-30 21:18:12 +00:00
|
|
|
else
|
2015-09-23 19:27:44 +00:00
|
|
|
showPopup();
|
2015-09-21 19:51:39 +00:00
|
|
|
} else if (ev->key() == Qt::Key_Escape) {
|
|
|
|
view->hide();
|
2015-09-21 19:08:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-23 19:03:28 +00:00
|
|
|
void DiveLocationLineEdit::fixPopupPosition()
|
2015-09-21 19:08:58 +00:00
|
|
|
{
|
2022-02-10 01:00:48 +00:00
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
|
|
|
const QRect screen = this->screen()->availableGeometry();
|
|
|
|
#else
|
2015-09-22 17:13:51 +00:00
|
|
|
const QRect screen = QApplication::desktop()->availableGeometry(this);
|
2022-02-10 01:00:48 +00:00
|
|
|
#endif
|
2015-09-22 17:13:51 +00:00
|
|
|
const int maxVisibleItems = 5;
|
|
|
|
QPoint pos;
|
|
|
|
int rh, w;
|
|
|
|
int h = (view->sizeHintForRow(0) * qMin(maxVisibleItems, view->model()->rowCount()) + 3) + 3;
|
|
|
|
QScrollBar *hsb = view->horizontalScrollBar();
|
|
|
|
if (hsb && hsb->isVisible())
|
|
|
|
h += view->horizontalScrollBar()->sizeHint().height();
|
|
|
|
|
|
|
|
rh = height();
|
|
|
|
pos = mapToGlobal(QPoint(0, height() - 2));
|
|
|
|
w = width();
|
|
|
|
|
|
|
|
if (w > screen.width())
|
|
|
|
w = screen.width();
|
|
|
|
if ((pos.x() + w) > (screen.x() + screen.width()))
|
|
|
|
pos.setX(screen.x() + screen.width() - w);
|
|
|
|
if (pos.x() < screen.x())
|
|
|
|
pos.setX(screen.x());
|
|
|
|
|
|
|
|
int top = pos.y() - rh - screen.top() + 2;
|
|
|
|
int bottom = screen.bottom() - pos.y();
|
|
|
|
h = qMax(h, view->minimumHeight());
|
|
|
|
if (h > bottom) {
|
|
|
|
h = qMin(qMax(top, bottom), h);
|
|
|
|
if (top > bottom)
|
|
|
|
pos.setY(pos.y() - h - rh + 2);
|
|
|
|
}
|
2015-09-21 19:08:58 +00:00
|
|
|
|
2015-09-22 17:13:51 +00:00
|
|
|
view->setGeometry(pos.x(), pos.y(), w, h);
|
2015-10-01 19:27:22 +00:00
|
|
|
if (!view->currentIndex().isValid() && view->model()->rowCount()) {
|
2016-01-19 19:35:52 +00:00
|
|
|
view->setCurrentIndex(view->model()->index(0, 1));
|
2015-09-23 19:19:58 +00:00
|
|
|
}
|
2015-09-23 19:03:28 +00:00
|
|
|
}
|
2015-09-21 19:08:58 +00:00
|
|
|
|
2019-04-24 22:26:48 +00:00
|
|
|
void DiveLocationLineEdit::setCurrentDiveSite(struct dive *d)
|
2015-09-25 17:51:10 +00:00
|
|
|
{
|
2019-10-27 20:08:50 +00:00
|
|
|
location_t currentLocation;
|
|
|
|
if (d) {
|
|
|
|
currDs = get_dive_site_for_dive(d);
|
|
|
|
currentLocation = dive_get_gps_location(d);
|
|
|
|
} else {
|
|
|
|
currDs = nullptr;
|
2020-01-09 04:55:29 +00:00
|
|
|
currentLocation = zero_location;
|
2019-10-27 20:08:50 +00:00
|
|
|
}
|
2019-06-30 21:18:12 +00:00
|
|
|
if (!currDs)
|
2015-09-25 17:51:10 +00:00
|
|
|
clear();
|
2019-06-30 21:18:12 +00:00
|
|
|
else
|
2019-10-27 20:08:50 +00:00
|
|
|
setText(currDs->name);
|
2019-04-24 22:26:48 +00:00
|
|
|
proxy->setCurrentLocation(currentLocation);
|
2019-04-25 07:35:46 +00:00
|
|
|
delegate.setCurrentLocation(currentLocation);
|
2015-09-25 17:51:10 +00:00
|
|
|
}
|
|
|
|
|
2015-09-23 19:03:28 +00:00
|
|
|
void DiveLocationLineEdit::showPopup()
|
|
|
|
{
|
2019-04-25 11:09:11 +00:00
|
|
|
if (!view->isVisible())
|
2015-09-22 19:27:07 +00:00
|
|
|
setTemporaryDiveSiteName(text());
|
2015-09-21 19:08:58 +00:00
|
|
|
}
|
|
|
|
|
2019-04-25 11:16:30 +00:00
|
|
|
void DiveLocationLineEdit::showAllSites()
|
|
|
|
{
|
|
|
|
if (!view->isVisible()) {
|
|
|
|
// By setting the "temporary dive site name" to the empty string,
|
|
|
|
// all dive sites are shown sorted by distance from the site of
|
|
|
|
// the current dive.
|
|
|
|
setTemporaryDiveSiteName(QString());
|
|
|
|
|
|
|
|
// By selecting the whole text, the user can immediately start
|
|
|
|
// typing to activate the full-text filter.
|
|
|
|
selectAll();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-25 06:02:06 +00:00
|
|
|
struct dive_site *DiveLocationLineEdit::currDiveSite() const
|
2015-09-23 17:46:29 +00:00
|
|
|
{
|
2019-06-30 21:08:02 +00:00
|
|
|
// If there is no text, this corresponds to the empty dive site
|
|
|
|
return text().trimmed().isEmpty() ? nullptr : currDs;
|
2015-09-23 17:46:29 +00:00
|
|
|
}
|
|
|
|
|
2022-08-13 18:19:01 +00:00
|
|
|
DiveLocationListView::DiveLocationListView(QWidget *parent) : QListView(parent)
|
2015-09-21 17:01:58 +00:00
|
|
|
{
|
|
|
|
}
|
2015-09-25 16:21:23 +00:00
|
|
|
|
2015-09-29 23:57:53 +00:00
|
|
|
void DiveLocationListView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous)
|
2015-09-25 16:21:23 +00:00
|
|
|
{
|
|
|
|
QListView::currentChanged(current, previous);
|
|
|
|
emit currentIndexChanged(current);
|
|
|
|
}
|