diff options
-rw-r--r-- | core/divelist.c | 4 | ||||
-rw-r--r-- | core/file.c | 87 | ||||
-rw-r--r-- | core/git-access.c | 21 | ||||
-rw-r--r-- | core/imagedownloader.cpp | 2 | ||||
-rw-r--r-- | core/load-git.c | 6 | ||||
-rw-r--r-- | core/parse-xml.c | 2 | ||||
-rw-r--r-- | core/planner.c | 2 | ||||
-rw-r--r-- | core/qthelper.cpp | 22 | ||||
-rw-r--r-- | core/save-git.c | 8 | ||||
-rw-r--r-- | core/save-html.c | 2 | ||||
-rw-r--r-- | core/save-xml.c | 2 | ||||
-rw-r--r-- | core/statistics.c | 6 | ||||
-rw-r--r-- | core/subsurface-qt/DiveObjectHelper.cpp | 17 | ||||
-rw-r--r-- | core/time.c | 72 | ||||
-rw-r--r-- | core/uemis-downloader.c | 1 | ||||
-rw-r--r-- | desktop-widgets/divelogimportdialog.cpp | 45 | ||||
-rw-r--r-- | desktop-widgets/divelogimportdialog.h | 2 | ||||
-rw-r--r-- | desktop-widgets/maintab.cpp | 6 | ||||
-rw-r--r-- | desktop-widgets/mainwindow.cpp | 9 | ||||
-rw-r--r-- | dives/test0.xml | 2 | ||||
-rw-r--r-- | mobile-widgets/qml/DiveList.qml | 3 | ||||
-rw-r--r-- | mobile-widgets/qml/main.qml | 8 | ||||
-rw-r--r-- | mobile-widgets/qml/mobile-resources.qrc | 4 | ||||
-rw-r--r-- | packaging/ubuntu/make-package.sh | 6 | ||||
-rw-r--r-- | qt-models/cylindermodel.cpp | 5 | ||||
-rw-r--r-- | xslt/csv2xml.xslt | 11 |
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> |