2017-04-27 18:30:36 +00:00
// SPDX-License-Identifier: GPL-2.0
2015-06-04 10:29:50 +00:00
# include "qmlmanager.h"
2015-06-04 10:36:36 +00:00
# include <QUrl>
2018-09-04 17:02:26 +00:00
# include <QSettings>
2015-12-05 03:34:59 +00:00
# include <QNetworkAccessManager>
# include <QAuthenticator>
2015-12-26 21:22:50 +00:00
# include <QDesktopServices>
2016-01-08 06:30:58 +00:00
# include <QTextDocument>
2016-03-11 02:36:46 +00:00
# include <QRegularExpression>
2016-04-03 23:13:22 +00:00
# include <QApplication>
# include <QElapsedTimer>
2016-06-13 22:21:51 +00:00
# include <QTimer>
2017-07-18 18:25:41 +00:00
# include <QDateTime>
2018-05-16 14:50:17 +00:00
# include <QClipboard>
2018-06-21 08:09:45 +00:00
# include <QFile>
2018-10-20 15:59:35 +00:00
# include <QtConcurrent>
# include <QFuture>
2020-01-11 23:26:13 +00:00
# include <QUndoStack>
2017-10-16 01:43:38 +00:00
2017-10-12 07:43:40 +00:00
# include <QBluetoothLocalDevice>
2015-06-04 10:29:50 +00:00
2018-01-28 08:52:51 +00:00
# include "qt-models/completionmodels.h"
2018-05-16 14:50:17 +00:00
# include "qt-models/messagehandlermodel.h"
2019-05-31 14:09:14 +00:00
# include "qt-models/tankinfomodel.h"
2020-03-06 17:06:31 +00:00
# include "qt-models/mobilelistmodel.h"
2016-04-05 05:02:03 +00:00
# include "core/device.h"
2019-08-05 17:41:15 +00:00
# include "core/errorhelper.h"
2019-03-03 21:29:40 +00:00
# include "core/file.h"
2020-01-10 00:45:34 +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-06-01 21:37:36 +00:00
# include "core/filterconstraint.h"
2016-04-05 05:02:03 +00:00
# include "core/qthelper.h"
# include "core/qt-gui.h"
# include "core/git-access.h"
# include "core/cloudstorage.h"
2017-05-12 16:29:45 +00:00
# include "core/downloadfromdcthread.h"
2020-10-25 12:57:18 +00:00
# include "core/subsurfacestartup.h" // for ignore_bt flag
2018-06-13 16:06:11 +00:00
# include "core/subsurface-string.h"
2020-12-15 15:10:08 +00:00
# include "core/string-format.h"
2018-06-13 16:06:11 +00:00
# include "core/pref.h"
2019-11-24 14:02:34 +00:00
# include "core/selection.h"
2018-05-28 14:25:39 +00:00
# include "core/ssrf.h"
2019-11-19 18:27:20 +00:00
# include "core/save-profiledata.h"
2019-12-09 18:58:20 +00:00
# include "core/settings/qPrefLog.h"
2018-09-08 17:46:11 +00:00
# include "core/settings/qPrefTechnicalDetails.h"
# include "core/settings/qPrefPartialPressureGas.h"
2018-09-16 19:34:39 +00:00
# include "core/settings/qPrefUnit.h"
2019-05-31 14:09:14 +00:00
# include "core/trip.h"
2021-08-16 02:55:17 +00:00
# include "core/tag.h"
2019-12-14 20:56:31 +00:00
# include "backend-shared/exportfuncs.h"
2019-11-19 18:27:20 +00:00
# include "core/worldmap-save.h"
2019-11-30 19:16:49 +00:00
# include "core/uploadDiveLogsDE.h"
2019-12-08 10:52:38 +00:00
# include "core/uploadDiveShare.h"
2020-01-10 00:25:37 +00:00
# include "commands/command_base.h"
2019-11-14 20:38:30 +00:00
# include "commands/command.h"
2018-05-28 14:25:39 +00:00
2020-03-15 01:05:06 +00:00
# if defined(Q_OS_ANDROID)
2022-02-26 18:17:07 +00:00
# include <QtAndroid>
2020-03-15 01:05:06 +00:00
# include "core/serial_usb_android.h"
std : : vector < android_usb_serial_device_descriptor > androidSerialDevices ;
# endif
2019-09-27 23:26:54 +00:00
QMLManager * QMLManager : : m_instance = NULL ;
2018-06-18 09:14:05 +00:00
bool noCloudToCloud = false ;
2015-12-03 23:59:40 +00:00
2020-01-27 00:49:42 +00:00
# define RED_FONT QLatin1String("<font color=\"red\">")
# define END_FONT QLatin1String("< / font>")
2016-03-11 05:59:16 +00:00
2018-01-26 09:40:04 +00:00
extern " C " void showErrorFromC ( char * buf )
2018-01-24 21:56:52 +00:00
{
2018-01-26 09:40:04 +00:00
QString error ( buf ) ;
free ( buf ) ;
2018-01-24 21:56:52 +00:00
// By using invokeMethod with Qt:AutoConnection, the error string is safely
// transported across thread boundaries, if not called from the UI thread.
QMetaObject : : invokeMethod ( QMLManager : : instance ( ) , " registerError " , Qt : : AutoConnection , Q_ARG ( QString , error ) ) ;
}
2020-04-01 13:59:11 +00:00
// this gets called from libdivecomputer
2017-07-09 23:07:48 +00:00
static void progressCallback ( const char * text )
{
QMLManager * self = QMLManager : : instance ( ) ;
if ( self ) {
self - > appendTextToLog ( QString ( text ) ) ;
self - > setProgressMessage ( QString ( text ) ) ;
}
}
2015-12-03 23:59:40 +00:00
static void appendTextToLogStandalone ( const char * text )
2015-07-13 00:39:13 +00:00
{
2016-01-26 14:45:03 +00:00
QMLManager * self = QMLManager : : instance ( ) ;
if ( self )
self - > appendTextToLog ( QString ( text ) ) ;
2015-07-13 00:39:13 +00:00
}
2015-06-09 19:20:44 +00:00
2020-10-21 21:34:15 +00:00
// This flag is set to true by operations that are not implemented in the
// undo system. It is therefore only cleared on save and load.
static bool dive_list_changed = false ;
void mark_divelist_changed ( bool changed )
{
if ( dive_list_changed = = changed )
return ;
dive_list_changed = changed ;
updateWindowTitle ( ) ;
}
int unsaved_changes ( )
{
return dive_list_changed ;
}
2020-04-01 13:59:11 +00:00
// this callback is used from the uiNotification() function
// the detour via callback allows us to keep the core code independent from QMLManager
// I'm not sure it makes sense to have three different progress callbacks,
// but the usage models (and the situations in the program flow where they are used)
// are really vastly different...
// this is mainly intended for the early stages of the app so the user sees that
// things are progressing
static void showProgress ( QString msg )
{
QMLManager * self = QMLManager : : instance ( ) ;
if ( self )
self - > setNotificationText ( msg ) ;
}
2017-06-18 06:22:37 +00:00
// show the git progress in the passive notification area
extern " C " int gitProgressCB ( const char * text )
2015-12-15 07:00:19 +00:00
{
2021-01-17 21:07:13 +00:00
// regular users, during regular operation, likely really don't
// care at all about the git progress
if ( verbose ) {
showProgress ( QString ( text ) ) ;
appendTextToLogStandalone ( text ) ;
}
2015-12-15 07:00:19 +00:00
// return 0 so that we don't end the download
return 0 ;
}
2018-03-12 09:55:26 +00:00
void QMLManager : : registerError ( QString error )
2018-01-24 21:56:52 +00:00
{
appendTextToLog ( error ) ;
if ( ! m_lastError . isEmpty ( ) )
m_lastError + = ' \n ' ;
m_lastError + = error ;
}
QString QMLManager : : consumeError ( )
{
QString ret ;
ret . swap ( m_lastError ) ;
return ret ;
}
2017-10-12 07:43:40 +00:00
void QMLManager : : btHostModeChange ( QBluetoothLocalDevice : : HostMode state )
{
BTDiscovery * btDiscovery = BTDiscovery : : instance ( ) ;
2024-03-25 20:17:29 +00:00
report_info ( " btHostModeChange to %d " , static_cast < int > ( state ) ) ;
2017-10-12 07:43:40 +00:00
if ( state ! = QBluetoothLocalDevice : : HostPoweredOff ) {
connectionListModel . removeAllAddresses ( ) ;
btDiscovery - > BTDiscoveryReDiscover ( ) ;
m_btEnabled = btDiscovery - > btAvailable ( ) ;
} else {
connectionListModel . removeAllAddresses ( ) ;
set_non_bt_addresses ( ) ;
m_btEnabled = false ;
}
emit btEnabledChanged ( ) ;
}
2017-12-05 19:58:54 +00:00
void QMLManager : : btRescan ( )
{
BTDiscovery : : instance ( ) - > BTDiscoveryReDiscover ( ) ;
}
2020-03-15 01:52:45 +00:00
void QMLManager : : rescanConnections ( )
{
connectionListModel . removeAllAddresses ( ) ;
usbRescan ( ) ;
2020-03-16 16:28:41 +00:00
btRescan ( ) ;
2020-03-15 01:52:45 +00:00
# if defined(SERIAL_FTDI)
connectionListModel . addAddress ( " FTDI " ) ;
# endif
}
void QMLManager : : usbRescan ( )
{
# if defined(Q_OS_ANDROID)
androidUsbPopoulateConnections ( ) ;
# endif
}
2020-04-01 13:59:11 +00:00
extern void ( * uiNotificationCallback ) ( QString ) ;
2020-04-04 21:47:31 +00:00
// Currently we have two markers for unsaved changes:
// 1) unsaved_changes() returns true for non-undoable changes.
// 2) Command::isClean() returns false for undoable changes.
static bool unsavedChanges ( )
{
return unsaved_changes ( ) | | ! Command : : isClean ( ) ;
}
2021-09-11 23:53:44 +00:00
QMLManager : : QMLManager ( ) :
2020-01-27 00:51:01 +00:00
m_verboseEnabled ( false ) ,
2020-03-29 19:53:01 +00:00
m_diveListProcessing ( false ) ,
2020-04-01 13:10:18 +00:00
m_initialized ( false ) ,
2020-01-27 00:51:01 +00:00
m_pluggedInDeviceName ( " " ) ,
m_showNonDiveComputers ( false ) ,
m_oldStatus ( qPrefCloudStorage : : CS_UNKNOWN )
2015-06-04 10:29:50 +00:00
{
2019-09-27 23:26:54 +00:00
m_instance = this ;
2017-04-04 00:29:06 +00:00
m_lastDevicePixelRatio = qApp - > devicePixelRatio ( ) ;
2018-01-18 11:49:22 +00:00
timer . start ( ) ;
2019-12-08 08:23:45 +00:00
// make upload signals available in QML
2019-12-27 12:26:05 +00:00
// Remark: signal - signal connect
2019-12-08 08:23:45 +00:00
connect ( uploadDiveLogsDE : : instance ( ) , & uploadDiveLogsDE : : uploadFinish ,
2020-01-27 00:51:01 +00:00
this , & QMLManager : : uploadFinish ) ;
2019-12-08 08:23:45 +00:00
connect ( uploadDiveLogsDE : : instance ( ) , & uploadDiveLogsDE : : uploadProgress ,
2020-01-27 00:51:01 +00:00
this , & QMLManager : : uploadProgress ) ;
2019-12-08 10:52:38 +00:00
connect ( uploadDiveShare : : instance ( ) , & uploadDiveShare : : uploadProgress ,
2020-01-27 00:51:01 +00:00
this , & QMLManager : : uploadProgress ) ;
2019-12-08 10:52:38 +00:00
// uploadDiveShare::uploadFinish() is defined with 3 parameters,
2019-12-27 12:26:05 +00:00
// whereas QMLManager::uploadFinish() is defined with 2 parameters,
2019-12-08 10:52:38 +00:00
// Solution add a slot as landing zone.
2019-12-27 12:26:05 +00:00
connect ( uploadDiveShare : : instance ( ) , & uploadDiveShare : : uploadFinish ,
2020-01-27 00:51:01 +00:00
this , & QMLManager : : uploadFinishSlot ) ;
2019-12-08 08:23:45 +00:00
2018-04-13 06:05:23 +00:00
# if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
2018-04-13 23:43:20 +00:00
# if defined(Q_OS_ANDROID)
2022-02-26 18:17:07 +00:00
// on Android we first try the AppDataLocation (which allows sharing of files), then the
// GenericDataLocation (typically /storage/emulated/0), and if that fails (as happened e.g. on a
// Sony Xperia phone) we try several other default locations, with the TempLocation as last resort
2018-06-28 11:54:08 +00:00
QStringList fileLocations =
2022-02-26 18:17:07 +00:00
QStandardPaths : : standardLocations ( QStandardPaths : : AppDataLocation ) +
2020-01-27 00:51:01 +00:00
QStandardPaths : : standardLocations ( QStandardPaths : : GenericDataLocation ) +
QStandardPaths : : standardLocations ( QStandardPaths : : DocumentsLocation ) +
QStandardPaths : : standardLocations ( QStandardPaths : : DownloadLocation ) +
QStandardPaths : : standardLocations ( QStandardPaths : : TempLocation ) ;
2018-04-13 23:43:20 +00:00
# elif defined(Q_OS_IOS)
// on iOS we should save the data to the DocumentsLocation so it becomes accessible to the user
2018-06-28 11:54:08 +00:00
QStringList fileLocations =
2020-01-27 00:51:01 +00:00
QStandardPaths : : standardLocations ( QStandardPaths : : DocumentsLocation ) ;
2018-04-13 23:43:20 +00:00
# endif
2018-06-28 11:54:08 +00:00
appLogFileOpen = false ;
for ( const QString & fileLocation : fileLocations ) {
appLogFileName = fileLocation + " /subsurface.log " ;
appLogFile . setFileName ( appLogFileName ) ;
if ( ! appLogFile . open ( QIODevice : : ReadWrite | QIODevice : : Truncate ) ) {
appendTextToLog ( " Failed to open logfile " + appLogFileName
2020-01-27 00:51:01 +00:00
+ " at " + QDateTime : : currentDateTime ( ) . toString ( )
+ " error: " + appLogFile . errorString ( ) ) ;
2018-06-28 11:54:08 +00:00
} else {
// found a directory that works
appLogFileOpen = true ;
break ;
}
}
if ( appLogFileOpen ) {
2017-07-18 18:25:41 +00:00
appendTextToLog ( " Successfully opened logfile " + appLogFileName
2020-01-27 00:51:01 +00:00
+ " at " + QDateTime : : currentDateTime ( ) . toString ( ) ) ;
2018-06-28 11:54:08 +00:00
// if we were able to write the overall logfile, also write the libdivecomputer logfile
2022-02-26 18:28:58 +00:00
QString libdcLogFileName = appLogFileName ;
libdcLogFileName = libdcLogFileName . replace ( " /subsurface.log " , " /libdivecomputer.log " ) ;
2018-06-28 11:54:08 +00:00
// remove the existing libdivecomputer logfile so we don't copy an old one by mistake
QFile libdcLog ( libdcLogFileName ) ;
libdcLog . remove ( ) ;
2024-03-16 09:29:05 +00:00
logfile_name = libdcLogFileName . toStdString ( ) ;
2018-06-28 11:54:08 +00:00
} else {
appendTextToLog ( " No writeable location found, in-memory log only and no libdivecomputer log " ) ;
2017-07-10 00:03:57 +00:00
}
# endif
2018-01-24 21:56:52 +00:00
set_error_cb ( & showErrorFromC ) ;
2020-04-01 13:59:11 +00:00
uiNotificationCallback = showProgress ;
2017-06-18 06:23:41 +00:00
appendTextToLog ( " Starting " + getUserAgent ( ) ) ;
2017-11-27 18:11:52 +00:00
appendTextToLog ( QStringLiteral ( " built with libdivecomputer v%1 " ) . arg ( dc_version ( NULL ) ) ) ;
appendTextToLog ( QStringLiteral ( " built with Qt Version %1, runtime from Qt Version %2 " ) . arg ( QT_VERSION_STR ) . arg ( qVersion ( ) ) ) ;
int git_maj , git_min , git_rev ;
git_libgit2_version ( & git_maj , & git_min , & git_rev ) ;
appendTextToLog ( QStringLiteral ( " built with libgit2 %1.%2.%3 " ) . arg ( git_maj ) . arg ( git_min ) . arg ( git_rev ) ) ;
2019-09-18 20:13:00 +00:00
appendTextToLog ( QStringLiteral ( " Running on %1 " ) . arg ( QSysInfo : : prettyProductName ( ) ) ) ;
2024-03-11 00:03:16 +00:00
appendTextToLog ( QStringLiteral ( " Locale Languages offered %1, picked %2 " ) . arg ( QLocale ( ) . uiLanguages ( ) . join ( " , " ) ) . arg ( prefs . locale . lang_locale ) ) ;
2019-09-18 20:35:22 +00:00
# if defined(Q_OS_ANDROID)
extern QString getAndroidHWInfo ( ) ;
appendTextToLog ( getAndroidHWInfo ( ) ) ;
# endif
2020-01-12 04:59:08 +00:00
if ( ignore_bt ) {
m_btEnabled = false ;
} else {
// ensure that we start the BTDiscovery - this should be triggered by the export of the class
// to QML, but that doesn't seem to always work
BTDiscovery * btDiscovery = BTDiscovery : : instance ( ) ;
m_btEnabled = btDiscovery - > btAvailable ( ) ;
connect ( & btDiscovery - > localBtDevice , & QBluetoothLocalDevice : : hostModeStateChanged ,
this , & QMLManager : : btHostModeChange ) ;
}
2021-09-27 15:42:50 +00:00
progress_callback = & progressCallback ;
2015-12-15 07:00:19 +00:00
set_git_update_cb ( & gitProgressCB ) ;
2016-02-08 19:08:49 +00:00
2019-03-12 16:28:43 +00:00
// present dive site lists sorted by name
locationModel . sort ( LocationInformationModel : : NAME ) ;
2016-02-08 19:08:49 +00:00
// make sure we know if the current cloud repo has been successfully synced
syncLoadFromCloud ( ) ;
2018-11-18 06:12:09 +00:00
memset ( & m_copyPasteDive , 0 , sizeof ( m_copyPasteDive ) ) ;
memset ( & what , 0 , sizeof ( what ) ) ;
// Let's set some defaults to be copied so users don't necessarily need
// to know how to configure this
2022-02-12 13:03:18 +00:00
what . diveguide = true ;
2018-11-18 06:12:09 +00:00
what . buddy = true ;
what . suit = true ;
what . tags = true ;
what . cylinders = true ;
what . weights = true ;
2020-02-18 01:16:11 +00:00
// monitor when dives changed - but only in verbose mode
// careful - changing verbose at runtime isn't enough (of course that could be added if we want it)
if ( verbose )
connect ( & diveListNotifier , & DiveListNotifier : : divesChanged , this , & QMLManager : : divesChanged ) ;
2020-01-11 23:26:13 +00:00
2021-01-01 20:55:37 +00:00
// now that everything is setup, connect the application changed signal
connect ( qobject_cast < QApplication * > ( QApplication : : instance ( ) ) , & QApplication : : applicationStateChanged , this , & QMLManager : : applicationStateChanged ) ;
2020-06-13 20:45:06 +00:00
// we start out with clean data
updateHaveLocalChanges ( false ) ;
2021-01-17 20:52:05 +00:00
// setup Command infrastructure
Command : : init ( ) ;
2022-03-17 22:24:46 +00:00
undoAction = Command : : undoAction ( this ) ;
2024-03-30 21:31:40 +00:00
// get updates to the undo/redo texts
connect ( Command : : getUndoStack ( ) , & QUndoStack : : undoTextChanged , this , & QMLManager : : undoTextChanged ) ;
connect ( Command : : getUndoStack ( ) , & QUndoStack : : redoTextChanged , this , & QMLManager : : redoTextChanged ) ;
2015-12-05 03:34:59 +00:00
}
2015-11-11 20:32:54 +00:00
QML UI: don't immediately save data after we make changes
Much as this felt like the prudent thing to do, it makes the UI painful
to use. In bad network conditions, with a large dive log, on a phone,
the save operation can take more than a minute - which is just completely
ludicrous.
So instead we mark the dive list changed when we make changes and wait
for the app to not be in the foreground. Once the OS tells us that we are
hidden (on the desktop that generally means we don't have focus, on a
mobile device it usually does mean that the app is not on the screen), we
check if there are data to be saved and do so.
There is of course a major problem with this logic. If the user switches
away from Subsurface-mobile but comes back fairly quickly (just reacting
to a notification or briefly checking something, changing a song,
something... then the app may still be non-responsive for quite a while.
So we need to do something about the time it takes us to save the git
tree locally, and then figure out if we can move at least some of the
network traffic to another thread.
And we need to make sure the user immediately notices that the app is not
crashed but is actually saving their data. But that's for another commit.
tl;dr: CAREFUL, don't kill Subsurface-mobile before it had time to save
your data or your changes may be gone. In typical use that shouldn't be
an issue, but it is something that we need to tell the user about.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-04 00:37:17 +00:00
void QMLManager : : applicationStateChanged ( Qt : : ApplicationState state )
{
2020-04-04 21:40:53 +00:00
static bool initializeOnce = false ;
QML UI: don't immediately save data after we make changes
Much as this felt like the prudent thing to do, it makes the UI painful
to use. In bad network conditions, with a large dive log, on a phone,
the save operation can take more than a minute - which is just completely
ludicrous.
So instead we mark the dive list changed when we make changes and wait
for the app to not be in the foreground. Once the OS tells us that we are
hidden (on the desktop that generally means we don't have focus, on a
mobile device it usually does mean that the app is not on the screen), we
check if there are data to be saved and do so.
There is of course a major problem with this logic. If the user switches
away from Subsurface-mobile but comes back fairly quickly (just reacting
to a notification or briefly checking something, changing a song,
something... then the app may still be non-responsive for quite a while.
So we need to do something about the time it takes us to save the git
tree locally, and then figure out if we can move at least some of the
network traffic to another thread.
And we need to make sure the user immediately notices that the app is not
crashed but is actually saving their data. But that's for another commit.
tl;dr: CAREFUL, don't kill Subsurface-mobile before it had time to save
your data or your changes may be gone. In typical use that shouldn't be
an issue, but it is something that we need to tell the user about.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-04 00:37:17 +00:00
QString stateText ;
switch ( state ) {
case Qt : : ApplicationActive : stateText = " active " ; break ;
case Qt : : ApplicationHidden : stateText = " hidden " ; break ;
case Qt : : ApplicationSuspended : stateText = " suspended " ; break ;
case Qt : : ApplicationInactive : stateText = " inactive " ; break ;
default : stateText = QString ( " none of the four: 0x " ) + QString : : number ( state , 16 ) ;
}
2016-04-18 13:15:54 +00:00
stateText . prepend ( " AppState changed to " ) ;
QML UI: don't immediately save data after we make changes
Much as this felt like the prudent thing to do, it makes the UI painful
to use. In bad network conditions, with a large dive log, on a phone,
the save operation can take more than a minute - which is just completely
ludicrous.
So instead we mark the dive list changed when we make changes and wait
for the app to not be in the foreground. Once the OS tells us that we are
hidden (on the desktop that generally means we don't have focus, on a
mobile device it usually does mean that the app is not on the screen), we
check if there are data to be saved and do so.
There is of course a major problem with this logic. If the user switches
away from Subsurface-mobile but comes back fairly quickly (just reacting
to a notification or briefly checking something, changing a song,
something... then the app may still be non-responsive for quite a while.
So we need to do something about the time it takes us to save the git
tree locally, and then figure out if we can move at least some of the
network traffic to another thread.
And we need to make sure the user immediately notices that the app is not
crashed but is actually saving their data. But that's for another commit.
tl;dr: CAREFUL, don't kill Subsurface-mobile before it had time to save
your data or your changes may be gone. In typical use that shouldn't be
an issue, but it is something that we need to tell the user about.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-04 00:37:17 +00:00
stateText . append ( " with " ) ;
2020-04-04 21:47:31 +00:00
stateText . append ( ( unsavedChanges ( ) ? QLatin1String ( " " ) : QLatin1String ( " no " ) ) + QLatin1String ( " unsaved changes " ) ) ;
QML UI: don't immediately save data after we make changes
Much as this felt like the prudent thing to do, it makes the UI painful
to use. In bad network conditions, with a large dive log, on a phone,
the save operation can take more than a minute - which is just completely
ludicrous.
So instead we mark the dive list changed when we make changes and wait
for the app to not be in the foreground. Once the OS tells us that we are
hidden (on the desktop that generally means we don't have focus, on a
mobile device it usually does mean that the app is not on the screen), we
check if there are data to be saved and do so.
There is of course a major problem with this logic. If the user switches
away from Subsurface-mobile but comes back fairly quickly (just reacting
to a notification or briefly checking something, changing a song,
something... then the app may still be non-responsive for quite a while.
So we need to do something about the time it takes us to save the git
tree locally, and then figure out if we can move at least some of the
network traffic to another thread.
And we need to make sure the user immediately notices that the app is not
crashed but is actually saving their data. But that's for another commit.
tl;dr: CAREFUL, don't kill Subsurface-mobile before it had time to save
your data or your changes may be gone. In typical use that shouldn't be
an issue, but it is something that we need to tell the user about.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-04 00:37:17 +00:00
appendTextToLog ( stateText ) ;
2020-04-04 21:40:53 +00:00
if ( state = = Qt : : ApplicationActive & & ! m_initialized & & ! initializeOnce ) {
2020-04-02 22:28:03 +00:00
// once the app UI is displayed, finish our setup and mark the app as initialized
2020-04-04 21:40:53 +00:00
initializeOnce = true ;
2020-04-02 22:28:03 +00:00
finishSetup ( ) ;
appInitialized ( ) ;
}
2020-04-04 21:47:31 +00:00
if ( state = = Qt : : ApplicationInactive & & unsavedChanges ( ) ) {
2020-03-28 17:33:10 +00:00
// saveChangesCloud ensures that we don't have two conflicting saves going on
appendTextToLog ( " trying to save data as user switched away from app " ) ;
2016-04-08 19:35:45 +00:00
saveChangesCloud ( false ) ;
2020-03-28 17:33:10 +00:00
appendTextToLog ( " done trying to save to git local / remote " ) ;
QML UI: don't immediately save data after we make changes
Much as this felt like the prudent thing to do, it makes the UI painful
to use. In bad network conditions, with a large dive log, on a phone,
the save operation can take more than a minute - which is just completely
ludicrous.
So instead we mark the dive list changed when we make changes and wait
for the app to not be in the foreground. Once the OS tells us that we are
hidden (on the desktop that generally means we don't have focus, on a
mobile device it usually does mean that the app is not on the screen), we
check if there are data to be saved and do so.
There is of course a major problem with this logic. If the user switches
away from Subsurface-mobile but comes back fairly quickly (just reacting
to a notification or briefly checking something, changing a song,
something... then the app may still be non-responsive for quite a while.
So we need to do something about the time it takes us to save the git
tree locally, and then figure out if we can move at least some of the
network traffic to another thread.
And we need to make sure the user immediately notices that the app is not
crashed but is actually saving their data. But that's for another commit.
tl;dr: CAREFUL, don't kill Subsurface-mobile before it had time to save
your data or your changes may be gone. In typical use that shouldn't be
an issue, but it is something that we need to tell the user about.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-04 00:37:17 +00:00
}
}
2016-02-09 15:53:22 +00:00
void QMLManager : : openLocalThenRemote ( QString url )
{
2020-03-17 17:20:42 +00:00
// clear out the models and the fulltext index
cleanup: invert control-flow when resetting the core structures
To reset the core data structures, the mobile and desktop UIs
were calling into the dive-list models, which then reset the
core data structures, themselves and the unrelated
locationinformation model. The UI code then reset various other
things, such as the TankInformation model or the map. . This was
unsatisfying from a control-flow perspective, as the models should
display the core data, not act on it. Moreover, this meant lots
of intricate intermodule-dependencies.
Thus, straighten up the control flow: give the C core the
possibility to send a "all data reset" event. And do that
in those functions that reset the core data structures.
Let each module react to this event by itself. This removes
inter-module dependencies. For example, the MainWindow now
doesn't have to reset the TankInfoModel or the MapWidget.
Then, to reset the core data structures, let the UI code
simply directly call the respective core functions.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2020-05-04 22:12:36 +00:00
clear_dive_file_data ( ) ;
2020-03-29 19:53:01 +00:00
setDiveListProcessing ( true ) ;
2017-06-18 06:22:37 +00:00
setNotificationText ( tr ( " Open local dive data file " ) ) ;
2020-03-17 17:20:42 +00:00
appendTextToLog ( QString ( " Open dive data file %1 - git_local only is %2 " ) . arg ( url ) . arg ( git_local_only ) ) ;
2020-03-28 17:33:10 +00:00
QByteArray encodedFilename = QFile : : encodeName ( url ) ;
2020-03-17 17:20:42 +00:00
2019-11-03 13:05:05 +00:00
/* if this is a cloud storage repo and we have no local cache (i.e., it's the first time
2020-03-28 17:33:10 +00:00
* we try to open this ) , parse_file ( which is called by openAndMaybeSync ) will ALWAYS connect
* to the remote and populate the cache .
2019-11-03 13:05:05 +00:00
* Otherwise parse_file will respect the git_local_only flag and only update if that isn ' t set */
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
int error = parse_file ( encodedFilename . constData ( ) , & divelog ) ;
2016-02-09 15:53:22 +00:00
if ( error ) {
2017-10-17 11:28:36 +00:00
/* there can be 2 reasons for this:
* 1 ) we have cloud credentials , but there is no local repo ( yet ) .
* This implies that the PIN verify is still to be done .
* 2 ) we are in a very clean state after installing the app , and
* want to use a NO CLOUD setup . The intial repo has no initial
* commit in it , so its master branch does not yet exist . We do not
* care about this , as the very first commit of dive data to the
* no cloud repo solves this .
*/
2019-12-28 16:30:14 +00:00
auto credStatus = qPrefCloudStorage : : cloud_verification_status ( ) ;
2020-03-11 23:49:32 +00:00
if ( credStatus ! = qPrefCloudStorage : : CS_NOCLOUD ) {
appendTextToLog ( QStringLiteral ( " loading dives from cache failed %1 " ) . arg ( error ) ) ;
setNotificationText ( tr ( " Opening local data file failed " ) ) ;
2024-02-24 00:21:17 +00:00
if ( credStatus ! = qPrefCloudStorage : : CS_INCORRECT_USER_PASSWD ) {
2020-03-11 23:49:32 +00:00
qPrefCloudStorage : : set_cloud_verification_status ( qPrefCloudStorage : : CS_NEED_TO_VERIFY ) ;
2024-02-24 00:21:17 +00:00
emit passwordStateChanged ( ) ;
}
2020-03-11 23:49:32 +00:00
}
2016-02-09 15:53:22 +00:00
} else {
mobile: remove superfluous state VALID_EMAIL
This is a no-brainer removal of the VALID_EMAIL state used in QMLManager.
All current usage of this state is "if state is VALID or VALID_EMAIL",
so there is no distinction between the two states.
It is even a little different. The comment suggests "when we can open
a local cloud storage, tied to a cloud account (so explicitly not
the no-cloud status), we have at least a valid email". While this
is formally true, this implies that there is also a cloud account
on the cloud server (ie. the cloud account is in a VERIFIED state).
In other words: currently, there can't exist a valid local storage
that is tied to a valid email adress, without valid cloud account
on the server.
Notice that this touches the discussion on GitHub for commit
e76f527fe530636 (pull request #520). Can we implement the creation
of a valid cloud account without data link to the cloud server?
Currently, we need the server to confirm the email address (for
example for uniqueness reasons on server side). Obviously, we could
hack our way out of this, but we have a perfect solution already
in place. Create a no-cloud account, and transfer that later to
a true and valid cloud account.
Signed-off-by: Jan Mulder <jlmulder@xs4all.nl>
2017-08-01 09:38:59 +00:00
// if we can load from the cache, we know that we have a valid cloud account
2019-11-03 13:06:10 +00:00
// and we know that there was at least one successful sync with the cloud when
// that local cache was created - so there is a common ancestor
setLoadFromCloud ( true ) ;
2024-02-24 00:21:17 +00:00
if ( qPrefCloudStorage : : cloud_verification_status ( ) = = qPrefCloudStorage : : CS_UNKNOWN ) {
2019-12-28 17:18:06 +00:00
qPrefCloudStorage : : set_cloud_verification_status ( qPrefCloudStorage : : CS_VERIFIED ) ;
2024-02-24 00:21:17 +00:00
emit passwordStateChanged ( ) ;
}
2020-01-07 13:31:49 +00:00
qPrefUnits : : set_unit_system ( git_prefs . unit_system ) ;
2018-08-15 09:53:49 +00:00
qPrefTechnicalDetails : : set_tankbar ( git_prefs . tankbar ) ;
qPrefTechnicalDetails : : set_show_ccr_setpoint ( git_prefs . show_ccr_setpoint ) ;
qPrefTechnicalDetails : : set_show_ccr_sensors ( git_prefs . show_ccr_sensors ) ;
qPrefPartialPressureGas : : set_po2 ( git_prefs . pp_graphs . po2 ) ;
2020-04-01 14:34:18 +00:00
// the following steps can take a long time, so provide updates
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
setNotificationText ( tr ( " Processing %1 dives " ) . arg ( divelog . dives - > nr ) ) ;
2018-09-23 10:53:35 +00:00
process_loaded_dives ( ) ;
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
setNotificationText ( tr ( " %1 dives loaded from local dive data file " ) . arg ( divelog . dives - > nr ) ) ;
2016-02-09 15:53:22 +00:00
}
2019-12-28 16:30:14 +00:00
if ( qPrefCloudStorage : : cloud_verification_status ( ) = = qPrefCloudStorage : : CS_NEED_TO_VERIFY ) {
2017-09-27 15:08:30 +00:00
appendTextToLog ( QStringLiteral ( " have cloud credentials, but still needs PIN " ) ) ;
}
2019-12-28 16:30:14 +00:00
if ( qPrefCloudStorage : : cloud_verification_status ( ) = = qPrefCloudStorage : : CS_INCORRECT_USER_PASSWD ) {
2019-09-10 10:26:09 +00:00
appendTextToLog ( QStringLiteral ( " incorrect password for cloud credentials " ) ) ;
setNotificationText ( tr ( " Incorrect cloud credentials " ) ) ;
}
2019-12-29 11:33:30 +00:00
if ( m_oldStatus = = qPrefCloudStorage : : CS_NOCLOUD ) {
2017-08-03 12:55:09 +00:00
// if we switch to credentials from CS_NOCLOUD, we take things online temporarily
2018-09-10 13:30:01 +00:00
git_local_only = false ;
2016-04-29 13:28:09 +00:00
appendTextToLog ( QStringLiteral ( " taking things online to be able to switch to cloud account " ) ) ;
}
2024-03-16 15:06:39 +00:00
existing_filename = encodedFilename . toStdString ( ) ;
2020-03-28 17:33:10 +00:00
if ( git_local_only & & qPrefCloudStorage : : cloud_verification_status ( ) ! = qPrefCloudStorage : : CS_NOCLOUD )
appendTextToLog ( QStringLiteral ( " have cloud credentials, but user asked not to connect to network " ) ) ;
2020-06-13 20:45:06 +00:00
// if we were unable to sync with the remote, we assume that there are local changes
updateHaveLocalChanges ( ! git_remote_sync_successful ) ;
2018-01-28 16:39:01 +00:00
updateAllGlobalLists ( ) ;
2020-03-29 19:53:01 +00:00
setDiveListProcessing ( false ) ;
2020-06-06 16:51:40 +00:00
// this could have added a new local cache directory
emit cloudCacheListChanged ( ) ;
2018-01-28 16:39:01 +00:00
}
2019-12-06 13:14:00 +00:00
// Convenience function to accesss dive directly via its row.
static struct dive * diveInRow ( const QAbstractItemModel * model , int row )
{
QModelIndex index = model - > index ( row , 0 , QModelIndex ( ) ) ;
return index . isValid ( ) ? model - > data ( index , DiveTripModelBase : : DIVE_ROLE ) . value < struct dive * > ( ) : nullptr ;
}
void QMLManager : : selectRow ( int row )
{
2019-12-11 09:37:50 +00:00
dive * d = diveInRow ( MobileModels : : instance ( ) - > listModel ( ) , row ) ;
2019-12-06 13:14:00 +00:00
select_single_dive ( d ) ;
2019-12-21 19:41:06 +00:00
}
void QMLManager : : selectSwipeRow ( int row )
{
dive * d = diveInRow ( MobileModels : : instance ( ) - > swipeModel ( ) , row ) ;
select_single_dive ( d ) ;
2019-12-06 13:14:00 +00:00
}
2018-01-28 16:39:01 +00:00
void QMLManager : : updateAllGlobalLists ( )
{
2020-11-14 16:42:59 +00:00
emit buddyListChanged ( ) ;
emit suitListChanged ( ) ;
2022-02-12 13:03:18 +00:00
emit diveguideListChanged ( ) ;
2020-03-18 18:55:28 +00:00
// TODO: It would be nice if we could export the list of locations via model/view instead of a Q_PROPERTY
emit locationListChanged ( ) ;
2016-02-09 15:53:22 +00:00
}
2020-03-09 16:56:32 +00:00
static QString nocloud_localstorage ( )
{
return QString ( system_default_directory ( ) ) + " /cloudstorage/localrepo[master] " ;
}
2016-04-29 13:28:09 +00:00
void QMLManager : : mergeLocalRepo ( )
{
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 divelog log ;
parse_file ( qPrintable ( nocloud_localstorage ( ) ) , & log ) ;
add_imported_dives ( & log , IMPORT_MERGE_ALL_TRIPS ) ;
2020-10-21 21:34:15 +00:00
mark_divelist_changed ( true ) ;
2016-04-29 13:28:09 +00:00
}
2018-05-16 14:50:17 +00:00
void QMLManager : : copyAppLogToClipboard ( )
{
2019-11-09 18:14:15 +00:00
// The About page offers a button to copy logs so they can be pasted elsewhere
QApplication : : clipboard ( ) - > setText ( getCombinedLogs ( ) , QClipboard : : Clipboard ) ;
}
2018-05-21 08:55:07 +00:00
2019-11-09 18:14:15 +00:00
bool QMLManager : : createSupportEmail ( )
{
2022-02-26 18:28:58 +00:00
QString messageBody = " Please describe your issue here and keep the logs below: \n \n \n \n " ;
# if defined(Q_OS_ANDROID)
// let's use our nifty Java shareFile function
QAndroidJniObject activity = QtAndroid : : androidActivity ( ) ;
if ( activity . isValid ( ) ) {
QAndroidJniObject applogfilepath = QAndroidJniObject : : fromString ( appLogFileName ) ;
2024-03-16 09:29:05 +00:00
QAndroidJniObject libdcfilepath = QAndroidJniObject : : fromString ( QString : : fromStdString ( logfile_name ) ) ;
2022-08-30 17:06:37 +00:00
bool success = activity . callMethod < jboolean > ( " supportEmail " ,
2022-02-26 18:28:58 +00:00
" (Ljava/lang/String;Ljava/lang/String;)Z " , // two string arguments, return bool
applogfilepath . object < jstring > ( ) , libdcfilepath . object < jstring > ( ) ) ;
2024-03-25 20:17:29 +00:00
report_info ( " %s supportEmail %s " , __func__ , success ? " succeeded " : " failed " ) ;
2022-02-26 18:28:58 +00:00
if ( success )
return true ;
}
2024-03-25 20:17:29 +00:00
report_info ( " %s failed to share the logFiles via intent, use the fall-back mail body method " , __func__ ) ;
2022-08-29 23:46:58 +00:00
# elif defined(Q_OS_IOS)
// call into objC++ code to share on iOS
2024-03-16 09:29:05 +00:00
QString libdcLogFileName = QString : : fromStdString ( logfile_name ) ;
2022-08-29 23:46:58 +00:00
iosshare . supportEmail ( appLogFileName , libdcLogFileName ) ;
// Unfortunately I haven't been able to figure out how to wait until the mail was sent
// so that this could tell us whether this was successful or not
// We always assume it worked and return to the caller.
return true ;
2022-02-26 18:28:58 +00:00
# endif
2022-08-29 23:46:58 +00:00
// fallback code that tries to copy the logs into the message body and uses the Qt send email method
2019-11-09 18:14:15 +00:00
QString mailToLink = " mailto:in-app-support@subsurface-divelog.org?subject=Subsurface-mobile support request " ;
2022-02-26 18:28:58 +00:00
mailToLink + = " &body= " ;
mailToLink + = messageBody ;
2019-11-09 18:14:15 +00:00
mailToLink + = getCombinedLogs ( ) ;
if ( QDesktopServices : : openUrl ( QUrl ( mailToLink ) ) ) {
appendTextToLog ( " OS accepted support email " ) ;
return true ;
}
appendTextToLog ( " failed to create support email " ) ;
return false ;
}
// useful for support requests
QString QMLManager : : getCombinedLogs ( )
{
2018-05-21 08:55:07 +00:00
// Add heading and append subsurface.log
2019-11-09 18:14:15 +00:00
QString copyString = " \n ---------- subsurface.log ---------- \n " ;
2018-05-21 08:55:07 +00:00
copyString + = MessageHandlerModel : : self ( ) - > logAsString ( ) ;
// Add heading and append libdivecomputer.log
2024-03-16 09:29:05 +00:00
QFile f ( logfile_name . c_str ( ) ) ;
2018-05-21 08:55:07 +00:00
if ( f . open ( QFile : : ReadOnly | QFile : : Text ) ) {
copyString + = " \n \n \n ---------- libdivecomputer.log ---------- \n " ;
QTextStream in ( & f ) ;
copyString + = in . readAll ( ) ;
}
2018-06-28 13:42:13 +00:00
2018-05-21 08:55:07 +00:00
copyString + = " ---------- finish ---------- \n " ;
2018-06-28 13:42:13 +00:00
# if defined(Q_OS_ANDROID)
// on Android, the clipboard is effectively limited in size, but there is no
// predefined hard limit. All remote procedure calls use a shared Binder
// transaction buffer that is limited to 1MB. To work around this let's truncate
// the log once it is more than half a million characters. Qt doesn't tell us if
// the clipboard transaction fails, hopefully this will typically leave enough
// margin of error.
if ( copyString . size ( ) > 500000 ) {
copyString . truncate ( 500000 ) ;
copyString + = " \n \n ---------- truncated ---------- \n " ;
}
# endif
2019-11-09 18:14:15 +00:00
return copyString ;
2018-05-16 14:50:17 +00:00
}
2015-12-05 03:34:59 +00:00
void QMLManager : : finishSetup ( )
{
2020-03-17 17:20:42 +00:00
appendTextToLog ( " finishSetup called " ) ;
2015-11-11 20:32:54 +00:00
// Initialize cloud credentials.
2018-10-09 08:23:24 +00:00
git_local_only = ! prefs . cloud_auto_sync ;
2019-12-28 17:37:46 +00:00
2024-03-24 21:05:28 +00:00
std : : optional < std : : string > url ;
2019-12-20 15:35:12 +00:00
if ( ! qPrefCloudStorage : : cloud_storage_email ( ) . isEmpty ( ) & &
2019-12-20 16:04:28 +00:00
! qPrefCloudStorage : : cloud_storage_password ( ) . isEmpty ( ) & &
2024-03-24 21:05:28 +00:00
( url = getCloudURL ( ) ) ) {
openLocalThenRemote ( QString : : fromStdString ( * url ) ) ;
2024-03-16 15:06:39 +00:00
} else if ( ! existing_filename . empty ( ) & &
2020-04-04 21:10:19 +00:00
qPrefCloudStorage : : cloud_verification_status ( ) ! = qPrefCloudStorage : : CS_UNKNOWN ) {
2020-04-04 21:23:39 +00:00
rememberOldStatus ( ) ;
2024-03-16 15:06:39 +00:00
existing_filename = nocloud_localstorage ( ) . toStdString ( ) ;
2019-12-28 17:42:04 +00:00
qPrefCloudStorage : : set_cloud_verification_status ( qPrefCloudStorage : : CS_NOCLOUD ) ;
2024-02-24 00:21:17 +00:00
emit passwordStateChanged ( ) ;
2019-12-27 12:56:33 +00:00
saveCloudCredentials ( qPrefCloudStorage : : cloud_storage_email ( ) , qPrefCloudStorage : : cloud_storage_password ( ) , qPrefCloudStorage : : cloud_storage_pin ( ) ) ;
2016-04-22 14:13:14 +00:00
appendTextToLog ( tr ( " working in no-cloud mode " ) ) ;
2024-03-16 15:06:39 +00:00
int error = parse_file ( existing_filename . c_str ( ) , & divelog ) ;
2016-04-27 13:14:13 +00:00
if ( error ) {
// we got an error loading the local file
2017-06-18 06:22:37 +00:00
setNotificationText ( tr ( " Error parsing local storage, giving up " ) ) ;
2024-03-16 15:06:39 +00:00
existing_filename . clear ( ) ;
2016-04-27 13:14:13 +00:00
} else {
// successfully opened the local file, now add thigs to the dive list
2020-01-10 00:39:24 +00:00
consumeFinishedLoad ( ) ;
2020-06-13 20:45:06 +00:00
updateHaveLocalChanges ( true ) ;
2024-03-16 15:06:39 +00:00
appendTextToLog ( QString ( " working in no-cloud mode, finished loading %1 dives from %2 " ) . arg ( divelog . dives - > nr ) . arg ( existing_filename . c_str ( ) ) ) ;
2016-04-27 13:14:13 +00:00
}
2015-12-20 00:08:10 +00:00
} else {
2019-12-28 17:18:06 +00:00
qPrefCloudStorage : : set_cloud_verification_status ( qPrefCloudStorage : : CS_UNKNOWN ) ;
2024-02-24 00:21:17 +00:00
emit passwordStateChanged ( ) ;
2016-04-22 14:13:14 +00:00
appendTextToLog ( tr ( " no cloud credentials " ) ) ;
2016-03-11 05:59:16 +00:00
setStartPageText ( RED_FONT + tr ( " Please enter valid cloud credentials. " ) + END_FONT ) ;
2015-12-20 00:08:10 +00:00
}
2020-04-01 13:10:18 +00:00
m_initialized = true ;
emit initializedChanged ( ) ;
2020-06-06 16:51:40 +00:00
// this could have brought in new cache directories, so make sure QML
// calls our getter function again and doesn't show us outdated information
emit cloudCacheListChanged ( ) ;
2024-03-30 20:55:22 +00:00
// This is used to instruct the main-thread to sync the current state to disk/cloud.
// We must not sync to cloud from signal-handlers in the main thread, because cloud
// access runs the main event loop and that might delete the object that caused the
// signal. By using a connection of the "QueuedConnection" type, the signal will be
// queued and only executed once the signal handler finishes and the main event
// loop retakes control.
connect ( this , & QMLManager : : changesNeedSavingSignal , this , & QMLManager : : saveUnsaved , Qt : : QueuedConnection ) ;
2015-06-04 10:29:50 +00:00
}
QMLManager : : ~ QMLManager ( )
{
2018-04-13 23:53:51 +00:00
# if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
2017-07-10 00:03:57 +00:00
if ( appLogFileOpen )
appLogFile . close ( ) ;
# endif
2019-09-27 23:26:54 +00:00
m_instance = NULL ;
}
QMLManager * QMLManager : : instance ( )
{
return m_instance ;
2015-06-04 10:29:50 +00:00
}
2019-12-27 12:56:33 +00:00
void QMLManager : : saveCloudCredentials ( const QString & newEmail , const QString & newPassword , const QString & pin )
2015-12-03 22:30:30 +00:00
{
bool cloudCredentialsChanged = false ;
2019-12-28 16:30:14 +00:00
bool noCloud = qPrefCloudStorage : : cloud_verification_status ( ) = = qPrefCloudStorage : : CS_NOCLOUD ;
2019-09-10 10:24:06 +00:00
2020-05-23 19:08:38 +00:00
// email address MUST be lower case or bad things happen
QString email = newEmail . toLower ( ) ;
2017-07-15 05:15:31 +00:00
// make sure we only have letters, numbers, and +-_. in password and email address
QRegularExpression regExp ( " ^[a-zA-Z0-9@.+_-]+$ " ) ;
2019-09-10 10:24:06 +00:00
if ( ! noCloud ) {
2017-09-27 16:19:53 +00:00
// in case of NO_CLOUD, the email address + passwd do not care, so do not check it.
2019-12-20 16:04:28 +00:00
if ( newPassword . isEmpty ( ) | |
2020-01-27 00:51:01 +00:00
! regExp . match ( newPassword ) . hasMatch ( ) | |
2020-05-23 19:08:38 +00:00
! regExp . match ( email ) . hasMatch ( ) ) {
2017-09-27 16:19:53 +00:00
setStartPageText ( RED_FONT + tr ( " Cloud storage email and password can only consist of letters, numbers, and '.', '-', '_', and '+'. " ) + END_FONT ) ;
return ;
}
// use the same simplistic regex as the backend to check email addresses
regExp = QRegularExpression ( " ^[a-zA-Z0-9.+_-]+@[a-zA-Z0-9.+_-]+ \\ .[a-zA-Z0-9]+ " ) ;
2020-05-23 19:08:38 +00:00
if ( ! regExp . match ( email ) . hasMatch ( ) ) {
2017-09-27 16:19:53 +00:00
setStartPageText ( RED_FONT + tr ( " Invalid format for email address " ) + END_FONT ) ;
return ;
}
2017-07-15 05:15:31 +00:00
}
2020-05-23 19:08:38 +00:00
if ( ! same_string ( prefs . cloud_storage_email , qPrintable ( email ) ) ) {
2015-11-18 21:14:19 +00:00
cloudCredentialsChanged = true ;
2015-07-13 00:39:13 +00:00
}
2015-11-18 21:14:19 +00:00
2019-12-20 16:04:28 +00:00
if ( ! same_string ( prefs . cloud_storage_password , qPrintable ( newPassword ) ) ) {
cloudCredentialsChanged = true ;
}
2015-11-18 21:14:19 +00:00
2019-12-28 16:30:14 +00:00
if ( qPrefCloudStorage : : cloud_verification_status ( ) ! = qPrefCloudStorage : : CS_NOCLOUD & &
2020-01-27 00:51:01 +00:00
! cloudCredentialsChanged ) {
2016-04-06 18:42:38 +00:00
// just go back to the dive list
2019-12-29 11:33:30 +00:00
qPrefCloudStorage : : set_cloud_verification_status ( m_oldStatus ) ;
2024-02-24 00:21:17 +00:00
emit passwordStateChanged ( ) ;
2016-04-06 18:42:38 +00:00
}
2016-04-29 13:24:21 +00:00
2020-05-23 19:08:38 +00:00
if ( ! noCloud & & ! verifyCredentials ( email , newPassword , pin ) ) {
2020-04-04 21:10:19 +00:00
appendTextToLog ( " saveCloudCredentials: given cloud credentials didn't verify " ) ;
2019-09-10 10:24:06 +00:00
return ;
2020-04-04 21:10:19 +00:00
}
2020-05-23 19:08:38 +00:00
qPrefCloudStorage : : set_cloud_storage_email ( email ) ;
2019-12-20 16:04:28 +00:00
qPrefCloudStorage : : set_cloud_storage_password ( newPassword ) ;
2019-09-10 10:24:06 +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
if ( m_oldStatus = = qPrefCloudStorage : : CS_NOCLOUD & & cloudCredentialsChanged & & divelog . dives - > nr ) {
2018-06-18 09:14:05 +00:00
// we came from NOCLOUD and are connecting to a cloud account;
// since we already have dives in the table, let's remember that so we can keep them
noCloudToCloud = true ;
appendTextToLog ( " transitioning from no-cloud to cloud and have dives " ) ;
}
2019-12-20 15:35:12 +00:00
if ( qPrefCloudStorage : : cloud_storage_email ( ) . isEmpty ( ) | |
2020-01-27 00:51:01 +00:00
qPrefCloudStorage : : cloud_storage_password ( ) . isEmpty ( ) ) {
2016-03-11 05:59:16 +00:00
setStartPageText ( RED_FONT + tr ( " Please enter valid cloud credentials. " ) + END_FONT ) ;
2016-02-13 06:06:38 +00:00
} else if ( cloudCredentialsChanged ) {
2016-04-29 12:04:47 +00:00
// let's make sure there are no unsaved changes
saveChangesLocal ( ) ;
2016-02-08 19:14:11 +00:00
syncLoadFromCloud ( ) ;
2016-02-11 02:14:09 +00:00
manager ( ) - > clearAccessCache ( ) ; // remove any chached credentials
2016-02-11 05:24:10 +00:00
clear_git_id ( ) ; // invalidate our remembered GIT SHA
cleanup: invert control-flow when resetting the core structures
To reset the core data structures, the mobile and desktop UIs
were calling into the dive-list models, which then reset the
core data structures, themselves and the unrelated
locationinformation model. The UI code then reset various other
things, such as the TankInformation model or the map. . This was
unsatisfying from a control-flow perspective, as the models should
display the core data, not act on it. Moreover, this meant lots
of intricate intermodule-dependencies.
Thus, straighten up the control flow: give the C core the
possibility to send a "all data reset" event. And do that
in those functions that reset the core data structures.
Let each module react to this event by itself. This removes
inter-module dependencies. For example, the MainWindow now
doesn't have to reset the TankInfoModel or the MapWidget.
Then, to reset the core data structures, let the UI code
simply directly call the respective core functions.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2020-05-04 22:12:36 +00:00
clear_dive_file_data ( ) ;
2016-02-12 14:54:52 +00:00
setStartPageText ( tr ( " Attempting to open cloud storage with new credentials " ) ) ;
2016-04-30 18:08:33 +00:00
// since we changed credentials, we need to try to connect to the cloud, regardless
// of whether we're in offline mode or not, to make sure the repository is synced
2018-09-10 13:30:01 +00:00
currentGitLocalOnly = git_local_only ;
git_local_only = false ;
2020-04-04 21:15:29 +00:00
loadDivesWithValidCredentials ( ) ;
2015-12-05 03:34:59 +00:00
}
2020-04-04 21:23:39 +00:00
rememberOldStatus ( ) ;
2015-12-05 03:34:59 +00:00
}
2019-09-10 10:19:36 +00:00
bool QMLManager : : verifyCredentials ( QString email , QString password , QString pin )
{
setStartPageText ( tr ( " Testing cloud credentials " ) ) ;
if ( pin . isEmpty ( ) )
2019-11-03 12:36:35 +00:00
appendTextToLog ( QStringLiteral ( " verify credentials for email %1 (no PIN) " ) . arg ( email ) ) ;
2019-09-10 10:19:36 +00:00
else
appendTextToLog ( QStringLiteral ( " verify credentials for email %1 PIN %2 " ) . arg ( email , pin ) ) ;
CloudStorageAuthenticate * csa = new CloudStorageAuthenticate ( this ) ;
csa - > backend ( email , password , pin ) ;
// let's wait here for the signal to avoid too many more nested functions
QTimer myTimer ;
myTimer . setSingleShot ( true ) ;
QEventLoop loop ;
connect ( csa , & CloudStorageAuthenticate : : finishedAuthenticate , & loop , & QEventLoop : : quit ) ;
connect ( & myTimer , & QTimer : : timeout , & loop , & QEventLoop : : quit ) ;
2021-08-16 02:53:07 +00:00
myTimer . start ( prefs . cloud_timeout * 1000 ) ;
2019-09-10 10:19:36 +00:00
loop . exec ( ) ;
if ( ! myTimer . isActive ( ) ) {
// got no response from the server
setStartPageText ( RED_FONT + tr ( " No response from cloud server to validate the credentials " ) + END_FONT ) ;
return false ;
}
myTimer . stop ( ) ;
if ( prefs . cloud_verification_status = = qPrefCloudStorage : : CS_INCORRECT_USER_PASSWD ) {
appendTextToLog ( QStringLiteral ( " Incorrect email / password combination " ) ) ;
setStartPageText ( RED_FONT + tr ( " Incorrect email / password combination " ) + END_FONT ) ;
return false ;
} else if ( prefs . cloud_verification_status = = qPrefCloudStorage : : CS_NEED_TO_VERIFY ) {
if ( pin . isEmpty ( ) ) {
appendTextToLog ( QStringLiteral ( " Cloud credentials require PIN entry " ) ) ;
setStartPageText ( RED_FONT + tr ( " Cloud credentials require verification PIN " ) + END_FONT ) ;
} else {
appendTextToLog ( QStringLiteral ( " PIN provided but not accepted " ) ) ;
setStartPageText ( RED_FONT + tr ( " Incorrect PIN, please try again " ) + END_FONT ) ;
}
return false ;
} else if ( prefs . cloud_verification_status = = qPrefCloudStorage : : CS_VERIFIED ) {
appendTextToLog ( QStringLiteral ( " PIN accepted " ) ) ;
setStartPageText ( RED_FONT + tr ( " PIN accepted, credentials verified " ) + END_FONT ) ;
}
return true ;
}
2022-08-08 00:11:05 +00:00
void QMLManager : : deleteAccount ( )
{
QString email ( prefs . cloud_storage_email ) ;
QString passwd ( prefs . cloud_storage_password ) ;
if ( email . isEmpty ( ) | | passwd . isEmpty ( ) )
return ;
setStartPageText ( tr ( " Deleting cloud account... " ) ) ;
appendTextToLog ( QStringLiteral ( " user requested that we delete cloud account for email %1 " ) . arg ( email ) ) ;
CloudStorageAuthenticate * csa = new CloudStorageAuthenticate ( this ) ;
csa - > deleteAccount ( email , passwd ) ;
// let's wait here for the signal to avoid too many more nested functions
QTimer myTimer ;
myTimer . setSingleShot ( true ) ;
QEventLoop loop ;
connect ( csa , & CloudStorageAuthenticate : : finishedDelete , & loop , & QEventLoop : : quit ) ;
connect ( & myTimer , & QTimer : : timeout , & loop , & QEventLoop : : quit ) ;
myTimer . start ( prefs . cloud_timeout * 3 * 1000 ) ; // give it extra time
loop . exec ( ) ;
if ( ! myTimer . isActive ( ) ) {
// got no response from the server
setStartPageText ( RED_FONT + tr ( " No response from cloud server to delete account " ) + END_FONT ) ;
appendTextToLog ( QStringLiteral ( " no response from cloud server to delete account " ) ) ;
return ;
}
myTimer . stop ( ) ;
appendTextToLog ( QStringLiteral ( " deleted the account " ) ) ;
qPrefCloudStorage : : set_cloud_storage_email ( " " ) ;
qPrefCloudStorage : : set_cloud_storage_email_encoded ( " " ) ;
qPrefCloudStorage : : set_cloud_storage_password ( " " ) ;
qPrefCloudStorage : : set_cloud_verification_status ( qPrefCloudStorage : : CS_NOCLOUD ) ;
2024-02-24 00:21:17 +00:00
emit passwordStateChanged ( ) ;
2024-03-16 15:06:39 +00:00
existing_filename = nocloud_localstorage ( ) . toStdString ( ) ;
2022-08-08 00:11:05 +00:00
setStartPageText ( tr ( " Cloud storage account deleted. " ) ) ;
return ;
}
2015-12-05 03:34:59 +00:00
void QMLManager : : loadDivesWithValidCredentials ( )
{
2024-03-24 21:05:28 +00:00
auto url = getCloudURL ( ) ;
if ( ! url ) {
2018-01-24 21:56:52 +00:00
setStartPageText ( RED_FONT + tr ( " Cloud storage error: %1 " ) . arg ( consumeError ( ) ) + END_FONT ) ;
2016-04-29 13:28:09 +00:00
revertToNoCloudIfNeeded ( ) ;
2015-07-10 08:31:24 +00:00
return ;
}
2024-03-24 21:05:28 +00:00
QByteArray fileNamePrt = QFile : : encodeName ( QString : : fromStdString ( * url ) ) ;
2022-04-13 16:43:37 +00:00
struct git_info info ;
2016-04-06 05:51:09 +00:00
int error ;
2022-04-13 16:43:37 +00:00
2022-04-18 21:37:55 +00:00
if ( remote_repo_uptodate ( fileNamePrt . data ( ) , & info ) ) {
2015-12-27 18:05:19 +00:00
appendTextToLog ( " Cloud sync shows local cache was current " ) ;
2018-06-18 09:14:05 +00:00
} else {
2020-03-29 01:14:00 +00:00
appendTextToLog ( " Cloud sync brought newer data, reloading the dive list " ) ;
2020-03-29 19:53:01 +00:00
setDiveListProcessing ( true ) ;
2020-03-29 01:14:00 +00:00
// if we aren't switching from no-cloud mode, let's clear the dive data
if ( ! noCloudToCloud ) {
appendTextToLog ( " Clear out in memory dive data " ) ;
cleanup: invert control-flow when resetting the core structures
To reset the core data structures, the mobile and desktop UIs
were calling into the dive-list models, which then reset the
core data structures, themselves and the unrelated
locationinformation model. The UI code then reset various other
things, such as the TankInformation model or the map. . This was
unsatisfying from a control-flow perspective, as the models should
display the core data, not act on it. Moreover, this meant lots
of intricate intermodule-dependencies.
Thus, straighten up the control flow: give the C core the
possibility to send a "all data reset" event. And do that
in those functions that reset the core data structures.
Let each module react to this event by itself. This removes
inter-module dependencies. For example, the MainWindow now
doesn't have to reset the TankInfoModel or the MapWidget.
Then, to reset the core data structures, let the UI code
simply directly call the respective core functions.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2020-05-04 22:12:36 +00:00
clear_dive_file_data ( ) ;
2020-03-29 01:14:00 +00:00
} else {
appendTextToLog ( " Switching from no cloud mode; keep in memory dive data " ) ;
}
2022-04-13 16:43:37 +00:00
if ( info . repo ) {
2024-03-11 20:41:14 +00:00
appendTextToLog ( QString ( " have repository and branch %1 " ) . arg ( info . branch . c_str ( ) ) ) ;
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
error = git_load_dives ( & info , & divelog ) ;
2020-03-29 01:14:00 +00:00
} else {
appendTextToLog ( QString ( " didn't receive valid git repo, try again " ) ) ;
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
error = parse_file ( fileNamePrt . data ( ) , & divelog ) ;
2020-03-29 01:14:00 +00:00
}
2020-03-29 19:53:01 +00:00
setDiveListProcessing ( false ) ;
2020-03-29 01:14:00 +00:00
if ( ! error ) {
report_error ( " filename is now %s " , fileNamePrt . data ( ) ) ;
2024-03-16 15:06:39 +00:00
existing_filename = fileNamePrt . toStdString ( ) ;
2020-03-29 01:14:00 +00:00
} else {
report_error ( " failed to open file %s " , fileNamePrt . data ( ) ) ;
setNotificationText ( consumeError ( ) ) ;
revertToNoCloudIfNeeded ( ) ;
2024-03-16 15:06:39 +00:00
existing_filename . clear ( ) ;
2020-03-29 01:14:00 +00:00
return ;
}
consumeFinishedLoad ( ) ;
}
2022-04-18 21:36:00 +00:00
2016-04-22 14:10:20 +00:00
setLoadFromCloud ( true ) ;
2020-06-13 20:45:06 +00:00
2016-04-29 13:28:09 +00:00
// if we came from local storage mode, let's merge the local data into the local cache
// for the remote data - which then later gets merged with the remote data if necessary
2018-06-18 09:14:05 +00:00
if ( noCloudToCloud ) {
2017-06-18 06:50:22 +00:00
git_storage_update_progress ( qPrintable ( tr ( " Loading dives from local storage ('no cloud' mode) " ) ) ) ;
2016-04-29 13:28:09 +00:00
mergeLocalRepo ( ) ;
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
appendTextToLog ( QStringLiteral ( " %1 dives loaded after importing nocloud local storage " ) . arg ( divelog . dives - > nr ) ) ;
2018-06-18 09:14:05 +00:00
noCloudToCloud = false ;
2018-10-08 17:10:11 +00:00
mark_divelist_changed ( true ) ;
2020-06-13 20:52:04 +00:00
emit syncStateChanged ( ) ;
2016-04-29 13:28:09 +00:00
saveChangesLocal ( ) ;
2018-10-09 08:23:24 +00:00
if ( git_local_only = = false ) {
2016-04-29 13:28:09 +00:00
appendTextToLog ( QStringLiteral ( " taking things back offline now that storage is synced " ) ) ;
2018-10-09 08:23:24 +00:00
git_local_only = true ;
2016-04-29 13:28:09 +00:00
}
}
2020-06-13 20:45:06 +00:00
// if we successfully synced with the cloud, update that status
updateHaveLocalChanges ( ! git_remote_sync_successful ) ;
2016-04-30 18:08:33 +00:00
// if we got here just for an initial connection to the cloud, reset to offline
if ( currentGitLocalOnly ) {
currentGitLocalOnly = false ;
2018-09-10 13:30:01 +00:00
git_local_only = true ;
2016-04-30 18:08:33 +00:00
}
2016-04-29 13:28:09 +00:00
return ;
}
void QMLManager : : revertToNoCloudIfNeeded ( )
{
2016-04-30 18:08:33 +00:00
if ( currentGitLocalOnly ) {
// we tried to connect to the cloud for the first time and that failed
currentGitLocalOnly = false ;
2018-09-10 13:30:01 +00:00
git_local_only = true ;
2016-04-30 18:08:33 +00:00
}
2019-12-29 11:33:30 +00:00
if ( m_oldStatus = = qPrefCloudStorage : : CS_NOCLOUD ) {
2016-04-29 13:28:09 +00:00
// we tried to switch to a cloud account and had previously used local data,
// but connecting to the cloud account (and subsequently merging the local
// and cloud data) failed - so let's delete the cloud credentials and go
2017-08-03 12:55:09 +00:00
// back to CS_NOCLOUD mode in order to prevent us from losing the locally stored
2016-04-29 13:28:09 +00:00
// dives
2018-10-09 08:23:24 +00:00
if ( git_local_only = = true ) {
2016-04-29 13:28:09 +00:00
appendTextToLog ( QStringLiteral ( " taking things back offline since sync with cloud failed " ) ) ;
2018-10-09 08:23:24 +00:00
git_local_only = false ;
2016-04-29 13:28:09 +00:00
}
2017-11-18 18:57:50 +00:00
free ( ( void * ) prefs . cloud_storage_email ) ;
2016-04-29 13:28:09 +00:00
prefs . cloud_storage_email = NULL ;
2017-11-18 18:57:50 +00:00
free ( ( void * ) prefs . cloud_storage_password ) ;
2016-04-29 13:28:09 +00:00
prefs . cloud_storage_password = NULL ;
2019-12-20 15:35:12 +00:00
qPrefCloudStorage : : set_cloud_storage_email ( " " ) ;
2019-12-20 16:04:28 +00:00
qPrefCloudStorage : : set_cloud_storage_password ( " " ) ;
2020-04-04 21:23:39 +00:00
rememberOldStatus ( ) ;
2019-12-28 17:29:47 +00:00
qPrefCloudStorage : : set_cloud_verification_status ( qPrefCloudStorage : : CS_NOCLOUD ) ;
2024-02-24 00:21:17 +00:00
emit passwordStateChanged ( ) ;
2024-03-16 15:06:39 +00:00
existing_filename = nocloud_localstorage ( ) . toStdString ( ) ;
2016-04-29 13:28:09 +00:00
setStartPageText ( RED_FONT + tr ( " Failed to connect to cloud server, reverting to no cloud status " ) + END_FONT ) ;
}
2016-04-22 14:10:20 +00:00
}
2020-01-10 00:39:24 +00:00
void QMLManager : : consumeFinishedLoad ( )
2016-04-22 14:10:20 +00:00
{
2017-02-04 16:55:25 +00:00
prefs . unit_system = git_prefs . unit_system ;
if ( git_prefs . unit_system = = IMPERIAL )
git_prefs . units = IMPERIAL_units ;
2017-03-11 23:44:55 +00:00
else if ( git_prefs . unit_system = = METRIC )
git_prefs . units = SI_units ;
2017-02-04 16:55:25 +00:00
prefs . units = git_prefs . units ;
2017-02-04 09:42:00 +00:00
prefs . tankbar = git_prefs . tankbar ;
2017-03-25 12:03:37 +00:00
prefs . show_ccr_setpoint = git_prefs . show_ccr_setpoint ;
prefs . show_ccr_sensors = git_prefs . show_ccr_sensors ;
prefs . pp_graphs . po2 = git_prefs . pp_graphs . po2 ;
2018-09-23 10:53:35 +00:00
process_loaded_dives ( ) ;
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
appendTextToLog ( QStringLiteral ( " %1 dives loaded " ) . arg ( divelog . dives - > nr ) ) ;
if ( divelog . dives - > nr = = 0 )
2015-12-15 07:00:19 +00:00
setStartPageText ( tr ( " Cloud storage open successfully. No dives in dive list. " ) ) ;
2015-07-17 15:28:01 +00:00
}
2016-01-11 03:34:21 +00:00
void QMLManager : : refreshDiveList ( )
{
cleanup: invert control-flow when resetting the core structures
To reset the core data structures, the mobile and desktop UIs
were calling into the dive-list models, which then reset the
core data structures, themselves and the unrelated
locationinformation model. The UI code then reset various other
things, such as the TankInformation model or the map. . This was
unsatisfying from a control-flow perspective, as the models should
display the core data, not act on it. Moreover, this meant lots
of intricate intermodule-dependencies.
Thus, straighten up the control flow: give the C core the
possibility to send a "all data reset" event. And do that
in those functions that reset the core data structures.
Let each module react to this event by itself. This removes
inter-module dependencies. For example, the MainWindow now
doesn't have to reset the TankInfoModel or the MapWidget.
Then, to reset the core data structures, let the UI code
simply directly call the respective core functions.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2020-05-04 22:12:36 +00:00
MobileModels : : instance ( ) - > invalidate ( ) ;
2016-01-11 03:34:21 +00:00
}
2020-01-10 00:25:37 +00:00
// Ouch. Editing a dive might create a dive site or change an existing dive site.
// The following structure describes such a change caused by a dive edit.
// Hopefully, we can remove this in due course by using finer-grained undo-commands.
struct DiveSiteChange {
2024-05-04 11:39:04 +00:00
std : : unique_ptr < dive_site > createdDs ; // not-null if we created a dive site.
2020-01-10 00:25:37 +00:00
dive_site * editDs = nullptr ; // not-null if we are supposed to edit an existing dive site.
location_t location = zero_location ; // new value of the location if we edit an existing dive site.
bool changed = false ; // true if either a dive site or the dive was changed.
} ;
static void setupDivesite ( DiveSiteChange & res , struct dive * d , struct dive_site * ds , double lat , double lon , const char * locationtext )
2015-07-17 15:28:01 +00:00
{
2018-10-20 18:12:15 +00:00
location_t location = create_location ( lat , lon ) ;
QML UI: rewrite the commitChanges function
I couldn't figure out how to break this down into small, useful commits.
Part of the problem is that I kept going while working on this and as you
can see from looking at the commit, diff tries so hard to find small code
fragments that moved around, that the diff overall becomes quite
unreadable and it seemed impossible to recreate the sequence of steps
after the fact.
It all started with adding the parsing for the GPS coordinates. But while
testing that code I found several issues with the rest of the function.
Most importantly it seemed ridiculous that we carefully tried to match the
texts that the DiveObjectHelper would create for the various fields,
instead of just using the DiveObjectHelper to do just that. And once I had
converted that I once again realized just how long and hard to understand
that function was getting and decided to break out some of the more
complex parts into their own helper functions.
But of course all this didn't happen in this logical, structured, ordered
way. Instead I did all of these things at the same time, testing,
rearranging, etc.
So in the end I went with one BIG commit that does all of this in one fell
swoop.
This adds four helper functions to deal with start time/date, duration,
location and gps coordinates, and depth of the dive.
To avoid mistakes when dealing with the GPS coordinates, there's another
helper to encapsulate the creation of the dive site and we switched to a
current GPS location.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-15 13:01:14 +00:00
if ( ds ) {
2020-01-10 00:25:37 +00:00
res . editDs = ds ;
res . location = location ;
QML UI: rewrite the commitChanges function
I couldn't figure out how to break this down into small, useful commits.
Part of the problem is that I kept going while working on this and as you
can see from looking at the commit, diff tries so hard to find small code
fragments that moved around, that the diff overall becomes quite
unreadable and it seemed impossible to recreate the sequence of steps
after the fact.
It all started with adding the parsing for the GPS coordinates. But while
testing that code I found several issues with the rest of the function.
Most importantly it seemed ridiculous that we carefully tried to match the
texts that the DiveObjectHelper would create for the various fields,
instead of just using the DiveObjectHelper to do just that. And once I had
converted that I once again realized just how long and hard to understand
that function was getting and decided to break out some of the more
complex parts into their own helper functions.
But of course all this didn't happen in this logical, structured, ordered
way. Instead I did all of these things at the same time, testing,
rearranging, etc.
So in the end I went with one BIG commit that does all of this in one fell
swoop.
This adds four helper functions to deal with start time/date, duration,
location and gps coordinates, and depth of the dive.
To avoid mistakes when dealing with the GPS coordinates, there's another
helper to encapsulate the creation of the dive site and we switched to a
current GPS location.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-15 13:01:14 +00:00
} else {
2024-05-04 11:39:04 +00:00
res . createdDs = std : : make_unique < dive_site > ( locationtext ) ;
2021-11-13 20:13:15 +00:00
res . createdDs - > location = location ;
2020-04-04 14:35:24 +00:00
d - > dive_site = res . createdDs . get ( ) ;
2015-12-27 05:24:29 +00:00
}
2020-01-10 00:25:37 +00:00
res . changed = true ;
QML UI: rewrite the commitChanges function
I couldn't figure out how to break this down into small, useful commits.
Part of the problem is that I kept going while working on this and as you
can see from looking at the commit, diff tries so hard to find small code
fragments that moved around, that the diff overall becomes quite
unreadable and it seemed impossible to recreate the sequence of steps
after the fact.
It all started with adding the parsing for the GPS coordinates. But while
testing that code I found several issues with the rest of the function.
Most importantly it seemed ridiculous that we carefully tried to match the
texts that the DiveObjectHelper would create for the various fields,
instead of just using the DiveObjectHelper to do just that. And once I had
converted that I once again realized just how long and hard to understand
that function was getting and decided to break out some of the more
complex parts into their own helper functions.
But of course all this didn't happen in this logical, structured, ordered
way. Instead I did all of these things at the same time, testing,
rearranging, etc.
So in the end I went with one BIG commit that does all of this in one fell
swoop.
This adds four helper functions to deal with start time/date, duration,
location and gps coordinates, and depth of the dive.
To avoid mistakes when dealing with the GPS coordinates, there's another
helper to encapsulate the creation of the dive site and we switched to a
current GPS location.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-15 13:01:14 +00:00
}
2015-07-17 15:28:01 +00:00
2020-12-15 15:10:08 +00:00
bool QMLManager : : checkDate ( struct dive * d , QString date )
QML UI: rewrite the commitChanges function
I couldn't figure out how to break this down into small, useful commits.
Part of the problem is that I kept going while working on this and as you
can see from looking at the commit, diff tries so hard to find small code
fragments that moved around, that the diff overall becomes quite
unreadable and it seemed impossible to recreate the sequence of steps
after the fact.
It all started with adding the parsing for the GPS coordinates. But while
testing that code I found several issues with the rest of the function.
Most importantly it seemed ridiculous that we carefully tried to match the
texts that the DiveObjectHelper would create for the various fields,
instead of just using the DiveObjectHelper to do just that. And once I had
converted that I once again realized just how long and hard to understand
that function was getting and decided to break out some of the more
complex parts into their own helper functions.
But of course all this didn't happen in this logical, structured, ordered
way. Instead I did all of these things at the same time, testing,
rearranging, etc.
So in the end I went with one BIG commit that does all of this in one fell
swoop.
This adds four helper functions to deal with start time/date, duration,
location and gps coordinates, and depth of the dive.
To avoid mistakes when dealing with the GPS coordinates, there's another
helper to encapsulate the creation of the dive site and we switched to a
current GPS location.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-15 13:01:14 +00:00
{
2020-12-15 15:10:08 +00:00
QString oldDate = formatDiveDateTime ( d ) ;
QML UI: rewrite the commitChanges function
I couldn't figure out how to break this down into small, useful commits.
Part of the problem is that I kept going while working on this and as you
can see from looking at the commit, diff tries so hard to find small code
fragments that moved around, that the diff overall becomes quite
unreadable and it seemed impossible to recreate the sequence of steps
after the fact.
It all started with adding the parsing for the GPS coordinates. But while
testing that code I found several issues with the rest of the function.
Most importantly it seemed ridiculous that we carefully tried to match the
texts that the DiveObjectHelper would create for the various fields,
instead of just using the DiveObjectHelper to do just that. And once I had
converted that I once again realized just how long and hard to understand
that function was getting and decided to break out some of the more
complex parts into their own helper functions.
But of course all this didn't happen in this logical, structured, ordered
way. Instead I did all of these things at the same time, testing,
rearranging, etc.
So in the end I went with one BIG commit that does all of this in one fell
swoop.
This adds four helper functions to deal with start time/date, duration,
location and gps coordinates, and depth of the dive.
To avoid mistakes when dealing with the GPS coordinates, there's another
helper to encapsulate the creation of the dive site and we switched to a
current GPS location.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-15 13:01:14 +00:00
if ( date ! = oldDate ) {
2016-01-06 06:53:32 +00:00
QDateTime newDate ;
// what a pain - Qt will not parse dates if the day of the week is incorrect
// so if the user changed the date but didn't update the day of the week (most likely behavior, actually),
// we need to make sure we don't try to parse that
2017-12-17 08:02:48 +00:00
QString format ( QString ( prefs . date_format_short ) + QChar ( ' ' ) + prefs . time_format ) ;
2016-01-26 15:02:42 +00:00
if ( format . contains ( QLatin1String ( " ddd " ) ) | | format . contains ( QLatin1String ( " dddd " ) ) ) {
QString dateFormatToDrop = format . contains ( QLatin1String ( " ddd " ) ) ? QStringLiteral ( " ddd " ) : QStringLiteral ( " dddd " ) ;
2016-01-06 06:53:32 +00:00
QDateTime ts ;
QLocale loc = getLocale ( ) ;
ts . setMSecsSinceEpoch ( d - > when * 1000L ) ;
QString drop = loc . toString ( ts . toUTC ( ) , dateFormatToDrop ) ;
format . replace ( dateFormatToDrop , " " ) ;
date . replace ( drop , " " ) ;
}
2017-04-16 01:51:03 +00:00
// set date from string and make sure it's treated as UTC (like all our time stamps)
2016-01-06 06:53:32 +00:00
newDate = QDateTime : : fromString ( date , format ) ;
2017-04-16 01:51:03 +00:00
newDate . setTimeSpec ( Qt : : UTC ) ;
2016-03-11 02:36:46 +00:00
if ( ! newDate . isValid ( ) ) {
2017-06-18 06:23:41 +00:00
appendTextToLog ( " unable to parse date " + date + " with the given format " + format ) ;
2016-03-11 02:36:46 +00:00
QRegularExpression isoDate ( " \\ d+- \\ d+- \\ d+[^ \\ d]+ \\ d+: \\ d+ " ) ;
if ( date . contains ( isoDate ) ) {
newDate = QDateTime : : fromString ( date , " yyyy-M-d h:m:s " ) ;
if ( newDate . isValid ( ) )
goto parsed ;
newDate = QDateTime : : fromString ( date , " yy-M-d h:m:s " ) ;
if ( newDate . isValid ( ) )
goto parsed ;
}
QRegularExpression isoDateNoSecs ( " \\ d+- \\ d+- \\ d+[^ \\ d]+ \\ d+ " ) ;
if ( date . contains ( isoDateNoSecs ) ) {
newDate = QDateTime : : fromString ( date , " yyyy-M-d h:m " ) ;
if ( newDate . isValid ( ) )
goto parsed ;
newDate = QDateTime : : fromString ( date , " yy-M-d h:m " ) ;
if ( newDate . isValid ( ) )
goto parsed ;
}
QRegularExpression usDate ( " \\ d+/ \\ d+/ \\ d+[^ \\ d]+ \\ d+: \\ d+: \\ d+ " ) ;
if ( date . contains ( usDate ) ) {
newDate = QDateTime : : fromString ( date , " M/d/yyyy h:m:s " ) ;
if ( newDate . isValid ( ) )
goto parsed ;
newDate = QDateTime : : fromString ( date , " M/d/yy h:m:s " ) ;
if ( newDate . isValid ( ) )
goto parsed ;
newDate = QDateTime : : fromString ( date . toLower ( ) , " M/d/yyyy h:m:sap " ) ;
if ( newDate . isValid ( ) )
goto parsed ;
newDate = QDateTime : : fromString ( date . toLower ( ) , " M/d/yy h:m:sap " ) ;
if ( newDate . isValid ( ) )
goto parsed ;
}
QRegularExpression usDateNoSecs ( " \\ d+/ \\ d+/ \\ d+[^ \\ d]+ \\ d+: \\ d+ " ) ;
if ( date . contains ( usDateNoSecs ) ) {
newDate = QDateTime : : fromString ( date , " M/d/yyyy h:m " ) ;
if ( newDate . isValid ( ) )
goto parsed ;
newDate = QDateTime : : fromString ( date , " M/d/yy h:m " ) ;
if ( newDate . isValid ( ) )
goto parsed ;
newDate = QDateTime : : fromString ( date . toLower ( ) , " M/d/yyyy h:map " ) ;
if ( newDate . isValid ( ) )
goto parsed ;
newDate = QDateTime : : fromString ( date . toLower ( ) , " M/d/yy h:map " ) ;
if ( newDate . isValid ( ) )
goto parsed ;
}
QRegularExpression leDate ( " \\ d+ \\ . \\ d+ \\ . \\ d+[^ \\ d]+ \\ d+: \\ d+: \\ d+ " ) ;
if ( date . contains ( leDate ) ) {
newDate = QDateTime : : fromString ( date , " d.M.yyyy h:m:s " ) ;
if ( newDate . isValid ( ) )
goto parsed ;
newDate = QDateTime : : fromString ( date , " d.M.yy h:m:s " ) ;
if ( newDate . isValid ( ) )
goto parsed ;
}
QRegularExpression leDateNoSecs ( " \\ d+ \\ . \\ d+ \\ . \\ d+[^ \\ d]+ \\ d+: \\ d+ " ) ;
if ( date . contains ( leDateNoSecs ) ) {
newDate = QDateTime : : fromString ( date , " d.M.yyyy h:m " ) ;
if ( newDate . isValid ( ) )
goto parsed ;
newDate = QDateTime : : fromString ( date , " d.M.yy h:m " ) ;
if ( newDate . isValid ( ) )
goto parsed ;
}
2022-03-17 21:37:20 +00:00
// if everything else failed - maybe the user is entering dives where they
// don't recall the time? So let's try date only patterns...
QRegularExpression usDateOnly ( " \\ d+/ \\ d+/ \\ d+ " ) ;
if ( date . contains ( usDateOnly ) ) {
newDate = QDateTime : : fromString ( date , " M/d/yy " ) ;
if ( newDate . isValid ( ) )
goto parsed ;
newDate = QDateTime : : fromString ( date , " M/d/yyyy " ) ;
if ( newDate . isValid ( ) )
goto parsed ;
}
QRegularExpression leDateOnly ( " \\ d+ \\ . \\ d+ \\ . \\ d+ " ) ;
if ( date . contains ( usDateOnly ) ) {
newDate = QDateTime : : fromString ( date , " d.M.yy " ) ;
if ( newDate . isValid ( ) )
goto parsed ;
newDate = QDateTime : : fromString ( date , " d.M.yyyy " ) ;
if ( newDate . isValid ( ) )
goto parsed ;
}
QRegularExpression isoDateOnly ( " \\ d+- \\ d+- \\ d+ " ) ;
if ( date . contains ( usDateOnly ) ) {
newDate = QDateTime : : fromString ( date , " yy-M-d " ) ;
if ( newDate . isValid ( ) )
goto parsed ;
newDate = QDateTime : : fromString ( date , " yyyy-M-d " ) ;
if ( newDate . isValid ( ) )
goto parsed ;
}
2016-03-11 02:36:46 +00:00
}
parsed :
2016-01-27 20:07:10 +00:00
if ( newDate . isValid ( ) ) {
// stupid Qt... two digit years are always 19xx - WTF???
// so if adding a hundred years gets you into something before a year from now...
// add a hundred years.
if ( newDate . addYears ( 100 ) < QDateTime : : currentDateTime ( ) . addYears ( 1 ) )
newDate = newDate . addYears ( 100 ) ;
2020-05-22 16:53:25 +00:00
d - > dc . when = d - > when = dateTimeToTimestamp ( newDate ) ;
QML UI: rewrite the commitChanges function
I couldn't figure out how to break this down into small, useful commits.
Part of the problem is that I kept going while working on this and as you
can see from looking at the commit, diff tries so hard to find small code
fragments that moved around, that the diff overall becomes quite
unreadable and it seemed impossible to recreate the sequence of steps
after the fact.
It all started with adding the parsing for the GPS coordinates. But while
testing that code I found several issues with the rest of the function.
Most importantly it seemed ridiculous that we carefully tried to match the
texts that the DiveObjectHelper would create for the various fields,
instead of just using the DiveObjectHelper to do just that. And once I had
converted that I once again realized just how long and hard to understand
that function was getting and decided to break out some of the more
complex parts into their own helper functions.
But of course all this didn't happen in this logical, structured, ordered
way. Instead I did all of these things at the same time, testing,
rearranging, etc.
So in the end I went with one BIG commit that does all of this in one fell
swoop.
This adds four helper functions to deal with start time/date, duration,
location and gps coordinates, and depth of the dive.
To avoid mistakes when dealing with the GPS coordinates, there's another
helper to encapsulate the creation of the dive site and we switched to a
current GPS location.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-15 13:01:14 +00:00
return true ;
2016-01-27 20:07:10 +00:00
}
2017-06-18 06:23:41 +00:00
appendTextToLog ( " none of our parsing attempts worked for the date string " ) ;
2016-01-06 06:53:32 +00:00
}
QML UI: rewrite the commitChanges function
I couldn't figure out how to break this down into small, useful commits.
Part of the problem is that I kept going while working on this and as you
can see from looking at the commit, diff tries so hard to find small code
fragments that moved around, that the diff overall becomes quite
unreadable and it seemed impossible to recreate the sequence of steps
after the fact.
It all started with adding the parsing for the GPS coordinates. But while
testing that code I found several issues with the rest of the function.
Most importantly it seemed ridiculous that we carefully tried to match the
texts that the DiveObjectHelper would create for the various fields,
instead of just using the DiveObjectHelper to do just that. And once I had
converted that I once again realized just how long and hard to understand
that function was getting and decided to break out some of the more
complex parts into their own helper functions.
But of course all this didn't happen in this logical, structured, ordered
way. Instead I did all of these things at the same time, testing,
rearranging, etc.
So in the end I went with one BIG commit that does all of this in one fell
swoop.
This adds four helper functions to deal with start time/date, duration,
location and gps coordinates, and depth of the dive.
To avoid mistakes when dealing with the GPS coordinates, there's another
helper to encapsulate the creation of the dive site and we switched to a
current GPS location.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-15 13:01:14 +00:00
return false ;
}
2016-04-14 19:39:32 +00:00
2020-12-15 15:10:08 +00:00
bool QMLManager : : checkLocation ( DiveSiteChange & res , struct dive * d , QString location , QString gps )
QML UI: rewrite the commitChanges function
I couldn't figure out how to break this down into small, useful commits.
Part of the problem is that I kept going while working on this and as you
can see from looking at the commit, diff tries so hard to find small code
fragments that moved around, that the diff overall becomes quite
unreadable and it seemed impossible to recreate the sequence of steps
after the fact.
It all started with adding the parsing for the GPS coordinates. But while
testing that code I found several issues with the rest of the function.
Most importantly it seemed ridiculous that we carefully tried to match the
texts that the DiveObjectHelper would create for the various fields,
instead of just using the DiveObjectHelper to do just that. And once I had
converted that I once again realized just how long and hard to understand
that function was getting and decided to break out some of the more
complex parts into their own helper functions.
But of course all this didn't happen in this logical, structured, ordered
way. Instead I did all of these things at the same time, testing,
rearranging, etc.
So in the end I went with one BIG commit that does all of this in one fell
swoop.
This adds four helper functions to deal with start time/date, duration,
location and gps coordinates, and depth of the dive.
To avoid mistakes when dealing with the GPS coordinates, there's another
helper to encapsulate the creation of the dive site and we switched to a
current GPS location.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-15 13:01:14 +00:00
{
2017-12-12 13:48:41 +00:00
struct dive_site * ds = get_dive_site_for_dive ( d ) ;
2020-04-04 20:13:18 +00:00
bool changed = false ;
2020-12-15 15:10:08 +00:00
QString oldLocation = get_dive_location ( d ) ;
if ( oldLocation ! = location ) {
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
ds = get_dive_site_by_name ( qPrintable ( location ) , divelog . sites ) ;
2020-01-10 00:25:37 +00:00
if ( ! ds & & ! location . isEmpty ( ) ) {
2024-05-04 11:39:04 +00:00
res . createdDs = std : : make_unique < dive_site > ( qPrintable ( location ) ) ;
2020-01-10 00:25:37 +00:00
res . changed = true ;
ds = res . createdDs . get ( ) ;
}
2020-04-04 14:35:24 +00:00
d - > dive_site = ds ;
2020-04-04 20:13:18 +00:00
changed = true ;
2015-12-27 06:57:47 +00:00
}
QML UI: rewrite the commitChanges function
I couldn't figure out how to break this down into small, useful commits.
Part of the problem is that I kept going while working on this and as you
can see from looking at the commit, diff tries so hard to find small code
fragments that moved around, that the diff overall becomes quite
unreadable and it seemed impossible to recreate the sequence of steps
after the fact.
It all started with adding the parsing for the GPS coordinates. But while
testing that code I found several issues with the rest of the function.
Most importantly it seemed ridiculous that we carefully tried to match the
texts that the DiveObjectHelper would create for the various fields,
instead of just using the DiveObjectHelper to do just that. And once I had
converted that I once again realized just how long and hard to understand
that function was getting and decided to break out some of the more
complex parts into their own helper functions.
But of course all this didn't happen in this logical, structured, ordered
way. Instead I did all of these things at the same time, testing,
rearranging, etc.
So in the end I went with one BIG commit that does all of this in one fell
swoop.
This adds four helper functions to deal with start time/date, duration,
location and gps coordinates, and depth of the dive.
To avoid mistakes when dealing with the GPS coordinates, there's another
helper to encapsulate the creation of the dive site and we switched to a
current GPS location.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-15 13:01:14 +00:00
// now make sure that the GPS coordinates match - if the user changed the name but not
// the GPS coordinates, this still does the right thing as the now new dive site will
// have no coordinates, so the coordinates from the edit screen will get added
2020-12-15 15:10:08 +00:00
if ( formatDiveGPS ( d ) ! = gps ) {
QML UI: rewrite the commitChanges function
I couldn't figure out how to break this down into small, useful commits.
Part of the problem is that I kept going while working on this and as you
can see from looking at the commit, diff tries so hard to find small code
fragments that moved around, that the diff overall becomes quite
unreadable and it seemed impossible to recreate the sequence of steps
after the fact.
It all started with adding the parsing for the GPS coordinates. But while
testing that code I found several issues with the rest of the function.
Most importantly it seemed ridiculous that we carefully tried to match the
texts that the DiveObjectHelper would create for the various fields,
instead of just using the DiveObjectHelper to do just that. And once I had
converted that I once again realized just how long and hard to understand
that function was getting and decided to break out some of the more
complex parts into their own helper functions.
But of course all this didn't happen in this logical, structured, ordered
way. Instead I did all of these things at the same time, testing,
rearranging, etc.
So in the end I went with one BIG commit that does all of this in one fell
swoop.
This adds four helper functions to deal with start time/date, duration,
location and gps coordinates, and depth of the dive.
To avoid mistakes when dealing with the GPS coordinates, there's another
helper to encapsulate the creation of the dive site and we switched to a
current GPS location.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-15 13:01:14 +00:00
double lat , lon ;
if ( parseGpsText ( gps , & lat , & lon ) ) {
2021-11-13 20:13:15 +00:00
if ( location . isEmpty ( ) )
location = gps ;
if ( verbose )
2024-03-25 20:17:29 +00:00
report_info ( " parsed GPS %s, using it for dive site %s " , qPrintable ( gps ) , qPrintable ( location ) ) ;
QML UI: rewrite the commitChanges function
I couldn't figure out how to break this down into small, useful commits.
Part of the problem is that I kept going while working on this and as you
can see from looking at the commit, diff tries so hard to find small code
fragments that moved around, that the diff overall becomes quite
unreadable and it seemed impossible to recreate the sequence of steps
after the fact.
It all started with adding the parsing for the GPS coordinates. But while
testing that code I found several issues with the rest of the function.
Most importantly it seemed ridiculous that we carefully tried to match the
texts that the DiveObjectHelper would create for the various fields,
instead of just using the DiveObjectHelper to do just that. And once I had
converted that I once again realized just how long and hard to understand
that function was getting and decided to break out some of the more
complex parts into their own helper functions.
But of course all this didn't happen in this logical, structured, ordered
way. Instead I did all of these things at the same time, testing,
rearranging, etc.
So in the end I went with one BIG commit that does all of this in one fell
swoop.
This adds four helper functions to deal with start time/date, duration,
location and gps coordinates, and depth of the dive.
To avoid mistakes when dealing with the GPS coordinates, there's another
helper to encapsulate the creation of the dive site and we switched to a
current GPS location.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-15 13:01:14 +00:00
// there are valid GPS coordinates - just use them
2020-04-04 20:15:52 +00:00
setupDivesite ( res , d , ds , lat , lon , qPrintable ( location ) ) ;
2016-01-27 14:29:14 +00:00
} else {
QML UI: rewrite the commitChanges function
I couldn't figure out how to break this down into small, useful commits.
Part of the problem is that I kept going while working on this and as you
can see from looking at the commit, diff tries so hard to find small code
fragments that moved around, that the diff overall becomes quite
unreadable and it seemed impossible to recreate the sequence of steps
after the fact.
It all started with adding the parsing for the GPS coordinates. But while
testing that code I found several issues with the rest of the function.
Most importantly it seemed ridiculous that we carefully tried to match the
texts that the DiveObjectHelper would create for the various fields,
instead of just using the DiveObjectHelper to do just that. And once I had
converted that I once again realized just how long and hard to understand
that function was getting and decided to break out some of the more
complex parts into their own helper functions.
But of course all this didn't happen in this logical, structured, ordered
way. Instead I did all of these things at the same time, testing,
rearranging, etc.
So in the end I went with one BIG commit that does all of this in one fell
swoop.
This adds four helper functions to deal with start time/date, duration,
location and gps coordinates, and depth of the dive.
To avoid mistakes when dealing with the GPS coordinates, there's another
helper to encapsulate the creation of the dive site and we switched to a
current GPS location.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-15 13:01:14 +00:00
// just something we can't parse, so tell the user
appendTextToLog ( QString ( " wasn't able to parse gps string '%1' " ) . arg ( gps ) ) ;
2016-01-02 01:23:29 +00:00
}
}
2020-04-04 20:13:18 +00:00
return changed | res . changed ;
QML UI: rewrite the commitChanges function
I couldn't figure out how to break this down into small, useful commits.
Part of the problem is that I kept going while working on this and as you
can see from looking at the commit, diff tries so hard to find small code
fragments that moved around, that the diff overall becomes quite
unreadable and it seemed impossible to recreate the sequence of steps
after the fact.
It all started with adding the parsing for the GPS coordinates. But while
testing that code I found several issues with the rest of the function.
Most importantly it seemed ridiculous that we carefully tried to match the
texts that the DiveObjectHelper would create for the various fields,
instead of just using the DiveObjectHelper to do just that. And once I had
converted that I once again realized just how long and hard to understand
that function was getting and decided to break out some of the more
complex parts into their own helper functions.
But of course all this didn't happen in this logical, structured, ordered
way. Instead I did all of these things at the same time, testing,
rearranging, etc.
So in the end I went with one BIG commit that does all of this in one fell
swoop.
This adds four helper functions to deal with start time/date, duration,
location and gps coordinates, and depth of the dive.
To avoid mistakes when dealing with the GPS coordinates, there's another
helper to encapsulate the creation of the dive site and we switched to a
current GPS location.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-15 13:01:14 +00:00
}
2020-12-15 15:10:08 +00:00
bool QMLManager : : checkDuration ( struct dive * d , QString duration )
QML UI: rewrite the commitChanges function
I couldn't figure out how to break this down into small, useful commits.
Part of the problem is that I kept going while working on this and as you
can see from looking at the commit, diff tries so hard to find small code
fragments that moved around, that the diff overall becomes quite
unreadable and it seemed impossible to recreate the sequence of steps
after the fact.
It all started with adding the parsing for the GPS coordinates. But while
testing that code I found several issues with the rest of the function.
Most importantly it seemed ridiculous that we carefully tried to match the
texts that the DiveObjectHelper would create for the various fields,
instead of just using the DiveObjectHelper to do just that. And once I had
converted that I once again realized just how long and hard to understand
that function was getting and decided to break out some of the more
complex parts into their own helper functions.
But of course all this didn't happen in this logical, structured, ordered
way. Instead I did all of these things at the same time, testing,
rearranging, etc.
So in the end I went with one BIG commit that does all of this in one fell
swoop.
This adds four helper functions to deal with start time/date, duration,
location and gps coordinates, and depth of the dive.
To avoid mistakes when dealing with the GPS coordinates, there's another
helper to encapsulate the creation of the dive site and we switched to a
current GPS location.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-15 13:01:14 +00:00
{
2020-12-15 15:10:08 +00:00
if ( formatDiveDuration ( d ) ! = duration ) {
2016-01-01 08:23:15 +00:00
int h = 0 , m = 0 , s = 0 ;
2021-10-26 00:02:35 +00:00
QRegularExpression r1 ( QStringLiteral ( " ( \\ d*) \ \ s * % 1 [ \ \ s , : ] * ( \ \ d * ) \ \ s * % 2 [ \ \ s , : ] * ( \ \ d * ) \ \ s * % 3 " ).arg(tr( " h " )).arg(tr( " min " )).arg(tr( " sec " )), QRegularExpression::CaseInsensitiveOption) ;
QRegularExpressionMatch m1 = r1 . match ( duration ) ;
QRegularExpression r2 ( QStringLiteral ( " ( \\ d*) \ \ s * % 1 [ \ \ s , : ] * ( \ \ d * ) \ \ s * % 2 " ).arg(tr( " h " )).arg(tr( " min " )), QRegularExpression::CaseInsensitiveOption) ;
QRegularExpressionMatch m2 = r2 . match ( duration ) ;
QRegularExpression r3 ( QStringLiteral ( " ( \\ d*) \ \ s * % 1 " ).arg(tr( " min " )), QRegularExpression::CaseInsensitiveOption) ;
QRegularExpressionMatch m3 = r3 . match ( duration ) ;
QRegularExpression r4 ( QStringLiteral ( " ( \\ d*) : ( \ \ d * ) : ( \ \ d * ) " )) ;
QRegularExpressionMatch m4 = r4 . match ( duration ) ;
QRegularExpression r5 ( QStringLiteral ( " ( \\ d*) : ( \ \ d * ) " )) ;
QRegularExpressionMatch m5 = r5 . match ( duration ) ;
QRegularExpression r6 ( QStringLiteral ( " ( \\ d*) " )) ;
QRegularExpressionMatch m6 = r6 . match ( duration ) ;
if ( m1 . hasMatch ( ) ) {
h = m1 . captured ( 1 ) . toInt ( ) ;
m = m1 . captured ( 2 ) . toInt ( ) ;
s = m1 . captured ( 3 ) . toInt ( ) ;
} else if ( m2 . hasMatch ( ) ) {
h = m2 . captured ( 1 ) . toInt ( ) ;
m = m2 . captured ( 2 ) . toInt ( ) ;
} else if ( m3 . hasMatch ( ) ) {
m = m3 . captured ( 1 ) . toInt ( ) ;
} else if ( m4 . hasMatch ( ) ) {
h = m4 . captured ( 1 ) . toInt ( ) ;
m = m4 . captured ( 2 ) . toInt ( ) ;
s = m4 . captured ( 3 ) . toInt ( ) ;
} else if ( m5 . hasMatch ( ) ) {
h = m5 . captured ( 1 ) . toInt ( ) ;
m = m5 . captured ( 2 ) . toInt ( ) ;
} else if ( m6 . hasMatch ( ) ) {
m = m6 . captured ( 1 ) . toInt ( ) ;
2016-01-01 08:23:15 +00:00
}
2016-01-11 06:08:42 +00:00
d - > dc . duration . seconds = d - > duration . seconds = h * 3600 + m * 60 + s ;
2024-05-23 04:06:23 +00:00
if ( is_dc_manually_added_dive ( & d - > dc ) )
2018-06-20 00:20:32 +00:00
free_samples ( & d - > dc ) ;
else
2017-06-18 06:23:41 +00:00
appendTextToLog ( " Cannot change the duration on a dive that wasn't manually added " ) ;
QML UI: rewrite the commitChanges function
I couldn't figure out how to break this down into small, useful commits.
Part of the problem is that I kept going while working on this and as you
can see from looking at the commit, diff tries so hard to find small code
fragments that moved around, that the diff overall becomes quite
unreadable and it seemed impossible to recreate the sequence of steps
after the fact.
It all started with adding the parsing for the GPS coordinates. But while
testing that code I found several issues with the rest of the function.
Most importantly it seemed ridiculous that we carefully tried to match the
texts that the DiveObjectHelper would create for the various fields,
instead of just using the DiveObjectHelper to do just that. And once I had
converted that I once again realized just how long and hard to understand
that function was getting and decided to break out some of the more
complex parts into their own helper functions.
But of course all this didn't happen in this logical, structured, ordered
way. Instead I did all of these things at the same time, testing,
rearranging, etc.
So in the end I went with one BIG commit that does all of this in one fell
swoop.
This adds four helper functions to deal with start time/date, duration,
location and gps coordinates, and depth of the dive.
To avoid mistakes when dealing with the GPS coordinates, there's another
helper to encapsulate the creation of the dive site and we switched to a
current GPS location.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-15 13:01:14 +00:00
return true ;
2016-01-01 08:23:15 +00:00
}
QML UI: rewrite the commitChanges function
I couldn't figure out how to break this down into small, useful commits.
Part of the problem is that I kept going while working on this and as you
can see from looking at the commit, diff tries so hard to find small code
fragments that moved around, that the diff overall becomes quite
unreadable and it seemed impossible to recreate the sequence of steps
after the fact.
It all started with adding the parsing for the GPS coordinates. But while
testing that code I found several issues with the rest of the function.
Most importantly it seemed ridiculous that we carefully tried to match the
texts that the DiveObjectHelper would create for the various fields,
instead of just using the DiveObjectHelper to do just that. And once I had
converted that I once again realized just how long and hard to understand
that function was getting and decided to break out some of the more
complex parts into their own helper functions.
But of course all this didn't happen in this logical, structured, ordered
way. Instead I did all of these things at the same time, testing,
rearranging, etc.
So in the end I went with one BIG commit that does all of this in one fell
swoop.
This adds four helper functions to deal with start time/date, duration,
location and gps coordinates, and depth of the dive.
To avoid mistakes when dealing with the GPS coordinates, there's another
helper to encapsulate the creation of the dive site and we switched to a
current GPS location.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-15 13:01:14 +00:00
return false ;
}
2020-12-15 15:10:08 +00:00
bool QMLManager : : checkDepth ( dive * d , QString depth )
QML UI: rewrite the commitChanges function
I couldn't figure out how to break this down into small, useful commits.
Part of the problem is that I kept going while working on this and as you
can see from looking at the commit, diff tries so hard to find small code
fragments that moved around, that the diff overall becomes quite
unreadable and it seemed impossible to recreate the sequence of steps
after the fact.
It all started with adding the parsing for the GPS coordinates. But while
testing that code I found several issues with the rest of the function.
Most importantly it seemed ridiculous that we carefully tried to match the
texts that the DiveObjectHelper would create for the various fields,
instead of just using the DiveObjectHelper to do just that. And once I had
converted that I once again realized just how long and hard to understand
that function was getting and decided to break out some of the more
complex parts into their own helper functions.
But of course all this didn't happen in this logical, structured, ordered
way. Instead I did all of these things at the same time, testing,
rearranging, etc.
So in the end I went with one BIG commit that does all of this in one fell
swoop.
This adds four helper functions to deal with start time/date, duration,
location and gps coordinates, and depth of the dive.
To avoid mistakes when dealing with the GPS coordinates, there's another
helper to encapsulate the creation of the dive site and we switched to a
current GPS location.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-15 13:01:14 +00:00
{
2020-12-15 15:10:08 +00:00
if ( get_depth_string ( d - > dc . maxdepth . mm , true , true ) ! = depth ) {
2016-02-21 06:11:03 +00:00
int depthValue = parseLengthToMm ( depth ) ;
// the QML code should stop negative depth, but massively huge depth can make
// the profile extremely slow or even run out of memory and crash, so keep
// the depth <= 500m
if ( 0 < = depthValue & & depthValue < = 500000 ) {
d - > maxdepth . mm = depthValue ;
2024-05-23 04:06:23 +00:00
if ( is_dc_manually_added_dive ( & d - > dc ) ) {
2016-02-21 06:11:03 +00:00
d - > dc . maxdepth . mm = d - > maxdepth . mm ;
2018-06-20 00:20:32 +00:00
free_samples ( & d - > dc ) ;
2016-02-21 06:11:03 +00:00
}
QML UI: rewrite the commitChanges function
I couldn't figure out how to break this down into small, useful commits.
Part of the problem is that I kept going while working on this and as you
can see from looking at the commit, diff tries so hard to find small code
fragments that moved around, that the diff overall becomes quite
unreadable and it seemed impossible to recreate the sequence of steps
after the fact.
It all started with adding the parsing for the GPS coordinates. But while
testing that code I found several issues with the rest of the function.
Most importantly it seemed ridiculous that we carefully tried to match the
texts that the DiveObjectHelper would create for the various fields,
instead of just using the DiveObjectHelper to do just that. And once I had
converted that I once again realized just how long and hard to understand
that function was getting and decided to break out some of the more
complex parts into their own helper functions.
But of course all this didn't happen in this logical, structured, ordered
way. Instead I did all of these things at the same time, testing,
rearranging, etc.
So in the end I went with one BIG commit that does all of this in one fell
swoop.
This adds four helper functions to deal with start time/date, duration,
location and gps coordinates, and depth of the dive.
To avoid mistakes when dealing with the GPS coordinates, there's another
helper to encapsulate the creation of the dive site and we switched to a
current GPS location.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-15 13:01:14 +00:00
return true ;
2016-02-15 15:51:23 +00:00
}
2016-01-01 08:32:30 +00:00
}
QML UI: rewrite the commitChanges function
I couldn't figure out how to break this down into small, useful commits.
Part of the problem is that I kept going while working on this and as you
can see from looking at the commit, diff tries so hard to find small code
fragments that moved around, that the diff overall becomes quite
unreadable and it seemed impossible to recreate the sequence of steps
after the fact.
It all started with adding the parsing for the GPS coordinates. But while
testing that code I found several issues with the rest of the function.
Most importantly it seemed ridiculous that we carefully tried to match the
texts that the DiveObjectHelper would create for the various fields,
instead of just using the DiveObjectHelper to do just that. And once I had
converted that I once again realized just how long and hard to understand
that function was getting and decided to break out some of the more
complex parts into their own helper functions.
But of course all this didn't happen in this logical, structured, ordered
way. Instead I did all of these things at the same time, testing,
rearranging, etc.
So in the end I went with one BIG commit that does all of this in one fell
swoop.
This adds four helper functions to deal with start time/date, duration,
location and gps coordinates, and depth of the dive.
To avoid mistakes when dealing with the GPS coordinates, there's another
helper to encapsulate the creation of the dive site and we switched to a
current GPS location.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-15 13:01:14 +00:00
return false ;
}
// update the dive and return the notes field, stripped of the HTML junk
2019-10-21 00:13:59 +00:00
void QMLManager : : commitChanges ( QString diveId , QString number , QString date , QString location , QString gps , QString duration , QString depth ,
2022-02-12 13:03:18 +00:00
QString airtemp , QString watertemp , QString suit , QString buddy , QString diveGuide , QString tags , QString weight , QString notes ,
2020-01-27 00:51:01 +00:00
QStringList startpressure , QStringList endpressure , QStringList gasmix , QStringList usedCylinder , int rating , int visibility , QString state )
QML UI: rewrite the commitChanges function
I couldn't figure out how to break this down into small, useful commits.
Part of the problem is that I kept going while working on this and as you
can see from looking at the commit, diff tries so hard to find small code
fragments that moved around, that the diff overall becomes quite
unreadable and it seemed impossible to recreate the sequence of steps
after the fact.
It all started with adding the parsing for the GPS coordinates. But while
testing that code I found several issues with the rest of the function.
Most importantly it seemed ridiculous that we carefully tried to match the
texts that the DiveObjectHelper would create for the various fields,
instead of just using the DiveObjectHelper to do just that. And once I had
converted that I once again realized just how long and hard to understand
that function was getting and decided to break out some of the more
complex parts into their own helper functions.
But of course all this didn't happen in this logical, structured, ordered
way. Instead I did all of these things at the same time, testing,
rearranging, etc.
So in the end I went with one BIG commit that does all of this in one fell
swoop.
This adds four helper functions to deal with start time/date, duration,
location and gps coordinates, and depth of the dive.
To avoid mistakes when dealing with the GPS coordinates, there's another
helper to encapsulate the creation of the dive site and we switched to a
current GPS location.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-15 13:01:14 +00:00
{
2020-01-10 00:25:37 +00:00
struct dive * orig = get_dive_by_uniq_id ( diveId . toInt ( ) ) ;
2017-12-26 21:37:40 +00:00
2020-01-10 00:25:37 +00:00
if ( ! orig ) {
2017-12-26 21:37:40 +00:00
appendTextToLog ( " cannot commit changes: no dive " ) ;
return ;
}
2020-12-30 20:36:16 +00:00
if ( verbose ) {
2024-03-25 20:17:29 +00:00
report_info ( " diveId :'%s' " , qPrintable ( diveId ) ) ;
report_info ( " number :'%s' " , qPrintable ( number ) ) ;
report_info ( " date :'%s' " , qPrintable ( date ) ) ;
report_info ( " location:'%s' " , qPrintable ( location ) ) ;
report_info ( " gps :'%s' " , qPrintable ( gps ) ) ;
report_info ( " duration:'%s' " , qPrintable ( duration ) ) ;
report_info ( " depth :'%s' " , qPrintable ( depth ) ) ;
report_info ( " airtemp :'%s' " , qPrintable ( airtemp ) ) ;
report_info ( " watertmp:'%s' " , qPrintable ( watertemp ) ) ;
report_info ( " suit :'%s' " , qPrintable ( suit ) ) ;
report_info ( " buddy :'%s' " , qPrintable ( buddy ) ) ;
report_info ( " diveGde :'%s' " , qPrintable ( diveGuide ) ) ;
report_info ( " tags :'%s' " , qPrintable ( tags ) ) ;
report_info ( " weight :'%s' " , qPrintable ( weight ) ) ;
report_info ( " state :'%s' " , qPrintable ( state ) ) ;
2020-12-30 20:36:16 +00:00
}
2017-12-26 21:37:40 +00:00
2022-11-06 11:18:27 +00:00
OwningDivePtr d_ptr ( alloc_dive ( ) ) ; // Automatically delete dive if we exit early!
2020-01-10 00:25:37 +00:00
dive * d = d_ptr . get ( ) ;
copy_dive ( orig , d ) ;
QML UI: rewrite the commitChanges function
I couldn't figure out how to break this down into small, useful commits.
Part of the problem is that I kept going while working on this and as you
can see from looking at the commit, diff tries so hard to find small code
fragments that moved around, that the diff overall becomes quite
unreadable and it seemed impossible to recreate the sequence of steps
after the fact.
It all started with adding the parsing for the GPS coordinates. But while
testing that code I found several issues with the rest of the function.
Most importantly it seemed ridiculous that we carefully tried to match the
texts that the DiveObjectHelper would create for the various fields,
instead of just using the DiveObjectHelper to do just that. And once I had
converted that I once again realized just how long and hard to understand
that function was getting and decided to break out some of the more
complex parts into their own helper functions.
But of course all this didn't happen in this logical, structured, ordered
way. Instead I did all of these things at the same time, testing,
rearranging, etc.
So in the end I went with one BIG commit that does all of this in one fell
swoop.
This adds four helper functions to deal with start time/date, duration,
location and gps coordinates, and depth of the dive.
To avoid mistakes when dealing with the GPS coordinates, there's another
helper to encapsulate the creation of the dive site and we switched to a
current GPS location.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-15 13:01:14 +00:00
// notes comes back as rich text - let's convert this into plain text
QTextDocument doc ;
doc . setHtml ( notes ) ;
notes = doc . toPlainText ( ) ;
bool diveChanged = false ;
2020-12-15 15:10:08 +00:00
diveChanged = checkDate ( d , date ) ;
QML UI: rewrite the commitChanges function
I couldn't figure out how to break this down into small, useful commits.
Part of the problem is that I kept going while working on this and as you
can see from looking at the commit, diff tries so hard to find small code
fragments that moved around, that the diff overall becomes quite
unreadable and it seemed impossible to recreate the sequence of steps
after the fact.
It all started with adding the parsing for the GPS coordinates. But while
testing that code I found several issues with the rest of the function.
Most importantly it seemed ridiculous that we carefully tried to match the
texts that the DiveObjectHelper would create for the various fields,
instead of just using the DiveObjectHelper to do just that. And once I had
converted that I once again realized just how long and hard to understand
that function was getting and decided to break out some of the more
complex parts into their own helper functions.
But of course all this didn't happen in this logical, structured, ordered
way. Instead I did all of these things at the same time, testing,
rearranging, etc.
So in the end I went with one BIG commit that does all of this in one fell
swoop.
This adds four helper functions to deal with start time/date, duration,
location and gps coordinates, and depth of the dive.
To avoid mistakes when dealing with the GPS coordinates, there's another
helper to encapsulate the creation of the dive site and we switched to a
current GPS location.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-15 13:01:14 +00:00
2020-01-10 00:25:37 +00:00
DiveSiteChange dsChange ;
2020-12-15 15:10:08 +00:00
diveChanged | = checkLocation ( dsChange , d , location , gps ) ;
QML UI: rewrite the commitChanges function
I couldn't figure out how to break this down into small, useful commits.
Part of the problem is that I kept going while working on this and as you
can see from looking at the commit, diff tries so hard to find small code
fragments that moved around, that the diff overall becomes quite
unreadable and it seemed impossible to recreate the sequence of steps
after the fact.
It all started with adding the parsing for the GPS coordinates. But while
testing that code I found several issues with the rest of the function.
Most importantly it seemed ridiculous that we carefully tried to match the
texts that the DiveObjectHelper would create for the various fields,
instead of just using the DiveObjectHelper to do just that. And once I had
converted that I once again realized just how long and hard to understand
that function was getting and decided to break out some of the more
complex parts into their own helper functions.
But of course all this didn't happen in this logical, structured, ordered
way. Instead I did all of these things at the same time, testing,
rearranging, etc.
So in the end I went with one BIG commit that does all of this in one fell
swoop.
This adds four helper functions to deal with start time/date, duration,
location and gps coordinates, and depth of the dive.
To avoid mistakes when dealing with the GPS coordinates, there's another
helper to encapsulate the creation of the dive site and we switched to a
current GPS location.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-15 13:01:14 +00:00
2020-12-15 15:10:08 +00:00
diveChanged | = checkDuration ( d , duration ) ;
QML UI: rewrite the commitChanges function
I couldn't figure out how to break this down into small, useful commits.
Part of the problem is that I kept going while working on this and as you
can see from looking at the commit, diff tries so hard to find small code
fragments that moved around, that the diff overall becomes quite
unreadable and it seemed impossible to recreate the sequence of steps
after the fact.
It all started with adding the parsing for the GPS coordinates. But while
testing that code I found several issues with the rest of the function.
Most importantly it seemed ridiculous that we carefully tried to match the
texts that the DiveObjectHelper would create for the various fields,
instead of just using the DiveObjectHelper to do just that. And once I had
converted that I once again realized just how long and hard to understand
that function was getting and decided to break out some of the more
complex parts into their own helper functions.
But of course all this didn't happen in this logical, structured, ordered
way. Instead I did all of these things at the same time, testing,
rearranging, etc.
So in the end I went with one BIG commit that does all of this in one fell
swoop.
This adds four helper functions to deal with start time/date, duration,
location and gps coordinates, and depth of the dive.
To avoid mistakes when dealing with the GPS coordinates, there's another
helper to encapsulate the creation of the dive site and we switched to a
current GPS location.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-15 13:01:14 +00:00
2020-12-15 15:10:08 +00:00
diveChanged | = checkDepth ( d , depth ) ;
QML UI: rewrite the commitChanges function
I couldn't figure out how to break this down into small, useful commits.
Part of the problem is that I kept going while working on this and as you
can see from looking at the commit, diff tries so hard to find small code
fragments that moved around, that the diff overall becomes quite
unreadable and it seemed impossible to recreate the sequence of steps
after the fact.
It all started with adding the parsing for the GPS coordinates. But while
testing that code I found several issues with the rest of the function.
Most importantly it seemed ridiculous that we carefully tried to match the
texts that the DiveObjectHelper would create for the various fields,
instead of just using the DiveObjectHelper to do just that. And once I had
converted that I once again realized just how long and hard to understand
that function was getting and decided to break out some of the more
complex parts into their own helper functions.
But of course all this didn't happen in this logical, structured, ordered
way. Instead I did all of these things at the same time, testing,
rearranging, etc.
So in the end I went with one BIG commit that does all of this in one fell
swoop.
This adds four helper functions to deal with start time/date, duration,
location and gps coordinates, and depth of the dive.
To avoid mistakes when dealing with the GPS coordinates, there's another
helper to encapsulate the creation of the dive site and we switched to a
current GPS location.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-15 13:01:14 +00:00
2020-12-15 15:10:08 +00:00
if ( QString : : number ( d - > number ) ! = number ) {
2019-10-21 00:13:59 +00:00
diveChanged = true ;
d - > number = number . toInt ( ) ;
}
2020-12-15 15:10:08 +00:00
if ( get_temperature_string ( d - > airtemp , true ) ! = airtemp ) {
2016-01-01 01:34:20 +00:00
diveChanged = true ;
d - > airtemp . mkelvin = parseTemperatureToMkelvin ( airtemp ) ;
}
2020-12-15 15:10:08 +00:00
if ( get_temperature_string ( d - > watertemp , true ) ! = watertemp ) {
2016-01-01 01:34:20 +00:00
diveChanged = true ;
d - > watertemp . mkelvin = parseTemperatureToMkelvin ( watertemp ) ;
}
2020-12-15 15:10:08 +00:00
if ( formatSumWeight ( d ) ! = weight ) {
2019-08-10 16:10:02 +00:00
diveChanged = true ;
// not sure what we'd do if there was more than one weight system
// defined - for now just ignore that case
if ( d - > weightsystems . nr = = 0 ) {
2022-01-02 13:24:04 +00:00
weightsystem_t ws = { { parseWeightToGrams ( weight ) } , strdup ( qPrintable ( tr ( " weight " ) ) ) , false } ;
2019-08-11 16:11:06 +00:00
add_to_weightsystem_table ( & d - > weightsystems , 0 , ws ) ; // takes ownership of the string
2019-08-10 16:10:02 +00:00
} else if ( d - > weightsystems . nr = = 1 ) {
2019-06-26 15:21:03 +00:00
d - > weightsystems . weightsystems [ 0 ] . weight . grams = parseWeightToGrams ( weight ) ;
2016-02-06 06:54:47 +00:00
}
}
2020-02-08 00:26:40 +00:00
// start and end pressures
// first, normalize the lists - QML gives us a list with just one empty string if nothing was entered
if ( startpressure = = QStringList ( QString ( ) ) )
startpressure = QStringList ( ) ;
if ( endpressure = = QStringList ( QString ( ) ) )
endpressure = QStringList ( ) ;
2020-12-15 15:10:08 +00:00
if ( formatStartPressure ( d ) ! = startpressure | | formatEndPressure ( d ) ! = endpressure ) {
2016-02-09 16:20:17 +00:00
diveChanged = true ;
2018-08-06 07:34:39 +00:00
for ( int i = 0 , j = 0 ; j < startpressure . length ( ) & & j < endpressure . length ( ) ; i + + ) {
2018-07-29 13:42:56 +00:00
if ( state ! = " add " & & ! is_cylinder_used ( d , i ) )
2018-08-06 07:34:39 +00:00
continue ;
2020-08-15 20:50:01 +00:00
cylinder_t * cyl = get_or_create_cylinder ( d , i ) ;
cyl - > start . mbar = parsePressureToMbar ( startpressure [ j ] ) ;
cyl - > end . mbar = parsePressureToMbar ( endpressure [ j ] ) ;
if ( cyl - > end . mbar > cyl - > start . mbar )
cyl - > end . mbar = cyl - > start . mbar ;
2018-08-06 07:34:39 +00:00
j + + ;
}
2016-02-09 16:20:17 +00:00
}
2016-02-13 17:34:31 +00:00
// gasmix for first cylinder
2020-12-15 15:10:08 +00:00
if ( formatFirstGas ( d ) ! = gasmix ) {
2018-08-06 07:29:00 +00:00
for ( int i = 0 , j = 0 ; j < gasmix . length ( ) ; i + + ) {
2018-07-29 13:42:56 +00:00
if ( state ! = " add " & & ! is_cylinder_used ( d , i ) )
2018-08-06 07:29:00 +00:00
continue ;
int o2 = parseGasMixO2 ( gasmix [ j ] ) ;
int he = parseGasMixHE ( gasmix [ j ] ) ;
// the QML code SHOULD only accept valid gas mixes, but just to make sure
if ( o2 > = 0 & & o2 < = 1000 & &
2020-01-27 00:51:01 +00:00
he > = 0 & & he < = 1000 & &
o2 + he < = 1000 ) {
2018-08-06 07:29:00 +00:00
diveChanged = true ;
2020-02-08 00:26:40 +00:00
get_or_create_cylinder ( d , i ) - > gasmix . o2 . permille = o2 ;
2019-08-04 20:13:49 +00:00
get_cylinder ( d , i ) - > gasmix . he . permille = he ;
2018-08-06 07:29:00 +00:00
}
j + + ;
2016-02-21 06:11:03 +00:00
}
2016-02-13 17:34:31 +00:00
}
2016-08-30 14:24:19 +00:00
// info for first cylinder
2020-12-15 15:10:08 +00:00
if ( formatGetCylinder ( d ) ! = usedCylinder ) {
2016-08-30 14:24:19 +00:00
diveChanged = true ;
2018-07-16 16:01:56 +00:00
int size = 0 , wp = 0 , j = 0 , k = 0 ;
2019-08-04 16:44:57 +00:00
for ( j = 0 ; k < usedCylinder . length ( ) ; j + + ) {
2018-07-29 13:42:56 +00:00
if ( state ! = " add " & & ! is_cylinder_used ( d , j ) )
2018-07-16 16:01:56 +00:00
continue ;
2024-05-03 21:18:45 +00:00
for ( const tank_info & ti : tank_info_table ) {
if ( ti . name = = usedCylinder [ k ] . toStdString ( ) ) {
2020-12-11 21:34:35 +00:00
if ( ti . ml > 0 ) {
size = ti . ml ;
wp = ti . bar * 1000 ;
2018-07-16 16:01:56 +00:00
} else {
2020-12-11 21:34:35 +00:00
size = ( int ) ( cuft_to_l ( ti . cuft ) * 1000 / bar_to_atm ( psi_to_bar ( ti . psi ) ) ) ;
wp = psi_to_mbar ( ti . psi ) ;
2018-07-16 16:01:56 +00:00
}
break ;
2016-08-30 14:24:19 +00:00
}
}
2020-02-08 00:26:40 +00:00
get_or_create_cylinder ( d , j ) - > type . description = copy_qstring ( usedCylinder [ k ] ) ;
2019-08-04 20:13:49 +00:00
get_cylinder ( d , j ) - > type . size . mliter = size ;
get_cylinder ( d , j ) - > type . workingpressure . mbar = wp ;
2018-07-16 16:01:56 +00:00
k + + ;
2016-08-30 14:24:19 +00:00
}
}
2020-12-15 15:10:08 +00:00
if ( d - > suit ! = suit ) {
2015-07-17 15:28:01 +00:00
diveChanged = true ;
free ( d - > suit ) ;
2018-02-28 22:37:09 +00:00
d - > suit = copy_qstring ( suit ) ;
2015-07-17 15:28:01 +00:00
}
2020-12-15 15:10:08 +00:00
if ( d - > buddy ! = buddy ) {
2017-01-29 16:32:13 +00:00
if ( buddy . contains ( " , " ) ) {
2021-10-26 00:02:35 +00:00
buddy = buddy . replace ( QRegularExpression ( " \\ s*, \\ s* " ) , " , " ) ;
2017-01-29 16:32:13 +00:00
}
2017-12-06 17:54:46 +00:00
diveChanged = true ;
free ( d - > buddy ) ;
2018-02-28 22:37:09 +00:00
d - > buddy = copy_qstring ( buddy ) ;
2015-07-17 15:28:01 +00:00
}
2022-02-12 13:03:18 +00:00
if ( d - > diveguide ! = diveGuide ) {
if ( diveGuide . contains ( " , " ) ) {
diveGuide = diveGuide . replace ( QRegularExpression ( " \\ s*, \\ s* " ) , " , " ) ;
2018-11-13 20:28:18 +00:00
}
2015-07-17 15:28:01 +00:00
diveChanged = true ;
2022-02-12 13:03:18 +00:00
free ( d - > diveguide ) ;
d - > diveguide = copy_qstring ( diveGuide ) ;
2015-07-17 15:28:01 +00:00
}
2021-08-16 02:55:17 +00:00
// normalize the tag list we have and the one we get from the UI
// try hard to deal with accidental white space issues
2024-03-26 20:06:13 +00:00
QStringList existingTagList = QString : : fromStdString ( taglist_get_tagstring ( d - > tag_list ) ) . split ( " , " , SKIP_EMPTY ) ;
2021-11-22 01:20:25 +00:00
QStringList newTagList = tags . split ( " , " , SKIP_EMPTY ) ;
2021-08-16 02:55:17 +00:00
QStringList newCleanTagList ;
for ( QString s : newTagList ) {
if ( ! s . simplified ( ) . isEmpty ( ) )
newCleanTagList < < s . simplified ( ) ;
}
newCleanTagList . sort ( ) ;
existingTagList . sort ( ) ;
if ( newCleanTagList . join ( " , " ) ! = existingTagList . join ( " , " ) ) {
diveChanged = true ;
taglist_free ( d - > tag_list ) ;
d - > tag_list = nullptr ;
for ( QString tag : newCleanTagList )
taglist_add_tag ( & d - > tag_list , qPrintable ( tag ) ) ;
}
2020-12-15 15:10:08 +00:00
if ( d - > rating ! = rating ) {
2017-07-28 17:28:52 +00:00
diveChanged = true ;
d - > rating = rating ;
}
2020-12-15 15:10:08 +00:00
if ( d - > visibility ! = visibility ) {
2017-07-28 17:28:52 +00:00
diveChanged = true ;
d - > visibility = visibility ;
}
2020-12-15 15:10:08 +00:00
if ( formatNotes ( d ) ! = notes ) {
2015-07-17 15:28:01 +00:00
diveChanged = true ;
free ( d - > notes ) ;
2018-02-28 22:37:09 +00:00
d - > notes = copy_qstring ( notes ) ;
2015-07-17 15:28:01 +00:00
}
2016-03-02 12:52:44 +00:00
// now that we have it all figured out, let's see what we need
// to update
2016-02-20 15:33:05 +00:00
if ( diveChanged ) {
if ( d - > maxdepth . mm = = d - > dc . maxdepth . mm & &
d - > maxdepth . mm > 0 & &
2024-05-23 04:06:23 +00:00
is_dc_manually_added_dive ( & d - > dc ) & &
2016-02-20 15:33:05 +00:00
d - > dc . samples = = 0 ) {
// so we have depth > 0, a manually added dive and no samples
// let's create an actual profile so the desktop version can work it
// first clear out the mean depth (or the fake_dc() function tries
2018-05-05 17:26:48 +00:00
// to be too clever)
2016-02-20 15:33:05 +00:00
d - > meandepth . mm = d - > dc . meandepth . mm = 0 ;
2018-05-05 17:26:48 +00:00
fake_dc ( & d - > dc ) ;
2016-02-20 15:33:05 +00:00
}
2018-04-15 17:05:41 +00:00
fixup_dive ( d ) ;
2020-01-10 00:25:37 +00:00
Command : : editDive ( orig , d_ptr . release ( ) , dsChange . createdDs . release ( ) , dsChange . editDs , dsChange . location ) ; // With release() we're giving up ownership
2016-04-11 02:22:16 +00:00
changesNeedSaving ( ) ;
2020-01-10 00:25:37 +00:00
}
2016-04-11 02:22:16 +00:00
}
QML UI: don't immediately save data after we make changes
Much as this felt like the prudent thing to do, it makes the UI painful
to use. In bad network conditions, with a large dive log, on a phone,
the save operation can take more than a minute - which is just completely
ludicrous.
So instead we mark the dive list changed when we make changes and wait
for the app to not be in the foreground. Once the OS tells us that we are
hidden (on the desktop that generally means we don't have focus, on a
mobile device it usually does mean that the app is not on the screen), we
check if there are data to be saved and do so.
There is of course a major problem with this logic. If the user switches
away from Subsurface-mobile but comes back fairly quickly (just reacting
to a notification or briefly checking something, changing a song,
something... then the app may still be non-responsive for quite a while.
So we need to do something about the time it takes us to save the git
tree locally, and then figure out if we can move at least some of the
network traffic to another thread.
And we need to make sure the user immediately notices that the app is not
crashed but is actually saving their data. But that's for another commit.
tl;dr: CAREFUL, don't kill Subsurface-mobile before it had time to save
your data or your changes may be gone. In typical use that shouldn't be
an issue, but it is something that we need to tell the user about.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-04 00:37:17 +00:00
2020-02-22 22:05:13 +00:00
void QMLManager : : updateTripDetails ( QString tripIdString , QString tripLocation , QString tripNotes )
{
int tripId = tripIdString . toInt ( ) ;
dive_trip_t * trip = get_trip_by_uniq_id ( tripId ) ;
if ( ! trip ) {
2024-03-25 20:17:29 +00:00
report_info ( " updateTripData: cannot find trip for tripId %s " , qPrintable ( tripIdString ) ) ;
2020-02-22 22:05:13 +00:00
return ;
}
bool changed = false ;
if ( tripLocation ! = trip - > location ) {
changed = true ;
Command : : editTripLocation ( trip , tripLocation ) ;
}
if ( tripNotes ! = trip - > notes ) {
changed = true ;
Command : : editTripNotes ( trip , tripNotes ) ;
}
if ( changed )
changesNeedSaving ( ) ;
}
2020-02-19 22:06:03 +00:00
void QMLManager : : removeDiveFromTrip ( int id )
{
struct dive * d = get_dive_by_uniq_id ( id ) ;
if ( ! d ) {
appendTextToLog ( QString ( " Asked to remove non-existing dive with id %1 from its trip. " ) . arg ( id ) ) ;
return ;
}
if ( ! d - > divetrip ) {
appendTextToLog ( QString ( " Asked to remove dive with id %1 from its trip (but it's not part of a trip). " ) . arg ( id ) ) ;
return ;
}
QVector < dive * > dives ;
dives . append ( d ) ;
Command : : removeDivesFromTrip ( dives ) ;
changesNeedSaving ( ) ;
}
2020-03-26 19:12:38 +00:00
void QMLManager : : addTripForDive ( int id )
{
struct dive * d = get_dive_by_uniq_id ( id ) ;
if ( ! d ) {
appendTextToLog ( QString ( " Asked to create trip for non-existing dive with id %1 " ) . arg ( id ) ) ;
return ;
}
if ( d - > divetrip ) {
appendTextToLog ( QString ( " Asked to create trip for dive %1 with id %2 but it's already part of a trip with location %3. " ) . arg ( d - > number ) . arg ( id ) . arg ( d - > divetrip - > location ) ) ;
return ;
}
QVector < dive * > dives ;
dives . append ( d ) ;
Command : : createTrip ( dives ) ;
changesNeedSaving ( ) ;
}
2020-02-20 21:24:38 +00:00
void QMLManager : : addDiveToTrip ( int id , int tripId )
{
struct dive * d = get_dive_by_uniq_id ( id ) ;
if ( ! d ) {
appendTextToLog ( QString ( " Asked to add non-existing dive with id %1 to trip %2. " ) . arg ( id ) . arg ( tripId ) ) ;
return ;
}
struct dive_trip * dt = get_trip_by_uniq_id ( tripId ) ;
if ( ! dt ) {
appendTextToLog ( QString ( " Asked to add dive with id %1 to trip with id %2 which cannot be found. " ) . arg ( id ) . arg ( tripId ) ) ;
return ;
}
QVector < dive * > dives ;
dives . append ( d ) ;
Command : : addDivesToTrip ( dives , dt ) ;
changesNeedSaving ( ) ;
}
2024-03-30 20:55:22 +00:00
void QMLManager : : saveUnsaved ( )
2016-04-11 02:22:16 +00:00
{
2024-03-30 20:55:22 +00:00
// There might have been spurious signals, so let's check if there is anything to save first.
if ( ! unsavedChanges ( ) )
return ;
2016-04-11 02:22:16 +00:00
// we no longer save right away on iOS because file access is so slow; on the other hand,
// on Android the save as the user switches away doesn't seem to work... drat.
// as a compromise for now we save just to local storage on Android right away (that appears
// to be reasonably fast), but don't save at all (and only remember that we need to save things
// on iOS
// on all other platforms we just save the changes and be done with it
2020-03-31 12:29:51 +00:00
# if defined(Q_OS_IOS)
2024-03-30 20:39:08 +00:00
saveChangesLocal ( ) ;
2020-03-31 12:29:51 +00:00
# else
2024-03-30 20:39:08 +00:00
saveChangesCloud ( false ) ;
2016-04-11 02:22:16 +00:00
# endif
2024-03-30 20:55:22 +00:00
}
void QMLManager : : changesNeedSaving ( bool fromUndo )
{
mark_divelist_changed ( true ) ;
emit syncStateChanged ( ) ;
2018-01-28 16:39:01 +00:00
updateAllGlobalLists ( ) ;
2024-03-30 20:39:08 +00:00
// provide a useful undo/redo notification
// NOTE: the QML UI interprets a leading '[action]' (where only the two brackets are checked for)
// as an indication to use the text between those two brackets as the label of a button that
// can be used to open the context menu
QString msgFormat = tr ( " [%1]Changes saved:'%2'. \n %1 possible via context menu " ) ;
if ( fromUndo )
setNotificationText ( msgFormat . arg ( tr ( " Redo " ) ) . arg ( tr ( " Undo: %1 " ) . arg ( getRedoText ( ) ) ) ) ;
else
setNotificationText ( msgFormat . arg ( tr ( " Undo " ) ) . arg ( getUndoText ( ) ) ) ;
2024-03-30 20:55:22 +00:00
// Asl the main event loop to save the changes to disk
emit changesNeedSavingSignal ( ) ;
2015-07-17 15:28:01 +00:00
}
mobile: No cloud repo creation more explicit
Before this change, there was only one way to create the local
no cloud repo on the device. The user needed to add at least
one dive to the no cloud account (so that there is something
to save). While this worked in some scenarios, it could also
get things in an inconsistent state: credential status = CS_NOCLOUD
but no local repo. This was a dead end.
In this commit, the creation of the no cloud repo is made more
explicit. When asking for no cloud mode, just create an (empty)
repo for it when it does not yet exist, and otherwise, just
open the existing (possibly empty) repo.
Now, a user can have no cloud repo, next to (any number of)
cloud accounts.
This leaves one functional aspect left: how does a user abandon
the no cloud repo, by merging his data into a true cloud
account. This is code for this, that tries to do this merge in
a smart way. This seems to be broken (too). To be clear: this
is no part of this commit.
Fixes: #667
Signed-off-by: Jan Mulder <jlmulder@xs4all.nl>
2017-10-17 07:51:00 +00:00
void QMLManager : : openNoCloudRepo ( )
{
2020-03-28 17:29:34 +00:00
/*
* Open the No Cloud repo . In case this repo does not ( yet )
* exists , create one first . When done , open the repo , which
* is obviously empty when just created .
*/
2020-03-09 16:56:32 +00:00
QString filename = nocloud_localstorage ( ) ;
2022-04-13 16:43:37 +00:00
struct git_info info ;
mobile: No cloud repo creation more explicit
Before this change, there was only one way to create the local
no cloud repo on the device. The user needed to add at least
one dive to the no cloud account (so that there is something
to save). While this worked in some scenarios, it could also
get things in an inconsistent state: credential status = CS_NOCLOUD
but no local repo. This was a dead end.
In this commit, the creation of the no cloud repo is made more
explicit. When asking for no cloud mode, just create an (empty)
repo for it when it does not yet exist, and otherwise, just
open the existing (possibly empty) repo.
Now, a user can have no cloud repo, next to (any number of)
cloud accounts.
This leaves one functional aspect left: how does a user abandon
the no cloud repo, by merging his data into a true cloud
account. This is code for this, that tries to do this merge in
a smart way. This seems to be broken (too). To be clear: this
is no part of this commit.
Fixes: #667
Signed-off-by: Jan Mulder <jlmulder@xs4all.nl>
2017-10-17 07:51:00 +00:00
2020-03-11 23:49:32 +00:00
appendTextToLog ( QString ( " User asked not to connect to cloud, using %1 as repo. " ) . arg ( filename ) ) ;
2022-04-13 16:43:37 +00:00
if ( is_git_repository ( qPrintable ( filename ) , & info ) & & ! open_git_repository ( & info ) ) {
2020-03-11 23:49:32 +00:00
// repo doesn't exist, create it and write the empty dive list to it
2024-03-11 20:41:14 +00:00
git_create_local_repo ( filename . toStdString ( ) ) ;
2020-03-11 23:49:32 +00:00
save_dives ( qPrintable ( filename ) ) ;
2024-03-16 15:06:39 +00:00
existing_filename = filename . toStdString ( ) ;
2019-12-09 18:58:20 +00:00
auto s = qPrefLog : : instance ( ) ;
2020-03-09 16:56:32 +00:00
s - > set_default_filename ( qPrintable ( filename ) ) ;
2018-08-12 19:58:25 +00:00
s - > set_default_file_behavior ( LOCAL_DEFAULT_FILE ) ;
mobile: No cloud repo creation more explicit
Before this change, there was only one way to create the local
no cloud repo on the device. The user needed to add at least
one dive to the no cloud account (so that there is something
to save). While this worked in some scenarios, it could also
get things in an inconsistent state: credential status = CS_NOCLOUD
but no local repo. This was a dead end.
In this commit, the creation of the no cloud repo is made more
explicit. When asking for no cloud mode, just create an (empty)
repo for it when it does not yet exist, and otherwise, just
open the existing (possibly empty) repo.
Now, a user can have no cloud repo, next to (any number of)
cloud accounts.
This leaves one functional aspect left: how does a user abandon
the no cloud repo, by merging his data into a true cloud
account. This is code for this, that tries to do this merge in
a smart way. This seems to be broken (too). To be clear: this
is no part of this commit.
Fixes: #667
Signed-off-by: Jan Mulder <jlmulder@xs4all.nl>
2017-10-17 07:51:00 +00:00
}
openLocalThenRemote ( filename ) ;
}
2024-03-30 20:39:08 +00:00
void QMLManager : : saveChangesLocal ( )
2015-07-17 15:28:01 +00:00
{
2020-04-04 21:47:31 +00:00
if ( unsavedChanges ( ) ) {
2019-12-28 16:30:14 +00:00
if ( qPrefCloudStorage : : cloud_verification_status ( ) = = qPrefCloudStorage : : CS_NOCLOUD ) {
2024-03-16 15:06:39 +00:00
if ( existing_filename . empty ( ) ) {
2020-03-09 16:56:32 +00:00
QString filename = nocloud_localstorage ( ) ;
2024-03-11 20:41:14 +00:00
git_create_local_repo ( filename . toStdString ( ) ) ;
2024-03-16 15:06:39 +00:00
existing_filename = filename . toStdString ( ) ;
2019-12-09 18:58:20 +00:00
auto s = qPrefLog : : instance ( ) ;
2020-03-09 16:56:32 +00:00
s - > set_default_filename ( qPrintable ( filename ) ) ;
2018-08-12 19:58:25 +00:00
s - > set_default_file_behavior ( LOCAL_DEFAULT_FILE ) ;
2016-04-22 14:08:23 +00:00
}
2018-01-17 20:15:43 +00:00
} else if ( ! m_loadFromCloud ) {
2016-04-06 18:47:12 +00:00
// this seems silly, but you need a common ancestor in the repository in
// order to be able to merge che changes later
appendTextToLog ( " Don't save dives without loading from the cloud, first. " ) ;
return ;
}
2018-09-10 13:30:01 +00:00
bool glo = git_local_only ;
git_local_only = true ;
2024-03-16 15:06:39 +00:00
int error = save_dives ( existing_filename . c_str ( ) ) ;
2020-03-28 17:33:10 +00:00
git_local_only = glo ;
if ( error ) {
2018-01-24 21:56:52 +00:00
setNotificationText ( consumeError ( ) ) ;
2024-03-16 15:06:39 +00:00
existing_filename . clear ( ) ;
2016-04-06 04:36:56 +00:00
return ;
}
2016-04-06 18:47:12 +00:00
mark_divelist_changed ( false ) ;
2020-04-04 21:51:28 +00:00
Command : : setClean ( ) ;
2020-06-13 20:45:06 +00:00
updateHaveLocalChanges ( true ) ;
2016-04-06 04:36:56 +00:00
} else {
2016-04-06 18:47:12 +00:00
appendTextToLog ( " local save requested with no unsaved changes " ) ;
}
}
2024-03-30 20:39:08 +00:00
void QMLManager : : saveChangesCloud ( bool forceRemoteSync )
2016-04-06 18:47:12 +00:00
{
2020-04-04 21:47:31 +00:00
if ( ! unsavedChanges ( ) & & ! forceRemoteSync ) {
2016-04-15 11:58:09 +00:00
appendTextToLog ( " asked to save changes but no unsaved changes " ) ;
2016-04-08 19:35:45 +00:00
return ;
2016-04-15 11:58:09 +00:00
}
// first we need to store any unsaved changes to the local repo
2017-06-18 06:22:37 +00:00
gitProgressCB ( " Save changes to local cache " ) ;
2024-03-30 20:39:08 +00:00
saveChangesLocal ( ) ;
2016-04-15 11:58:09 +00:00
// if the user asked not to push to the cloud we are done
2018-09-10 13:30:01 +00:00
if ( git_local_only & & ! forceRemoteSync )
2016-04-15 11:58:09 +00:00
return ;
2018-01-17 20:15:43 +00:00
if ( ! m_loadFromCloud ) {
2019-11-03 14:22:49 +00:00
setNotificationText ( tr ( " Fatal error: cannot save data file. Please copy log file and report. " ) ) ;
2016-04-29 13:24:21 +00:00
appendTextToLog ( " Don't save dives without loading from the cloud, first. " ) ;
return ;
}
2018-09-10 13:30:01 +00:00
bool glo = git_local_only ;
git_local_only = false ;
2016-04-06 04:36:56 +00:00
loadDivesWithValidCredentials ( ) ;
2018-09-10 13:30:01 +00:00
git_local_only = glo ;
2015-07-10 08:31:24 +00:00
}
2015-08-10 05:35:47 +00:00
2020-01-11 23:29:45 +00:00
void QMLManager : : undo ( )
2016-02-29 14:53:26 +00:00
{
2020-01-11 23:26:13 +00:00
Command : : getUndoStack ( ) - > undo ( ) ;
2021-01-17 21:09:01 +00:00
changesNeedSaving ( true ) ;
2020-01-11 23:26:13 +00:00
}
void QMLManager : : redo ( )
{
Command : : getUndoStack ( ) - > redo ( ) ;
2016-04-11 02:22:16 +00:00
changesNeedSaving ( ) ;
2016-02-29 14:53:26 +00:00
}
2019-10-08 03:37:31 +00:00
void QMLManager : : selectDive ( int id )
{
int i ;
2019-10-12 22:30:11 +00:00
extern int amount_selected ;
2019-10-08 03:37:31 +00:00
struct dive * dive = NULL ;
2019-10-12 22:30:11 +00:00
amount_selected = 0 ;
for_each_dive ( i , dive ) {
2019-10-08 03:37:31 +00:00
dive - > selected = ( dive - > id = = id ) ;
2019-10-12 22:30:11 +00:00
if ( dive - > selected )
amount_selected + + ;
}
if ( amount_selected = = 0 )
2024-03-25 20:17:29 +00:00
report_error ( " QManager::selectDive() called with unknown id %d " , id ) ;
2019-10-08 03:37:31 +00:00
}
2016-02-23 12:39:40 +00:00
void QMLManager : : deleteDive ( int id )
{
struct dive * d = get_dive_by_uniq_id ( id ) ;
if ( ! d ) {
2017-06-18 06:23:41 +00:00
appendTextToLog ( " trying to delete non-existing dive " ) ;
2016-02-23 12:39:40 +00:00
return ;
}
2019-11-14 20:38:30 +00:00
Command : : deleteDive ( QVector < dive * > { d } ) ;
2016-04-11 02:22:16 +00:00
changesNeedSaving ( ) ;
2016-02-23 12:39:40 +00:00
}
2020-03-20 22:59:38 +00:00
void QMLManager : : toggleDiveInvalid ( int id )
{
struct dive * d = get_dive_by_uniq_id ( id ) ;
if ( ! d ) {
appendTextToLog ( " trying to toggle invalid flag of non-existing dive " ) ;
return ;
}
Command : : editInvalid ( ! d - > invalid , true ) ;
2020-04-04 22:18:24 +00:00
changesNeedSaving ( ) ;
2020-03-20 22:59:38 +00:00
}
2018-11-18 05:42:15 +00:00
bool QMLManager : : toggleDiveSite ( bool toggle )
{
if ( toggle )
what . divesite = what . divesite ? false : true ;
return what . divesite ;
}
bool QMLManager : : toggleNotes ( bool toggle )
{
if ( toggle )
what . notes = what . notes ? false : true ;
return what . notes ;
}
2022-02-12 13:03:18 +00:00
bool QMLManager : : toggleDiveGuide ( bool toggle )
2018-11-18 05:42:15 +00:00
{
if ( toggle )
2022-02-12 13:03:18 +00:00
what . diveguide = what . diveguide ? false : true ;
2018-11-18 05:42:15 +00:00
2022-02-12 13:03:18 +00:00
return what . diveguide ;
2018-11-18 05:42:15 +00:00
}
bool QMLManager : : toggleBuddy ( bool toggle )
{
if ( toggle )
what . buddy = what . buddy ? false : true ;
return what . buddy ;
}
bool QMLManager : : toggleSuit ( bool toggle )
{
if ( toggle )
what . suit = what . suit ? false : true ;
return what . suit ;
}
bool QMLManager : : toggleRating ( bool toggle )
{
if ( toggle )
what . rating = what . rating ? false : true ;
return what . rating ;
}
bool QMLManager : : toggleVisibility ( bool toggle )
{
if ( toggle )
what . visibility = what . visibility ? false : true ;
return what . visibility ;
}
bool QMLManager : : toggleTags ( bool toggle )
{
if ( toggle )
what . tags = what . tags ? false : true ;
return what . tags ;
}
bool QMLManager : : toggleCylinders ( bool toggle )
{
if ( toggle )
what . cylinders = what . cylinders ? false : true ;
return what . cylinders ;
}
bool QMLManager : : toggleWeights ( bool toggle )
{
if ( toggle )
what . weights = what . weights ? false : true ;
return what . weights ;
}
2018-10-27 07:21:40 +00:00
void QMLManager : : copyDiveData ( int id )
{
m_copyPasteDive = get_dive_by_uniq_id ( id ) ;
if ( ! m_copyPasteDive ) {
appendTextToLog ( " trying to copy non-existing dive " ) ;
return ;
}
2018-11-04 05:10:35 +00:00
setNotificationText ( " Copy " ) ;
2018-10-27 07:21:40 +00:00
}
void QMLManager : : pasteDiveData ( int id )
{
2018-10-27 09:00:12 +00:00
if ( ! m_copyPasteDive ) {
appendTextToLog ( " dive to paste is not selected " ) ;
return ;
}
2020-01-03 13:56:58 +00:00
Command : : pasteDives ( m_copyPasteDive , what ) ;
2020-04-04 22:18:24 +00:00
changesNeedSaving ( ) ;
2018-10-27 07:21:40 +00:00
}
2017-07-19 07:16:47 +00:00
void QMLManager : : cancelDownloadDC ( )
{
import_thread_cancelled = true ;
}
2020-01-01 22:45:52 +00:00
int QMLManager : : addDive ( )
{
// TODO: Duplicate code with desktop-widgets/mainwindow.cpp
// create a dive an hour from now with a default depth (15m/45ft) and duration (40 minutes)
// as a starting point for the user to edit
struct dive d = { 0 } ;
int diveId = d . id = dive_getUniqID ( ) ;
d . when = QDateTime : : currentMSecsSinceEpoch ( ) / 1000L + gettimezoneoffset ( ) + 3600 ;
d . dc . duration . seconds = 40 * 60 ;
d . dc . maxdepth . mm = M_OR_FT ( 15 , 45 ) ;
d . dc . meandepth . mm = M_OR_FT ( 13 , 39 ) ; // this creates a resonable looking safety stop
2024-05-23 04:06:23 +00:00
make_manually_added_dive_dc ( & d . dc ) ;
2020-01-01 22:45:52 +00:00
fake_dc ( & d . dc ) ;
fixup_dive ( & d ) ;
// addDive takes over the dive and clears out the structure passed in
2020-04-04 22:18:24 +00:00
// we do NOT save the modified data at this stage because of the UI flow here... this will
// be saved once the user finishes editing the newly added dive
2022-11-12 08:14:00 +00:00
Command : : addDive ( & d , divelog . autogroup , true ) ;
2015-08-10 05:35:47 +00:00
2020-01-01 22:45:52 +00:00
if ( verbose )
appendTextToLog ( QString ( " Adding new dive with id '%1' " ) . arg ( diveId ) ) ;
// the QML UI uses the return value to set up the edit screen
return diveId ;
2016-01-29 14:25:13 +00:00
}
2015-08-19 07:17:52 +00:00
void QMLManager : : appendTextToLog ( const QString & newText )
{
2024-03-25 20:17:29 +00:00
report_info ( " %.3f: %s " , timer . elapsed ( ) / 1000.0 , qPrintable ( newText ) ) ;
2015-08-19 07:17:52 +00:00
}
2015-12-20 02:41:10 +00:00
void QMLManager : : setVerboseEnabled ( bool verboseMode )
{
m_verboseEnabled = verboseMode ;
verbose = verboseMode ;
2017-06-18 06:23:41 +00:00
appendTextToLog ( QStringLiteral ( " verbose is " ) + ( verbose ? QStringLiteral ( " on " ) : QStringLiteral ( " off " ) ) ) ;
2015-12-20 02:41:10 +00:00
emit verboseEnabledChanged ( ) ;
}
2016-02-08 19:08:49 +00:00
void QMLManager : : syncLoadFromCloud ( )
{
2018-09-04 17:02:26 +00:00
QSettings s ;
2020-01-27 00:49:42 +00:00
QString cloudMarker = QLatin1String ( " loadFromCloud " ) + QString ( prefs . cloud_storage_email ) ;
2018-09-04 17:02:26 +00:00
m_loadFromCloud = s . contains ( cloudMarker ) & & s . value ( cloudMarker ) . toBool ( ) ;
2016-02-08 19:08:49 +00:00
}
2015-12-03 02:49:02 +00:00
void QMLManager : : setLoadFromCloud ( bool done )
{
2018-09-04 17:02:26 +00:00
QSettings s ;
2020-01-27 00:49:42 +00:00
QString cloudMarker = QLatin1String ( " loadFromCloud " ) + QString ( prefs . cloud_storage_email ) ;
2018-09-04 17:02:26 +00:00
s . setValue ( cloudMarker , done ) ;
2015-12-03 02:49:02 +00:00
m_loadFromCloud = done ;
emit loadFromCloudChanged ( ) ;
}
2015-12-15 07:00:19 +00:00
2016-01-26 15:02:42 +00:00
void QMLManager : : setStartPageText ( const QString & text )
2015-12-15 07:00:19 +00:00
{
m_startPageText = text ;
emit startPageTextChanged ( ) ;
}
2015-12-26 21:22:50 +00:00
2016-01-26 15:02:42 +00:00
QString QMLManager : : getNumber ( const QString & diveId )
2015-12-27 04:02:23 +00:00
{
int dive_id = diveId . toInt ( ) ;
struct dive * d = get_dive_by_uniq_id ( dive_id ) ;
QString number ;
if ( d )
number = QString : : number ( d - > number ) ;
return number ;
}
2016-01-26 15:02:42 +00:00
QString QMLManager : : getDate ( const QString & diveId )
2015-12-27 04:02:23 +00:00
{
int dive_id = diveId . toInt ( ) ;
struct dive * d = get_dive_by_uniq_id ( dive_id ) ;
QString datestring ;
if ( d )
2017-12-17 08:02:48 +00:00
datestring = get_short_dive_date_string ( d - > when ) ;
2015-12-27 04:02:23 +00:00
return datestring ;
}
2016-02-10 20:53:58 +00:00
QString QMLManager : : getVersion ( ) const
{
2021-10-26 00:02:35 +00:00
QRegularExpression versionRe ( " :([() \ \ . , \ \ d ] + ) " ) ;
QRegularExpressionMatch match = versionRe . match ( getUserAgent ( ) ) ;
if ( ! match . hasMatch ( ) )
2016-02-10 20:53:58 +00:00
return QString ( ) ;
2021-10-26 00:02:35 +00:00
return match . captured ( 1 ) ;
2016-02-10 20:53:58 +00:00
}
2016-03-03 01:13:42 +00:00
2019-02-26 21:26:11 +00:00
QString QMLManager : : getGpsFromSiteName ( const QString & siteName )
2018-10-23 10:42:01 +00:00
{
2017-11-22 18:22:02 +00:00
struct dive_site * ds ;
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
ds = get_dive_site_by_name ( qPrintable ( siteName ) , divelog . sites ) ;
2019-03-24 20:50:01 +00:00
if ( ! ds )
return QString ( ) ;
2019-03-25 08:05:47 +00:00
return printGPSCoords ( & ds - > location ) ;
2017-11-22 18:22:02 +00:00
}
2017-06-18 06:22:37 +00:00
void QMLManager : : setNotificationText ( QString text )
2016-03-03 01:13:42 +00:00
{
2020-04-01 13:59:11 +00:00
appendTextToLog ( QStringLiteral ( " showProgress: " ) + text ) ;
2017-06-18 06:22:37 +00:00
m_notificationText = text ;
emit notificationTextChanged ( ) ;
2020-04-13 09:21:25 +00:00
// Once we're initialized, this may be called from signal context, notably when selecting an action from a context menu.
// Processing events may now cause the menu to be deleted. Deleting a QML object which sent a signal causes QML to quit the application.
// Therefore, don't process events once the application is started.
// During startup this is needed so that the notifications are shown.
if ( ! m_initialized )
qApp - > processEvents ( ) ;
2016-03-03 01:13:42 +00:00
}
2016-03-09 03:31:05 +00:00
qreal QMLManager : : lastDevicePixelRatio ( )
{
return m_lastDevicePixelRatio ;
}
2017-04-04 00:29:06 +00:00
void QMLManager : : setDevicePixelRatio ( qreal dpr , QScreen * screen )
{
if ( m_lastDevicePixelRatio ! = dpr ) {
m_lastDevicePixelRatio = dpr ;
emit sendScreenChanged ( screen ) ;
}
}
2016-03-09 03:31:05 +00:00
void QMLManager : : screenChanged ( QScreen * screen )
{
2024-03-25 20:17:29 +00:00
report_info ( " QMLManager received screen changed notification (%d,%d) " , screen - > size ( ) . width ( ) , screen - > size ( ) . height ( ) ) ;
2016-03-09 03:31:05 +00:00
m_lastDevicePixelRatio = screen - > devicePixelRatio ( ) ;
emit sendScreenChanged ( screen ) ;
}
2016-04-15 21:42:08 +00:00
void QMLManager : : quit ( )
{
2020-04-04 21:47:31 +00:00
if ( unsavedChanges ( ) )
2016-04-15 21:42:08 +00:00
saveChangesCloud ( false ) ;
QApplication : : quit ( ) ;
}
2016-05-20 16:48:35 +00:00
2018-01-28 08:52:51 +00:00
QStringList QMLManager : : suitList ( ) const
2016-05-20 16:48:35 +00:00
{
2018-01-28 08:52:51 +00:00
return suitModel . stringList ( ) ;
2016-05-20 16:48:35 +00:00
}
2018-01-28 09:26:45 +00:00
QStringList QMLManager : : buddyList ( ) const
2016-05-20 16:48:35 +00:00
{
2018-01-28 09:26:45 +00:00
return buddyModel . stringList ( ) ;
2016-05-20 16:48:35 +00:00
}
2022-02-12 13:03:18 +00:00
QStringList QMLManager : : diveguideList ( ) const
2016-05-20 16:48:35 +00:00
{
2022-02-12 13:03:18 +00:00
return diveguideModel . stringList ( ) ;
2016-05-20 16:48:35 +00:00
}
2016-06-13 23:41:26 +00:00
2018-01-28 14:21:28 +00:00
QStringList QMLManager : : locationList ( ) const
{
return locationModel . allSiteNames ( ) ;
}
2021-09-01 21:35:48 +00:00
// this is the cylinder list used when editing a dive
QStringList QMLManager : : cylinderListInit ( ) const
{
return formatFullCylinderList ( ) ;
}
2018-07-28 15:45:01 +00:00
2021-09-01 21:35:48 +00:00
// this is the cylinder list used to pick your default cylinder
// it starts with the (translated) "no default cylinder" in order to special-case this
QStringList QMLManager : : defaultCylinderListInit ( ) const
{
QStringList cylinders = cylinderListInit ( ) ;
2020-01-20 17:29:05 +00:00
// now add fist one that indicates that the user wants no default cylinder
cylinders . prepend ( tr ( " no default cylinder " ) ) ;
2017-04-15 00:32:32 +00:00
return cylinders ;
}
2017-07-09 22:07:16 +00:00
void QMLManager : : setProgressMessage ( QString text )
{
m_progressMessage = text ;
emit progressMessageChanged ( ) ;
}
2017-10-12 07:43:40 +00:00
void QMLManager : : setBtEnabled ( bool value )
{
m_btEnabled = value ;
}
2018-04-13 23:53:51 +00:00
# if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
2017-06-23 14:31:44 +00:00
2017-07-10 00:03:57 +00:00
void writeToAppLogFile ( QString logText )
{
// write to storage and flush so that the data doesn't get lost
logText . append ( " \n " ) ;
QMLManager * self = QMLManager : : instance ( ) ;
if ( self ) {
self - > writeToAppLogFile ( logText ) ;
}
}
void QMLManager : : writeToAppLogFile ( QString logText )
{
if ( appLogFileOpen ) {
2018-02-25 12:51:41 +00:00
appLogFile . write ( qPrintable ( logText ) ) ;
2017-07-10 00:03:57 +00:00
appLogFile . flush ( ) ;
}
}
2018-04-13 23:53:51 +00:00
# endif
2017-06-23 14:31:44 +00:00
2018-04-13 23:53:51 +00:00
# if defined(Q_OS_ANDROID)
2017-06-23 14:31:44 +00:00
//HACK to color the system bar on Android, use qtandroidextras and call the appropriate Java methods
//this code is based on code in the Kirigami example app for Android (under LGPL-2) Copyright 2017 Marco Martin
// there doesn't appear to be an include that defines these in an easily accessible way
// WindowManager.LayoutParams
# define FLAG_TRANSLUCENT_STATUS 0x04000000
# define FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS 0x80000000
// View
# define SYSTEM_UI_FLAG_LIGHT_STATUS_BAR 0x00002000
void QMLManager : : setStatusbarColor ( QColor color )
{
2020-02-04 13:09:12 +00:00
QtAndroid : : runOnAndroidThread ( [ color ] ( ) {
2017-06-23 14:31:44 +00:00
QAndroidJniObject window = QtAndroid : : androidActivity ( ) . callObjectMethod ( " getWindow " , " ()Landroid/view/Window; " ) ;
window . callMethod < void > ( " addFlags " , " (I)V " , FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS ) ;
window . callMethod < void > ( " clearFlags " , " (I)V " , FLAG_TRANSLUCENT_STATUS ) ;
window . callMethod < void > ( " setStatusBarColor " , " (I)V " , color . rgba ( ) ) ;
window . callMethod < void > ( " setNavigationBarColor " , " (I)V " , color . rgba ( ) ) ;
} ) ;
}
# else
2018-05-21 15:22:34 +00:00
void QMLManager : : setStatusbarColor ( QColor )
2017-06-23 14:31:44 +00:00
{
// noop
}
# endif
2018-06-09 11:14:54 +00:00
2018-12-21 22:52:02 +00:00
void QMLManager : : retrieveBluetoothName ( )
{
QString name = DC_devName ( ) ;
2019-04-01 20:15:19 +00:00
const QList < BTDiscovery : : btVendorProduct > btDCs = BTDiscovery : : instance ( ) - > getBtDcs ( ) ;
for ( BTDiscovery : : btVendorProduct btDC : btDCs ) {
2024-03-25 20:17:29 +00:00
report_info ( " compare %s %s " , qPrintable ( name ) , qPrintable ( btDC . btpdi . address ) ) ;
2018-12-21 22:52:02 +00:00
if ( name . contains ( btDC . btpdi . address ) )
DC_setDevBluetoothName ( btDC . btpdi . name ) ;
}
}
2018-06-09 11:14:54 +00:00
QString QMLManager : : DC_vendor ( ) const
{
2018-12-10 14:21:50 +00:00
return DCDeviceData : : instance ( ) - > vendor ( ) ;
2018-06-09 11:14:54 +00:00
}
QString QMLManager : : DC_product ( ) const
{
2018-12-10 14:21:50 +00:00
return DCDeviceData : : instance ( ) - > product ( ) ;
2018-06-09 11:14:54 +00:00
}
QString QMLManager : : DC_devName ( ) const
{
2018-12-10 14:21:50 +00:00
return DCDeviceData : : instance ( ) - > devName ( ) ;
2018-06-09 11:14:54 +00:00
}
QString QMLManager : : DC_devBluetoothName ( ) const
{
2018-12-10 14:21:50 +00:00
return DCDeviceData : : instance ( ) - > devBluetoothName ( ) ;
2018-06-09 11:14:54 +00:00
}
QString QMLManager : : DC_descriptor ( ) const
{
2018-12-10 14:21:50 +00:00
return DCDeviceData : : instance ( ) - > descriptor ( ) ;
2018-06-09 11:14:54 +00:00
}
bool QMLManager : : DC_forceDownload ( ) const
{
2018-12-10 14:21:50 +00:00
return DCDeviceData : : instance ( ) - > forceDownload ( ) ;
2018-06-09 11:14:54 +00:00
}
bool QMLManager : : DC_bluetoothMode ( ) const
{
2018-12-10 14:21:50 +00:00
return DCDeviceData : : instance ( ) - > bluetoothMode ( ) ;
2018-06-09 11:14:54 +00:00
}
bool QMLManager : : DC_saveDump ( ) const
{
2018-12-10 14:21:50 +00:00
return DCDeviceData : : instance ( ) - > saveDump ( ) ;
2018-06-09 11:14:54 +00:00
}
void QMLManager : : DC_setVendor ( const QString & vendor )
{
2018-12-10 14:21:50 +00:00
DCDeviceData : : instance ( ) - > setVendor ( vendor ) ;
2018-06-09 11:14:54 +00:00
}
void QMLManager : : DC_setProduct ( const QString & product )
{
2018-12-10 14:21:50 +00:00
DCDeviceData : : instance ( ) - > setProduct ( product ) ;
2018-06-09 11:14:54 +00:00
}
void QMLManager : : DC_setDevName ( const QString & devName )
{
2018-12-10 14:21:50 +00:00
DCDeviceData : : instance ( ) - > setDevName ( devName ) ;
2020-03-15 01:11:46 +00:00
# if defined(Q_OS_ANDROID)
// get the currently valid list of devices and set up the USB device descriptor
// if the connection string matches a connection in that list
androidSerialDevices = serial_usb_android_get_devices ( ) ;
std : : string connection = devName . toStdString ( ) ;
for ( unsigned int i = 0 ; i < androidSerialDevices . size ( ) ; i + + ) {
if ( androidSerialDevices [ i ] . uiRepresentation = = connection ) {
appendTextToLog ( QString ( " setDevName matches USB device %1 " ) . arg ( i ) ) ;
2020-03-15 22:12:30 +00:00
DCDeviceData : : instance ( ) - > setUsbDevice ( androidSerialDevices [ i ] ) ;
2020-03-15 01:11:46 +00:00
}
}
# endif
2018-06-09 11:14:54 +00:00
}
void QMLManager : : DC_setDevBluetoothName ( const QString & devBluetoothName )
{
2018-12-10 14:21:50 +00:00
DCDeviceData : : instance ( ) - > setDevBluetoothName ( devBluetoothName ) ;
2018-06-09 11:14:54 +00:00
}
void QMLManager : : DC_setBluetoothMode ( bool mode )
{
2018-12-10 14:21:50 +00:00
DCDeviceData : : instance ( ) - > setBluetoothMode ( mode ) ;
2018-06-09 11:14:54 +00:00
}
void QMLManager : : DC_setForceDownload ( bool force )
{
2018-12-10 14:21:50 +00:00
DCDeviceData : : instance ( ) - > setForceDownload ( force ) ;
2019-09-21 23:31:27 +00:00
DC_ForceDownloadChanged ( ) ;
2018-06-09 11:14:54 +00:00
}
void QMLManager : : DC_setSaveDump ( bool dumpMode )
{
2018-12-10 14:21:50 +00:00
DCDeviceData : : instance ( ) - > setSaveDump ( dumpMode ) ;
2018-06-09 11:14:54 +00:00
}
QStringList QMLManager : : getProductListFromVendor ( const QString & vendor )
{
2018-12-10 14:21:50 +00:00
return DCDeviceData : : instance ( ) - > getProductListFromVendor ( vendor ) ;
2018-06-09 11:14:54 +00:00
}
int QMLManager : : getMatchingAddress ( const QString & vendor , const QString & product )
{
2018-12-10 14:21:50 +00:00
return DCDeviceData : : instance ( ) - > getMatchingAddress ( vendor , product ) ;
2018-06-09 11:14:54 +00:00
}
int QMLManager : : getDetectedVendorIndex ( )
{
2018-12-10 14:21:50 +00:00
return DCDeviceData : : instance ( ) - > getDetectedVendorIndex ( ) ;
2018-06-09 11:14:54 +00:00
}
int QMLManager : : getDetectedProductIndex ( const QString & currentVendorText )
{
2018-12-10 14:21:50 +00:00
return DCDeviceData : : instance ( ) - > getDetectedProductIndex ( currentVendorText ) ;
2018-06-09 11:14:54 +00:00
}
2018-08-07 01:29:27 +00:00
2018-09-23 05:53:43 +00:00
int QMLManager : : getConnectionIndex ( const QString & deviceSubstr )
{
return connectionListModel . indexOf ( deviceSubstr ) ;
}
2018-10-09 08:23:24 +00:00
void QMLManager : : setGitLocalOnly ( const bool & value )
{
git_local_only = value ;
}
2020-03-15 01:05:06 +00:00
# if defined(Q_OS_ANDROID)
// try to guess which dive computer was plugged into the USB port
QString QMLManager : : getProductVendorConnectionIdx ( android_usb_serial_device_descriptor descriptor )
2018-08-07 01:29:27 +00:00
{
2020-03-15 01:05:06 +00:00
// convert the information we get from the Android USB layer into indices for the three
// combo boxes for vendor, product, and connection in the QML UI
2018-08-09 03:34:39 +00:00
// for each of these values '-1' means that no entry should be pre-selected
2020-03-15 01:05:06 +00:00
QString uiString = QString : : fromStdString ( descriptor . uiRepresentation ) ;
QString product = QString : : fromStdString ( descriptor . usbProduct ) ;
int connIdx = connectionListModel . indexOf ( uiString ) ;
int vendIdx = - 1 ;
int prodIdx = - 1 ;
if ( ! descriptor . manufacturer . empty ( ) )
vendIdx = vendorList . indexOf ( QString : : fromStdString ( descriptor . manufacturer ) ) ;
if ( ! descriptor . product . empty ( ) )
prodIdx = productList [ vendorList [ vendIdx ] ] . indexOf ( QString : : fromStdString ( descriptor . product ) ) ;
// the rest of them simply will get the default -1:-1:index of the uiString
return QString ( " %1;%2;%3 " ) . arg ( vendIdx ) . arg ( prodIdx ) . arg ( connIdx ) ;
}
2020-03-15 01:52:45 +00:00
void QMLManager : : androidUsbPopoulateConnections ( )
{
// let's understand what devices are available
androidSerialDevices = serial_usb_android_get_devices ( ) ;
appendTextToLog ( QString ( " rescanning USB: %1 devices reported " ) . arg ( androidSerialDevices . size ( ) ) ) ;
// list all USB devices, this will include the one that triggered the intent
for ( unsigned int i = 0 ; i < androidSerialDevices . size ( ) ; i + + ) {
QString uiString = QString : : fromStdString ( androidSerialDevices [ i ] . uiRepresentation ) ;
appendTextToLog ( QString ( " found USB device with ui string %1 " ) . arg ( uiString ) ) ;
connectionListModel . addAddress ( uiString ) ;
}
}
2020-03-15 01:05:06 +00:00
void QMLManager : : showDownloadPage ( QAndroidJniObject usbDevice )
{
if ( ! usbDevice . isValid ( ) ) {
2020-03-15 19:27:10 +00:00
// this really shouldn't happen anymore, but just in case...
m_pluggedInDeviceName = " " ;
2020-03-15 01:05:06 +00:00
} else {
2020-03-15 01:52:45 +00:00
// repopulate the connection list
rescanConnections ( ) ;
2020-03-15 01:05:06 +00:00
// parse the usbDevice
android_usb_serial_device_descriptor usbDeviceDescriptor = getDescriptor ( usbDevice ) ;
// inform the QML UI that it should show the download page
m_pluggedInDeviceName = getProductVendorConnectionIdx ( usbDeviceDescriptor ) ;
}
2018-08-07 01:29:27 +00:00
emit pluggedInDeviceNameChanged ( ) ;
}
2020-03-15 19:27:10 +00:00
void QMLManager : : restartDownload ( QAndroidJniObject usbDevice )
{
// this gets called if we received a permission intent after
// already trying to download from USB
if ( ! usbDevice . isValid ( ) ) {
appendTextToLog ( " permission intent with invalid UsbDevice - ignoring " ) ;
} else {
// inform that QML code that it should retry downloading
// we get the usbDevice again and could verify that this is
// still the same - but I don't see how this could change while we are waiting
// for permission
android_usb_serial_device_descriptor usbDeviceDescriptor = getDescriptor ( usbDevice ) ;
appendTextToLog ( QString ( " got permission from Android, restarting download for %1 " )
. arg ( QString : : fromStdString ( usbDeviceDescriptor . uiRepresentation ) ) ) ;
emit restartDownloadSignal ( ) ;
}
}
2020-03-15 01:05:06 +00:00
# endif
2018-08-09 14:12:32 +00:00
2020-06-01 21:37:36 +00:00
static filter_constraint make_filter_constraint ( filter_constraint_type type , const QString & s )
{
filter_constraint res ( type ) ;
filter_constraint_set_stringlist ( res , s ) ;
return res ;
}
2020-02-19 20:39:23 +00:00
void QMLManager : : setFilter ( const QString filterText , int index )
2018-10-20 15:59:35 +00:00
{
2020-03-07 17:13:50 +00:00
QString f = filterText . trimmed ( ) ;
FilterData data ;
if ( ! f . isEmpty ( ) ) {
2020-03-11 10:30:51 +00:00
// This is ugly - the indices of the mode are hardcoded!
2020-03-07 17:13:50 +00:00
switch ( index ) {
2020-06-01 21:37:36 +00:00
default :
case 0 :
2020-03-07 17:13:50 +00:00
data . fullText = f ;
2020-06-01 21:37:36 +00:00
break ;
case 1 :
data . constraints . push_back ( make_filter_constraint ( FILTER_CONSTRAINT_PEOPLE , f ) ) ;
break ;
case 2 :
data . constraints . push_back ( make_filter_constraint ( FILTER_CONSTRAINT_TAGS , f ) ) ;
break ;
}
2020-03-07 17:13:50 +00:00
}
DiveFilter : : instance ( ) - > setFilter ( data ) ;
2018-10-20 15:59:35 +00:00
}
2019-02-14 06:48:50 +00:00
void QMLManager : : setShowNonDiveComputers ( bool show )
{
m_showNonDiveComputers = show ;
BTDiscovery : : instance ( ) - > showNonDiveComputers ( show ) ;
}
2018-08-09 14:12:32 +00:00
# if defined(Q_OS_ANDROID)
// implemented in core/android.cpp
void checkPendingIntents ( ) ;
# endif
void QMLManager : : appInitialized ( )
{
# if defined(Q_OS_ANDROID)
checkPendingIntents ( ) ;
# endif
}
2019-11-19 18:27:20 +00:00
2020-02-15 19:44:14 +00:00
# if !defined(Q_OS_ANDROID)
2019-11-19 18:27:20 +00:00
void QMLManager : : exportToFile ( export_types type , QString dir , bool anonymize )
{
// dir starts with "file://" e.g. "file:///tmp"
// remove prefix and add standard filenamel
QString fileName = dir . right ( dir . size ( ) - 7 ) + " /Subsurface_export " ;
switch ( type )
{
2020-01-27 00:51:01 +00:00
case EX_DIVES_XML :
save_dives_logic ( qPrintable ( fileName + " .ssrf " ) , false , anonymize ) ;
break ;
case EX_DIVE_SITES_XML :
{
2020-02-06 21:38:29 +00:00
std : : vector < const dive_site * > sites = getDiveSitesToExport ( false ) ;
2020-04-21 07:34:40 +00:00
save_dive_sites_logic ( qPrintable ( fileName + " .xml " ) , sites . data ( ) , ( int ) sites . size ( ) , anonymize ) ;
2019-11-19 18:27:20 +00:00
break ;
2020-01-27 00:51:01 +00:00
}
default :
2024-03-25 20:17:29 +00:00
report_info ( " export to unknown type %d using %s remove names %d " , static_cast < int > ( type ) , qPrintable ( dir ) , anonymize ) ;
2020-01-27 00:51:01 +00:00
break ;
2019-11-19 18:27:20 +00:00
}
}
2020-02-15 19:44:14 +00:00
# endif
2019-11-19 18:27:20 +00:00
void QMLManager : : exportToWEB ( export_types type , QString userId , QString password , bool anonymize )
{
switch ( type )
{
2020-01-27 00:51:01 +00:00
case EX_DIVELOGS_DE :
uploadDiveLogsDE : : instance ( ) - > doUpload ( false , userId , password ) ;
break ;
case EX_DIVESHARE :
uploadDiveShare : : instance ( ) - > doUpload ( false , userId , anonymize ) ;
break ;
default :
2024-03-25 20:17:29 +00:00
report_info ( " upload to unknown type %d using %s/%s remove names %d " , static_cast < int > ( type ) , qPrintable ( userId ) , qPrintable ( password ) , anonymize ) ;
2020-01-27 00:51:01 +00:00
break ;
2019-11-19 18:27:20 +00:00
}
}
2019-12-08 10:52:38 +00:00
2022-08-30 18:53:02 +00:00
void QMLManager : : shareViaEmail ( export_types type , bool anonymize )
{
# if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
QString fileName = appLogFileName ;
# else
QString fileName = system_default_directory ( ) ;
# endif
QString body ;
switch ( type ) {
case EX_DIVES_XML :
fileName . replace ( " subsurface.log " , " subsurface.ssrf " ) ;
if ( save_dives_logic ( qPrintable ( fileName ) , false , anonymize ) = = 0 ) {
// ok, we have a file, let's send it
body = " Subsurface dive log data " ;
} else {
appendTextToLog ( " failure to save dive log, aborting attempt to send via email " ) ;
}
break ;
case EX_DIVE_SITES_XML :
fileName . replace ( " subsurface.log " , " subsurface_divesites.xml " ) ;
{ // need a block so the compiler doesn't complain about creating the sites variable here
std : : vector < const dive_site * > sites = getDiveSitesToExport ( false ) ;
if ( save_dive_sites_logic ( qPrintable ( fileName ) , sites . data ( ) , ( int ) sites . size ( ) , anonymize ) = = 0 ) {
// ok, we have a file, let's send it
body = " Subsurface dive site data " ;
} else {
appendTextToLog ( " failure to save dive site data, aborting attempt to send via email " ) ;
}
}
break ;
default :
2024-03-25 20:17:29 +00:00
report_info ( " cannot export type %d via email " , static_cast < int > ( type ) ) ;
2022-08-30 18:53:02 +00:00
return ;
}
# if defined(Q_OS_ANDROID)
// let's use our nifty Java shareViaEmail function
QAndroidJniObject activity = QtAndroid : : androidActivity ( ) ;
if ( activity . isValid ( ) ) {
QAndroidJniObject attachmentPath = QAndroidJniObject : : fromString ( fileName ) ;
QAndroidJniObject subject = QAndroidJniObject : : fromString ( " Subsurface export " ) ;
QAndroidJniObject bodyString = QAndroidJniObject : : fromString ( body ) ;
QAndroidJniObject emptyString = QAndroidJniObject : : fromString ( " " ) ;
bool success = activity . callMethod < jboolean > ( " shareViaEmail " ,
" (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z " , // five string arguments, return bool
subject . object < jstring > ( ) , emptyString . object < jstring > ( ) , bodyString . object < jstring > ( ) ,
attachmentPath . object < jstring > ( ) , emptyString . object < jstring > ( ) ) ;
2024-03-25 20:17:29 +00:00
report_info ( " %s shareViaEmail %s " , __func__ , success ? " succeeded " : " failed " ) ;
2022-08-30 18:53:02 +00:00
}
# elif defined(Q_OS_IOS)
// call into objC++ code to share on iOS
QString subject ( " Subsurface export " ) ;
QString emptyString ( " " ) ;
iosshare . shareViaEmail ( subject , emptyString , body , fileName , emptyString ) ;
# else
appendTextToLog ( " on a mobile platform this would send " + fileName + " via email with body " + body ) ;
# endif
}
2019-12-08 10:52:38 +00:00
void QMLManager : : uploadFinishSlot ( bool success , const QString & text , const QByteArray & html )
{
emit uploadFinish ( success , text ) ;
}
2019-12-29 11:29:05 +00:00
qPrefCloudStorage : : cloud_status QMLManager : : oldStatus ( ) const
{
return m_oldStatus ;
}
void QMLManager : : setOldStatus ( const qPrefCloudStorage : : cloud_status value )
{
if ( m_oldStatus ! = value ) {
m_oldStatus = value ;
emit oldStatusChanged ( ) ;
}
}
2020-02-18 01:16:11 +00:00
2020-04-04 21:23:39 +00:00
void QMLManager : : rememberOldStatus ( )
{
setOldStatus ( ( qPrefCloudStorage : : cloud_status ) qPrefCloudStorage : : cloud_verification_status ( ) ) ;
}
2020-02-18 01:16:11 +00:00
void QMLManager : : divesChanged ( const QVector < dive * > & dives , DiveField field )
{
Q_UNUSED ( field )
for ( struct dive * d : dives ) {
2024-03-25 20:17:29 +00:00
report_info ( " dive #%d changed, cache is %s " , d - > number , dive_cache_is_valid ( d ) ? " valid " : " invalidated " ) ;
2020-02-18 01:16:11 +00:00
// a brute force way to deal with that would of course be to call
// invalidate_dive_cache(d);
}
}
2020-01-11 23:26:13 +00:00
QString QMLManager : : getUndoText ( ) const
{
QString undoText = Command : : getUndoStack ( ) - > undoText ( ) ;
return undoText ;
}
QString QMLManager : : getRedoText ( ) const
{
QString redoText = Command : : getUndoStack ( ) - > redoText ( ) ;
return redoText ;
}
2020-03-29 19:53:01 +00:00
void QMLManager : : setDiveListProcessing ( bool value )
{
if ( m_diveListProcessing ! = value ) {
m_diveListProcessing = value ;
emit diveListProcessingChanged ( ) ;
}
}
2020-06-06 16:51:40 +00:00
2020-06-06 16:55:49 +00:00
void QMLManager : : importCacheRepo ( QString repo )
{
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 divelog log ;
2020-06-06 16:55:49 +00:00
QString repoPath = QString ( " %1/cloudstorage/%2 " ) . arg ( system_default_directory ( ) ) . arg ( repo ) ;
appendTextToLog ( QString ( " importing %1 " ) . arg ( repoPath ) ) ;
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
parse_file ( qPrintable ( repoPath ) , & log ) ;
add_imported_dives ( & log , IMPORT_MERGE_ALL_TRIPS ) ;
2020-06-06 16:55:49 +00:00
changesNeedSaving ( ) ;
}
2020-06-06 16:51:40 +00:00
QStringList QMLManager : : cloudCacheList ( ) const
{
QDir localCacheDir ( QString ( " %1/cloudstorage/ " ) . arg ( system_default_directory ( ) ) ) ;
2022-08-27 22:18:37 +00:00
QStringList dirs = localCacheDir . entryList ( ) ;
2020-06-06 16:51:40 +00:00
QStringList result ;
2024-03-16 15:50:43 +00:00
for ( const QString & dir : dirs ) {
2020-06-06 16:51:40 +00:00
QString originsDir = QString ( " %1/cloudstorage/%2/.git/refs/remotes/origin/ " ) . arg ( system_default_directory ( ) ) . arg ( dir ) ;
QDir remote ( originsDir ) ;
if ( dir = = " localrepo " ) {
result < < QString ( " localrepo[master] " ) ;
} else {
2024-03-16 15:50:43 +00:00
for ( const QString & branch : remote . entryList ( ) . filter ( QRegularExpression ( " ...+ " ) ) )
2020-06-06 16:51:40 +00:00
result < < QString ( " %1[%2] " ) . arg ( dir ) . arg ( branch ) ;
}
}
return result ;
}
2020-06-13 20:45:06 +00:00
void QMLManager : : updateHaveLocalChanges ( bool status )
{
localChanges = status ;
2020-06-13 20:52:04 +00:00
emit syncStateChanged ( ) ;
}
// show the state of the dive list
QString QMLManager : : getSyncState ( ) const
{
if ( unsavedChanges ( ) )
return tr ( " (unsaved changes in memory) " ) ;
if ( localChanges )
return tr ( " (changes synced locally) " ) ;
return tr ( " (synced with cloud) " ) ;
2020-06-13 20:45:06 +00:00
}
2024-02-24 00:21:17 +00:00
// show the state of the cloud login
QString QMLManager : : getPasswordState ( ) const
{
auto credStatus = qPrefCloudStorage : : cloud_verification_status ( ) ;
if ( credStatus = = qPrefCloudStorage : : CS_INCORRECT_USER_PASSWD )
return tr ( " (incorrect cloud email or password) " ) ;
return QString ( ) ;
}