diff options
-rw-r--r-- | Documentation/user-manual.txt | 30 | ||||
-rw-r--r-- | file.c | 218 | ||||
-rw-r--r-- | parse-xml.c | 2 | ||||
-rw-r--r-- | planner.c | 5 | ||||
-rw-r--r-- | pref.h | 1 | ||||
-rw-r--r-- | qt-ui/divelogimportdialog.cpp | 53 | ||||
-rw-r--r-- | qt-ui/divelogimportdialog.h | 2 | ||||
-rw-r--r-- | qt-ui/diveplanner.cpp | 6 | ||||
-rw-r--r-- | qt-ui/preferences.cpp | 40 | ||||
-rw-r--r-- | qt-ui/preferences.h | 1 | ||||
-rw-r--r-- | qt-ui/preferences.ui | 11 | ||||
-rw-r--r-- | qt-ui/subsurfacewebservices.cpp | 14 | ||||
-rw-r--r-- | qt-ui/subsurfacewebservices.h | 3 |
13 files changed, 246 insertions, 140 deletions
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index 99ab33a32..566e19ed9 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -3961,21 +3961,27 @@ which can be imported into _Subsurface_. 4. The password for accessing the .zip file is _mares_. [[S_ImportingDivingLog]] -=== Exporting from *DivingLog 5.0* +=== Exporting from *DivingLog 5.0 and 6.0* [icon="images/icons/divingloglogo.jpg"] [NOTE] -Unfortunately DivingLog XML files give us no -indication on the preferences set on one's system. So in order for -_Subsurface_ to be able to successfully import XML files from DivingLog -one first needs to ensure that DivingLog is configured -to use the Metric system (one can easily change this within Diving Log by -selecting 'File -> Preferences -> Units and Language' by clicking the 'Metric' -button). Then do the following: - -1. In DivingLog open the 'File -> Export -> XML' menu -2. Select the dives to export -3. Click on the export button and select the filename +The best way to bring your logs from DivingLog to Subsurface is to +convert the whole database. This is because other export formats do not +include all the details, and we would lack e.g. gas switches and +information of what units are used. With database import, all this +information is included and readily available for us. + +To transfer all files from DivingLog to Subsurface, do the following: + +1. In DivingLog open the 'File -> Export -> SQLite' menu +2. Select 'Settings' button +3. Set the 'RTF2Plaintext' to 'true' +4. Close the Settings dialog +5. Click 'Export' button and select the filename + +Once this is done, open the saved database file with Subsurface and the +dives are automatically converted to our own format. Last step to do is +save the log file in Subsurface. [[S_Appendix_D]] == APPENDIX D: Exporting a spreadsheet to CSV format @@ -824,98 +824,101 @@ int parse_txt_file(const char *filename, const char *csv) return 0; } -#define MAXCOLDIGITS 3 +#define MAXCOLDIGITS 10 #define MAXCOLS 100 #define DATESTR 9 #define TIMESTR 6 -void init_csv_file_parsing(char **params, char *timebuf, char *depthbuf, char *tempbuf, char *po2buf, char *o2sensor1buf, char *o2sensor2buf, char *o2sensor3buf, char *cnsbuf, char *ndlbuf, char *ttsbuf, char *stopdepthbuf, char *pressurebuf, char *setpointbuf, char *unitbuf, char *separator_index, time_t *now, struct tm *timep, char *curdate, char *curtime, int timef, int depthf, int tempf, int po2f, int o2sensor1f, int o2sensor2f, int o2sensor3f, int cnsf, int ndlf, int ttsf, int stopdepthf, int pressuref, int setpointf, int sepidx, const char *csvtemplate, int unitidx) + +int init_csv_file_parsing(char **params, time_t *now, struct tm *timep, int timef, int depthf, int tempf, int po2f, int o2sensor1f, int o2sensor2f, int o2sensor3f, int cnsf, int ndlf, int ttsf, int stopdepthf, int pressuref, int setpointf, int sepidx, const char *csvtemplate, int unitidx) { int pnr = 0; + char tmpbuf[MAXCOLDIGITS]; - snprintf(timebuf, MAXCOLDIGITS, "%d", timef); - snprintf(depthbuf, MAXCOLDIGITS, "%d", depthf); - snprintf(tempbuf, MAXCOLDIGITS, "%d", tempf); - snprintf(po2buf, MAXCOLDIGITS, "%d", po2f); - snprintf(o2sensor1buf, MAXCOLDIGITS, "%d", o2sensor1f); - snprintf(o2sensor2buf, MAXCOLDIGITS, "%d", o2sensor2f); - snprintf(o2sensor3buf, MAXCOLDIGITS, "%d", o2sensor3f); - snprintf(cnsbuf, MAXCOLDIGITS, "%d", cnsf); - snprintf(ndlbuf, MAXCOLDIGITS, "%d", ndlf); - snprintf(ttsbuf, MAXCOLDIGITS, "%d", ttsf); - snprintf(stopdepthbuf, MAXCOLDIGITS, "%d", stopdepthf); - snprintf(pressurebuf, MAXCOLDIGITS, "%d", pressuref); - snprintf(setpointbuf, MAXCOLDIGITS, "%d", setpointf); - snprintf(separator_index, MAXCOLDIGITS, "%d", sepidx); - snprintf(unitbuf, MAXCOLDIGITS, "%d", unitidx); - time(now); - timep = localtime(now); - strftime(curdate, DATESTR, "%Y%m%d", timep); - - /* 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(curtime, TIMESTR, "1%H%M", timep); - + snprintf(tmpbuf, MAXCOLDIGITS, "%d", timef); params[pnr++] = "timeField"; - params[pnr++] = timebuf; + params[pnr++] = strdup(tmpbuf); + + snprintf(tmpbuf, MAXCOLDIGITS, "%d", depthf); params[pnr++] = "depthField"; - params[pnr++] = depthbuf; + params[pnr++] = strdup(tmpbuf); + + snprintf(tmpbuf, MAXCOLDIGITS, "%d", tempf); params[pnr++] = "tempField"; - params[pnr++] = tempbuf; + params[pnr++] = strdup(tmpbuf); + + snprintf(tmpbuf, MAXCOLDIGITS, "%d", po2f); params[pnr++] = "po2Field"; - params[pnr++] = po2buf; + params[pnr++] = strdup(tmpbuf); + + snprintf(tmpbuf, MAXCOLDIGITS, "%d", o2sensor1f); params[pnr++] = "o2sensor1Field"; - params[pnr++] = o2sensor1buf; + params[pnr++] = strdup(tmpbuf); + + snprintf(tmpbuf, MAXCOLDIGITS, "%d", o2sensor2f); params[pnr++] = "o2sensor2Field"; - params[pnr++] = o2sensor2buf; + params[pnr++] = strdup(tmpbuf); + + snprintf(tmpbuf, MAXCOLDIGITS, "%d", o2sensor3f); params[pnr++] = "o2sensor3Field"; - params[pnr++] = o2sensor3buf; + params[pnr++] = strdup(tmpbuf); + + snprintf(tmpbuf, MAXCOLDIGITS, "%d", cnsf); params[pnr++] = "cnsField"; - params[pnr++] = cnsbuf; + params[pnr++] = strdup(tmpbuf); + + snprintf(tmpbuf, MAXCOLDIGITS, "%d", ndlf); params[pnr++] = "ndlField"; - params[pnr++] = ndlbuf; + params[pnr++] = strdup(tmpbuf); + + snprintf(tmpbuf, MAXCOLDIGITS, "%d", ttsf); params[pnr++] = "ttsField"; - params[pnr++] = ttsbuf; + params[pnr++] = strdup(tmpbuf); + + snprintf(tmpbuf, MAXCOLDIGITS, "%d", stopdepthf); params[pnr++] = "stopdepthField"; - params[pnr++] = stopdepthbuf; + params[pnr++] = strdup(tmpbuf); + + snprintf(tmpbuf, MAXCOLDIGITS, "%d", pressuref); params[pnr++] = "pressureField"; - params[pnr++] = pressurebuf; + params[pnr++] = strdup(tmpbuf); + + snprintf(tmpbuf, MAXCOLDIGITS, "%d", setpointf); params[pnr++] = "setpointField"; - params[pnr++] = setpointbuf; + params[pnr++] = strdup(tmpbuf); + + snprintf(tmpbuf, MAXCOLDIGITS, "%d", sepidx); + params[pnr++] = "separatorIndex"; + params[pnr++] = strdup(tmpbuf); + + snprintf(tmpbuf, MAXCOLDIGITS, "%d", unitidx); + params[pnr++] = "units"; + params[pnr++] = strdup(tmpbuf); + + time(now); + timep = localtime(now); + + strftime(tmpbuf, MAXCOLDIGITS, "%Y%m%d", timep); params[pnr++] = "date"; - params[pnr++] = curdate; + 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++] = curtime; - params[pnr++] = "units"; - params[pnr++] = unitbuf; - params[pnr++] = "separatorIndex"; - params[pnr++] = separator_index; + params[pnr++] = strdup(tmpbuf); + params[pnr++] = NULL; + + return pnr - 1; } int parse_csv_file(const char *filename, int timef, int depthf, int tempf, int po2f, int o2sensor1f, int o2sensor2f, int o2sensor3f, int cnsf, int ndlf, int ttsf, int stopdepthf, int pressuref, int setpointf, int sepidx, const char *csvtemplate, int unitidx) { - int ret; + int ret, i; struct memblock mem; char *params[35]; - char timebuf[MAXCOLDIGITS]; - char depthbuf[MAXCOLDIGITS]; - char tempbuf[MAXCOLDIGITS]; - char po2buf[MAXCOLDIGITS]; - char o2sensor1buf[MAXCOLDIGITS]; - char o2sensor2buf[MAXCOLDIGITS]; - char o2sensor3buf[MAXCOLDIGITS]; - char cnsbuf[MAXCOLDIGITS]; - char ndlbuf[MAXCOLDIGITS]; - char ttsbuf[MAXCOLDIGITS]; - char stopdepthbuf[MAXCOLDIGITS]; - char pressurebuf[MAXCOLDIGITS]; - char setpointbuf[MAXCOLDIGITS]; - char unitbuf[MAXCOLDIGITS]; - char separator_index[MAXCOLDIGITS]; time_t now; struct tm *timep = NULL; - char curdate[DATESTR]; - char curtime[TIMESTR]; int previous; /* Increase the limits for recursion and variables on XSLT @@ -928,7 +931,7 @@ int parse_csv_file(const char *filename, int timef, int depthf, int tempf, int p if (timef >= MAXCOLS || depthf >= MAXCOLS || tempf >= MAXCOLS || po2f >= MAXCOLS || o2sensor1f >= MAXCOLS || o2sensor2f >= MAXCOLS || o2sensor3f >= MAXCOLS || cnsf >= MAXCOLS || ndlf >= MAXCOLS || cnsf >= MAXCOLS || stopdepthf >= MAXCOLS || pressuref >= MAXCOLS || setpointf >= MAXCOLS) return report_error(translate("gettextFromC", "Maximum number of supported columns on CSV import is %d"), MAXCOLS); - init_csv_file_parsing(params, timebuf, depthbuf, tempbuf, po2buf, o2sensor1buf, o2sensor2buf, o2sensor3buf, cnsbuf, ndlbuf, ttsbuf, stopdepthbuf, pressurebuf, setpointbuf, unitbuf, separator_index, &now, timep, curdate, curtime, timef, depthf, tempf, po2f, o2sensor1f, o2sensor2f, o2sensor3f, cnsf, ndlf, ttsf, stopdepthf, pressuref, setpointf, sepidx, csvtemplate, unitidx); + init_csv_file_parsing(params, &now, timep, timef, depthf, tempf, po2f, o2sensor1f, o2sensor2f, o2sensor3f, cnsf, ndlf, ttsf, stopdepthf, pressuref, setpointf, sepidx, csvtemplate, unitidx); if (filename == NULL) return report_error("No CSV filename"); @@ -941,42 +944,35 @@ int parse_csv_file(const char *filename, int timef, int depthf, int tempf, int p ret = parse_xml_buffer(filename, mem.buffer, mem.size, &dive_table, (const char **)params); free(mem.buffer); + for (i = 0; params[i]; i += 2) + free(params[i + 1]); + return ret; } -#define SBPARAMS 35 +#define SBPARAMS 38 int parse_seabear_csv_file(const char *filename, int timef, int depthf, int tempf, int po2f, int o2sensor1f, int o2sensor2f, int o2sensor3f, int cnsf, int ndlf, int ttsf, int stopdepthf, int pressuref, int sepidx, const char *csvtemplate, int unitidx, const char *delta) { - int ret; + int ret, i, pnr; struct memblock mem; char *params[SBPARAMS]; - char timebuf[MAXCOLDIGITS]; - char depthbuf[MAXCOLDIGITS]; - char tempbuf[MAXCOLDIGITS]; - char po2buf[MAXCOLDIGITS]; - char o2sensor1buf[MAXCOLDIGITS]; - char o2sensor2buf[MAXCOLDIGITS]; - char o2sensor3buf[MAXCOLDIGITS]; - char cnsbuf[MAXCOLDIGITS]; - char ndlbuf[MAXCOLDIGITS]; - char ttsbuf[MAXCOLDIGITS]; - char stopdepthbuf[MAXCOLDIGITS]; - char pressurebuf[MAXCOLDIGITS]; - char setpointbuf[MAXCOLDIGITS]; - char unitbuf[MAXCOLDIGITS]; - char separator_index[MAXCOLDIGITS]; char deltabuf[MAXCOLDIGITS]; time_t now; struct tm *timep = NULL; - char curdate[DATESTR]; - char curtime[TIMESTR]; char *ptr, *ptr_old = NULL; char *NL = NULL; + /* Increase the limits for recursion and variables on XSLT + * parsing */ + xsltMaxDepth = 30000; +#if LIBXSLT_VERSION > 10126 + xsltMaxVars = 150000; +#endif + if (timef >= MAXCOLS || depthf >= MAXCOLS || tempf >= MAXCOLS || po2f >= MAXCOLS || o2sensor1f >= MAXCOLS || o2sensor2f >= MAXCOLS || o2sensor3f >= MAXCOLS || cnsf >= MAXCOLS || ndlf >= MAXCOLS || cnsf >= MAXCOLS || stopdepthf >= MAXCOLS || pressuref >= MAXCOLS) return report_error(translate("gettextFromC", "Maximum number of supported columns on CSV import is %d"), MAXCOLS); - init_csv_file_parsing(params, timebuf, depthbuf, tempbuf, po2buf, o2sensor1buf, o2sensor2buf, o2sensor3buf, cnsbuf, ndlbuf, ttsbuf, stopdepthbuf, pressurebuf, setpointbuf, unitbuf, separator_index, &now, timep, curdate, curtime, timef, depthf, tempf, po2f, o2sensor1f, o2sensor2f, o2sensor3f, cnsf, ndlf, ttsf, stopdepthf, pressuref, -1, sepidx, csvtemplate, unitidx); + pnr = init_csv_file_parsing(params, &now, timep, timef, depthf, tempf, po2f, o2sensor1f, o2sensor2f, o2sensor3f, cnsf, ndlf, ttsf, stopdepthf, pressuref, -1, sepidx, csvtemplate, unitidx); if (filename == NULL) return report_error("No CSV filename"); @@ -993,6 +989,7 @@ int parse_seabear_csv_file(const char *filename, int timef, int depthf, int temp } if (!ptr_old) { + ptr = mem.buffer; while ((ptr = strstr(ptr, "\n\n")) != NULL) { ptr_old = ptr; ptr += 1; @@ -1012,30 +1009,41 @@ int parse_seabear_csv_file(const char *filename, int timef, int depthf, int temp /* * On my current sample of Seabear DC log file, the date is * without any identifier. Thus we must search for the previous - * line and step through from there. + * line and step through from there. That is the line after + * Serial number. */ ptr = strstr(mem.buffer, "Serial number:"); if (ptr) ptr = strstr(ptr, NL); - /* Write date and time values to params array */ + /* + * Write date and time values to params array, if available in + * the CSV header + */ + if (ptr) { ptr += strlen(NL) + 2; - memcpy(params[19], ptr, 4); - memcpy(params[19] + 4, ptr + 5, 2); - memcpy(params[19] + 6, ptr + 8, 2); - params[19][8] = 0; - - params[21][0] = '1'; - memcpy(params[21] + 1, ptr + 11, 2); - memcpy(params[21] + 3, ptr + 14, 2); - params[21][5] = 0; + /* + * pnr is the index of NULL on the params as filled by + * the init function. The two last entries should be + * date and time. Here we overwrite them with the data + * from the CSV header. + */ + + memcpy(params[pnr - 3], ptr, 4); + memcpy(params[pnr - 3] + 4, ptr + 5, 2); + memcpy(params[pnr - 3] + 6, ptr + 8, 2); + params[pnr - 3][8] = 0; + + memcpy(params[pnr - 1] + 1, ptr + 11, 2); + memcpy(params[pnr - 1] + 3, ptr + 14, 2); + params[pnr - 1][5] = 0; } snprintf(deltabuf, MAXCOLDIGITS, "%s", delta); - params[SBPARAMS - 3] = "delta"; - params[SBPARAMS - 2] = deltabuf; - params[SBPARAMS - 1] = NULL; + params[pnr++] = "delta"; + params[pnr++] = strdup(deltabuf); + params[pnr++] = NULL; /* Move the CSV data to the start of mem buffer */ memmove(mem.buffer, ptr_old, mem.size - (ptr_old - (char*)mem.buffer)); @@ -1044,8 +1052,24 @@ int parse_seabear_csv_file(const char *filename, int timef, int depthf, int temp if (try_to_xslt_open_csv(filename, &mem, csvtemplate)) return -1; + /* + * Lets print command line for manual testing with xsltproc if + * verbosity level is high enough. The printed line needs the + * input file added as last parameter. + */ + + if (verbose >= 2) { + fprintf(stderr, "xsltproc "); + for (i=0; params[i]; i+=2) + fprintf(stderr, "--stringparam %s %s ", params[i], params[i+1]); + fprintf(stderr, "xslt/csv2xml.xslt\n"); + } + ret = parse_xml_buffer(filename, mem.buffer, mem.size, &dive_table, (const char **)params); free(mem.buffer); + for (i = 0; params[i]; i += 2) + free(params[i + 1]); + return ret; } diff --git a/parse-xml.c b/parse-xml.c index 3d0c6a1e5..429277f49 100644 --- a/parse-xml.c +++ b/parse-xml.c @@ -3109,7 +3109,7 @@ extern int divinglog_dive(void *param, int columns, char **data, char **column) utf8_string(data[4], &cur_dive->notes); if (data[5]) - cur_dive->dc.maxdepth.mm = atoi(data[5]) * 1000; + cur_dive->dc.maxdepth.mm = atof(data[5]) * 1000; if (data[6]) cur_dive->dc.duration.seconds = atoi(data[6]) * 60; @@ -542,7 +542,10 @@ static void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool snprintf(temp, sizeof(temp), translate("gettextFromC", "based on Buhlmann ZHL-16B with GFlow = %d and GFhigh = %d"), diveplan->gflow, diveplan->gfhigh); } else if (prefs.deco_mode == VPMB){ - strncat(temp, translate("gettextFromC", "based on VPM-B"), sizeof(temp) - 1); + snprintf(temp, sizeof(temp), translate("gettextFromC", "based on VPM-B")); + } else if (prefs.deco_mode == RECREATIONAL){ + snprintf(temp, sizeof(temp), translate("gettextFromC", "recreational mode based on Buhlmann ZHL-16B with GFlow = %d and GFhigh = %d"), + diveplan->gflow, diveplan->gfhigh); } len += snprintf(buffer + len, sizeof(buffer) - len, "<div><b>%s</b><br>%s</div><br>", translate("gettextFromC", "Subsurface dive plan"), temp); @@ -109,6 +109,7 @@ struct preferences { short default_file_behavior; facebook_prefs_t facebook; char *cloud_storage_password; + char *cloud_storage_newpassword; char *cloud_storage_email; char *cloud_storage_email_encoded; bool save_password_local; diff --git a/qt-ui/divelogimportdialog.cpp b/qt-ui/divelogimportdialog.cpp index ea783b093..d799bf17f 100644 --- a/qt-ui/divelogimportdialog.cpp +++ b/qt-ui/divelogimportdialog.cpp @@ -13,7 +13,8 @@ const DiveLogImportDialog::CSVAppConfig DiveLogImportDialog::CSVApps[CSVAPPS] = // time, depth, temperature, po2, sensor1, sensor2, sensor3, cns, ndl, tts, stopdepth, pressure, setpoint // indices are 0 based, -1 means the column doesn't exist { "Manual import", }, - { "APD Log Viewer", 0, 1, 15, 6, 3, 4, 5, 17, -1, -1, 18, -1, 2, "Tab" }, + { "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" }, { "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, ";" }, @@ -21,6 +22,16 @@ const DiveLogImportDialog::CSVAppConfig DiveLogImportDialog::CSVApps[CSVAPPS] = { NULL, } }; +static enum { + MANUAL, + APD, + APD2, + XP5, + SENSUS, + SEABEAR, + SUBSURFACE +} known; + ColumnNameProvider::ColumnNameProvider(QObject *parent) : QAbstractListModel(parent) { columnNames << tr("Dive #") << tr("Date") << tr("Time") << tr("Duration") << tr("Location") << tr("GPS") << tr("Weight") << tr("Cyl. size") << tr("Start pressure") << @@ -387,7 +398,7 @@ void DiveLogImportDialog::loadFileContents(int value, whatChanged triggeredBy) while ((firstLine = f.readLine()).length() > 3 && !f.atEnd()) { if (firstLine.contains("//Log interval: ")) - delta = firstLine.remove(QString::fromLatin1("//Log interval: ")).trimmed(); + delta = firstLine.remove(QString::fromLatin1("//Log interval: ")).trimmed().remove(QString::fromLatin1(" s")); } /* @@ -416,6 +427,8 @@ void DiveLogImportDialog::loadFileContents(int value, whatChanged triggeredBy) headers.append("Sample sensor3 pO₂"); } else if (columnText == "Ceiling") { headers.append("Sample ceiling"); + } else if (columnText == "Tank pressure") { + headers.append("Sample pressure"); } else { // We do not know about this value qDebug() << "Seabear import found an un-handled field: " << columnText; @@ -437,13 +450,13 @@ void DiveLogImportDialog::loadFileContents(int value, whatChanged triggeredBy) } // Special handling for APD Log Viewer - if ((triggeredBy == KNOWNTYPES && value == 1) || (triggeredBy == INITIAL && fileNames.first().endsWith(".apd", Qt::CaseInsensitive))) { + if ((triggeredBy == KNOWNTYPES && (value == APD || value == APD2)) || (triggeredBy == INITIAL && fileNames.first().endsWith(".apd", Qt::CaseInsensitive))) { apd=true; - firstLine = "Sample time\tSample depth\tSample setpoint\t\t\t\tSample pO₂\t\t\t\t\t\t\t\t\tSample temperature\t\tSample CNS\tSample stopdepth"; + firstLine = "Sample time\tSample depth\tSample setpoint\tSample sensor1 pO₂\tSample sensor2 pO₂\tSample sensor3 pO₂\tSample pO₂\t\t\t\t\t\t\t\t\tSample temperature\t\tSample CNS\tSample stopdepth"; blockSignals(true); ui->CSVSeparator->setCurrentText(tr("Tab")); if (triggeredBy == INITIAL && fileNames.first().contains(".apd", Qt::CaseInsensitive)) - ui->knownImports->setCurrentText("APD Log Viewer"); + ui->knownImports->setCurrentText("APD Log Viewer - DC1"); blockSignals(false); } @@ -467,13 +480,21 @@ void DiveLogImportDialog::loadFileContents(int value, whatChanged triggeredBy) currColumns = firstLine.split(separator); } } - if (triggeredBy == INITIAL || (triggeredBy == KNOWNTYPES && value == 0) || triggeredBy == SEPARATOR) { + if (triggeredBy == INITIAL || (triggeredBy == KNOWNTYPES && value == MANUAL) || triggeredBy == SEPARATOR) { // now try and guess the columns Q_FOREACH (QString columnText, currColumns) { - columnText.replace("\"", ""); - columnText.replace("number", "#", Qt::CaseInsensitive); - columnText.replace("2", "₂", Qt::CaseInsensitive); - columnText.replace("cylinder", "cyl.", Qt::CaseInsensitive); + /* + * We have to skip the conversion of 2 to ₂ for APD Log + * viewer as that would mess up the sensor numbering. We + * also know that the column headers do not need this + * conversion. + */ + if (triggeredBy == KNOWNTYPES && value != APD) { + columnText.replace("\"", ""); + columnText.replace("number", "#", Qt::CaseInsensitive); + columnText.replace("2", "₂", Qt::CaseInsensitive); + columnText.replace("cylinder", "cyl.", Qt::CaseInsensitive); + } int idx = provider->mymatch(columnText); if (idx >= 0) { QString foundHeading = provider->data(provider->index(idx, 0), Qt::DisplayRole).toString(); @@ -493,9 +514,9 @@ void DiveLogImportDialog::loadFileContents(int value, whatChanged triggeredBy) } } } - if (triggeredBy == KNOWNTYPES && value != 0) { + if (triggeredBy == KNOWNTYPES && value != MANUAL) { // an actual known type - if (value == 5) { + if (value == SUBSURFACE) { /* * Subsurface CSV file needs separator detection * as we used to default to comma but switched @@ -530,11 +551,11 @@ void DiveLogImportDialog::loadFileContents(int value, whatChanged triggeredBy) headers.replace(CSVApps[value].temperature, tr("Sample temperature")); if (CSVApps[value].po2 > -1 && CSVApps[value].po2 < currColumns.count()) headers.replace(CSVApps[value].po2, tr("Sample pO₂")); - if (CSVApps[value].po2 > -1 && CSVApps[value].po2 < currColumns.count()) + if (CSVApps[value].sensor1 > -1 && CSVApps[value].sensor1 < currColumns.count()) headers.replace(CSVApps[value].sensor1, tr("Sample sensor1 pO₂")); - if (CSVApps[value].po2 > -1 && CSVApps[value].po2 < currColumns.count()) + if (CSVApps[value].sensor2 > -1 && CSVApps[value].sensor2 < currColumns.count()) headers.replace(CSVApps[value].sensor2, tr("Sample sensor2 pO₂")); - if (CSVApps[value].po2 > -1 && CSVApps[value].po2 < currColumns.count()) + if (CSVApps[value].sensor3 > -1 && CSVApps[value].sensor3 < currColumns.count()) headers.replace(CSVApps[value].sensor3, tr("Sample sensor3 pO₂")); if (CSVApps[value].cns > -1 && CSVApps[value].cns < currColumns.count()) headers.replace(CSVApps[value].cns, tr("Sample CNS")); @@ -601,7 +622,7 @@ void DiveLogImportDialog::on_buttonBox_accepted() r.indexOf(tr("Sample stopdepth")), r.indexOf(tr("Sample pressure")), ui->CSVSeparator->currentIndex(), - specialCSV.contains(ui->knownImports->currentIndex()) ? CSVApps[ui->knownImports->currentIndex()].name.toUtf8().data() : "csv", + "csv", ui->CSVUnits->currentIndex(), delta.toUtf8().data() ) < 0) { diff --git a/qt-ui/divelogimportdialog.h b/qt-ui/divelogimportdialog.h index c5c750973..050090f97 100644 --- a/qt-ui/divelogimportdialog.h +++ b/qt-ui/divelogimportdialog.h @@ -114,7 +114,7 @@ private: QString separator; }; -#define CSVAPPS 7 +#define CSVAPPS 8 static const CSVAppConfig CSVApps[CSVAPPS]; }; diff --git a/qt-ui/diveplanner.cpp b/qt-ui/diveplanner.cpp index f6f6c3894..efa75ddec 100644 --- a/qt-ui/diveplanner.cpp +++ b/qt-ui/diveplanner.cpp @@ -256,7 +256,7 @@ PlannerSettingsWidget::PlannerSettingsWidget(QWidget *parent, Qt::WindowFlags f) ui.setupUi(this); QSettings s; - QStringList rebreater_modes; + QStringList rebreather_modes; s.beginGroup("Planner"); prefs.last_stop = s.value("last_stop", prefs.last_stop).toBool(); prefs.verbatim_plan = s.value("verbatim_plan", prefs.verbatim_plan).toBool(); @@ -302,8 +302,8 @@ PlannerSettingsWidget::PlannerSettingsWidget(QWidget *parent, Qt::WindowFlags f) ui.vpmb_deco->setChecked(prefs.deco_mode == VPMB); // should be the same order as in dive_comp_type! - rebreater_modes << tr("Open circuit") << tr("CCR") << tr("pSCR"); - ui.rebreathermode->insertItems(0, rebreater_modes); + rebreather_modes << tr("Open circuit") << tr("CCR") << tr("pSCR"); + ui.rebreathermode->insertItems(0, rebreather_modes); modeMapper = new QSignalMapper(this); connect(modeMapper, SIGNAL(mapped(int)) , plannerModel, SLOT(setDecoMode(int))); diff --git a/qt-ui/preferences.cpp b/qt-ui/preferences.cpp index b30e76e03..04fb3a825 100644 --- a/qt-ui/preferences.cpp +++ b/qt-ui/preferences.cpp @@ -110,6 +110,10 @@ void PreferencesDialog::cloudPinNeeded() ui.cloud_storage_pin->setVisible(prefs.cloud_verification_status == CS_NEED_TO_VERIFY); ui.cloud_storage_pin_label->setEnabled(prefs.cloud_verification_status == CS_NEED_TO_VERIFY); ui.cloud_storage_pin_label->setVisible(prefs.cloud_verification_status == CS_NEED_TO_VERIFY); + ui.cloud_storage_new_passwd->setEnabled(prefs.cloud_verification_status == CS_VERIFIED); + ui.cloud_storage_new_passwd->setVisible(prefs.cloud_verification_status == CS_VERIFIED); + ui.cloud_storage_new_passwd_label->setEnabled(prefs.cloud_verification_status == CS_VERIFIED); + ui.cloud_storage_new_passwd_label->setVisible(prefs.cloud_verification_status == CS_VERIFIED); if (prefs.cloud_verification_status == CS_VERIFIED) { ui.cloudStorageGroupBox->setTitle(tr("Subsurface cloud storage (credentials verified)")); ui.cloudDefaultFile->setEnabled(true); @@ -408,10 +412,29 @@ void PreferencesDialog::syncSettings() s.beginGroup("CloudStorage"); QString email = ui.cloud_storage_email->text(); QString password = ui.cloud_storage_password->text(); - if (prefs.cloud_verification_status == CS_UNKNOWN || - prefs.cloud_verification_status == CS_INCORRECT_USER_PASSWD || - email != prefs.cloud_storage_email || - password != prefs.cloud_storage_password) { + QString newpassword = ui.cloud_storage_new_passwd->text(); + if (prefs.cloud_verification_status == CS_VERIFIED && !newpassword.isEmpty()) { + // deal with password change + if (!email.isEmpty() && !password.isEmpty()) { + // connect to backend server to check / create credentials + QRegularExpression reg("^[a-zA-Z0-9@.+_-]+$"); + if (!reg.match(email).hasMatch() || (!password.isEmpty() && !reg.match(password).hasMatch())) { + report_error(qPrintable(tr("Cloud storage email and password can only consist of letters, numbers, and '.', '-', '_', and '+'."))); + } else { + CloudStorageAuthenticate *cloudAuth = new CloudStorageAuthenticate(this); + connect(cloudAuth, SIGNAL(finishedAuthenticate()), this, SLOT(cloudPinNeeded())); + connect(cloudAuth, SIGNAL(passwordChangeSuccessful()), this, SLOT(passwordUpdateSuccessfull())); + QNetworkReply *reply = cloudAuth->backend(email, password, "", newpassword); + ui.cloud_storage_new_passwd->setText(""); + free(prefs.cloud_storage_newpassword); + prefs.cloud_storage_newpassword = strdup(qPrintable(newpassword)); + } + } + } else if (prefs.cloud_verification_status == CS_UNKNOWN || + prefs.cloud_verification_status == CS_INCORRECT_USER_PASSWD || + email != prefs.cloud_storage_email || + password != prefs.cloud_storage_password) { + // different credentials - reset verification status prefs.cloud_verification_status = CS_UNKNOWN; if (!email.isEmpty() && !password.isEmpty()) { @@ -422,7 +445,7 @@ void PreferencesDialog::syncSettings() } else { CloudStorageAuthenticate *cloudAuth = new CloudStorageAuthenticate(this); connect(cloudAuth, SIGNAL(finishedAuthenticate()), this, SLOT(cloudPinNeeded())); - QNetworkReply *reply = cloudAuth->authenticate(email, password); + QNetworkReply *reply = cloudAuth->backend(email, password); } } } else if (prefs.cloud_verification_status == CS_NEED_TO_VERIFY) { @@ -435,7 +458,7 @@ void PreferencesDialog::syncSettings() } CloudStorageAuthenticate *cloudAuth = new CloudStorageAuthenticate(this); connect(cloudAuth, SIGNAL(finishedAuthenticate()), this, SLOT(cloudPinNeeded())); - QNetworkReply *reply = cloudAuth->authenticate(email, password, pin); + QNetworkReply *reply = cloudAuth->backend(email, password, pin); } } SAVE_OR_REMOVE("email", default_prefs.cloud_storage_email, email); @@ -666,6 +689,11 @@ void PreferencesDialog::on_resetSettings_clicked() } } +void PreferencesDialog::passwordUpdateSuccessfull() +{ + ui.cloud_storage_password->setText(prefs.cloud_storage_password); +} + void PreferencesDialog::emitSettingsChanged() { emit settingsChanged(); diff --git a/qt-ui/preferences.h b/qt-ui/preferences.h index 2402a7964..326b1f964 100644 --- a/qt-ui/preferences.h +++ b/qt-ui/preferences.h @@ -40,6 +40,7 @@ slots: void facebookLoggedIn(); void facebookDisconnect(); void cloudPinNeeded(); + void passwordUpdateSuccessfull(); private: explicit PreferencesDialog(QWidget *parent = 0, Qt::WindowFlags f = 0); void setUiFromPrefs(); diff --git a/qt-ui/preferences.ui b/qt-ui/preferences.ui index 1b59e77a5..dc0d04a5a 100644 --- a/qt-ui/preferences.ui +++ b/qt-ui/preferences.ui @@ -1374,6 +1374,13 @@ </property> </widget> </item> + <item row="0" column="3"> + <widget class="QLabel" name="cloud_storage_new_passwd_label"> + <property name="text"> + <string>New password</string> + </property> + </widget> + </item> <item row="1" column="0"> <widget class="QLineEdit" name="cloud_storage_email"> <property name="toolTip"> @@ -1391,6 +1398,10 @@ </property> </widget> </item> + <item row="1" column="3"> + <widget class="QLineEdit" name="cloud_storage_new_passwd"> + </widget> + </item> <item row="2" column="0"> <widget class="QCheckBox" name="cloud_background_sync"> <property name="text"> diff --git a/qt-ui/subsurfacewebservices.cpp b/qt-ui/subsurfacewebservices.cpp index fd9f2fad1..8154ce5fb 100644 --- a/qt-ui/subsurfacewebservices.cpp +++ b/qt-ui/subsurfacewebservices.cpp @@ -988,13 +988,17 @@ CloudStorageAuthenticate::CloudStorageAuthenticate(QObject *parent) : #define CLOUDURL QString(prefs.cloud_base_url) #define CLOUDBACKENDSTORAGE CLOUDURL + "/storage" #define CLOUDBACKENDVERIFY CLOUDURL + "/verify" +#define CLOUDBACKENDUPDATE CLOUDURL + "/update" -QNetworkReply* CloudStorageAuthenticate::authenticate(QString email, QString password, QString pin) +QNetworkReply* CloudStorageAuthenticate::backend(QString email, QString password, QString pin, QString newpasswd) { QString payload(email + " " + password); QUrl requestUrl; - if (pin == "") { + if (pin == "" && newpasswd == "") { requestUrl = QUrl(CLOUDBACKENDSTORAGE); + } else if (newpasswd != "") { + requestUrl = QUrl(CLOUDBACKENDUPDATE); + payload += " " + newpasswd; } else { requestUrl = QUrl(CLOUDBACKENDVERIFY); payload += " " + pin; @@ -1025,6 +1029,12 @@ void CloudStorageAuthenticate::uploadFinished() myLastError.clear(); } else if (cloudAuthReply == "[VERIFY]") { prefs.cloud_verification_status = CS_NEED_TO_VERIFY; + } else if (cloudAuthReply == "[PASSWDCHANGED]") { + free(prefs.cloud_storage_password); + prefs.cloud_storage_password = prefs.cloud_storage_newpassword; + prefs.cloud_storage_newpassword = NULL; + emit passwordChangeSuccessful(); + return; } else { prefs.cloud_verification_status = CS_INCORRECT_USER_PASSWD; myLastError = cloudAuthReply; diff --git a/qt-ui/subsurfacewebservices.h b/qt-ui/subsurfacewebservices.h index e4866c529..2b454ebc7 100644 --- a/qt-ui/subsurfacewebservices.h +++ b/qt-ui/subsurfacewebservices.h @@ -114,10 +114,11 @@ slots: class CloudStorageAuthenticate : public QObject { Q_OBJECT public: - QNetworkReply* authenticate(QString email, QString password, QString pin = ""); + QNetworkReply* backend(QString email, QString password, QString pin = "", QString newpasswd = ""); explicit CloudStorageAuthenticate(QObject *parent); signals: void finishedAuthenticate(); + void passwordChangeSuccessful(); private slots: void uploadError(QNetworkReply::NetworkError error); |