summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/FAQ.wordpress12
-rw-r--r--Documentation/images/PrintDiveLog.jpgbin35196 -> 33884 bytes
-rw-r--r--Documentation/images/Printpreview.jpgbin23664 -> 30041 bytes
-rw-r--r--Documentation/user-manual.txt172
-rw-r--r--deco.c35
-rw-r--r--dive.h8
-rw-r--r--divelist.c3
-rw-r--r--file.c157
-rw-r--r--planner.c31
-rw-r--r--pref.h4
-rw-r--r--printer.cpp111
-rw-r--r--printer.h1
-rw-r--r--printing_templates/Six Dives.html17
-rw-r--r--printing_templates/statistics/Default.html97
-rw-r--r--profile.c2
-rw-r--r--qt-ui/divelogimportdialog.cpp155
-rw-r--r--qt-ui/diveplanner.cpp6
-rw-r--r--qt-ui/mainwindow.cpp31
-rw-r--r--qt-ui/mainwindow.h5
-rw-r--r--qt-ui/plannerSettings.ui3
-rw-r--r--qt-ui/printdialog.cpp20
-rw-r--r--qt-ui/printoptions.cpp63
-rw-r--r--qt-ui/printoptions.h1
-rw-r--r--qt-ui/templateedit.cpp7
-rw-r--r--templatelayout.cpp69
-rw-r--r--templatelayout.h54
-rw-r--r--tests/testparse.cpp128
-rw-r--r--tests/testplan.cpp327
-rw-r--r--tests/testplan.h6
29 files changed, 1165 insertions, 360 deletions
diff --git a/Documentation/FAQ.wordpress b/Documentation/FAQ.wordpress
index af3537426..64d8ff0d3 100644
--- a/Documentation/FAQ.wordpress
+++ b/Documentation/FAQ.wordpress
@@ -249,3 +249,15 @@ echo -n $MYGRP ; echo '"') | sudo tee /etc/udev/rules.d/99-cobalt.rules </code><
If you disconnect and reconnect your Cobalt it should now get the correct access permissions.
[/expand]
+[expand title="How do I fix permission errors when trying to download from my Suunto EON Steel under Linux?"]
+
+By default new devices are sometimes given permissions that prevent a regular user from accessing them. If you get a permission error when trying to download from an EON Steel under Linux, please try these steps.
+
+This should work on most Linux flavors. We'd appreciate feedback if this doesn't work for you. Open a terminal window and cut and paste the following command. It may ask you to enter your password in order to allow access as super user (which is required to set up the udev rule that changes the device permissions as you plug in your EON Steel).
+
+<pre><code style="font-size: 8pt;">echo 'SUBSYSTEM=="usb",ATTR{idVendor}=="1493",ATTR{idProduct}=="0030", MODE="0666"' |
+sudo tee /etc/udev/rules.d/99-cobalt.rules </code></pre>
+
+If you disconnect and reconnect your DC it should now get the correct access permissions.
+
+[/expand]
diff --git a/Documentation/images/PrintDiveLog.jpg b/Documentation/images/PrintDiveLog.jpg
index 7a485c4ff..8ff972dd7 100644
--- a/Documentation/images/PrintDiveLog.jpg
+++ b/Documentation/images/PrintDiveLog.jpg
Binary files differ
diff --git a/Documentation/images/Printpreview.jpg b/Documentation/images/Printpreview.jpg
index ab5db7fa5..7297d80cc 100644
--- a/Documentation/images/Printpreview.jpg
+++ b/Documentation/images/Printpreview.jpg
Binary files differ
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index 815bb9b2a..943d2268d 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -2711,8 +2711,9 @@ process could be used for the Cloud-based storage of dive logs.
[[S_PrintDivelog]]
== Printing a dive log
-_Subsurface_ provides a simple interface to print a whole dive log or only a
-few selected dives, including dive profiles and other contextual information.
+_Subsurface_ provides a simple interface to print a whole dive log or only a few selected dives.
+ Pre-installed templates or a custom written template can be used to choose where the data will
+ be fitted into the page.
Before printing, two decisions are required:
@@ -2727,15 +2728,10 @@ below).
image::images/PrintDiveLog.jpg["FIGURE: Print dialogue",align="center"]
-Under _Print type_ users need to select one of three options:
+Under _Print type_ users need to select one of two options:
-- Print the complete Dive List: to do this, _Table Print_ should be selected.
-- Print the selected dives (dive profiles and all other information) at 6
- dives per printed page: to do this, users should select _6 dives per page_.
-- Print the selected dives (dive profiles and all other information) at 2
- dives per printed page: to do this, users should select _2 dives per page_.
-- Print the selected dives (dive profiles and all other information) at 1
- dive per printed page: to do this, users should select _1 dive per page_.
+- _Dive list_ print: print dives from the list with profiles and other information.
+- _Statistics_ print: print yearly statistics of the dives.
Under _Print options_ users need to select:
@@ -2744,12 +2740,20 @@ Under _Print options_ users need to select:
selected dives_.
- Printing in colour, achieved by checking the box with _Print in colour_.
-The _Ordering_ affects the layout of the page (or part of it) for each dive.
-The dive profile could be printed at the top of each dive, with the textual
-information underneath, or it could be printed with the textual information at
-the top with the dive profile underneath. Users should select the appropriate option in the
-print dialogue. See the image below which has a layout with
-text below the dive profile.
+Under _Template_ users can select:
+
+- A template to be used as the page layout.
+
+- _Delete_ a template from the template list
+- _Import_ a new template to the template list
+- _Export_ a template from the template list
+- _Edit_ a template (choose colors, fonts, font-type)
+
+When editing a template the following options are available:
+
+- _Style_: edit font type, size, choose color palette; this will not affect the template HTML code
+- _Template_: edit the template HTML code; by saving, the "Custom" template in the template list will be replaced
+- _Colors_: edit the current color palette; the new color palette will overwrite the "Custom" color palette
Users can _Preview_ the printed page by selecting the _Preview_ button on the
dialogue. After preview, changes to the options in the print dialogue can be
@@ -2767,6 +2771,11 @@ the output for one particular page.
image::images/Printpreview.jpg["FIGURE: Print preview page",align="center"]
+=== Write a custom printing template (advanced)
+
+Writing a custom template is an effective way to produce highly customized printouts. Subsurface uses HTML templates with Grantlee as the printing backend.
+
+See <<_appendix_f_write_a_custom_printing_template ,APPENDIX F>> for information on how to write your own template.
[[S_Configure]]
== Configuring a dive computer
@@ -4137,3 +4146,134 @@ If you have downloaded your dives to different dive logging software
before they were overwritten, there is a high change that Subsurface can
import these. However, if the logs are only on your dive computer, they
cannot be salvaged after being over written by new dives.
+
+== APPENDIX F: Write a custom printing template
+
+_Subsurface_ provides a customizable printing support which is based on templates that are transformed by the _Grantlee_
+backend to correct _HTML_ syntax, The _HTML_ output is then rendered by _Subsurface_.
+
+To write a custom template the following elements must exist so that the template will be correctly handled and rendered.
+
+=== Main dive loop
+_Subsurface_ exports a dive list called (*dives*) to the _Grantlee_ backend. It is possible to iterate over the list as follows:
+
+.template.html
+....
+ {% for dive in dives %}
+ <h1> {{ dive.number }} </h1>
+ {% endfor %}
+....
+
+.output.html
+....
+ <h1> 1 </h1>
+ <h1> 2 </h1>
+ <h1> 3 </h1>
+....
+
+Additional information about _Grantlee_ can be found http://www.grantlee.org/apidox/for_themers.html[here]
+
+=== Grantlee exported variables
+Only a subset of the dive data is exported:
+|====================
+|*Name*|*Description*
+|number| (*int*) dive number
+|id| (*int*) unique dive ID, should be used to fetch the dive profile
+|date| (*string*) data of the dive
+|time| (*string*) time of the dive
+|location| (*string*) location of the dive
+|duration| (*string*) duration of the dive
+|depth| (*string*) depth of the dive
+|divemaster| (*string*) divemaster data
+|buddy| (*string*) buddy data
+|airTemp| (*string*) air temperature of dive
+|waterTemp| (*string*) water temperature of dive
+|notes| (*string*) dive notes
+|rating| (*int*) dive rating ranges from 0 to 5
+|sac| (*string*) sac value
+|tags| (*string*) all dive tags concatenate together
+|gas| (*string*) used gas cylinder
+|=====================
+
+_Subsurface_ also exports *template_options* data. This data must be used as _CSS_ values to provide a dynamically
+editable template. The exported data is shown in the following table:
+|====================
+|*Name*|*Description*
+|font| (*string*) font family
+|borderwidth| (*int*) border-width value dynamically calculated as 0.1% of the page width with minimum value of 1px
+|font_size| (*double*) size of fonts in vw, ranges between 1.0 and 2.0
+|line_spacing| (*double*) distance between text lines, ranges between 1.0 and 3.0
+|color1| (*string*) background color
+|color2| (*string*) primary table cell color
+|color3| (*string*) secondary table cell color
+|color4| (*string*) primary text color
+|color5| (*string*) secondary text color
+|color6| (*string*) border colors
+|=====================
+
+.template.html
+....
+ border-width: {{ template_options.borderwidth }}px;
+....
+
+.output.html
+....
+ border-width: 3px;
+....
+
+Another variable that _Subsurface_ exports is *print_options*. This variable contains a single member:
+|=====================
+|*Name*|*Description*
+|grayscale | Use _CSS_ filters to convert the page into grayscale (should be added to body style to enable printing grayscale prints)
+|=====================
+
+
+.template.html
+....
+ body {
+ {{ print_options.grayscale }};
+ }
+....
+
+.output.html
+....
+ body {
+ -webkit-filter: grayscale(100%);
+ }
+....
+
+=== Defined CSS selectors
+
+As the dive profile is placed after rendering, _Subsurface_ uses a special _CSS_ selectors to do some searches
+in the HTML output. The _CSS_ selectors in the following table should be added.
+
+|====================
+|*Selector*|*Type*|*Description*
+|dive_{{ dive.id }} | id | is used to fetch the relevant dive profile
+|diveProfile | class | each div that will contain a dive profile should have this class selector in addition to the dive_{{ dive.id }} id selector
+|dontbreak | class | prevents the div with this class to be divided into two pages, this can be used
+in flow layout templates only (when data-numberofdives = 0)
+|=====================
+
+IMPORTANT: Rendering dive profiles is not supported for flow layout templates (when data-numberofdives = 0).
+
+=== Special attributes
+
+There are two ways of rendering- either rendering a specific number of dives in each page or make _Subsurface_ try to
+fit as much dives as possible into one page (_flow_ rendering).
+
+The *data-numberofdives* data attribute is added to the body tag to set the rendering mode
+
+- render 6 dives per page:
+
+....
+ <body data-numberofdives = 6>
+....
+
+- render as much dives as possible:
+
+....
+ <body data-numberofdives = 0>
+....
+
+IMPORTANT: All CSS units should be in relative lengths only, to support printing on any page size.
diff --git a/deco.c b/deco.c
index d802ca539..20d8b0479 100644
--- a/deco.c
+++ b/deco.c
@@ -346,16 +346,35 @@ double solve_cubic(double A, double B, double C)
}
+// Solve another cubic equation, this time
+// x^3 - B x - C == 0
+// Use trigonometric formula for negative discriminants (see Wikipedia for details)
+
+double solve_cubic2(double B, double C)
+{
+ double discriminant = 27 * C * C - 4 * cube(B);
+ if (discriminant < 0.0) {
+ return 2.0 * sqrt(B / 3.0) * cos(acos(3.0 * C * sqrt(3.0 / B) / (2.0 * B)) / 3.0);
+ }
+
+ double denominator = pow(9 * C + sqrt(3 * discriminant), 1 / 3.0);
+
+ return pow(2.0 / 3.0, 1.0 / 3.0) * B / denominator + denominator / pow(18.0, 1.0 / 3.0);
+}
+
+// This is a simplified formula avoiding radii. It uses the fact that Boyle's law says
+// pV = (G + P_amb) / G^3 is constant to solve for the new gradient G.
+
double update_gradient(double next_stop_pressure, double first_gradient)
{
- double first_radius = 2.0 * vpmb_config.surface_tension_gamma / first_gradient;
- double A = next_stop_pressure;
- double B = -2.0 * vpmb_config.surface_tension_gamma;
- double C = (first_stop_pressure.mbar / 1000.0 + 2.0 * vpmb_config.surface_tension_gamma / first_radius) * cube(first_radius);
+ double B = cube(first_gradient) / (first_stop_pressure.mbar / 1000.0 + first_gradient);
+ double C = next_stop_pressure * B;
- double next_radius = solve_cubic(A, B, C);
+ double new_gradient = solve_cubic2(B, C);
- return 2.0 * vpmb_config.surface_tension_gamma / next_radius;
+ if (new_gradient < 0.0)
+ report_error("Negative gradient encountered!");
+ return new_gradient;
}
void boyles_law(double next_stop_pressure)
@@ -469,7 +488,6 @@ double add_segment(double pressure, const struct gasmix *gasmix, int period_in_s
return tissue_tolerance_calc(dive);
}
-#ifdef DECO_CALC_DEBUG
void dump_tissues()
{
int ci;
@@ -481,7 +499,6 @@ void dump_tissues()
printf(" %6.3e", tissue_he_sat[ci]);
printf("\n");
}
-#endif
void clear_deco(double surface_pressure)
{
@@ -491,6 +508,8 @@ void clear_deco(double surface_pressure)
tissue_he_sat[ci] = 0.0;
max_n2_crushing_pressure[ci] = 0.0;
max_he_crushing_pressure[ci] = 0.0;
+ n2_regen_radius[ci] = get_crit_radius_N2();
+ he_regen_radius[ci] = get_crit_radius_He();
}
gf_low_pressure_this_dive = surface_pressure;
if (!buehlmann_config.gf_low_at_maxdepth)
diff --git a/dive.h b/dive.h
index 0125b76a5..2585555ea 100644
--- a/dive.h
+++ b/dive.h
@@ -669,10 +669,9 @@ extern int parse_dlf_buffer(unsigned char *buffer, size_t size);
extern int parse_file(const char *filename);
extern 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, const char *hw);
-extern 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, const char *hw);
+extern int parse_seabear_csv_file(const char *filename, char **params, int pnr, const char *csvtemplate);
extern int parse_txt_file(const char *filename, const char *csv);
-extern int parse_manual_file(const char *filename, int separator_index, int units, int dateformat, int durationformat, int number, int date, int time, int duration, int location, int gps, int maxdepth, int meandepth, int divemaster, int buddy, int suit, int notes, int weight, int tags, int cylsizef, int startpresf, int endpresf, int o2f, int hef, int airtempf, int watertempf);
-
+extern int parse_manual_file(const char *filename, char **params, int pnr);
extern int save_dives(const char *filename);
extern int save_dives_logic(const char *filename, bool select_only);
extern int save_dive(FILE *f, struct dive *dive);
@@ -830,6 +829,9 @@ struct divedatapoint *create_dp(int time_incr, int depth, struct gasmix gasmix,
void dump_plan(struct diveplan *diveplan);
#endif
bool plan(struct diveplan *diveplan, char **cached_datap, bool is_planner, bool show_disclaimer);
+void calc_crushing_pressure(double pressure);
+void vpmb_start_gradient();
+
void delete_single_dive(int idx);
struct event *get_next_event(struct event *event, const char *name);
diff --git a/divelist.c b/divelist.c
index 18ff99135..85a70f9fd 100644
--- a/divelist.c
+++ b/divelist.c
@@ -1106,6 +1106,9 @@ void clear_dive_file_data()
while (dive_site_table.nr)
delete_dive_site(get_dive_site(0)->uuid);
+ clear_dive(&displayed_dive);
+ clear_dive_site(&displayed_dive_site);
+
free((void *)existing_filename);
existing_filename = NULL;
diff --git a/file.c b/file.c
index f612656f9..5ea6bb2e5 100644
--- a/file.c
+++ b/file.c
@@ -980,16 +980,15 @@ int parse_csv_file(const char *filename, int timef, int depthf, int tempf, int p
}
#define SBPARAMS 40
-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, const char *hw)
+int parse_seabear_csv_file(const char *filename, char **params, int pnr, const char *csvtemplate)
{
- int ret, i, pnr;
+ int ret, i;
struct memblock mem;
- char *params[SBPARAMS];
- char deltabuf[MAXCOLDIGITS];
time_t now;
struct tm *timep = NULL;
char *ptr, *ptr_old = NULL;
char *NL = NULL;
+ char tmpbuf[MAXCOLDIGITS];
/* Increase the limits for recursion and variables on XSLT
* parsing */
@@ -998,10 +997,19 @@ int parse_seabear_csv_file(const char *filename, int timef, int depthf, int temp
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);
+ 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);
- 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");
@@ -1069,13 +1077,6 @@ int parse_seabear_csv_file(const char *filename, int timef, int depthf, int temp
params[pnr - 1][5] = 0;
}
- snprintf(deltabuf, MAXCOLDIGITS, "%s", delta);
- params[pnr++] = "delta";
- params[pnr++] = strdup(deltabuf);
- if (strlen(hw)) {
- params[pnr++] = "hw";
- params[pnr++] = strdup(hw);
- }
params[pnr++] = NULL;
/* Move the CSV data to the start of mem buffer */
@@ -1106,79 +1107,16 @@ int parse_seabear_csv_file(const char *filename, int timef, int depthf, int temp
return ret;
}
-int parse_manual_file(const char *filename, int sepidx, int units, int dateformat, int durationformat,
- int numberf, int datef, int timef, int durationf, int locationf, int gpsf, int maxdepthf, int meandepthf,
- int divemasterf, int buddyf, int suitf, int notesf, int weightf, int tagsf, int cylsizef, int startpresf, int endpresf,
- int o2f, int hef, int airtempf, int watertempf)
+int parse_manual_file(const char *filename, char **params, int pnr)
{
- if (verbose > 4) {
- fprintf(stderr, "filename %s, sepidx %d, units %d, dateformat %d, durationformat %d\n", filename, sepidx, units, dateformat, durationformat);
- fprintf(stderr, "numberf %d, datef %d, timef %d, durationf %d, locationf %d, gpsf %d, maxdepthf %d, meandepthf %d\n", numberf, datef, timef, durationf, locationf, gpsf, maxdepthf, meandepthf);
- fprintf(stderr, "divemasterf %d, buddyf %d, suitf %d, notesf %d, weightf %d, tagsf %d, cylsizef %d, startpresf %d, endpresf %d\n", divemasterf, buddyf, suitf, notesf, weightf, tagsf, cylsizef, startpresf, endpresf);
- fprintf(stderr, "o2f %d, hef %d, airtempf %d, watertempf %d\n", o2f, hef, airtempf, watertempf);
- }
struct memblock mem;
- int pnr = 0;
- char *params[55];
- char numberbuf[MAXCOLDIGITS];
- char datebuf[MAXCOLDIGITS];
- char timebuf[MAXCOLDIGITS];
- char durationbuf[MAXCOLDIGITS];
- char locationbuf[MAXCOLDIGITS];
- char gpsbuf[MAXCOLDIGITS];
- char maxdepthbuf[MAXCOLDIGITS];
- char meandepthbuf[MAXCOLDIGITS];
- char divemasterbuf[MAXCOLDIGITS];
- char buddybuf[MAXCOLDIGITS];
- char suitbuf[MAXCOLDIGITS];
- char notesbuf[MAXCOLDIGITS];
- char weightbuf[MAXCOLDIGITS];
- char tagsbuf[MAXCOLDIGITS];
- char separator_index[MAXCOLDIGITS];
- char unit[MAXCOLDIGITS];
- char datefmt[MAXCOLDIGITS];
- char durationfmt[MAXCOLDIGITS];
- char cylsizebuf[MAXCOLDIGITS];
- char startpresbuf[MAXCOLDIGITS];
- char endpresbuf[MAXCOLDIGITS];
- char o2buf[MAXCOLDIGITS];
- char hebuf[MAXCOLDIGITS];
- char airtempbuf[MAXCOLDIGITS];
- char watertempbuf[MAXCOLDIGITS];
time_t now;
struct tm *timep;
char curdate[9];
char curtime[6];
- int ret;
+ int ret, i;
- if (numberf >= MAXCOLS || datef >= MAXCOLS || timef >= MAXCOLS || durationf >= MAXCOLS || locationf >= MAXCOLS || gpsf >= MAXCOLS || maxdepthf >= MAXCOLS || meandepthf >= MAXCOLS || buddyf >= MAXCOLS || suitf >= MAXCOLS || notesf >= MAXCOLS || weightf >= MAXCOLS || tagsf >= MAXCOLS || cylsizef >= MAXCOLS || startpresf >= MAXCOLS || endpresf >= MAXCOLS || o2f >= MAXCOLS || hef >= MAXCOLS || airtempf >= MAXCOLS || watertempf >= MAXCOLS)
- return report_error(translate("gettextFromC", "Maximum number of supported columns on CSV import is %d"), MAXCOLS);
- snprintf(numberbuf, MAXCOLDIGITS, "%d", numberf);
- snprintf(datebuf, MAXCOLDIGITS, "%d", datef);
- snprintf(timebuf, MAXCOLDIGITS, "%d", timef);
- snprintf(durationbuf, MAXCOLDIGITS, "%d", durationf);
- snprintf(locationbuf, MAXCOLDIGITS, "%d", locationf);
- snprintf(gpsbuf, MAXCOLDIGITS, "%d", gpsf);
- snprintf(maxdepthbuf, MAXCOLDIGITS, "%d", maxdepthf);
- snprintf(meandepthbuf, MAXCOLDIGITS, "%d", meandepthf);
- snprintf(divemasterbuf, MAXCOLDIGITS, "%d", divemasterf);
- snprintf(buddybuf, MAXCOLDIGITS, "%d", buddyf);
- snprintf(suitbuf, MAXCOLDIGITS, "%d", suitf);
- snprintf(notesbuf, MAXCOLDIGITS, "%d", notesf);
- snprintf(weightbuf, MAXCOLDIGITS, "%d", weightf);
- snprintf(tagsbuf, MAXCOLDIGITS, "%d", tagsf);
- snprintf(separator_index, MAXCOLDIGITS, "%d", sepidx);
- snprintf(unit, MAXCOLDIGITS, "%d", units);
- snprintf(datefmt, MAXCOLDIGITS, "%d", dateformat);
- snprintf(durationfmt, MAXCOLDIGITS, "%d", durationformat);
- snprintf(cylsizebuf, MAXCOLDIGITS, "%d", cylsizef);
- snprintf(startpresbuf, MAXCOLDIGITS, "%d", startpresf);
- snprintf(endpresbuf, MAXCOLDIGITS, "%d", endpresf);
- snprintf(o2buf, MAXCOLDIGITS, "%d", o2f);
- snprintf(hebuf, MAXCOLDIGITS, "%d", hef);
- snprintf(airtempbuf, MAXCOLDIGITS, "%d", airtempf);
- snprintf(watertempbuf, MAXCOLDIGITS, "%d", watertempf);
time(&now);
timep = localtime(&now);
strftime(curdate, DATESTR, "%Y%m%d", timep);
@@ -1187,60 +1125,11 @@ int parse_manual_file(const char *filename, int sepidx, int units, int dateforma
* is not discarded during the transform, thus prepend time with 1 */
strftime(curtime, TIMESTR, "1%H%M", timep);
- params[pnr++] = "numberField";
- params[pnr++] = numberbuf;
- params[pnr++] = "dateField";
- params[pnr++] = datebuf;
- params[pnr++] = "timeField";
- params[pnr++] = timebuf;
- params[pnr++] = "durationField";
- params[pnr++] = durationbuf;
- params[pnr++] = "locationField";
- params[pnr++] = locationbuf;
- params[pnr++] = "gpsField";
- params[pnr++] = gpsbuf;
- params[pnr++] = "maxDepthField";
- params[pnr++] = maxdepthbuf;
- params[pnr++] = "meanDepthField";
- params[pnr++] = meandepthbuf;
- params[pnr++] = "divemasterField";
- params[pnr++] = divemasterbuf;
- params[pnr++] = "buddyField";
- params[pnr++] = buddybuf;
- params[pnr++] = "suitField";
- params[pnr++] = suitbuf;
- params[pnr++] = "notesField";
- params[pnr++] = notesbuf;
- params[pnr++] = "weightField";
- params[pnr++] = weightbuf;
- params[pnr++] = "tagsField";
- params[pnr++] = tagsbuf;
- params[pnr++] = "date";
- params[pnr++] = curdate;
- params[pnr++] = "time";
- params[pnr++] = curtime;
- params[pnr++] = "separatorIndex";
- params[pnr++] = separator_index;
- params[pnr++] = "units";
- params[pnr++] = unit;
- params[pnr++] = "datefmt";
- params[pnr++] = datefmt;
- params[pnr++] = "durationfmt";
- params[pnr++] = durationfmt;
- params[pnr++] = "cylindersizeField";
- params[pnr++] = cylsizebuf;
- params[pnr++] = "startpressureField";
- params[pnr++] = startpresbuf;
- params[pnr++] = "endpressureField";
- params[pnr++] = endpresbuf;
- params[pnr++] = "o2Field";
- params[pnr++] = o2buf;
- params[pnr++] = "heField";
- params[pnr++] = hebuf;
- params[pnr++] = "airtempField";
- params[pnr++] = airtempbuf;
- params[pnr++] = "watertempField";
- params[pnr++] = watertempbuf;
+
+ params[pnr++] = strdup("date");
+ params[pnr++] = strdup(curdate);
+ params[pnr++] = strdup("time");
+ params[pnr++] = strdup(curtime);
params[pnr++] = NULL;
if (filename == NULL)
@@ -1253,5 +1142,7 @@ int parse_manual_file(const char *filename, int sepidx, int units, int dateforma
ret = parse_xml_buffer(filename, mem.buffer, mem.size, &dive_table, (const char **)params);
free(mem.buffer);
+ for (i = 0; i < pnr - 2; ++i)
+ free(params[i]);
return ret;
}
diff --git a/planner.c b/planner.c
index aca5a0594..5afc11771 100644
--- a/planner.c
+++ b/planner.c
@@ -939,6 +939,7 @@ bool plan(struct diveplan *diveplan, char **cached_datap, bool is_planner, bool
int *decostoplevels;
int decostoplevelcount;
unsigned int *stoplevels = NULL;
+ int vpmb_first_stop;
bool stopping = false;
bool pendinggaschange = false;
bool clear_to_ascend;
@@ -996,6 +997,13 @@ bool plan(struct diveplan *diveplan, char **cached_datap, bool is_planner, bool
create_dive_from_plan(diveplan, is_planner);
return(false);
}
+ calc_crushing_pressure(depth_to_mbar(depth, &displayed_dive) / 1000.0);
+ nuclear_regeneration(clock);
+ clear_deco(displayed_dive.surface_pressure.mbar / 1000.0);
+ vpmb_start_gradient();
+ previous_deco_time = 100000000;
+ deco_time = 10000000;
+
tissue_tolerance = tissue_at_end(&displayed_dive, cached_datap);
displayed_dive.surface_pressure.mbar = diveplan->surface_pressure;
@@ -1100,6 +1108,22 @@ bool plan(struct diveplan *diveplan, char **cached_datap, bool is_planner, bool
bottom_gi = gi;
bottom_gas = gas;
bottom_stopidx = stopidx;
+
+ // Find first stop used for VPM-B Boyle's law compensation
+ if (prefs.deco_mode == VPMB) {
+ vpmb_first_stop = deco_allowed_depth(tissue_tolerance, diveplan->surface_pressure / 1000, &displayed_dive, 1);
+ if (vpmb_first_stop > 0) {
+ while (stoplevels[stopidx] > vpmb_first_stop) {
+ stopidx--;
+ }
+ stopidx++;
+ vpmb_first_stop = stoplevels[stopidx];
+ }
+ first_stop_pressure.mbar = depth_to_mbar(vpmb_first_stop, &displayed_dive);
+ } else {
+ first_stop_pressure.mbar = 0;
+ }
+
//CVA
do {
is_final_plan = (prefs.deco_mode == BUEHLMANN) || (previous_deco_time - deco_time < 10); // CVA time converges
@@ -1107,7 +1131,7 @@ bool plan(struct diveplan *diveplan, char **cached_datap, bool is_planner, bool
vpmb_next_gradient(deco_time, diveplan->surface_pressure / 1000.0);
previous_deco_time = deco_time;
- restore_deco_state(bottom_cache);
+ tissue_tolerance = restore_deco_state(bottom_cache);
depth = bottom_depth;
gi = bottom_gi;
@@ -1119,7 +1143,6 @@ bool plan(struct diveplan *diveplan, char **cached_datap, bool is_planner, bool
breaktime = -1;
breakcylinder = 0;
o2time = 0;
- first_stop_pressure.mbar = 0;
last_ascend_rate = ascent_velocity(depth, avg_depth, bottom_time);
if ((current_cylinder = get_gasidx(&displayed_dive, &gas)) == -1) {
report_error(translate("gettextFromC", "Can't find gas %s"), gasname(&gas));
@@ -1160,8 +1183,6 @@ bool plan(struct diveplan *diveplan, char **cached_datap, bool is_planner, bool
stopping = true;
// Boyles Law compensation
- if (first_stop_pressure.mbar == 0)
- first_stop_pressure.mbar = depth_to_mbar(depth, &displayed_dive);
boyles_law(depth_to_mbar(stoplevels[stopidx], &displayed_dive) / 1000.0);
/* Check we need to change cylinder.
@@ -1215,8 +1236,6 @@ bool plan(struct diveplan *diveplan, char **cached_datap, bool is_planner, bool
stopping = true;
// Boyles Law compensation
- if (first_stop_pressure.mbar == 0)
- first_stop_pressure.mbar = depth_to_mbar(depth, &displayed_dive);
boyles_law(depth_to_mbar(stoplevels[stopidx], &displayed_dive) / 1000.0);
}
diff --git a/pref.h b/pref.h
index e471add73..84975aaaa 100644
--- a/pref.h
+++ b/pref.h
@@ -75,7 +75,7 @@ struct preferences {
short tankbar;
short save_userid_local;
char *userid;
- int ascrate75;
+ int ascrate75; // All rates in mm / sec
int ascrate50;
int ascratestops;
int ascratelast6m;
@@ -90,7 +90,7 @@ struct preferences {
char *proxy_pass;
bool doo2breaks;
bool drop_stone_mode;
- bool last_stop;
+ bool last_stop; // At 6m?
bool verbatim_plan;
bool display_runtime;
bool display_duration;
diff --git a/printer.cpp b/printer.cpp
index b0caa0815..f0197d446 100644
--- a/printer.cpp
+++ b/printer.cpp
@@ -59,6 +59,11 @@ void Printer::putProfileImage(QRect profilePlaceholder, QRect viewPort, QPainter
void Printer::flowRender()
{
+ // add extra padding at the bottom to pages with height not divisible by view port
+ int paddingBottom = pageSize.height() - (webView->page()->mainFrame()->contentsSize().height() % pageSize.height());
+ QString styleString = QString::fromUtf8("padding-bottom: ") + QString::number(paddingBottom) + "px;";
+ webView->page()->mainFrame()->findFirstElement("body").setAttribute("style", styleString);
+
// render the Qwebview
QPainter painter;
QRect viewPort(0, 0, 0, 0);
@@ -90,7 +95,14 @@ void Printer::flowRender()
// rendering progress is 4/5 of total work
emit(progessUpdated((end * 80.0 / fullPageResolution) + done));
- static_cast<QPrinter*>(paintDevice)->newPage();
+
+ // add new pages only in print mode, while previewing we don't add new pages
+ if (printMode == Printer::PRINT)
+ static_cast<QPrinter*>(paintDevice)->newPage();
+ else {
+ painter.end();
+ return;
+ }
start = dontbreakElement.geometry().y();
}
}
@@ -187,6 +199,7 @@ void Printer::print()
return;
}
+
QPrinter *printerPtr;
printerPtr = static_cast<QPrinter*>(paintDevice);
@@ -194,18 +207,17 @@ void Printer::print()
connect(&t, SIGNAL(progressUpdated(int)), this, SLOT(templateProgessUpdated(int)));
dpi = printerPtr->resolution();
//rendering resolution = selected paper size in inchs * printer dpi
-#if QT_VERSION >= 0x050300
- pageSize.setHeight(printerPtr->pageLayout().paintRectPixels(dpi).height());
- pageSize.setWidth(printerPtr->pageLayout().paintRectPixels(dpi).width());
-#else
- pageSize.setHeight(printerPtr->pageRect(QPrinter::Inch).height() * dpi);
- pageSize.setWidth(printerPtr->pageRect(QPrinter::Inch).width() * dpi);
-#endif
+ pageSize.setHeight(qCeil(printerPtr->pageRect(QPrinter::Inch).height() * dpi));
+ pageSize.setWidth(qCeil(printerPtr->pageRect(QPrinter::Inch).width() * dpi));
webView->page()->setViewportSize(pageSize);
webView->page()->mainFrame()->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff);
// export border width with at least 1 pixel
templateOptions->border_width = std::max(1, pageSize.width() / 1000);
- webView->setHtml(t.generate());
+ if (printOptions->type == print_options::DIVELIST) {
+ webView->setHtml(t.generate());
+ } else if (printOptions->type == print_options::STATISTICS ) {
+ webView->setHtml(t.generateStatistics());
+ }
if (printOptions->color_selected && printerPtr->colorMode()) {
printerPtr->setColorMode(QPrinter::Color);
} else {
@@ -223,71 +235,13 @@ void Printer::print()
}
int Pages;
if (divesPerPage == 0) {
- // add extra padding at the bottom to pages with height not divisible by view port
- int paddingBottom = pageSize.height() - (webView->page()->mainFrame()->contentsSize().height() % pageSize.height());
- QString styleString = QString::fromUtf8("padding-bottom: ") + QString::number(paddingBottom) + "px;";
- webView->page()->mainFrame()->findFirstElement("body").setAttribute("style", styleString);
flowRender();
} else {
- Pages = ceil(getTotalWork(printOptions) / (float)divesPerPage);
+ Pages = qCeil(getTotalWork(printOptions) / (float)divesPerPage);
render(Pages);
}
}
-void Printer::print_statistics()
-{
- QPrinter *printerPtr;
- printerPtr = static_cast<QPrinter*>(paintDevice);
- stats_t total_stats;
-
- total_stats.selection_size = 0;
- total_stats.total_time.seconds = 0;
-
- QString html;
- html += "<table border=1>";
- html += "<tr>";
- html += "<td>Year</td>";
- html += "<td>Dives</td>";
- html += "<td>Total Time</td>";
- html += "<td>Avg Time</td>";
- html += "<td>Shortest Time</td>";
- html += "<td>Longest Time</td>";
- html += "<td>Avg Depth</td>";
- html += "<td>Min Depth</td>";
- html += "<td>Max Depth</td>";
- html += "<td>Avg SAC</td>";
- html += "<td>Min SAC</td>";
- html += "<td>Max SAC</td>";
- html += "<td>Min Temp</td>";
- html += "<td>Max Temp</td>";
- html += "</tr>";
- int i = 0;
- while (stats_yearly != NULL && stats_yearly[i].period) {
- html += "<tr>";
- html += "<td>" + QString::number(stats_yearly[i].period) + "</td>";
- html += "<td>" + QString::number(stats_yearly[i].selection_size) + "</td>";
- html += "<td>" + QString::fromUtf8(get_time_string(stats_yearly[i].total_time.seconds, 0)) + "</td>";
- html += "<td>" + QString::fromUtf8(get_minutes(stats_yearly[i].total_time.seconds / stats_yearly[i].selection_size)) + "</td>";
- html += "<td>" + QString::fromUtf8(get_minutes(stats_yearly[i].shortest_time.seconds)) + "</td>";
- html += "<td>" + QString::fromUtf8(get_minutes(stats_yearly[i].longest_time.seconds)) + "</td>";
- html += "<td>" + get_depth_string(stats_yearly[i].avg_depth) + "</td>";
- html += "<td>" + get_depth_string(stats_yearly[i].min_depth) + "</td>";
- html += "<td>" + get_depth_string(stats_yearly[i].max_depth) + "</td>";
- html += "<td>" + get_volume_string(stats_yearly[i].avg_sac) + "</td>";
- html += "<td>" + get_volume_string(stats_yearly[i].min_sac) + "</td>";
- html += "<td>" + get_volume_string(stats_yearly[i].max_sac) + "</td>";
- html += "<td>" + QString::number(stats_yearly[i].min_temp == 0 ? 0 : get_temp_units(stats_yearly[i].min_temp, NULL)) + "</td>";
- html += "<td>" + QString::number(stats_yearly[i].max_temp == 0 ? 0 : get_temp_units(stats_yearly[i].max_temp, NULL)) + "</td>";
- html += "</tr>";
- total_stats.selection_size += stats_yearly[i].selection_size;
- total_stats.total_time.seconds += stats_yearly[i].total_time.seconds;
- i++;
- }
- html += "</table>";
- webView->setHtml(html);
- webView->print(printerPtr);
-}
-
void Printer::previewOnePage()
{
if (printMode == PREVIEW) {
@@ -296,9 +250,24 @@ void Printer::previewOnePage()
pageSize.setHeight(paintDevice->height());
pageSize.setWidth(paintDevice->width());
webView->page()->setViewportSize(pageSize);
- webView->setHtml(t.generate());
+ // initialize the border settings
+ templateOptions->border_width = std::max(1, pageSize.width() / 1000);
+ if (printOptions->type == print_options::DIVELIST) {
+ webView->setHtml(t.generate());
+ } else if (printOptions->type == print_options::STATISTICS ) {
+ webView->setHtml(t.generateStatistics());
+ }
- // render only one page
- render(1);
+ bool ok;
+ int divesPerPage = webView->page()->mainFrame()->findFirstElement("body").attribute("data-numberofdives").toInt(&ok);
+ if (!ok) {
+ divesPerPage = 1; // print each dive in a single page if the attribute is missing or malformed
+ //TODO: show warning
+ }
+ if (divesPerPage == 0) {
+ flowRender();
+ } else {
+ render(1);
+ }
}
}
diff --git a/printer.h b/printer.h
index 5c7652a8d..979cacd6a 100644
--- a/printer.h
+++ b/printer.h
@@ -39,7 +39,6 @@ public:
Printer(QPaintDevice *paintDevice, print_options *printOptions, template_options *templateOptions, PrintMode printMode);
~Printer();
void print();
- void print_statistics();
void previewOnePage();
signals:
diff --git a/printing_templates/Six Dives.html b/printing_templates/Six Dives.html
index 3f2c7b95e..9d4d1341e 100644
--- a/printing_templates/Six Dives.html
+++ b/printing_templates/Six Dives.html
@@ -40,11 +40,11 @@
.mainContainer {
width: 50%;
- height: 33.333333%;
+ height: 33.333%;
margin-left: 0%;
margin-right: 0%;
- margin-top: 0;
- margin-bottom: 0;
+ margin-top: 0%;
+ margin-bottom: 0%;
overflow: hidden;
page-break-inside: avoid;
float: left;
@@ -111,12 +111,20 @@
text-overflow: ellipsis;
line-height: {{ template_options.line_spacing }};
}
+
+ #sixdivespack {
+ width: 100%;
+ height: 100%;
+ }
</style>
</head>
<body data-numberofdives = 6>
<div id="body_div">
{% block main_rows %}
{% for dive in dives %}
+ {% if forloop.counter|divisibleby:6 %}
+ <div id = "sixdivespack">
+ {% endif %}
<div class="mainContainer">
<div class="innerContainer">
<div class="diveDetails">
@@ -173,6 +181,9 @@
</div>
</div>
</div>
+ {% if forloop.counter|divisibleby:6 %}
+ </div>
+ {% endif %}
{% endfor %}
{% endblock %}
<div id="footer">
diff --git a/printing_templates/statistics/Default.html b/printing_templates/statistics/Default.html
new file mode 100644
index 000000000..4ec02766a
--- /dev/null
+++ b/printing_templates/statistics/Default.html
@@ -0,0 +1,97 @@
+<html>
+<head>
+ <style>
+ body {
+ {{ print_options.grayscale }};
+ padding: 0;
+ margin: 0;
+ font-size: {{ template_options.font_size }}vw;
+ line-height: {{ template_options.line_spacing }};
+ font-family: {{ template_options.font }};
+ }
+
+ h1 {
+ float: left;
+ font-size: {{ template_options.font_size }}vw;
+ }
+
+ th {
+ font-size: {{ template_options.font_size }}vw;
+ page-break-inside: avoid;
+ -webkit-column-break-inside: avoid;
+ padding-top: 1vh;
+ padding-bottom: 1vh;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+ border-width: {{ template_options.borderwidth }}px;
+ border-style:solid;
+ border-color: {{ template_options.color6 }};
+ }
+
+ #body_div {
+ background-color: {{ template_options.color1 }};
+ }
+
+ .mainContainer {
+ width: 99%;
+ height: 100%;
+ margin-left: 0.5%;
+ margin-right: 0.5%;
+ margin-top: 0%;
+ margin-bottom: 0%;
+ overflow: hidden;
+ border-width: 0;
+ page-break-inside: avoid;
+ }
+
+ .table_class {
+ overflow: hidden;
+ width: 100%;
+ margin: 0%;
+ }
+
+ </style>
+</head>
+<body data-numberofdives = 0>
+<div id="body_div">
+<table class="table_class">
+ <tr style="background-color: {{ template_options.color2 }}; color: {{ template_options.color4 }}">
+ <td>Year</td>
+ <td>Dives</td>
+ <td>Total Time</td>
+ <td>Avg Time</td>
+ <td>Shortest Time</td>
+ <td>Longest Time</td>
+ <td>Avg Depth</td>
+ <td>Min Depth</td>
+ <td>Max Depth</td>
+ <td>Avg SAC</td>
+ <td>Min SAC</td>
+ <td>Max SAC</td>
+ <td>Min Temp</td>
+ <td>Max Temp</td>
+ </tr>
+{% block main_rows %}
+ {% for year in years %}
+ <tr class="dontbreak" style="background-color: {{ template_options.color3 }}; color: {{ template_options.color5 }};">
+ <td> {{ year.year }} </td>
+ <td> {{ year.dives }} </td>
+ <td> {{ year.total_time }} </td>
+ <td> {{ year.avg_time }} </td>
+ <td> {{ year.shortest_time }} </td>
+ <td> {{ year.longest_time }} </td>
+ <td> {{ year.avg_depth }} </td>
+ <td> {{ year.min_depth }} </td>
+ <td> {{ year.max_depth }} </td>
+ <td> {{ year.avg_sac }} </td>
+ <td> {{ year.min_sac }} </td>
+ <td> {{ year.max_sac }} </td>
+ <td> {{ year.min_temp }} </td>
+ <td> {{ year.max_temp }} </td>
+ </tr>
+ {% endfor %}
+{% endblock %}
+</table>
+</div>
+</body>
+</html>
diff --git a/profile.c b/profile.c
index 67168547b..0722151a3 100644
--- a/profile.c
+++ b/profile.c
@@ -1209,7 +1209,7 @@ static void plot_string(struct plot_info *pi, struct plot_data *entry, struct me
for (k = 0; k < 16; k++) {
if (entry->ceilings[k]) {
depthvalue = get_depth_units(entry->ceilings[k], NULL, &depth_unit);
- put_format(b, translate("gettextFromC", "Tissue %.0fmin: %.0f%s\n"), buehlmann_N2_t_halflife[k], depthvalue, depth_unit);
+ put_format(b, translate("gettextFromC", "Tissue %.0fmin: %.1f%s\n"), buehlmann_N2_t_halflife[k], depthvalue, depth_unit);
}
}
}
diff --git a/qt-ui/divelogimportdialog.cpp b/qt-ui/divelogimportdialog.cpp
index 3a3402371..39bcc3423 100644
--- a/qt-ui/divelogimportdialog.cpp
+++ b/qt-ui/divelogimportdialog.cpp
@@ -614,31 +614,64 @@ void DiveLogImportDialog::loadFileContents(int value, whatChanged triggeredBy)
resultModel->setData(resultModel->index(0, i),headers.at(i),Qt::EditRole);
}
+char *intdup(int index)
+{
+ char tmpbuf[21];
+
+ snprintf(tmpbuf, sizeof(tmpbuf) - 2, "%d", index);
+ tmpbuf[20] = 0;
+ return strdup(tmpbuf);
+}
+
void DiveLogImportDialog::on_buttonBox_accepted()
{
QStringList r = resultModel->result();
if (ui->knownImports->currentText() != "Manual import") {
for (int i = 0; i < fileNames.size(); ++i) {
if (ui->knownImports->currentText() == "Seabear CSV") {
+ char *params[40];
+ int pnr = 0;
+
+ params[pnr++] = strdup("timeField");
+ params[pnr++] = intdup(r.indexOf(tr("Sample time")));
+ params[pnr++] = strdup("depthField");
+ params[pnr++] = intdup(r.indexOf(tr("Sample depth")));
+ params[pnr++] = strdup("tempField");
+ params[pnr++] = intdup(r.indexOf(tr("Sample temperature")));
+ params[pnr++] = strdup("po2Field");
+ params[pnr++] = intdup(r.indexOf(tr("Sample pO₂")));
+ params[pnr++] = strdup("o2sensor1Field");
+ params[pnr++] = intdup(r.indexOf(tr("Sample sensor1 pO₂")));
+ params[pnr++] = strdup("o2sensor2Field");
+ params[pnr++] = intdup(r.indexOf(tr("Sample sensor2 pO₂")));
+ params[pnr++] = strdup("o2sensor3Field");
+ params[pnr++] = intdup(r.indexOf(tr("Sample sensor3 pO₂")));
+ params[pnr++] = strdup("cnsField");
+ params[pnr++] = intdup(r.indexOf(tr("Sample CNS")));
+ params[pnr++] = strdup("ndlField");
+ params[pnr++] = intdup(r.indexOf(tr("Sample NDL")));
+ params[pnr++] = strdup("ttsField");
+ params[pnr++] = intdup(r.indexOf(tr("Sample TTS")));
+ params[pnr++] = strdup("stopdepthField");
+ params[pnr++] = intdup(r.indexOf(tr("Sample stopdepth")));
+ params[pnr++] = strdup("pressureField");
+ params[pnr++] = intdup(r.indexOf(tr("Sample pressure")));
+ params[pnr++] = strdup("setpointFiend");
+ params[pnr++] = intdup(-1);
+ params[pnr++] = strdup("separatorIndex");
+ params[pnr++] = intdup(ui->CSVSeparator->currentIndex());
+ params[pnr++] = strdup("units");
+ params[pnr++] = intdup(ui->CSVUnits->currentIndex());
+ params[pnr++] = strdup("delta");
+ params[pnr++] = strdup(delta.toUtf8().data());
+ if (hw.length()) {
+ params[pnr++] = strdup("hw");
+ params[pnr++] = strdup(hw.toUtf8().data());
+ }
+ params[pnr++] = NULL;
+
if (parse_seabear_csv_file(fileNames[i].toUtf8().data(),
- r.indexOf(tr("Sample time")),
- r.indexOf(tr("Sample depth")),
- r.indexOf(tr("Sample temperature")),
- r.indexOf(tr("Sample pO₂")),
- r.indexOf(tr("Sample sensor1 pO₂")),
- r.indexOf(tr("Sample sensor2 pO₂")),
- r.indexOf(tr("Sample sensor3 pO₂")),
- r.indexOf(tr("Sample CNS")),
- r.indexOf(tr("Sample NDL")),
- r.indexOf(tr("Sample TTS")),
- r.indexOf(tr("Sample stopdepth")),
- r.indexOf(tr("Sample pressure")),
- ui->CSVSeparator->currentIndex(),
- "csv",
- ui->CSVUnits->currentIndex(),
- delta.toUtf8().data(),
- hw.toUtf8().data()
- ) < 0) {
+ params, pnr - 1, "csv") < 0) {
return;
}
// Seabear CSV stores NDL and TTS in Minutes, not seconds
@@ -672,35 +705,63 @@ void DiveLogImportDialog::on_buttonBox_accepted()
}
} else {
for (int i = 0; i < fileNames.size(); ++i) {
- if (r.indexOf(tr("Sample time")) < 0)
- parse_manual_file(fileNames[i].toUtf8().data(),
- ui->CSVSeparator->currentIndex(),
- ui->CSVUnits->currentIndex(),
- ui->DateFormat->currentIndex(),
- ui->DurationFormat->currentIndex(),
- r.indexOf(tr("Dive #")),
- r.indexOf(tr("Date")),
- r.indexOf(tr("Time")),
- r.indexOf(tr("Duration")),
- r.indexOf(tr("Location")),
- r.indexOf(tr("GPS")),
- r.indexOf(tr("Max. depth")),
- r.indexOf(tr("Avg. depth")),
- r.indexOf(tr("Divemaster")),
- r.indexOf(tr("Buddy")),
- r.indexOf(tr("Suit")),
- r.indexOf(tr("Notes")),
- r.indexOf(tr("Weight")),
- r.indexOf(tr("Tags")),
- r.indexOf(tr("Cyl. size")),
- r.indexOf(tr("Start pressure")),
- r.indexOf(tr("End pressure")),
- r.indexOf(tr("O₂")),
- r.indexOf(tr("He")),
- r.indexOf(tr("Air temp.")),
- r.indexOf(tr("Water temp."))
- );
- else
+ if (r.indexOf(tr("Sample time")) < 0) {
+ char *params[55];
+ int pnr = 0;
+ params[pnr++] = strdup("numberField");
+ params[pnr++] = intdup(r.indexOf(tr("Dive #")));
+ params[pnr++] = strdup("dateField");
+ params[pnr++] = intdup(r.indexOf(tr("Date")));
+ params[pnr++] = strdup("timeField");
+ params[pnr++] = intdup(r.indexOf(tr("Time")));
+ params[pnr++] = strdup("durationField");
+ params[pnr++] = intdup(r.indexOf(tr("Duration")));
+ params[pnr++] = strdup("locationField");
+ params[pnr++] = intdup(r.indexOf(tr("Location")));
+ params[pnr++] = strdup("gpsField");
+ params[pnr++] = intdup(r.indexOf(tr("GPS")));
+ params[pnr++] = strdup("maxDepthField");
+ params[pnr++] = intdup(r.indexOf(tr("Max. depth")));
+ params[pnr++] = strdup("meanDepthField");
+ params[pnr++] = intdup(r.indexOf(tr("Avg. depth")));
+ params[pnr++] = strdup("divemasterField");
+ params[pnr++] = intdup(r.indexOf(tr("Divemaster")));
+ params[pnr++] = strdup("buddyField");
+ params[pnr++] = intdup(r.indexOf(tr("Buddy")));
+ params[pnr++] = strdup("suitField");
+ params[pnr++] = intdup(r.indexOf(tr("Suit")));
+ params[pnr++] = strdup("notesField");
+ params[pnr++] = intdup(r.indexOf(tr("Notes")));
+ params[pnr++] = strdup("weightField");
+ params[pnr++] = intdup(r.indexOf(tr("Weight")));
+ params[pnr++] = strdup("tagsField");
+ params[pnr++] = intdup(r.indexOf(tr("Tags")));
+ params[pnr++] = strdup("separatorIndex");
+ params[pnr++] = intdup(ui->CSVSeparator->currentIndex());
+ params[pnr++] = strdup("units");
+ params[pnr++] = intdup(ui->CSVUnits->currentIndex());
+ params[pnr++] = strdup("datefmt");
+ params[pnr++] = intdup(ui->DateFormat->currentIndex());
+ params[pnr++] = strdup("durationfmt");
+ params[pnr++] = intdup(ui->DurationFormat->currentIndex());
+ params[pnr++] = strdup("cylindersizeField");
+ params[pnr++] = intdup(r.indexOf(tr("Cyl. size")));
+ params[pnr++] = strdup("startpressureField");
+ params[pnr++] = intdup(r.indexOf(tr("Start pressure")));
+ params[pnr++] = strdup("endpressureField");
+ params[pnr++] = intdup(r.indexOf(tr("End pressure")));
+ params[pnr++] = strdup("o2Field");
+ params[pnr++] = intdup(r.indexOf(tr("O₂")));
+ params[pnr++] = strdup("heField");
+ params[pnr++] = intdup(r.indexOf(tr("He")));
+ params[pnr++] = strdup("airtempField");
+ params[pnr++] = intdup(r.indexOf(tr("Air temp.")));
+ params[pnr++] = strdup("watertempField");
+ params[pnr++] = intdup(r.indexOf(tr("Water temp.")));
+ params[pnr++] = NULL;
+
+ parse_manual_file(fileNames[i].toUtf8().data(), params, pnr - 1);
+ } else
parse_csv_file(fileNames[i].toUtf8().data(),
r.indexOf(tr("Sample time")),
r.indexOf(tr("Sample depth")),
diff --git a/qt-ui/diveplanner.cpp b/qt-ui/diveplanner.cpp
index 82ecb05dc..7f0d129a2 100644
--- a/qt-ui/diveplanner.cpp
+++ b/qt-ui/diveplanner.cpp
@@ -228,6 +228,8 @@ void PlannerSettingsWidget::disableDecoElements(int mode)
ui.decopo2->setDisabled(true);
ui.reserve_gas->setDisabled(false);
ui.conservatism_lvl->setDisabled(true);
+ ui.switch_at_req_stop->setDisabled(true);
+ ui.min_switch_duration->setDisabled(true);
}
else if (mode == VPMB) {
ui.gflow->setDisabled(true);
@@ -238,6 +240,8 @@ void PlannerSettingsWidget::disableDecoElements(int mode)
ui.decopo2->setDisabled(false);
ui.reserve_gas->setDisabled(true);
ui.conservatism_lvl->setDisabled(false);
+ ui.switch_at_req_stop->setDisabled(false);
+ ui.min_switch_duration->setDisabled(false);
}
else if (mode == BUEHLMANN) {
ui.gflow->setDisabled(false);
@@ -248,6 +252,8 @@ void PlannerSettingsWidget::disableDecoElements(int mode)
ui.decopo2->setDisabled(false);
ui.reserve_gas->setDisabled(true);
ui.conservatism_lvl->setDisabled(true);
+ ui.switch_at_req_stop->setDisabled(false);
+ ui.min_switch_duration->setDisabled(false);
}
}
diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp
index 4d416d2be..7593caf47 100644
--- a/qt-ui/mainwindow.cpp
+++ b/qt-ui/mainwindow.cpp
@@ -58,7 +58,8 @@ MainWindow::MainWindow() : QMainWindow(),
actionPreviousDive(0),
helpView(0),
state(VIEWALL),
- survey(0)
+ survey(0),
+ spinner(0)
{
Q_ASSERT_X(m_Instance == NULL, "MainWindow", "MainWindow recreated!");
m_Instance = this;
@@ -374,6 +375,7 @@ void MainWindow::on_actionCloudstorageopen_triggered()
int error;
+ startSpinner();
QByteArray fileNamePtr = QFile::encodeName(filename);
error = parse_file(fileNamePtr.data());
if (!error) {
@@ -382,6 +384,7 @@ void MainWindow::on_actionCloudstorageopen_triggered()
}
getNotificationWidget()->hideNotification();
process_dives(false, false);
+ stopSpinner();
refreshDisplay();
ui.actionAutoGroup->setChecked(autogroup);
}
@@ -397,10 +400,15 @@ void MainWindow::on_actionCloudstoragesave_triggered()
if (information()->isEditing())
information()->acceptChanges();
+ startSpinner();
+
if (save_dives(filename.toUtf8().data())) {
getNotificationWidget()->showNotification(get_error_string(), KMessageWidget::Error);
return;
}
+
+ stopSpinner();
+
getNotificationWidget()->showNotification(get_error_string(), KMessageWidget::Error);
set_filename(filename.toUtf8().data(), true);
setTitle(MWTF_FILENAME);
@@ -1762,3 +1770,24 @@ void MainWindow::setApplicationState(const QByteArray& state) {
}
#undef SET_CURRENT_INDEX
}
+
+void MainWindow::startSpinner()
+{
+ if (!spinner) {
+ spinner = new QtWaitingSpinner(Qt::WindowModal, this, true);
+ spinner->setRevolutionsPerSecond(1);
+ spinner->setColor(WHITE1);
+ spinner->setLineWidth(7);
+ spinner->setRoundness(40.0);
+ spinner->setMinimumTrailOpacity(0.25);
+ }
+ int shorterEdge = MIN(this->geometry().height(), this->geometry().width());
+ spinner->setInnerRadius(shorterEdge / 12);
+ spinner->setLineLength(shorterEdge / 8);
+ spinner->start();
+}
+
+void MainWindow::stopSpinner()
+{
+ spinner->stop();
+}
diff --git a/qt-ui/mainwindow.h b/qt-ui/mainwindow.h
index 226e9b6ee..759132146 100644
--- a/qt-ui/mainwindow.h
+++ b/qt-ui/mainwindow.h
@@ -15,6 +15,7 @@
#include "ui_mainwindow.h"
#include "notificationwidget.h"
#include "windowtitleupdate.h"
+#include "qtwaitingspinner.h"
struct DiveList;
class QSortFilterProxyModel;
@@ -189,7 +190,8 @@ slots:
void planCreated();
void setEnabledToolbar(bool arg1);
void setPlanNotes();
-
+ void startSpinner();
+ void stopSpinner();
private:
Ui::MainWindow ui;
@@ -244,6 +246,7 @@ private:
QHash<QByteArray, PropertiesForQuadrant> stateProperties;
WindowTitleUpdate *wtu;
+ QtWaitingSpinner *spinner;
};
#endif // MAINWINDOW_H
diff --git a/qt-ui/plannerSettings.ui b/qt-ui/plannerSettings.ui
index 4ebc868af..54ea5762c 100644
--- a/qt-ui/plannerSettings.ui
+++ b/qt-ui/plannerSettings.ui
@@ -491,6 +491,9 @@
<property name="text">
<string>Conservatism level</string>
</property>
+ <property name="indent">
+ <number>25</number>
+ </property>
</widget>
</item>
<item row="12" column="2">
diff --git a/qt-ui/printdialog.cpp b/qt-ui/printdialog.cpp
index 002f9b9f4..cf08062d2 100644
--- a/qt-ui/printdialog.cpp
+++ b/qt-ui/printdialog.cpp
@@ -178,15 +178,8 @@ void PrintDialog::printClicked(void)
{
QPrintDialog printDialog(&qprinter, this);
if (printDialog.exec() == QDialog::Accepted) {
- switch (printOptions.type) {
- case print_options::DIVELIST:
- connect(printer, SIGNAL(progessUpdated(int)), progressBar, SLOT(setValue(int)));
- printer->print();
- break;
- case print_options::STATISTICS:
- printer->print_statistics();
- break;
- }
+ connect(printer, SIGNAL(progessUpdated(int)), progressBar, SLOT(setValue(int)));
+ printer->print();
close();
}
}
@@ -194,14 +187,7 @@ void PrintDialog::printClicked(void)
void PrintDialog::onPaintRequested(QPrinter *printerPtr)
{
connect(printer, SIGNAL(progessUpdated(int)), progressBar, SLOT(setValue(int)));
- switch (printOptions.type) {
- case print_options::DIVELIST:
- printer->print();
- break;
- case print_options::STATISTICS:
- printer->print_statistics();
- break;
- }
+ printer->print();
progressBar->setValue(0);
disconnect(printer, SIGNAL(progessUpdated(int)), progressBar, SLOT(setValue(int)));
}
diff --git a/qt-ui/printoptions.cpp b/qt-ui/printoptions.cpp
index 419098cf8..cb944f9d7 100644
--- a/qt-ui/printoptions.cpp
+++ b/qt-ui/printoptions.cpp
@@ -31,21 +31,7 @@ void PrintOptions::setup()
break;
}
- // insert existing templates in the UI and select the current template
- qSort(grantlee_templates);
- int current_index = 0, index = 0;
- for (QList<QString>::iterator i = grantlee_templates.begin(); i != grantlee_templates.end(); ++i) {
- if ((*i).compare(printOptions->p_template) == 0) {
- current_index = index;
- break;
- }
- index++;
- }
- ui.printTemplate->clear();
- for (QList<QString>::iterator i = grantlee_templates.begin(); i != grantlee_templates.end(); ++i) {
- ui.printTemplate->addItem((*i).split('.')[0], QVariant::fromValue(*i));
- }
- ui.printTemplate->setCurrentIndex(current_index);
+ setupTemplates();
// general print option checkboxes
if (printOptions->color_selected)
@@ -63,6 +49,43 @@ void PrintOptions::setup()
hasSetupSlots = true;
}
+void PrintOptions::setupTemplates()
+{
+ if (printOptions->type == print_options::DIVELIST) {
+ // insert dive list templates in the UI and select the current template
+ qSort(grantlee_templates);
+ int current_index = 0, index = 0;
+ for (QList<QString>::iterator i = grantlee_templates.begin(); i != grantlee_templates.end(); ++i) {
+ if ((*i).compare(printOptions->p_template) == 0) {
+ current_index = index;
+ break;
+ }
+ index++;
+ }
+ ui.printTemplate->clear();
+ for (QList<QString>::iterator i = grantlee_templates.begin(); i != grantlee_templates.end(); ++i) {
+ ui.printTemplate->addItem((*i).split('.')[0], QVariant::fromValue(*i));
+ }
+ ui.printTemplate->setCurrentIndex(current_index);
+ } else if (printOptions->type == print_options::STATISTICS) {
+ // insert statistics templates in the UI and select the current template
+ qSort(grantlee_statistics_templates);
+ int current_index = 0, index = 0;
+ for (QList<QString>::iterator i = grantlee_statistics_templates.begin(); i != grantlee_statistics_templates.end(); ++i) {
+ if ((*i).compare(printOptions->p_template) == 0) {
+ current_index = index;
+ break;
+ }
+ index++;
+ }
+ ui.printTemplate->clear();
+ for (QList<QString>::iterator i = grantlee_statistics_templates.begin(); i != grantlee_statistics_templates.end(); ++i) {
+ ui.printTemplate->addItem((*i).split('.')[0], QVariant::fromValue(*i));
+ }
+ ui.printTemplate->setCurrentIndex(current_index);
+ }
+}
+
// print type radio buttons
void PrintOptions::on_radioDiveListPrint_toggled(bool check)
{
@@ -70,15 +93,14 @@ void PrintOptions::on_radioDiveListPrint_toggled(bool check)
printOptions->type = print_options::DIVELIST;
// print options
- ui.printInColor->setEnabled(true);
ui.printSelected->setEnabled(true);
// print template
ui.deleteButton->setEnabled(true);
- ui.editButton->setEnabled(true);
ui.exportButton->setEnabled(true);
ui.importButton->setEnabled(true);
- ui.printTemplate->setEnabled(true);
+
+ setupTemplates();
}
}
@@ -88,15 +110,14 @@ void PrintOptions::on_radioStatisticsPrint_toggled(bool check)
printOptions->type = print_options::STATISTICS;
// print options
- ui.printInColor->setEnabled(false);
ui.printSelected->setEnabled(false);
// print template
ui.deleteButton->setEnabled(false);
- ui.editButton->setEnabled(false);
ui.exportButton->setEnabled(false);
ui.importButton->setEnabled(false);
- ui.printTemplate->setEnabled(false);
+
+ setupTemplates();
}
}
diff --git a/qt-ui/printoptions.h b/qt-ui/printoptions.h
index 6d7ffffee..9c50b10f3 100644
--- a/qt-ui/printoptions.h
+++ b/qt-ui/printoptions.h
@@ -70,6 +70,7 @@ private:
struct print_options *printOptions;
struct template_options *templateOptions;
bool hasSetupSlots;
+ void setupTemplates();
private
slots:
diff --git a/qt-ui/templateedit.cpp b/qt-ui/templateedit.cpp
index e4e6453ac..b50338090 100644
--- a/qt-ui/templateedit.cpp
+++ b/qt-ui/templateedit.cpp
@@ -35,6 +35,9 @@ TemplateEdit::TemplateEdit(QWidget *parent, struct print_options *printOptions,
ui->plainTextEdit->setPlainText(grantlee_template);
editingCustomColors = false;
+ if (printOptions->type == print_options::STATISTICS) {
+ ui->plainTextEdit->setEnabled(false);
+ }
updatePreview();
}
@@ -125,8 +128,8 @@ void TemplateEdit::saveSettings()
if (msgBox.exec() == QMessageBox::Save) {
memcpy(templateOptions, &newTemplateOptions, sizeof(struct template_options));
if (grantlee_template.compare(ui->plainTextEdit->toPlainText())) {
- printOptions->p_template = "custom.html";
- TemplateLayout::writeTemplate("custom.html", ui->plainTextEdit->toPlainText());
+ printOptions->p_template = "Custom.html";
+ TemplateLayout::writeTemplate("Custom.html", ui->plainTextEdit->toPlainText());
}
if (templateOptions->color_palette_index == CUSTOM) {
custom_colors = templateOptions->color_palette;
diff --git a/templatelayout.cpp b/templatelayout.cpp
index b0098fbd9..c52968f09 100644
--- a/templatelayout.cpp
+++ b/templatelayout.cpp
@@ -4,7 +4,7 @@
#include "helpers.h"
#include "display.h"
-QList<QString> grantlee_templates;
+QList<QString> grantlee_templates, grantlee_statistics_templates;
int getTotalWork(print_options *printOptions)
{
@@ -24,6 +24,7 @@ int getTotalWork(print_options *printOptions)
void find_all_templates()
{
grantlee_templates.clear();
+ grantlee_statistics_templates.clear();
QDir dir(getSubsurfaceDataPath("printing_templates"));
QFileInfoList list = dir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot);
foreach (QFileInfo finfo, list) {
@@ -32,6 +33,15 @@ void find_all_templates()
grantlee_templates.append(finfo.fileName());
}
}
+ // find statistics templates
+ dir.setPath(getSubsurfaceDataPath("printing_templates") + QDir::separator() + "statistics");
+ list = dir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot);
+ foreach (QFileInfo finfo, list) {
+ QString filename = finfo.fileName();
+ if (filename.at(filename.size() - 1) != '~') {
+ grantlee_statistics_templates.append(finfo.fileName());
+ }
+ }
}
TemplateLayout::TemplateLayout(print_options *PrintOptions, template_options *templateOptions) :
@@ -98,6 +108,53 @@ QString TemplateLayout::generate()
return htmlContent;
}
+QString TemplateLayout::generateStatistics()
+{
+ QString htmlContent;
+ m_engine = new Grantlee::Engine(this);
+
+ QSharedPointer<Grantlee::FileSystemTemplateLoader> m_templateLoader =
+ QSharedPointer<Grantlee::FileSystemTemplateLoader>(new Grantlee::FileSystemTemplateLoader());
+ m_templateLoader->setTemplateDirs(QStringList() << getSubsurfaceDataPath("printing_templates/statistics"));
+ m_engine->addTemplateLoader(m_templateLoader);
+
+ Grantlee::registerMetaType<YearInfo>();
+ Grantlee::registerMetaType<template_options>();
+ Grantlee::registerMetaType<print_options>();
+
+ QVariantHash mapping;
+ QVariantList years;
+
+ int i = 0;
+ while (stats_yearly != NULL && stats_yearly[i].period) {
+ YearInfo year(stats_yearly[i]);
+ years.append(QVariant::fromValue(year));
+ i++;
+ }
+
+ mapping.insert("years", years);
+ mapping.insert("template_options", QVariant::fromValue(*templateOptions));
+ mapping.insert("print_options", QVariant::fromValue(*PrintOptions));
+
+ Grantlee::Context c(mapping);
+
+ Grantlee::Template t = m_engine->loadByName(PrintOptions->p_template);
+ if (!t || t->error()) {
+ qDebug() << "Can't load template";
+ return htmlContent;
+ }
+
+ htmlContent = t->render(&c);
+
+ if (t->error()) {
+ qDebug() << "Can't render template";
+ return htmlContent;
+ }
+
+ emit progressUpdated(100);
+ return htmlContent;
+}
+
QString TemplateLayout::readTemplate(QString template_name)
{
QFile qfile(getSubsurfaceDataPath("printing_templates") + QDir::separator() + template_name);
@@ -306,3 +363,13 @@ void Dive::put_sac()
m_sac = QString::number(value, 'f', decimal).append(unit);
}
}
+
+YearInfo::YearInfo()
+{
+
+}
+
+YearInfo::~YearInfo()
+{
+
+}
diff --git a/templatelayout.h b/templatelayout.h
index 41a3cbfa9..d9fa29295 100644
--- a/templatelayout.h
+++ b/templatelayout.h
@@ -4,11 +4,13 @@
#include <grantlee_templates.h>
#include "mainwindow.h"
#include "printoptions.h"
+#include "statistics.h"
+#include "helpers.h"
int getTotalWork(print_options *printOptions);
void find_all_templates();
-extern QList<QString> grantlee_templates;
+extern QList<QString> grantlee_templates, grantlee_statistics_templates;
class TemplateLayout : public QObject {
Q_OBJECT
@@ -16,6 +18,7 @@ public:
TemplateLayout(print_options *PrintOptions, template_options *templateOptions);
~TemplateLayout();
QString generate();
+ QString generateStatistics();
static QString readTemplate(QString template_name);
static void writeTemplate(QString template_name, QString grantlee_template);
@@ -98,9 +101,22 @@ public:
QString sac() const;
};
+class YearInfo {
+public:
+ stats_t *year;
+ YearInfo(stats_t& year)
+ :year(&year)
+ {
+
+ }
+ YearInfo();
+ ~YearInfo();
+};
+
Q_DECLARE_METATYPE(Dive)
Q_DECLARE_METATYPE(template_options)
Q_DECLARE_METATYPE(print_options)
+Q_DECLARE_METATYPE(YearInfo)
GRANTLEE_BEGIN_LOOKUP(Dive)
if (property == "number")
@@ -182,4 +198,40 @@ if (property == "grayscale") {
}
GRANTLEE_END_LOOKUP
+GRANTLEE_BEGIN_LOOKUP(YearInfo)
+if (property == "year") {
+ return object.year->period;
+} else if (property == "dives") {
+ return object.year->selection_size;
+} else if (property == "min_temp") {
+ const char *unit;
+ double temp = get_temp_units(object.year->min_temp, &unit);
+ return object.year->min_temp == 0 ? "0" : QString::number(temp, 'g', 2) + unit;
+} else if (property == "max_temp") {
+ const char *unit;
+ double temp = get_temp_units(object.year->max_temp, &unit);
+ return object.year->max_temp == 0 ? "0" : QString::number(temp, 'g', 2) + unit;
+} else if (property == "total_time") {
+ return get_time_string(object.year->total_time.seconds, 0);
+} else if (property == "avg_time") {
+ return get_minutes(object.year->total_time.seconds / object.year->selection_size);
+} else if (property == "shortest_time") {
+ return get_minutes(object.year->shortest_time.seconds);
+} else if (property == "longest_time") {
+ return get_minutes(object.year->longest_time.seconds);
+} else if (property == "avg_depth") {
+ return get_depth_string(object.year->avg_depth);
+} else if (property == "min_depth") {
+ return get_depth_string(object.year->min_depth);
+} else if (property == "max_depth") {
+ return get_depth_string(object.year->max_depth);
+} else if (property == "avg_sac") {
+ return get_volume_string(object.year->avg_sac);
+} else if (property == "min_sac") {
+ return get_volume_string(object.year->min_sac);
+} else if (property == "max_sac") {
+ return get_volume_string(object.year->max_sac);
+}
+GRANTLEE_END_LOOKUP
+
#endif
diff --git a/tests/testparse.cpp b/tests/testparse.cpp
index fa1d6d223..76b3912da 100644
--- a/tests/testparse.cpp
+++ b/tests/testparse.cpp
@@ -4,19 +4,77 @@
#include "divelist.h"
#include <QTextStream>
+char *intdup(int index)
+{
+ char tmpbuf[21];
+
+ snprintf(tmpbuf, sizeof(tmpbuf) - 2, "%d", index);
+ tmpbuf[20] = 0;
+ return strdup(tmpbuf);
+}
+
void TestParse::testParseCSV()
{
// some basic file parsing tests
//
// CSV import should work
verbose = 1;
- QCOMPARE(parse_manual_file(SUBSURFACE_SOURCE "/dives/test41.csv",
- 0, // tab separator
- 0, // metric units
- 1, // mm/dd/yyyy
- 2, // min:sec
- 0, 1, 2, 3, -1, -1, 4, 5, // Dive #, date, time, duration, maxdepth, avgdepth
- -1, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), 0); // buddy, suit
+ char *params[55];
+ int pnr = 0;
+
+ params[pnr++] = strdup(strdup("numberField"));
+ params[pnr++] = intdup(0);
+ params[pnr++] = strdup("dateField");
+ params[pnr++] = intdup(1);
+ params[pnr++] = strdup("timeField");
+ params[pnr++] = intdup(2);
+ params[pnr++] = strdup("durationField");
+ params[pnr++] = intdup(3);
+ params[pnr++] = strdup("locationField");
+ params[pnr++] = intdup(-1);
+ params[pnr++] = strdup("gpsField");
+ params[pnr++] = intdup(-1);
+ params[pnr++] = strdup("maxDepthField");
+ params[pnr++] = intdup(4);
+ params[pnr++] = strdup("meanDepthField");
+ params[pnr++] = intdup(5);
+ params[pnr++] = strdup("divemasterField");
+ params[pnr++] = intdup(-1);
+ params[pnr++] = strdup("buddyField");
+ params[pnr++] = intdup(6);
+ params[pnr++] = strdup("suitField");
+ params[pnr++] = intdup(7);
+ params[pnr++] = strdup("notesField");
+ params[pnr++] = intdup(-1);
+ params[pnr++] = strdup("weightField");
+ params[pnr++] = intdup(-1);
+ params[pnr++] = strdup("tagsField");
+ params[pnr++] = intdup(-1);
+ params[pnr++] = strdup("separatorIndex");
+ params[pnr++] = intdup(0);
+ params[pnr++] = strdup("units");
+ params[pnr++] = intdup(0);
+ params[pnr++] = strdup("datefmt");
+ params[pnr++] = intdup(1);
+ params[pnr++] = strdup("durationfmt");
+ params[pnr++] = intdup(2);
+ params[pnr++] = strdup("cylindersizeField");
+ params[pnr++] = intdup(-1);
+ params[pnr++] = strdup("startpressureField");
+ params[pnr++] = intdup(-1);
+ params[pnr++] = strdup("endpressureField");
+ params[pnr++] = intdup(-1);
+ params[pnr++] = strdup("o2Field");
+ params[pnr++] = intdup(-1);
+ params[pnr++] = strdup("heField");
+ params[pnr++] = intdup(-1);
+ params[pnr++] = strdup("airtempField");
+ params[pnr++] = intdup(-1);
+ params[pnr++] = strdup("watertempField");
+ params[pnr++] = intdup(-1);
+ params[pnr++] = NULL;
+
+ QCOMPARE(parse_manual_file(SUBSURFACE_SOURCE "/dives/test41.csv", params, pnr - 1), 0);
fprintf(stderr, "number of dives %d \n", dive_table.nr);
}
@@ -210,25 +268,45 @@ void TestParse::testParseNewFormat()
* Validate the parsing routine returns success.
*/
+ char *params[40];
+ int pnr = 0;
+
+ params[pnr++] = strdup("timeField");
+ params[pnr++] = intdup(headers.indexOf(tr("Sample time")));
+ params[pnr++] = strdup("depthField");
+ params[pnr++] = intdup(headers.indexOf(tr("Sample depth")));
+ params[pnr++] = strdup("tempField");
+ params[pnr++] = intdup(headers.indexOf(tr("Sample temperature")));
+ params[pnr++] = strdup("po2Field");
+ params[pnr++] = intdup(headers.indexOf(tr("Sample pO₂")));
+ params[pnr++] = strdup("o2sensor1Field");
+ params[pnr++] = intdup(headers.indexOf(tr("Sample sensor1 pO₂")));
+ params[pnr++] = strdup("o2sensor2Field");
+ params[pnr++] = intdup(headers.indexOf(tr("Sample sensor2 pO₂")));
+ params[pnr++] = strdup("o2sensor3Field");
+ params[pnr++] = intdup(headers.indexOf(tr("Sample sensor3 pO₂")));
+ params[pnr++] = strdup("cnsField");
+ params[pnr++] = intdup(headers.indexOf(tr("Sample CNS")));
+ params[pnr++] = strdup("ndlField");
+ params[pnr++] = intdup(headers.indexOf(tr("Sample NDL")));
+ params[pnr++] = strdup("ttsField");
+ params[pnr++] = intdup(headers.indexOf(tr("Sample TTS")));
+ params[pnr++] = strdup("stopdepthField");
+ params[pnr++] = intdup(headers.indexOf(tr("Sample stopdepth")));
+ params[pnr++] = strdup("pressureField");
+ params[pnr++] = intdup(headers.indexOf(tr("Sample pressure")));
+ params[pnr++] = strdup("setpointFiend");
+ params[pnr++] = intdup(-1);
+ params[pnr++] = strdup("separatorIndex");
+ params[pnr++] = intdup(2);
+ params[pnr++] = strdup("units");
+ params[pnr++] = intdup(0);
+ params[pnr++] = strdup("delta");
+ params[pnr++] = strdup(delta.toUtf8().data());
+ params[pnr++] = NULL;
+
QCOMPARE(parse_seabear_csv_file(file.toUtf8().data(),
- headers.indexOf(tr("Sample time")),
- headers.indexOf(tr("Sample depth")),
- headers.indexOf(tr("Sample temperature")),
- headers.indexOf(tr("Sample pO₂")),
- headers.indexOf(tr("Sample sensor1 pO₂")),
- headers.indexOf(tr("Sample sensor2 pO₂")),
- headers.indexOf(tr("Sample sensor3 pO₂")),
- headers.indexOf(tr("Sample CNS")),
- headers.indexOf(tr("Sample NDL")),
- headers.indexOf(tr("Sample TTS")),
- headers.indexOf(tr("Sample stopdepth")),
- headers.indexOf(tr("Sample pressure")),
- 2,
- "csv",
- 0,
- delta.toUtf8().data(),
- ""
- ), 0);
+ params, pnr - 1, "csv"), 0);
/*
* Set artificial but static dive times so the result
diff --git a/tests/testplan.cpp b/tests/testplan.cpp
index 88f136602..f09773524 100644
--- a/tests/testplan.cpp
+++ b/tests/testplan.cpp
@@ -2,6 +2,7 @@
#include "testplan.h"
#include "planner.h"
#include "units.h"
+#include "qthelper.h"
#include <QDebug>
#define DEBUG 1
@@ -19,6 +20,18 @@ void setupPrefs()
prefs.last_stop = true;
}
+void setupPrefsVpmb()
+{
+ prefs = default_prefs;
+ prefs.ascrate50 = 10000 / 60;
+ prefs.ascrate75 = prefs.ascrate50;
+ prefs.ascratestops = prefs.ascrate50;
+ prefs.ascratelast6m = prefs.ascrate50;
+ prefs.descrate = 99000 / 60;
+ prefs.last_stop = false;
+ prefs.deco_mode = VPMB;
+}
+
void setupPlan(struct diveplan *dp)
{
dp->salinity = 10300;
@@ -45,6 +58,140 @@ void setupPlan(struct diveplan *dp)
plan_add_segment(dp, 0, gas_mod(&oxygen, po2, &displayed_dive, M_OR_FT(3,10)).mm, oxygen, 0, 1);
}
+void setupPlanVpmb60m30minAir(struct diveplan *dp)
+{
+ dp->salinity = 10300;
+ dp->surface_pressure = 1013;
+ dp->bottomsac = 0;
+ dp->decosac = 0;
+
+ struct gasmix bottomgas = { {210}, {0} };
+ pressure_t po2 = { 1600 };
+ displayed_dive.cylinder[0].gasmix = bottomgas;
+ displayed_dive.surface_pressure.mbar = 1013;
+ reset_cylinders(&displayed_dive, true);
+ free_dps(dp);
+
+ int droptime = M_OR_FT(60, 200) * 60 / M_OR_FT(99, 330);
+ plan_add_segment(dp, droptime, M_OR_FT(60, 200), bottomgas, 0, 1);
+ plan_add_segment(dp, 30*60 - droptime, M_OR_FT(60, 200), bottomgas, 0, 1);
+}
+
+void setupPlanVpmb60m30minEan50(struct diveplan *dp)
+{
+ dp->salinity = 10300;
+ dp->surface_pressure = 1013;
+ dp->bottomsac = 0;
+ dp->decosac = 0;
+
+ struct gasmix bottomgas = { {210}, {0} };
+ struct gasmix ean50 = { {500}, {0} };
+ pressure_t po2 = { 1600 };
+ displayed_dive.cylinder[0].gasmix = bottomgas;
+ displayed_dive.cylinder[1].gasmix = ean50;
+ displayed_dive.surface_pressure.mbar = 1013;
+ reset_cylinders(&displayed_dive, true);
+ free_dps(dp);
+
+ int droptime = M_OR_FT(60, 200) * 60 / M_OR_FT(99, 330);
+ plan_add_segment(dp, droptime, M_OR_FT(60, 200), bottomgas, 0, 1);
+ plan_add_segment(dp, 30*60 - droptime, M_OR_FT(60, 200), bottomgas, 0, 1);
+ plan_add_segment(dp, 0, gas_mod(&ean50, po2, &displayed_dive, M_OR_FT(3,10)).mm, ean50, 0, 1);
+}
+
+void setupPlanVpmb60m30minTx(struct diveplan *dp)
+{
+ dp->salinity = 10300;
+ dp->surface_pressure = 1013;
+ dp->bottomsac = 0;
+ dp->decosac = 0;
+
+ struct gasmix bottomgas = { {180}, {450} };
+ struct gasmix ean50 = { {500}, {0} };
+ pressure_t po2 = { 1600 };
+ displayed_dive.cylinder[0].gasmix = bottomgas;
+ displayed_dive.cylinder[1].gasmix = ean50;
+ displayed_dive.surface_pressure.mbar = 1013;
+ reset_cylinders(&displayed_dive, true);
+ free_dps(dp);
+
+ int droptime = M_OR_FT(60, 200) * 60 / M_OR_FT(99, 330);
+ plan_add_segment(dp, droptime, M_OR_FT(60, 200), bottomgas, 0, 1);
+ plan_add_segment(dp, 30*60 - droptime, M_OR_FT(60, 200), bottomgas, 0, 1);
+ plan_add_segment(dp, 0, gas_mod(&ean50, po2, &displayed_dive, M_OR_FT(3,10)).mm, ean50, 0, 1);
+}
+
+void setupPlanVpmbMultiLevelAir(struct diveplan *dp)
+{
+ dp->salinity = 10300;
+ dp->surface_pressure = 1013;
+ dp->bottomsac = 0;
+ dp->decosac = 0;
+
+ struct gasmix bottomgas = { {210}, {0} };
+ pressure_t po2 = { 1600 };
+ displayed_dive.cylinder[0].gasmix = bottomgas;
+ displayed_dive.surface_pressure.mbar = 1013;
+ reset_cylinders(&displayed_dive, true);
+ free_dps(dp);
+
+ int droptime = M_OR_FT(20, 66) * 60 / M_OR_FT(99, 330);
+ plan_add_segment(dp, droptime, M_OR_FT(20, 66), bottomgas, 0, 1);
+ plan_add_segment(dp, 10*60 - droptime, M_OR_FT(20, 66), bottomgas, 0, 1);
+ plan_add_segment(dp, 1*60, M_OR_FT(60, 200), bottomgas, 0, 1);
+ plan_add_segment(dp, 29*60, M_OR_FT(60, 200), bottomgas, 0, 1);
+}
+
+void setupPlanVpmb100m60min(struct diveplan *dp)
+{
+ dp->salinity = 10300;
+ dp->surface_pressure = 1013;
+ dp->bottomsac = 0;
+ dp->decosac = 0;
+
+ struct gasmix bottomgas = { {180}, {450} };
+ struct gasmix ean50 = { {500}, {0} };
+ struct gasmix oxygen = { {1000}, {0} };
+ pressure_t po2 = { 1600 };
+ displayed_dive.cylinder[0].gasmix = bottomgas;
+ displayed_dive.cylinder[1].gasmix = ean50;
+ displayed_dive.cylinder[2].gasmix = oxygen;
+ displayed_dive.surface_pressure.mbar = 1013;
+ reset_cylinders(&displayed_dive, true);
+ free_dps(dp);
+
+ int droptime = M_OR_FT(100, 330) * 60 / M_OR_FT(99, 330);
+ plan_add_segment(dp, droptime, M_OR_FT(100, 330), bottomgas, 0, 1);
+ plan_add_segment(dp, 60*60 - droptime, M_OR_FT(100, 330), bottomgas, 0, 1);
+ plan_add_segment(dp, 0, gas_mod(&ean50, po2, &displayed_dive, M_OR_FT(3,10)).mm, ean50, 0, 1);
+ plan_add_segment(dp, 0, gas_mod(&oxygen, po2, &displayed_dive, M_OR_FT(3,10)).mm, oxygen, 0, 1);
+}
+
+void setupPlanVpmb100m10min(struct diveplan *dp)
+{
+ dp->salinity = 10300;
+ dp->surface_pressure = 1013;
+ dp->bottomsac = 0;
+ dp->decosac = 0;
+
+ struct gasmix bottomgas = { {180}, {450} };
+ struct gasmix ean50 = { {500}, {0} };
+ struct gasmix oxygen = { {1000}, {0} };
+ pressure_t po2 = { 1600 };
+ displayed_dive.cylinder[0].gasmix = bottomgas;
+ displayed_dive.cylinder[1].gasmix = ean50;
+ displayed_dive.cylinder[2].gasmix = oxygen;
+ displayed_dive.surface_pressure.mbar = 1013;
+ reset_cylinders(&displayed_dive, true);
+ free_dps(dp);
+
+ int droptime = M_OR_FT(100, 330) * 60 / M_OR_FT(99, 330);
+ plan_add_segment(dp, droptime, M_OR_FT(100, 330), bottomgas, 0, 1);
+ plan_add_segment(dp, 10*60 - droptime, M_OR_FT(100, 330), bottomgas, 0, 1);
+ plan_add_segment(dp, 0, gas_mod(&ean50, po2, &displayed_dive, M_OR_FT(3,10)).mm, ean50, 0, 1);
+ plan_add_segment(dp, 0, gas_mod(&oxygen, po2, &displayed_dive, M_OR_FT(3,10)).mm, oxygen, 0, 1);
+}
+
void TestPlan::testMetric()
{
char *cache = NULL;
@@ -117,4 +264,184 @@ void TestPlan::testImperial()
QCOMPARE(displayed_dive.dc.duration.seconds, 110u * 60u - 2u);
}
+void TestPlan::testVpmbMetric60m30minAir()
+{
+ char *cache = NULL;
+
+ setupPrefsVpmb();
+ prefs.unit_system = METRIC;
+ prefs.units.length = units::METERS;
+
+ struct diveplan testPlan = { 0 };
+ setupPlanVpmb60m30minAir(&testPlan);
+ setCurrentAppState("PlanDive");
+
+ plan(&testPlan, &cache, 1, 0);
+
+#if DEBUG
+ free(displayed_dive.notes);
+ displayed_dive.notes = NULL;
+ save_dive(stdout, &displayed_dive);
+#endif
+
+ // check expected run time of 141 minutes
+ QCOMPARE(displayed_dive.dc.duration.seconds, 8480u);
+}
+
+void TestPlan::testVpmbMetric60m30minEan50()
+{
+ char *cache = NULL;
+
+ setupPrefsVpmb();
+ prefs.unit_system = METRIC;
+ prefs.units.length = units::METERS;
+
+ struct diveplan testPlan = { 0 };
+ setupPlanVpmb60m30minEan50(&testPlan);
+ setCurrentAppState("PlanDive");
+
+ plan(&testPlan, &cache, 1, 0);
+
+#if DEBUG
+ free(displayed_dive.notes);
+ displayed_dive.notes = NULL;
+ save_dive(stdout, &displayed_dive);
+#endif
+
+ // check first gas change to EAN50 at 21m
+ struct event *ev = displayed_dive.dc.events;
+ QVERIFY(ev != NULL);
+ QCOMPARE(ev->gas.index, 1);
+ QCOMPARE(ev->value, 50);
+ QCOMPARE(get_depth_at_time(&displayed_dive.dc, ev->time.seconds), 21000);
+ // check expected run time of 95 minutes
+ QCOMPARE(displayed_dive.dc.duration.seconds, 5720u);
+}
+
+void TestPlan::testVpmbMetric60m30minTx()
+{
+ char *cache = NULL;
+
+ setupPrefsVpmb();
+ prefs.unit_system = METRIC;
+ prefs.units.length = units::METERS;
+
+ struct diveplan testPlan = { 0 };
+ setupPlanVpmb60m30minTx(&testPlan);
+ setCurrentAppState("PlanDive");
+
+ plan(&testPlan, &cache, 1, 0);
+
+#if DEBUG
+ free(displayed_dive.notes);
+ displayed_dive.notes = NULL;
+ save_dive(stdout, &displayed_dive);
+#endif
+
+ // check first gas change to EAN50 at 21m
+ struct event *ev = displayed_dive.dc.events;
+ QVERIFY(ev != NULL);
+ QCOMPARE(ev->gas.index, 1);
+ QCOMPARE(ev->value, 50);
+ QCOMPARE(get_depth_at_time(&displayed_dive.dc, ev->time.seconds), 21000);
+ // check expected run time of 89 minutes
+ QCOMPARE(displayed_dive.dc.duration.seconds, 5360u);
+}
+
+void TestPlan::testVpmbMetric100m60min()
+{
+ char *cache = NULL;
+
+ setupPrefsVpmb();
+ prefs.unit_system = METRIC;
+ prefs.units.length = units::METERS;
+
+ struct diveplan testPlan = { 0 };
+ setupPlanVpmb100m60min(&testPlan);
+ setCurrentAppState("PlanDive");
+
+ plan(&testPlan, &cache, 1, 0);
+
+#if DEBUG
+ free(displayed_dive.notes);
+ displayed_dive.notes = NULL;
+ save_dive(stdout, &displayed_dive);
+#endif
+
+ // check first gas change to EAN50 at 21m
+ struct event *ev = displayed_dive.dc.events;
+ QVERIFY(ev != NULL);
+ QCOMPARE(ev->gas.index, 1);
+ QCOMPARE(ev->value, 50);
+ QCOMPARE(get_depth_at_time(&displayed_dive.dc, ev->time.seconds), 21000);
+ // check second gas change to Oxygen at 6m
+ ev = ev->next;
+ QVERIFY(ev != NULL);
+ QCOMPARE(ev->gas.index, 2);
+ QCOMPARE(ev->value, 100);
+ QCOMPARE(get_depth_at_time(&displayed_dive.dc, ev->time.seconds), 6000);
+ // check expected run time of 316 minutes
+ QCOMPARE(displayed_dive.dc.duration.seconds, 18980u);
+}
+
+void TestPlan::testVpmbMetricMultiLevelAir()
+{
+ char *cache = NULL;
+
+ setupPrefsVpmb();
+ prefs.unit_system = METRIC;
+ prefs.units.length = units::METERS;
+
+ struct diveplan testPlan = { 0 };
+ setupPlanVpmbMultiLevelAir(&testPlan);
+ setCurrentAppState("PlanDive");
+
+ plan(&testPlan, &cache, 1, 0);
+
+#if DEBUG
+ free(displayed_dive.notes);
+ displayed_dive.notes = NULL;
+ save_dive(stdout, &displayed_dive);
+#endif
+
+ // check expected run time of 167 minutes
+ QCOMPARE(displayed_dive.dc.duration.seconds, 10040u);
+}
+
+void TestPlan::testVpmbMetric100m10min()
+{
+ char *cache = NULL;
+
+ setupPrefsVpmb();
+ prefs.unit_system = METRIC;
+ prefs.units.length = units::METERS;
+
+ struct diveplan testPlan = { 0 };
+ setupPlanVpmb100m10min(&testPlan);
+ setCurrentAppState("PlanDive");
+
+ plan(&testPlan, &cache, 1, 0);
+
+#if DEBUG
+ free(displayed_dive.notes);
+ displayed_dive.notes = NULL;
+ save_dive(stdout, &displayed_dive);
+#endif
+
+ // check first gas change to EAN50 at 21m
+ struct event *ev = displayed_dive.dc.events;
+ QVERIFY(ev != NULL);
+ QCOMPARE(ev->gas.index, 1);
+ QCOMPARE(ev->value, 50);
+ QCOMPARE(get_depth_at_time(&displayed_dive.dc, ev->time.seconds), 21000);
+ // check second gas change to Oxygen at 6m
+ ev = ev->next;
+ QVERIFY(ev != NULL);
+ QCOMPARE(ev->gas.index, 2);
+ QCOMPARE(ev->value, 100);
+ QCOMPARE(get_depth_at_time(&displayed_dive.dc, ev->time.seconds), 6000);
+ // check expected run time of 58 minutes
+ QCOMPARE(displayed_dive.dc.duration.seconds, 3500u);
+}
+
QTEST_MAIN(TestPlan)
diff --git a/tests/testplan.h b/tests/testplan.h
index b35cd75c6..f73ad0d9a 100644
--- a/tests/testplan.h
+++ b/tests/testplan.h
@@ -8,6 +8,12 @@ class TestPlan : public QObject {
private slots:
void testImperial();
void testMetric();
+ void testVpmbMetric60m30minAir();
+ void testVpmbMetric60m30minEan50();
+ void testVpmbMetric60m30minTx();
+ void testVpmbMetricMultiLevelAir();
+ void testVpmbMetric100m60min();
+ void testVpmbMetric100m10min();
};
#endif // TESTPLAN_H