summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/divelist.c4
-rw-r--r--core/file.c87
-rw-r--r--core/git-access.c21
-rw-r--r--core/imagedownloader.cpp2
-rw-r--r--core/load-git.c6
-rw-r--r--core/parse-xml.c2
-rw-r--r--core/planner.c2
-rw-r--r--core/qthelper.cpp22
-rw-r--r--core/save-git.c8
-rw-r--r--core/save-html.c2
-rw-r--r--core/save-xml.c2
-rw-r--r--core/statistics.c6
-rw-r--r--core/subsurface-qt/DiveObjectHelper.cpp17
-rw-r--r--core/time.c72
-rw-r--r--core/uemis-downloader.c1
-rw-r--r--desktop-widgets/divelogimportdialog.cpp45
-rw-r--r--desktop-widgets/divelogimportdialog.h2
-rw-r--r--desktop-widgets/maintab.cpp6
-rw-r--r--desktop-widgets/mainwindow.cpp9
-rw-r--r--dives/test0.xml2
-rw-r--r--mobile-widgets/qml/DiveList.qml3
-rw-r--r--mobile-widgets/qml/main.qml8
-rw-r--r--mobile-widgets/qml/mobile-resources.qrc4
-rw-r--r--packaging/ubuntu/make-package.sh6
-rw-r--r--qt-models/cylindermodel.cpp5
-rw-r--r--xslt/csv2xml.xslt11
26 files changed, 276 insertions, 79 deletions
diff --git a/core/divelist.c b/core/divelist.c
index 52545af1a..1ac19bd3f 100644
--- a/core/divelist.c
+++ b/core/divelist.c
@@ -504,7 +504,7 @@ void dump_trip_list(void)
printf("%s trip %d to \"%s\" on %04u-%02u-%02u %02u:%02u:%02u (%d dives - %p)\n",
trip->autogen ? "autogen " : "",
++i, trip->location,
- tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
+ tm.tm_year, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
trip->nrdives, trip);
last_time = trip->when;
}
@@ -531,7 +531,7 @@ dive_trip_t *find_matching_trip(timestamp_t when)
utc_mkdate(trip->when, &tm);
printf("found trip %p @ %04d-%02d-%02d %02d:%02d:%02d\n",
trip,
- tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_year, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec);
}
#endif
diff --git a/core/file.c b/core/file.c
index 3ce084e03..4c0ae8fd0 100644
--- a/core/file.c
+++ b/core/file.c
@@ -912,22 +912,85 @@ int parse_csv_file(const char *filename, char **params, int pnr, const char *csv
if (filename == NULL)
return report_error("No CSV filename");
- time(&now);
- timep = localtime(&now);
+ mem.size = 0;
+ if (!strcmp("DL7", csvtemplate)) {
+ char *ptr = NULL;
+ char *NL = NULL;
+ char *iter = NULL;
+ char *tmp = NULL;
+
+ csvtemplate = "csv";
+ if (readfile(filename, &mem) < 0)
+ return report_error(translate("gettextFromC", "Failed to read '%s'"), filename);
+
+ /* Determine NL (new line) character and the start of CSV data */
+ if ((ptr = strstr(mem.buffer, "\r\n")) != NULL) {
+ NL = "\r\n";
+ } else if ((ptr = strstr(mem.buffer, "\n")) != NULL) {
+ NL = "\n";
+ } else {
+ fprintf(stderr, "DEBUG: failed to detect NL\n");
+ return -1;
+ }
- strftime(tmpbuf, MAXCOLDIGITS, "%Y%m%d", timep);
- params[pnr++] = "date";
- params[pnr++] = strdup(tmpbuf);
+ ptr = strstr(mem.buffer, "ZDH");
+ if (ptr) {
+ iter = ptr + 1;
+ for (i = 0; i <= 4 && iter; ++i) {
+ iter = strchr(iter, '|');
+ if (iter)
+ ++iter;
+ }
- /* As the parameter is numeric, we need to ensure that the leading zero
- * is not discarded during the transform, thus prepend time with 1 */
+ /* Setting date */
+ memcpy(tmpbuf, iter, 8);
+ tmpbuf[8] = 0;
+ params[pnr++] = "date";
+ params[pnr++] = strdup(tmpbuf);
+
+ /* Setting time, gotta prepend it with 1 to
+ * avoid octal parsing (this is stripped out in
+ * XSLT */
+ tmpbuf[0] = '1';
+ memcpy(tmpbuf + 1, iter + 8, 6);
+ tmpbuf[7] = 0;
+ params[pnr++] = "time";
+ params[pnr++] = strdup(tmpbuf);
+ }
- strftime(tmpbuf, MAXCOLDIGITS, "1%H%M", timep);
- params[pnr++] = "time";
- params[pnr++] = strdup(tmpbuf);
- params[pnr++] = NULL;
+ ptr = strstr(mem.buffer, "ZDP");
+ if (ptr)
+ ptr = strstr(ptr, NL);
+ if (ptr)
+ ptr + strlen(NL);
+
+ /* Move the CSV data to the start of mem buffer */
+ memmove(mem.buffer, ptr, mem.size - (ptr - (char*)mem.buffer));
+ ptr = strstr(mem.buffer, "ZDP");
+ if (ptr) {
+ *ptr = 0;
+ } else {
+ fprintf(stderr, "DEBUG: failed to find end ZDP\n");
+ return -1;
+ }
+ mem.size = ptr - (char*)mem.buffer;
+ } else {
+ time(&now);
+ timep = localtime(&now);
+
+ strftime(tmpbuf, MAXCOLDIGITS, "%Y%m%d", timep);
+ params[pnr++] = "date";
+ params[pnr++] = strdup(tmpbuf);
+
+ /* As the parameter is numeric, we need to ensure that the leading zero
+ * is not discarded during the transform, thus prepend time with 1 */
+
+ strftime(tmpbuf, MAXCOLDIGITS, "1%H%M", timep);
+ params[pnr++] = "time";
+ params[pnr++] = strdup(tmpbuf);
+ params[pnr++] = NULL;
+ }
- mem.size = 0;
if (try_to_xslt_open_csv(filename, &mem, csvtemplate))
return -1;
diff --git a/core/git-access.c b/core/git-access.c
index fe9a27452..fe3a918ac 100644
--- a/core/git-access.c
+++ b/core/git-access.c
@@ -194,9 +194,20 @@ int credential_ssh_cb(git_cred **out,
(void) url;
(void) allowed_types;
(void) payload;
+ static int attempt = 0;
const char *priv_key = format_string("%s/%s", system_default_directory(), "ssrf_remote.key");
const char *passphrase = prefs.cloud_storage_password ? strdup(prefs.cloud_storage_password) : strdup("");
+
+ /* Bail out from libgit authentication loop when credentials are
+ * incorrect */
+
+ if (attempt++ > 2) {
+ report_error("Authentication to cloud storage failed.");
+ attempt = 0;
+ return GIT_EUSER;
+ }
+
return git_cred_ssh_key_new(out, username_from_url, NULL, priv_key, passphrase);
}
@@ -210,8 +221,18 @@ int credential_https_cb(git_cred **out,
(void) username_from_url;
(void) payload;
(void) allowed_types;
+ static int attempt = 0;
const char *username = prefs.cloud_storage_email_encoded;
const char *password = prefs.cloud_storage_password ? strdup(prefs.cloud_storage_password) : strdup("");
+
+ /* Bail out from libgit authentication loop when credentials are
+ * incorrect */
+
+ if (attempt++ > 2) {
+ report_error("Authentication to cloud storage failed.");
+ attempt = 0;
+ return GIT_EUSER;
+ }
return git_cred_userpass_plaintext_new(out, username, password);
}
diff --git a/core/imagedownloader.cpp b/core/imagedownloader.cpp
index f406ee45a..dc74a1e27 100644
--- a/core/imagedownloader.cpp
+++ b/core/imagedownloader.cpp
@@ -77,6 +77,8 @@ void ImageDownloader::saveImage(QNetworkReply *reply)
void loadPicture(struct picture *picture, bool fromHash)
{
+ if (!picture)
+ return;
ImageDownloader download(picture);
download.load(fromHash);
}
diff --git a/core/load-git.c b/core/load-git.c
index 339621c63..be7fac7d8 100644
--- a/core/load-git.c
+++ b/core/load-git.c
@@ -126,7 +126,7 @@ static void update_date(timestamp_t *when, const char *line)
if (sscanf(line, "%04u-%02u-%02u", &yyyy, &mm, &dd) != 3)
return;
utc_mkdate(*when, &tm);
- tm.tm_year = yyyy - 1900;
+ tm.tm_year = yyyy;
tm.tm_mon = mm - 1;
tm.tm_mday = dd;
*when = utc_mktime(&tm);
@@ -1199,7 +1199,7 @@ static dive_trip_t *create_new_trip(int yyyy, int mm, int dd)
static bool validate_date(int yyyy, int mm, int dd)
{
- return yyyy > 1970 && yyyy < 3000 &&
+ return yyyy > 1930 && yyyy < 3000 &&
mm > 0 && mm < 13 &&
dd > 0 && dd < 32;
}
@@ -1308,7 +1308,7 @@ static int dive_directory(const char *root, const git_tree_entry *entry, const c
tm.tm_hour = h;
tm.tm_min = m;
tm.tm_sec = s;
- tm.tm_year = yyyy - 1900;
+ tm.tm_year = yyyy;
tm.tm_mon = mm-1;
tm.tm_mday = dd;
diff --git a/core/parse-xml.c b/core/parse-xml.c
index e8782251e..746d4ce16 100644
--- a/core/parse-xml.c
+++ b/core/parse-xml.c
@@ -3441,7 +3441,7 @@ int parse_dlf_buffer(unsigned char *buffer, size_t size)
while (ptr < buffer + size) {
time = ((ptr[0] >> 4) & 0x0f) +
((ptr[1] << 4) & 0xff0) +
- (ptr[2] & 0x0f) * 3600; /* hours */
+ ((ptr[2] << 12) & 0x1f000);
event = ptr[0] & 0x0f;
switch (event) {
case 0:
diff --git a/core/planner.c b/core/planner.c
index a58c698ab..e67033dd8 100644
--- a/core/planner.c
+++ b/core/planner.c
@@ -51,7 +51,7 @@ void dump_plan(struct diveplan *diveplan)
utc_mkdate(diveplan->when, &tm);
printf("\nDiveplan @ %04d-%02d-%02d %02d:%02d:%02d (surfpres %dmbar):\n",
- tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_year, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec,
diveplan->surface_pressure);
dp = diveplan->dp;
diff --git a/core/qthelper.cpp b/core/qthelper.cpp
index a261cca38..24c51d2b9 100644
--- a/core/qthelper.cpp
+++ b/core/qthelper.cpp
@@ -950,7 +950,7 @@ QString get_trip_date_string(timestamp_t when, int nr, bool getday)
{
struct tm tm;
utc_mkdate(when, &tm);
- QDateTime localTime = QDateTime::fromTime_t(when);
+ QDateTime localTime = QDateTime::fromMSecsSinceEpoch(1000*when,Qt::UTC);
localTime.setTimeSpec(Qt::UTC);
QString ret ;
@@ -996,6 +996,7 @@ QHash <QString, QImage > thumbnailCache;
extern "C" char * hashstring(char * filename)
{
+ QMutexLocker locker(&hashOfMutex);
return hashOf[QString(filename)].toHex().data();
}
@@ -1012,6 +1013,7 @@ extern "C" char *hashfile_name_string()
void read_hashes()
{
QFile hashfile(hashfile_name());
+ QMutexLocker locker(&hashOfMutex);
if (hashfile.open(QIODevice::ReadOnly)) {
QDataStream stream(&hashfile);
stream >> localFilenameOf;
@@ -1024,6 +1026,8 @@ void read_hashes()
void write_hashes()
{
QSaveFile hashfile(hashfile_name());
+ QMutexLocker locker(&hashOfMutex);
+
if (hashfile.open(QIODevice::WriteOnly)) {
QDataStream stream(&hashfile);
stream << localFilenameOf;
@@ -1064,8 +1068,16 @@ void learnHash(struct picture *picture, QByteArray hash)
picture->hash = strdup(hash.toHex());
}
+bool haveHash(QString &filename)
+{
+ QMutexLocker locker(&hashOfMutex);
+ return hashOf.contains(filename);
+}
+
QString localFilePath(const QString originalFilename)
{
+ QMutexLocker locker(&hashOfMutex);
+
if (hashOf.contains(originalFilename) && localFilenameOf.contains(hashOf[originalFilename]))
return localFilenameOf[hashOf[originalFilename]];
else
@@ -1074,11 +1086,15 @@ QString localFilePath(const QString originalFilename)
QString fileFromHash(char *hash)
{
+ QMutexLocker locker(&hashOfMutex);
+
return localFilenameOf[QByteArray::fromHex(hash)];
}
// This needs to operate on a copy of picture as it frees it after finishing!
void updateHash(struct picture *picture) {
+ if (!picture)
+ return;
QByteArray hash = hashFile(fileFromHash(picture->hash));
learnHash(picture, hash);
picture_free(picture);
@@ -1087,6 +1103,8 @@ void updateHash(struct picture *picture) {
// This needs to operate on a copy of picture as it frees it after finishing!
void hashPicture(struct picture *picture)
{
+ if (!picture)
+ return;
char *oldHash = copy_string(picture->hash);
learnHash(picture, hashFile(QString(picture->filename)));
if (!same_string(picture->hash, "") && !same_string(picture->hash, oldHash))
@@ -1098,7 +1116,7 @@ void hashPicture(struct picture *picture)
extern "C" void cache_picture(struct picture *picture)
{
QString filename = picture->filename;
- if (!hashOf.contains(filename))
+ if (!haveHash(filename))
QtConcurrent::run(hashPicture, clone_picture(picture));
}
diff --git a/core/save-git.c b/core/save-git.c
index 08161f3de..0620a3f3d 100644
--- a/core/save-git.c
+++ b/core/save-git.c
@@ -215,7 +215,7 @@ static void show_date(struct membuffer *b, timestamp_t when)
utc_mkdate(when, &tm);
put_format(b, "date %04u-%02u-%02u\n",
- tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
+ tm.tm_year, tm.tm_mon + 1, tm.tm_mday);
put_format(b, "time %02u:%02u:%02u\n",
tm.tm_hour, tm.tm_min, tm.tm_sec);
}
@@ -535,7 +535,7 @@ static void create_dive_name(struct dive *dive, struct membuffer *name, struct t
utc_mkdate(dive->when, &tm);
if (tm.tm_year != dirtm->tm_year)
- put_format(name, "%04u-", tm.tm_year + 1900);
+ put_format(name, "%04u-", tm.tm_year);
if (tm.tm_mon != dirtm->tm_mon)
put_format(name, "%02u-", tm.tm_mon+1);
@@ -734,7 +734,7 @@ static int save_trip_description(git_repository *repo, struct dir *dir, dive_tri
struct membuffer desc = { 0 };
put_format(&desc, "date %04u-%02u-%02u\n",
- tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
+ tm->tm_year, tm->tm_mon + 1, tm->tm_mday);
put_format(&desc, "time %02u:%02u:%02u\n",
tm->tm_hour, tm->tm_min, tm->tm_sec);
@@ -949,7 +949,7 @@ static int create_git_tree(git_repository *repo, struct dir *root, bool select_o
/* Create the date-based hierarchy */
utc_mkdate(trip ? trip->when : dive->when, &tm);
- tree = mktree(repo, root, "%04d", tm.tm_year + 1900);
+ tree = mktree(repo, root, "%04d", tm.tm_year);
tree = mktree(repo, tree, "%02d", tm.tm_mon + 1);
if (trip) {
diff --git a/core/save-html.c b/core/save-html.c
index 2d0ea9cf3..4f811558a 100644
--- a/core/save-html.c
+++ b/core/save-html.c
@@ -201,7 +201,7 @@ void put_HTML_date(struct membuffer *b, struct dive *dive, const char *pre, cons
{
struct tm tm;
utc_mkdate(dive->when, &tm);
- put_format(b, "%s%04u-%02u-%02u%s", pre, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, post);
+ put_format(b, "%s%04u-%02u-%02u%s", pre, tm.tm_year, tm.tm_mon + 1, tm.tm_mday, post);
}
void put_HTML_quoted(struct membuffer *b, const char *text)
diff --git a/core/save-xml.c b/core/save-xml.c
index 80dda63b8..c9b6a5b99 100644
--- a/core/save-xml.c
+++ b/core/save-xml.c
@@ -323,7 +323,7 @@ static void show_date(struct membuffer *b, timestamp_t when)
utc_mkdate(when, &tm);
put_format(b, " date='%04u-%02u-%02u'",
- tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
+ tm.tm_year, tm.tm_mon + 1, tm.tm_mday);
put_format(b, " time='%02u:%02u:%02u'",
tm.tm_hour, tm.tm_min, tm.tm_sec);
}
diff --git a/core/statistics.c b/core/statistics.c
index 6a05cffc1..1b9865443 100644
--- a/core/statistics.c
+++ b/core/statistics.c
@@ -160,10 +160,10 @@ void process_all_dives(struct dive *dive, struct dive **prev_dive)
/* yearly statistics */
utc_mkdate(dp->when, &tm);
if (current_year == 0)
- current_year = tm.tm_year + 1900;
+ current_year = tm.tm_year;
- if (current_year != tm.tm_year + 1900) {
- current_year = tm.tm_year + 1900;
+ if (current_year != tm.tm_year) {
+ current_year = tm.tm_year;
process_dive(dp, &(stats_yearly[++year_iter]));
stats_yearly[year_iter].is_year = true;
} else {
diff --git a/core/subsurface-qt/DiveObjectHelper.cpp b/core/subsurface-qt/DiveObjectHelper.cpp
index dc22e59a2..e2847f7c9 100644
--- a/core/subsurface-qt/DiveObjectHelper.cpp
+++ b/core/subsurface-qt/DiveObjectHelper.cpp
@@ -73,7 +73,7 @@ int DiveObjectHelper::id() const
QString DiveObjectHelper::date() const
{
- QDateTime localTime = QDateTime::fromTime_t(m_dive->when - gettimezoneoffset(m_dive->when));
+ QDateTime localTime = QDateTime::fromMSecsSinceEpoch(1000*m_dive->when, Qt::UTC);
localTime.setTimeSpec(Qt::UTC);
return localTime.date().toString(prefs.date_format);
}
@@ -85,7 +85,7 @@ timestamp_t DiveObjectHelper::timestamp() const
QString DiveObjectHelper::time() const
{
- QDateTime localTime = QDateTime::fromTime_t(m_dive->when - gettimezoneoffset(m_dive->when));
+ QDateTime localTime = QDateTime::fromMSecsSinceEpoch(1000*m_dive->when, Qt::UTC);
localTime.setTimeSpec(Qt::UTC);
return localTime.time().toString(prefs.time_format);
}
@@ -288,13 +288,12 @@ QString DiveObjectHelper::tripMeta() const
QString title(dt->location);
if (title.isEmpty()) {
// so use the date range
- timestamp_t firstTime = dt->when - gettimezoneoffset(dt->when);
- QString firstMonth = QDateTime::fromTime_t(firstTime).toString("MMM");
- QString firstYear = QDateTime::fromTime_t(firstTime).toString("yyyy");
- struct dive *lastDive = dt->dives;
- timestamp_t lastTime = lastDive->when - gettimezoneoffset(lastDive->when);
- QString lastMonth = QDateTime::fromTime_t(lastTime).toString("MMM");
- QString lastYear = QDateTime::fromTime_t(lastTime).toString("yyyy");
+ QDateTime firstTime = QDateTime::fromMSecsSinceEpoch(1000*dt->when, Qt::UTC);
+ QString firstMonth = firstTime.toString("MMM");
+ QString firstYear = firstTime.toString("yyyy");
+ QDateTime lastTime = QDateTime::fromMSecsSinceEpoch(1000*dt->dives->when, Qt::UTC);
+ QString lastMonth = lastTime.toString("MMM");
+ QString lastYear = lastTime.toString("yyyy");
if (lastMonth == firstMonth && lastYear == firstYear)
title = firstMonth + " " + firstYear;
else if (lastMonth != firstMonth && lastYear == firstYear)
diff --git a/core/time.c b/core/time.c
index 0893f19d8..9992658b6 100644
--- a/core/time.c
+++ b/core/time.c
@@ -2,6 +2,24 @@
#include "dive.h"
/*
+ * The date handling internally works in seconds since
+ * Jan 1, 1900. That avoids negative numbers which avoids
+ * some silly problems.
+ *
+ * But we then use the same base epoch base (Jan 1, 1970)
+ * that POSIX uses, so that we can use the normal date
+ * handling functions for getting current time etc.
+ *
+ * There's 25567 dats from Jan 1, 1900 to Jan 1, 1970.
+ *
+ * NOTE! The SEC_PER_DAY is not so much because the
+ * number is complicated, as to make sure we always
+ * expand the type to "timestamp_t" in the arithmetic.
+ */
+#define SEC_PER_DAY ((timestamp_t) 24*60*60)
+#define EPOCH_OFFSET (25567 * SEC_PER_DAY)
+
+/*
* Convert 64-bit timestamp to 'struct tm' in UTC.
*
* On 32-bit machines, only do 64-bit arithmetic for the seconds
@@ -24,7 +42,14 @@ void utc_mkdate(timestamp_t timestamp, struct tm *tm)
memset(tm, 0, sizeof(*tm));
- /* seconds since 1970 -> minutes since 1970 */
+ // Midnight at Jan 1, 1970 means "no date"
+ if (!timestamp)
+ return;
+
+ /* Convert to seconds since 1900 */
+ timestamp += EPOCH_OFFSET;
+
+ /* minutes since 1900 */
tm->tm_sec = timestamp % 60;
val = timestamp /= 60;
@@ -34,19 +59,21 @@ void utc_mkdate(timestamp_t timestamp, struct tm *tm)
tm->tm_hour = val % 24;
val /= 24;
- /* Jan 1, 1970 was a Thursday (tm_wday=4) */
- tm->tm_wday = (val + 4) % 7;
+ /* Jan 1, 1900 was a Monday (tm_wday=1) */
+ tm->tm_wday = (val + 1) % 7;
/*
- * Now we're in "days since Jan 1, 1970". To make things easier,
- * let's make it "days since Jan 1, 1968", since that's a leap-year
+ * Now we're in "days since Jan 1, 1900". To make things easier,
+ * let's make it "days since Jan 1, 1904", since that's a leap-year.
+ * 1900 itself was not. The following logic will get 1900-1903
+ * wrong. If you were diving back then, you're kind of screwed.
*/
- val += 365 + 366;
+ val -= 365*4;
/* This only works up until 2099 (2100 isn't a leap-year) */
leapyears = val / (365 * 4 + 1);
val %= (365 * 4 + 1);
- tm->tm_year = 68 + leapyears * 4;
+ tm->tm_year = 1904 + leapyears * 4;
/* Handle the leap-year itself */
mp = mdays_leap;
@@ -75,24 +102,33 @@ timestamp_t utc_mktime(struct tm *tm)
int year = tm->tm_year;
int month = tm->tm_mon;
int day = tm->tm_mday;
+ int days_since_1900;
+ timestamp_t when;
/* First normalize relative to 1900 */
- if (year < 70)
+ if (year < 50)
year += 100;
else if (year > 1900)
year -= 1900;
- /* Normalized to Jan 1, 1970: unix time */
- year -= 70;
-
- if (year < 0 || year > 129) /* algo only works for 1970-2099 */
- return -1;
+ if (year < 0 || year > 129) /* algo only works for 1900-2099 */
+ return 0;
if (month < 0 || month > 11) /* array bounds */
- return -1;
- if (month < 2 || (year + 2) % 4)
+ return 0;
+ if (month < 2 || (year && year % 4))
day--;
if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_sec < 0)
- return -1;
- return (year * 365 + (year + 1) / 4 + mdays[month] + day) * 24 * 60 * 60UL +
- tm->tm_hour * 60 * 60 + tm->tm_min * 60 + tm->tm_sec;
+ return 0;
+
+ /* This works until 2099 */
+ days_since_1900 = year * 365 + (year - 1) / 4;
+
+ /* Note the 'day' fixup for non-leapyears above */
+ days_since_1900 += mdays[month] + day;
+
+ /* Now add it all up, making sure to do this part in "timestamp_t" */
+ when = days_since_1900 * SEC_PER_DAY;
+ when += tm->tm_hour * 60 * 60 + tm->tm_min * 60 + tm->tm_sec;
+
+ return when - EPOCH_OFFSET;
}
diff --git a/core/uemis-downloader.c b/core/uemis-downloader.c
index b9b532303..925783a6d 100644
--- a/core/uemis-downloader.c
+++ b/core/uemis-downloader.c
@@ -86,7 +86,6 @@ static void uemis_ts(char *buffer, void *_when)
&tm.tm_year, &tm.tm_mon, &tm.tm_mday,
&tm.tm_hour, &tm.tm_min, &tm.tm_sec);
tm.tm_mon -= 1;
- tm.tm_year -= 1900;
*when = utc_mktime(&tm);
}
diff --git a/desktop-widgets/divelogimportdialog.cpp b/desktop-widgets/divelogimportdialog.cpp
index 757cad59e..ecc2d24fb 100644
--- a/desktop-widgets/divelogimportdialog.cpp
+++ b/desktop-widgets/divelogimportdialog.cpp
@@ -17,6 +17,7 @@ const DiveLogImportDialog::CSVAppConfig DiveLogImportDialog::CSVApps[CSVAPPS] =
{ "Manual import", SILENCE_WARNING },
{ "APD Log Viewer - DC1", 0, 1, 15, 6, 3, 4, 5, 17, -1, -1, 18, -1, 2, "Tab" },
{ "APD Log Viewer - DC2", 0, 1, 15, 6, 7, 8, 9, 17, -1, -1, 18, -1, 2, "Tab" },
+ { "DL7", 1, 2, -1, -1, -1, -1, -1, -1, -1, 8, -1, 10, -1, "|" },
{ "XP5", 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, "Tab" },
{ "SensusCSV", 9, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, "," },
{ "Seabear CSV", 0, 1, 5, -1, -1, -1, -1, -1, 2, 3, 4, 6, -1, ";" },
@@ -28,6 +29,7 @@ enum Known {
MANUAL,
APD,
APD2,
+ DL7,
XP5,
SENSUS,
SEABEAR,
@@ -343,11 +345,12 @@ DiveLogImportDialog::DiveLogImportDialog(QStringList fn, QWidget *parent) : QDia
/* Add indexes of XSLTs requiring special handling to the list */
specialCSV << SENSUS;
specialCSV << SUBSURFACE;
+ specialCSV << DL7;
for (int i = 0; !CSVApps[i].name.isNull(); ++i)
ui->knownImports->addItem(CSVApps[i].name);
- ui->CSVSeparator->addItems( QStringList() << tr("Tab") << "," << ";");
+ ui->CSVSeparator->addItems( QStringList() << tr("Tab") << "," << ";" << "|");
loadFileContents(-1, INITIAL);
@@ -386,6 +389,7 @@ void DiveLogImportDialog::loadFileContents(int value, whatChanged triggeredBy)
bool seabear = false;
bool xp5 = false;
bool apd = false;
+ bool dl7 = false;
// reset everything
ColumnNameProvider *provider = new ColumnNameProvider(this);
@@ -467,6 +471,23 @@ void DiveLogImportDialog::loadFileContents(int value, whatChanged triggeredBy)
blockSignals(true);
ui->knownImports->setCurrentText("XP5");
blockSignals(false);
+ } else if (firstLine.contains("FSH")) {
+ QString units = "Metric";
+ dl7 = true;
+ while ((firstLine = f.readLine().trimmed()).length() > 0 && !f.atEnd()) {
+ /* DL7 actually defines individual units (e.g. depth, temp, pressure, etc.)
+ * and there are quite a few other options as well, but let's use metric
+ * unless depth unit is clearly Imperial. */
+
+ if (firstLine.contains("ThFt")) {
+ units = "Imperial";
+ }
+ }
+ firstLine = "|Sample time|Sample depth||||||Sample temperature||Sample pressure";
+ blockSignals(true);
+ ui->knownImports->setCurrentText("DL7");
+ ui->CSVUnits->setCurrentText(units);
+ blockSignals(false);
}
// Special handling for APD Log Viewer
@@ -487,11 +508,14 @@ void DiveLogImportDialog::loadFileContents(int value, whatChanged triggeredBy)
int tabs = firstLine.count('\t');
int commas = firstLine.count(',');
int semis = firstLine.count(';');
- if (tabs > commas && tabs > semis)
+ int pipes = firstLine.count('|');
+ if (tabs > commas && tabs > semis && tabs > pipes)
separator = "\t";
- else if (commas > tabs && commas > semis)
+ else if (commas > tabs && commas > semis && commas > pipes)
separator = ",";
- else if (semis > tabs && semis > commas)
+ else if (pipes > tabs && pipes > commas && pipes > semis)
+ separator = "|";
+ else if (semis > tabs && semis > commas && semis > pipes)
separator = ";";
if (ui->CSVSeparator->currentText() != separator) {
blockSignals(true);
@@ -548,7 +572,7 @@ void DiveLogImportDialog::loadFileContents(int value, whatChanged triggeredBy)
}
if (matchedSome) {
ui->dragInstructions->setText(tr("Some column headers were pre-populated; please drag and drop the headers so they match the column they are in."));
- if (triggeredBy != KNOWNTYPES && !seabear && !xp5 && !apd) {
+ if (triggeredBy != KNOWNTYPES && !seabear && !xp5 && !apd && !dl7) {
blockSignals(true);
ui->knownImports->setCurrentIndex(0); // <- that's "Manual import"
blockSignals(false);
@@ -662,6 +686,13 @@ void DiveLogImportDialog::loadFileContents(int value, whatChanged triggeredBy)
* actual data.
*/
while (strlen(f.readLine()) > 3 && !f.atEnd());
+ } else if (dl7) {
+ while ((firstLine = f.readLine().trimmed()).length() > 0 && !f.atEnd()) {
+ if (firstLine.contains("ZDP")) {
+ firstLine = f.readLine().trimmed();
+ break;
+ }
+ }
}
while (rows < 10 && !f.atEnd()) {
@@ -788,7 +819,7 @@ void DiveLogImportDialog::on_buttonBox_accepted()
sample->tts.seconds *= 60;
}
} else {
- char *params[37];
+ char *params[39];
int pnr = 0;
pnr = setup_csv_params(r, params, pnr);
@@ -855,7 +886,7 @@ void DiveLogImportDialog::on_buttonBox_accepted()
parse_manual_file(fileNames[i].toUtf8().data(), params, pnr - 1);
} else {
- char *params[37];
+ char *params[39];
int pnr = 0;
pnr = setup_csv_params(r, params, pnr);
diff --git a/desktop-widgets/divelogimportdialog.h b/desktop-widgets/divelogimportdialog.h
index 811775379..750bc10fa 100644
--- a/desktop-widgets/divelogimportdialog.h
+++ b/desktop-widgets/divelogimportdialog.h
@@ -116,7 +116,7 @@ private:
QString separator;
};
-#define CSVAPPS 8
+#define CSVAPPS 9
static const CSVAppConfig CSVApps[CSVAPPS];
};
diff --git a/desktop-widgets/maintab.cpp b/desktop-widgets/maintab.cpp
index a3cc151ef..f6e5b7dc6 100644
--- a/desktop-widgets/maintab.cpp
+++ b/desktop-widgets/maintab.cpp
@@ -493,7 +493,7 @@ void MainTab::updateDiveInfo(bool clear)
// Subsurface always uses "local time" as in "whatever was the local time at the location"
// so all time stamps have no time zone information and are in UTC
- QDateTime localTime = QDateTime::fromTime_t(displayed_dive.when - gettimezoneoffset(displayed_dive.when));
+ QDateTime localTime = QDateTime::fromMSecsSinceEpoch(1000*displayed_dive.when, Qt::UTC);
localTime.setTimeSpec(Qt::UTC);
ui.dateEdit->setDate(localTime.date());
ui.timeEdit->setTime(localTime.time());
@@ -1284,7 +1284,7 @@ void MainTab::on_dateEdit_dateChanged(const QDate &date)
if (editMode == IGNORE || acceptingEdit == true)
return;
markChangedWidget(ui.dateEdit);
- QDateTime dateTime = QDateTime::fromTime_t(displayed_dive.when - gettimezoneoffset(displayed_dive.when));
+ QDateTime dateTime = QDateTime::fromMSecsSinceEpoch(1000*displayed_dive.when, Qt::UTC);
dateTime.setTimeSpec(Qt::UTC);
dateTime.setDate(date);
DivePlannerPointsModel::instance()->getDiveplan().when = displayed_dive.when = dateTime.toTime_t();
@@ -1296,7 +1296,7 @@ void MainTab::on_timeEdit_timeChanged(const QTime &time)
if (editMode == IGNORE || acceptingEdit == true)
return;
markChangedWidget(ui.timeEdit);
- QDateTime dateTime = QDateTime::fromTime_t(displayed_dive.when - gettimezoneoffset(displayed_dive.when));
+ QDateTime dateTime = QDateTime::fromMSecsSinceEpoch(1000*displayed_dive.when, Qt::UTC);
dateTime.setTimeSpec(Qt::UTC);
dateTime.setTime(time);
DivePlannerPointsModel::instance()->getDiveplan().when = displayed_dive.when = dateTime.toTime_t();
diff --git a/desktop-widgets/mainwindow.cpp b/desktop-widgets/mainwindow.cpp
index a86901f98..c6624df5a 100644
--- a/desktop-widgets/mainwindow.cpp
+++ b/desktop-widgets/mainwindow.cpp
@@ -1792,8 +1792,8 @@ void MainWindow::loadFiles(const QStringList fileNames)
void MainWindow::on_actionImportDiveLog_triggered()
{
QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Open dive log file"), lastUsedDir(),
- tr("Dive log files (*.ssrf *.can *.csv *.db *.sql *.dld *.jlb *.lvd *.sde *.udcf *.uddf *.xml *.txt *.dlf *.apd"
- "*.SSRF *.CAN *.CSV *.DB *.SQL *.DLD *.JLB *.LVD *.SDE *.UDCF *.UDDF *.xml *.TXT *.DLF *.APD);;"
+ tr("Dive log files (*.ssrf *.can *.csv *.db *.sql *.dld *.jlb *.lvd *.sde *.udcf *.uddf *.xml *.txt *.dlf *.apd *.zxu *.zxl"
+ "*.SSRF *.CAN *.CSV *.DB *.SQL *.DLD *.JLB *.LVD *.SDE *.UDCF *.UDDF *.xml *.TXT *.DLF *.APD *.ZXU *.ZXL);;"
"Cochran files (*.can *.CAN);;"
"CSV files (*.csv *.CSV);;"
"DiveLog.de files (*.dld *.DLD);;"
@@ -1807,15 +1807,18 @@ void MainWindow::on_actionImportDiveLog_triggered()
"APD log viewer (*.apd *.APD);;"
"Datatrak/WLog Files (*.log *.LOG);;"
"OSTCtools Files (*.dive *.DIVE);;"
+ "DAN DL7 (*.zxu *.zxl *.ZXU *.ZXL);;"
"All files (*)"));
if (fileNames.isEmpty())
return;
updateLastUsedDir(QFileInfo(fileNames[0]).dir().path());
- QStringList logFiles = fileNames.filter(QRegExp("^.*\\.(?!(csv|txt|apd))", Qt::CaseInsensitive));
+ QStringList logFiles = fileNames.filter(QRegExp("^(?!.*\\.(csv|txt|apd|zxu|zxl))", Qt::CaseInsensitive));
QStringList csvFiles = fileNames.filter(".csv", Qt::CaseInsensitive);
csvFiles += fileNames.filter(".apd", Qt::CaseInsensitive);
+ csvFiles += fileNames.filter(".zxu", Qt::CaseInsensitive);
+ csvFiles += fileNames.filter(".zxl", Qt::CaseInsensitive);
QStringList txtFiles = fileNames.filter(".txt", Qt::CaseInsensitive);
if (logFiles.size()) {
diff --git a/dives/test0.xml b/dives/test0.xml
index 6ade06519..75ad23e7d 100644
--- a/dives/test0.xml
+++ b/dives/test0.xml
@@ -1,6 +1,6 @@
<dives>
<program name='subsurface' version='1'></program>
- <dive number='0' date='2011-01-01' time='07:00:00' duration='30:00 min'>
+ <dive number='0' date='1968-01-01' time='07:00:00' duration='30:00 min'>
<depth max='30.00 m' mean='15.00 m' />
</dive>
</dives>
diff --git a/mobile-widgets/qml/DiveList.qml b/mobile-widgets/qml/DiveList.qml
index 436bf3d8d..9ffb2ee88 100644
--- a/mobile-widgets/qml/DiveList.qml
+++ b/mobile-widgets/qml/DiveList.qml
@@ -236,6 +236,9 @@ Kirigami.ScrollablePage {
StartPage {
id: startPage
}
+ Component.onCompleted: {
+ page.actions.main = page.saveAction
+ }
}
ListView {
diff --git a/mobile-widgets/qml/main.qml b/mobile-widgets/qml/main.qml
index 1927d6224..b4ab897e9 100644
--- a/mobile-widgets/qml/main.qml
+++ b/mobile-widgets/qml/main.qml
@@ -12,9 +12,11 @@ Kirigami.ApplicationWindow {
id: rootItem
title: qsTr("Subsurface-mobile")
- header.minimumHeight: 0
- header.preferredHeight: Kirigami.Units.gridUnit * (Qt.platform.os == "ios" ? 2 : 1)
- header.maximumHeight: Kirigami.Units.gridUnit * 2
+ header: Kirigami.ApplicationHeader {
+ minimumHeight: 0
+ preferredHeight: Kirigami.Units.gridUnit * (Qt.platform.os == "ios" ? 2 : 1)
+ maximumHeight: Kirigami.Units.gridUnit * 2
+ }
property bool fullscreen: true
property alias oldStatus: manager.oldStatus
property alias accessingCloud: manager.accessingCloud
diff --git a/mobile-widgets/qml/mobile-resources.qrc b/mobile-widgets/qml/mobile-resources.qrc
index 4fe82a66a..ab556789b 100644
--- a/mobile-widgets/qml/mobile-resources.qrc
+++ b/mobile-widgets/qml/mobile-resources.qrc
@@ -36,6 +36,8 @@
<file alias="org/kde/kirigami/Heading.qml">kirigami/Heading.qml</file>
<file alias="org/kde/kirigami/OverlaySheet.qml">kirigami/OverlaySheet.qml</file>
<file alias="org/kde/kirigami/ApplicationHeader.qml">kirigami/ApplicationHeader.qml</file>
+ <file alias="org/kde/kirigami/AbstractApplicationHeader.qml">kirigami/AbstractApplicationHeader.qml</file>
+ <file alias="org/kde/kirigami/AbstractApplicationWindow.qml">kirigami/AbstractApplicationWindow.qml</file>
<file alias="org/kde/kirigami/private/PageRow.qml">kirigami/private/PageRow.qml</file>
<file alias="org/kde/kirigami/Label.qml">kirigami/Label.qml</file>
<file alias="org/kde/kirigami/AbstractListItem.qml">kirigami/AbstractListItem.qml</file>
@@ -50,7 +52,7 @@
<file alias="org/kde/kirigami/private/DefaultListItemBackground.qml">kirigami/private/DefaultListItemBackground.qml</file>
<file alias="org/kde/kirigami/private/EdgeShadow.qml">kirigami/private/EdgeShadow.qml</file>
<file alias="org/kde/kirigami/private/MenuIcon.qml">kirigami/private/MenuIcon.qml</file>
- <file alias="org/kde/kirigami/private/PageStack.js">kirigami/private/PageStack.js</file>
+ <file alias="org/kde/kirigami/private/DefaultListItemBackground.qml">kirigami/private/DefaultListItemBackground.qml</file>
<file alias="org/kde/kirigami/private/PageActionPropertyGroup.qml">kirigami/private/PageActionPropertyGroup.qml</file>
<file alias="org/kde/kirigami/private/PassiveNotification.qml">kirigami/private/PassiveNotification.qml</file>
<file alias="org/kde/kirigami/private/RefreshableScrollView.qml">kirigami/private/RefreshableScrollView.qml</file>
diff --git a/packaging/ubuntu/make-package.sh b/packaging/ubuntu/make-package.sh
index b29c32021..a825c5151 100644
--- a/packaging/ubuntu/make-package.sh
+++ b/packaging/ubuntu/make-package.sh
@@ -99,6 +99,12 @@ rel=wily
sed -i "s/${prev}/${rel}/g" debian/changelog
debuild -S
+# and now for wily
+prev=wily
+rel=xenial
+sed -i "s/${prev}/${rel}/g" debian/changelog
+debuild -S
+
# and now for precise (precise can't build Qt5 based packages)
# with the switch to cmake the amount of effort to build Qt4 packages
# on precise just doesn't seem worth it anymore
diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp
index b1ce0be24..350d15aa4 100644
--- a/qt-models/cylindermodel.cpp
+++ b/qt-models/cylindermodel.cpp
@@ -273,7 +273,10 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in
break;
case USE:
if (CHANGED()) {
- cyl->cylinder_use = (enum cylinderuse)vString.toInt();
+ int use = vString.toInt();
+ if (use > NUM_GAS_USE - 1 || use < 0)
+ use = 0;
+ cyl->cylinder_use = (enum cylinderuse)use;
changed = true;
}
break;
diff --git a/xslt/csv2xml.xslt b/xslt/csv2xml.xslt
index 903f45535..0f8362936 100644
--- a/xslt/csv2xml.xslt
+++ b/xslt/csv2xml.xslt
@@ -30,6 +30,7 @@
<xsl:choose>
<xsl:when test="$separatorIndex = 0"><xsl:text> </xsl:text></xsl:when>
<xsl:when test="$separatorIndex = 2"><xsl:text>;</xsl:text></xsl:when>
+ <xsl:when test="$separatorIndex = 3"><xsl:text>|</xsl:text></xsl:when>
<xsl:otherwise><xsl:text>,</xsl:text></xsl:otherwise>
</xsl:choose>
</xsl:variable>
@@ -165,7 +166,15 @@
<xsl:call-template name="sec2time">
<xsl:with-param name="timeSec">
- <xsl:value-of select="$value"/>
+ <xsl:choose>
+ <xsl:when test="substring-after($value, '.') != ''">
+ <!-- Well, I suppose it was min.sec -->
+ <xsl:value-of select="substring-before($value, '.') * 60 + substring-after($value, '.')" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$value"/>
+ </xsl:otherwise>
+ </xsl:choose>
</xsl:with-param>
</xsl:call-template>
</xsl:when>