diff options
49 files changed, 1293 insertions, 317 deletions
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index 9969b7b13..98ad599f2 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -672,8 +672,8 @@ information displayed. *Air/water temperatures*: Air and water temperatures during the dive are shown in these fields to the right of the Start time. Many dive computers supply water -temperature information and this field may therefore not require further -editing. If air temperature is not provided by the dive computer, the first temperature reading +temperature information and this field may therefore contain information obtained from the dive computer. +If air temperature is not provided by the dive computer, the first temperature reading might be used for the air temperature. Generally this is close enough to the real air temperature as the change in the temperature sensor reading is quite slow to follow the changes in the environment. If editing is required, only a value is required, the units of temperature will be @@ -697,7 +697,7 @@ at the appropriate place, the green bar disappears and the coordinates are stored. b. The user can obtain the coordinates from the _Subsurface_ Companion app if -an Android device with GPS was used and the if the coordinates of the dive site +an Android device with GPS was used and if the coordinates of the dive site were stored using that device. xref:S_Companion[Click here for more information] @@ -843,6 +843,32 @@ with two types of weights: integrated as well as a weight belt: image::images/WeightsDataEntry3.jpg["FIGURE: A completed weights information table",align="center"] +==== Editing several selected dives simultaneously + +After uploading dives from +a dive computer, the dive profiles of the uploaded dives are shown in the *Dive profile* tab, as well +as a few items of information +in the *Dive Notes* tab (e.g. water temperature) and in the *Equipment* tab (e.g. gas pressures +and gas composition). However the other fields remain empty. +It may be useful to simultaneously edit some of the +fields in the *Dive Notes* and *Equipment* tabs. For instance, it is possible +that a diver performed several dives during a single day, using identical equipment while diving at the same +dive site or with the same dive master and/or buddy or tags. Instead of completing the information for each +of these dives separately, one can select all the dives for that day in the *Dive List* and +insert the same information in the *Dive Notes* and *Equipment* fields that need +identical information. This is achieved by editing the dive notes or the equipment for any one +of the selected dives. + +The simultaneous editing only works with fields that do not already contain information. +This means that, if some fields have been edited for a particular dive among the selected dives, +these are not changed while +editing the dives simultaneously. Technically, the rule for editing several dives simultaneously is: +if the data field being edited contains _exactly the same information_ for all the dives that have been +selected, the new, edited information is substituted for all the selected dives, otherwise only the +edited dive is +changed, even though several dives have been selected in the *Dive List*. This greatly speeds +up the completion of the dive log after several similar dives. + ==== Adding Bookmarks to a dive Many divers wish to annotate their dives with text that indicate particular events @@ -1977,8 +2003,8 @@ Export dialog (image *B* above). ** Subsurface Numbers: if this option is checked, the dive(s) are exported with the numbers associated with them in Subsurface, Otherwise the dive(s) will be numbered starting from 1. -** Minimum Javascript: This will minimize the Javascript produced with the file, -some functionality will be lost. +** Export Yearly Statistics: if this option is checked, a yearly statistics table will +be attached with the HTML exports. ** Export List only: a list of dives will only be exported and the detailed dive information will not be available. diff --git a/Documentation/user-manual_es.txt b/Documentation/user-manual_es.txt index 515402c0f..dfb8a105c 100644 --- a/Documentation/user-manual_es.txt +++ b/Documentation/user-manual_es.txt @@ -1383,10 +1383,11 @@ perfil de la inmersión. Si está en un rango de 30 minutos, sí se muestra. === Visualizar las imágenes -Después que las imágenes han sido cargadas aparecen en la pestaña _Fotos_ del -panel *Notas de Inmersión*. También aparecen como pequeños iconos en el perfil -de inmersión en las posiciones apropiadas reflejando la hora en que se tomó cada -fotografía. Ver a continuación: +Después que las imágenes han sido cargadas aparecen en dos lugares: + + - La pestaña _Fotos_ del panel _Notas de la inmersión_. + - Como pequeños iconos (chinchetas) sobre el perfil de inmersión, en posiciones + que relejan la hora en que se tomó cada fotografía. Ver a continuación: image::images/LoadImage4.jpg["FIGURA: Fotos sobre un perfil de inmersión",align="center"] @@ -1395,14 +1396,14 @@ de la foto. Ver imagen a continuación: image::images/LoadImage5.jpg["FIGURA: Foto reducida sobre perfil de inmersión",align="center"] +Si hacemos clic sobre la imagen reducida aparecerá la fotografía a tamaño +completo sobre la ventana de _Subsurface_. Esto permite una buena visión de las +fotos que han sido importadas. Ver imagen a continuación. Cada imagen reducida tiene una pequeña papelera en su esquina superior izquierda (ver imagen anterior). Si se selecciona la papelera, la foto se borra de la inmersión, así pues, hay que tener un poco de cuidado al clicar sobre las imágenes. Las imágenes también puede borrase desde la pestaña _Fotos_ (ver en el siguiente punto). -Si hacemos clic sobre la imagen reducida aparecerá la fotografía a tamaño -completo sobre la ventana de _Subsurface_. Esto permite una buena visión de las -fotos que han sido importadas. Ver imagen a continuación. image::images/LoadImage6.jpg["FIGURA: Foto a pantalla completa sobre el perfil de inmersión",align="center"] diff --git a/SupportedDivecomputers.html b/SupportedDivecomputers.html index c7ac7ba5a..953ea3d1d 100644 --- a/SupportedDivecomputers.html +++ b/SupportedDivecomputers.html @@ -1,110 +1,67 @@ -<dl><dt>Aeris</dt><dd> - <ul> - <li>A300, A300 AI, Atmos 2, Atmos AI, Atmos AI 2, Compumask, Elite, Elite T3, Elite T3, Epic, Epic, F10, Manta, XR-1 NX, XR-2</li> - </ul> +<dl><dt>Aeris</dt><dd><ul> + <li>A300, A300 AI, Atmos 2, Atmos AI, Atmos AI 2, Compumask, Elite, Elite T3, Elite T3, Epic, Epic, F10, Manta, XR-1 NX, XR-2</li></ul> </dd> - <dt>Apeks</dt><dd> - <ul> - <li>Quantum X</li> - </ul> + <dt>Apeks</dt><dd><ul> + <li>Quantum X</li></ul> </dd> - <dt>Atomic Aquatics</dt><dd> - <ul> - <li>Cobalt, Cobalt 2</li> - </ul> + <dt>Atomic Aquatics</dt><dd><ul> + <li>Cobalt, Cobalt 2</li></ul> </dd> - <dt>Beuchat</dt><dd> - <ul> - <li>Voyager 2G</li> - </ul> + <dt>Beuchat</dt><dd><ul> + <li>Voyager 2G</li></ul> </dd> - <dt>Cressi</dt><dd> - <ul> - <li>Edy, Giotto, Leonardo</li> - </ul> + <dt>Cressi</dt><dd><ul> + <li>Edy, Giotto, Leonardo</li></ul> </dd> - <dt>Dive Rite</dt><dd> - <ul> - <li>NiTek Q, NiTek Trio</li> - </ul> + <dt>Dive Rite</dt><dd><ul> + <li>NiTek Q, NiTek Trio</li></ul> </dd> - <dt>Genesis</dt><dd> - <ul> - <li>React Pro, React Pro White</li> - </ul> + <dt>Genesis</dt><dd><ul> + <li>React Pro, React Pro White</li></ul> </dd> - <dt>Heinrichs Weikamp</dt><dd> - <ul> - <li>Frog, OSTC, OSTC 2C, OSTC 2N, OSTC 3, OSTC Mk2</li> - </ul> + <dt>Heinrichs Weikamp</dt><dd><ul> + <li>Frog, OSTC, OSTC 2C, OSTC 2N, OSTC 3, OSTC Mk2</li></ul> </dd> - <dt>Hollis</dt><dd> - <ul> - <li>DG03, TX1</li> - </ul> + <dt>Hollis</dt><dd><ul> + <li>DG03, TX1</li></ul> </dd> - <dt>Mares</dt><dd> - <ul> - <li>Darwin, Darwin Air, Icon HD, Icon HD Net Ready, M1, M2, Matrix, Nemo, Nemo Air, Nemo Apneist, Nemo Excel, Nemo Steel, Nemo Titanium, Nemo Wide, Nemo Wide 2, Puck, Puck 2, Puck Air, Puck Pro</li> - </ul> + <dt>Mares</dt><dd><ul> + <li>Darwin, Darwin Air, Icon HD, Icon HD Net Ready, M1, M2, Matrix, Nemo, Nemo Air, Nemo Apneist, Nemo Excel, Nemo Steel, Nemo Titanium, Nemo Wide, Nemo Wide 2, Puck, Puck 2, Puck Air, Puck Pro</li></ul> </dd> - <dt>Oceanic</dt><dd> - <ul> - <li>Atom 1.0, Atom 2.0, Atom 3.0, Atom 3.1, Datamask, Geo, Geo 2.0, OC1, OC1, OC1, OCS, OCi, Pro Plus 2, Pro Plus 2.1, Pro Plus 3, VT 4.1, VT Pro, VT3, VT4, Veo 1.0, Veo 180, Veo 2.0, Veo 200, Veo 250, Veo 3.0, Versa Pro</li> - </ul> + <dt>Oceanic</dt><dd><ul> + <li>Atom 1.0, Atom 2.0, Atom 3.0, Atom 3.1, Datamask, Geo, Geo 2.0, OC1, OC1, OC1, OCS, OCi, Pro Plus 2, Pro Plus 2.1, Pro Plus 3, VT 4.1, VT Pro, VT3, VT4, Veo 1.0, Veo 180, Veo 2.0, Veo 200, Veo 250, Veo 3.0, Versa Pro</li></ul> </dd> - <dt>Reefnet</dt><dd> - <ul> - <li>Sensus, Sensus Pro, Sensus Ultra</li> - </ul> + <dt>Reefnet</dt><dd><ul> + <li>Sensus, Sensus Pro, Sensus Ultra</li></ul> </dd> - <dt>Scubapro</dt><dd> - <ul> - <li>Meridian, XTender 5</li> - </ul> + <dt>Scubapro</dt><dd><ul> + <li>Meridian, XTender 5</li></ul> </dd> - <dt>Seemann</dt><dd> - <ul> - <li>XP5</li> - </ul> + <dt>Seemann</dt><dd><ul> + <li>XP5</li></ul> </dd> - <dt>Shearwater</dt><dd> - <ul> - <li>Petrel, Predator</li> - </ul> + <dt>Shearwater</dt><dd><ul> + <li>Petrel, Predator</li></ul> </dd> - <dt>Sherwood</dt><dd> - <ul> - <li>Amphos, Insight, Insight 2, Wisdom, Wisdom 2, Wisdom 3</li> - </ul> + <dt>Sherwood</dt><dd><ul> + <li>Amphos, Insight, Insight 2, Wisdom, Wisdom 2, Wisdom 3</li></ul> </dd> - <dt>Subgear</dt><dd> - <ul> - <li>XP Air, XP-10</li> - </ul> + <dt>Subgear</dt><dd><ul> + <li>XP Air, XP-10</li></ul> </dd> - <dt>Suunto</dt><dd> - <ul> - <li>Cobra, Cobra 2, Cobra 3, D3, D4, D4i, D6, D6i, D9, D9tx, DX, Eon, Gekko, HelO2, Mosquito, Solution, Solution Alpha, Solution Nitrox, Spyder, Stinger, Vyper, Vyper 2, Vyper Air, Vytec, Zoop</li> - </ul> + <dt>Suunto</dt><dd><ul> + <li>Cobra, Cobra 2, Cobra 3, D3, D4, D4i, D6, D6i, D9, D9tx, DX, Eon, Gekko, HelO2, Mosquito, Solution, Solution Alpha, Solution Nitrox, Spyder, Stinger, Vyper, Vyper 2, Vyper Air, Vytec, Zoop</li></ul> </dd> - <dt>Tusa</dt><dd> - <ul> - <li>Element II (IQ-750), Zen (IQ-900), Zen Air (IQ-950)</li> - </ul> + <dt>Tusa</dt><dd><ul> + <li>Element II (IQ-750), Zen (IQ-900), Zen Air (IQ-950)</li></ul> </dd> - <dt>Uemis</dt><dd> - <ul> - <li>Zürich SDA</li> - </ul> + <dt>Uemis</dt><dd><ul> + <li>Zürich SDA</li></ul> </dd> - <dt>Uwatec</dt><dd> - <ul> - <li>Aladin 2G, Aladin 2G, Aladin Air Twin, Aladin Air Z, Aladin Air Z Nitrox, Aladin Air Z O2, Aladin Prime, Aladin Pro, Aladin Pro Ultra, Aladin Sport Plus, Aladin Tec, Aladin Tec 2G, Galileo Luna, Galileo Sol, Galileo Terra, Galileo Trimix, Memomouse, Smart Com, Smart Pro, Smart Tec, Smart Z</li> - </ul> + <dt>Uwatec</dt><dd><ul> + <li>Aladin 2G, Aladin 2G, Aladin Air Twin, Aladin Air Z, Aladin Air Z Nitrox, Aladin Air Z O2, Aladin Prime, Aladin Pro, Aladin Pro Ultra, Aladin Sport Plus, Aladin Tec, Aladin Tec 2G, Galileo Luna, Galileo Sol, Galileo Terra, Galileo Trimix, Memomouse, Smart Com, Smart Pro, Smart Tec, Smart Z</li></ul> </dd> - <dt>Zeagle</dt><dd> - <ul> + <dt>Zeagle</dt><dd><ul> <li>N2iTiON3</li> </ul> </dd> @@ -20,6 +20,7 @@ #define RIOGRANDE1 QColor::fromRgbF(0.8, 0.8, 0.0, 1) #define EARLSGREEN1 QColor::fromRgbF(0.8, 0.8, 0.2, 1) #define FORESTGREEN1 QColor::fromRgbF(0.1, 0.5, 0.1, 1) +#define NITROX_GREEN QColor::fromRgbF(0, 0.54, 0.375, 1) // Reds #define PERSIANRED1 QColor::fromRgbF(0.8, 0.2, 0.2, 1) @@ -49,6 +50,7 @@ #define GOVERNORBAY1_MED_TRANS QColor::fromRgbF(0.2, 0.2, 0.8, 0.5) #define ROYALBLUE2 QColor::fromRgbF(0.2, 0.2, 0.9, 1) #define ROYALBLUE2_LOW_TRANS QColor::fromRgbF(0.2, 0.2, 0.9, 0.75) +#define AIR_BLUE QColor::fromRgbF(0.25, 0.75, 1.0, 1) // Yellows / BROWNS #define SPRINGWOOD1 QColor::fromRgbF(0.95, 0.95, 0.9, 1) @@ -56,6 +58,8 @@ #define BROOM1_LOWER_TRANS QColor::fromRgbF(1.0, 1.0, 0.1, 0.9) #define PEANUT QColor::fromRgbF(0.5, 0.2, 0.1, 1.0) #define PEANUT_MED_TRANS QColor::fromRgbF(0.5, 0.2, 0.1, 0.5) +#define NITROX_YELLOW QColor::fromRgbF(0.98, 0.89, 0.07, 1.0) + // Magentas #define MEDIUMREDVIOLET1_HIGHER_TRANS QColor::fromRgbF(0.7, 0.2, 0.7, 0.1) @@ -188,6 +188,17 @@ double get_volume_units(unsigned int ml, int *frac, const char **units) return vol; } +int units_to_sac(int volume) +{ + if(get_units()->volume == CUFT) + if (volume < 10) + return cuft_to_l(volume) * 100; + else + return cuft_to_l(volume) * 10; + else + return volume * 1000; +} + unsigned int units_to_depth(double depth) { if (get_units()->length == METERS) @@ -410,6 +421,38 @@ struct dive *clone_dive(struct dive *s) return dive; } +#define CONDITIONAL_COPY_STRING(_component) \ + if (what._component) \ + d->_component = copy_string(s->_component) + +// copy elements, depending on bits in what that are set +void selective_copy_dive(struct dive *s, struct dive *d, struct dive_components what, bool clear) +{ + if (clear) + clear_dive(d); + CONDITIONAL_COPY_STRING(location); + CONDITIONAL_COPY_STRING(notes); + CONDITIONAL_COPY_STRING(divemaster); + CONDITIONAL_COPY_STRING(buddy); + CONDITIONAL_COPY_STRING(suit); + if (what.rating) + d->rating = s->rating; + if (what.visibility) + d->visibility = s->visibility; + if (what.gps) { + d->longitude = s->longitude; + d->latitude = s->latitude; + } + if (what.tags) + STRUCTURED_LIST_COPY(struct tag_entry, s->tag_list, d->tag_list, copy_tl); + if (what.cylinders) + copy_cylinders(s, d, false); + if (what.weights) + for (int i = 0; i < MAX_WEIGHTSYSTEMS; i++) + d->weightsystem[i] = s->weightsystem[i]; +} +#undef CONDITIONAL_COPY_STRING + /* only copies events from the first dive computer */ void copy_events(struct divecomputer *s, struct divecomputer *d) { @@ -448,16 +491,21 @@ int nr_weightsystems(struct dive *dive) return nr; } +/* copy the equipment data part of the cylinders */ void copy_cylinders(struct dive *s, struct dive *d, bool used_only) { int i; if (!s || !d) return; - for (i = 0; i < MAX_CYLINDERS; i++) - if (!used_only || is_cylinder_used(s, i)) - d->cylinder[i] = s->cylinder[i]; - else - memset(&d->cylinder[i], 0, sizeof(cylinder_t)); + for (i = 0; i < MAX_CYLINDERS; i++) { + memset(&d->cylinder[i], 0, sizeof(cylinder_t)); + if (!used_only || is_cylinder_used(s, i)) { + d->cylinder[i].type = s->cylinder[i].type; + d->cylinder[i].gasmix = s->cylinder[i].gasmix; + d->cylinder[i].depth = s->cylinder[i].depth; + d->cylinder[i].manually_added = true; + } + } } void copy_samples(struct divecomputer *s, struct divecomputer *d) @@ -1849,6 +1897,11 @@ static int match_dc_dive(struct divecomputer *a, struct divecomputer *b) return 0; } +static bool new_without_trip(struct dive *a) +{ + return a->downloaded && !a->divetrip; +} + /* * Do we want to automatically try to merge two dives that * look like they are the same dive? @@ -1882,9 +1935,16 @@ static int likely_same_dive(struct dive *a, struct dive *b) { int match, fuzz = 20 * 60; - /* Don't try to merge dives in different trips */ - if (a->divetrip && b->divetrip && a->divetrip != b->divetrip) - return 0; + /* Don't try to merge dives with different trip information */ + if (a->divetrip != b->divetrip) { + /* + * Exception: if the dive is downloaded without any + * explicit trip information, we do want to merge it + * with existing old dives even if they have trips. + */ + if (!new_without_trip(a) && !new_without_trip(b)) + return 0; + } /* * Do some basic sanity testing of the values we @@ -99,6 +99,7 @@ extern double get_weight_units(unsigned int grams, int *frac, const char **units extern double get_vertical_speed_units(unsigned int mms, int *frac, const char **units); extern unsigned int units_to_depth(double depth); +extern int units_to_sac(int volume); /* Volume in mliter of a cylinder at pressure 'p' */ extern int gas_volume(cylinder_t *cyl, pressure_t p); @@ -294,6 +295,21 @@ struct dive { struct picture *picture_list; }; +/* when selectively copying dive information, which parts should be copied? */ +struct dive_components { + unsigned int location : 1; + unsigned int notes : 1; + unsigned int divemaster : 1; + unsigned int buddy : 1; + unsigned int suit : 1; + unsigned int rating : 1; + unsigned int visibility : 1; + unsigned int gps : 1; + unsigned int tags : 1; + unsigned int cylinders : 1; + unsigned int weights : 1; +}; + /* picture list and methods related to dive picture handling */ struct picture { char *filename; @@ -605,6 +621,7 @@ extern struct dive *alloc_dive(void); extern void record_dive(struct dive *dive); extern void clear_dive(struct dive *dive); extern void copy_dive(struct dive *s, struct dive *d); +extern void selective_copy_dive(struct dive *s, struct dive *d, struct dive_components what, bool clear); extern struct dive *clone_dive(struct dive *s); extern struct sample *prepare_sample(struct divecomputer *dc); diff --git a/divelist.c b/divelist.c index 1e279efca..cccd3a0a6 100644 --- a/divelist.c +++ b/divelist.c @@ -434,14 +434,14 @@ void update_cylinder_related_info(struct dive *dive) } } -#define MAX_NITROX_STRING 80 +#define MAX_GAS_STRING 80 #define UTF8_ELLIPSIS "\xE2\x80\xA6" /* callers needs to free the string */ -char *get_nitrox_string(struct dive *dive) +char *get_dive_gas_string(struct dive *dive) { int o2, he, o2low; - char *buffer = malloc(MAX_NITROX_STRING); + char *buffer = malloc(MAX_GAS_STRING); if (buffer) { get_dive_gas(dive, &o2, &he, &o2low); @@ -450,12 +450,12 @@ char *get_nitrox_string(struct dive *dive) o2low = (o2low + 5) / 10; if (he) - snprintf(buffer, MAX_NITROX_STRING, "%d/%d", o2, he); + snprintf(buffer, MAX_GAS_STRING, "%d/%d", o2, he); else if (o2) if (o2 == o2low) - snprintf(buffer, MAX_NITROX_STRING, "%d", o2); + snprintf(buffer, MAX_GAS_STRING, "%d%%", o2); else - snprintf(buffer, MAX_NITROX_STRING, "%d" UTF8_ELLIPSIS "%d", o2low, o2); + snprintf(buffer, MAX_GAS_STRING, "%d" UTF8_ELLIPSIS "%d%%", o2low, o2); else strcpy(buffer, translate("gettextFromC", "air")); } @@ -1040,3 +1040,11 @@ void process_dives(bool is_imported, bool prefer_imported) mark_divelist_changed(true); } } + +void set_dive_nr_for_current_dive() +{ + if (dive_table.nr == 1) + current_dive->number = 1; + else if (selected_dive == dive_table.nr - 1 && get_dive(dive_table.nr - 2)->number) + current_dive->number = get_dive(dive_table.nr - 2)->number + 1; +} diff --git a/divelist.h b/divelist.h index 4eafd5dd4..a24deea17 100644 --- a/divelist.h +++ b/divelist.h @@ -15,7 +15,7 @@ extern double init_decompression(struct dive *dive); /* divelist core logic functions */ extern void process_dives(bool imported, bool prefer_imported); -extern char *get_nitrox_string(struct dive *dive); +extern char *get_dive_gas_string(struct dive *dive); extern dive_trip_t *find_trip_by_idx(int idx); @@ -37,6 +37,7 @@ extern void find_new_trip_start_time(dive_trip_t *trip); extern struct dive *first_selected_dive(); extern struct dive *last_selected_dive(); extern bool is_trip_before_after(struct dive *dive, bool before); +extern void set_dive_nr_for_current_dive(); #ifdef DEBUG_TRIP extern void dump_selection(void); diff --git a/libdivecomputer.c b/libdivecomputer.c index 0011a201d..401fd8a6f 100644 --- a/libdivecomputer.c +++ b/libdivecomputer.c @@ -515,6 +515,13 @@ static int dive_cb(const unsigned char *data, unsigned int size, dive->dc.sample[0].temperature.mkelvin = 0; } + if (devdata->create_new_trip) { + if (!devdata->trip) + devdata->trip = create_and_hookup_trip_from_dive(dive); + else + add_dive_to_trip(dive, devdata->trip); + } + dive->downloaded = true; record_dive(dive); mark_divelist_changed(true); diff --git a/libdivecomputer.h b/libdivecomputer.h index d9c5ac90a..f277e298f 100644 --- a/libdivecomputer.h +++ b/libdivecomputer.h @@ -23,8 +23,10 @@ typedef struct device_data_t uint32_t deviceid, diveid; dc_device_t *device; dc_context_t *context; + struct dive_trip *trip; int preexisting; bool force_download; + bool create_new_trip; bool libdc_log; bool libdc_dump; FILE *libdc_logfile; @@ -46,6 +46,7 @@ struct preferences { short zoomed_plot; short hrgraph; short rulergraph; + short tankbar; short save_userid_local; char *userid; int ascrate75; diff --git a/qt-gui.cpp b/qt-gui.cpp index bb3001ff4..a7362d5f0 100644 --- a/qt-gui.cpp +++ b/qt-gui.cpp @@ -103,6 +103,7 @@ QString uiLanguage(QLocale *callerLoc) dateFormat.replace("'en' 'den' d:'e'", " d"); timeFormat = loc.timeFormat(); timeFormat.replace("(t)", "").replace(" t", "").replace("t", "").replace("hh", "h").replace("HH", "H").replace("'kl'.", ""); + timeFormat.replace(".ss", "").replace(":ss", "").replace("ss", ""); return uiLang; } diff --git a/qt-ui/divecomponentselection.ui b/qt-ui/divecomponentselection.ui new file mode 100644 index 000000000..dbd0839ba --- /dev/null +++ b/qt-ui/divecomponentselection.ui @@ -0,0 +1,172 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>DiveComponentSelectionDialog</class> + <widget class="QDialog" name="DiveComponentSelectionDialog"> + <property name="windowModality"> + <enum>Qt::WindowModal</enum> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>308</width> + <height>263</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="windowTitle"> + <string>Component selection</string> + </property> + <property name="windowIcon"> + <iconset> + <normalon>:/subsurface-icon</normalon> + </iconset> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Which components would you like to copy</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QCheckBox" name="location"> + <property name="text"> + <string>Location</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QCheckBox" name="suit"> + <property name="text"> + <string>Suit</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QCheckBox" name="gps"> + <property name="text"> + <string>GPS coordinates</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QCheckBox" name="divemaster"> + <property name="text"> + <string>Divemaster</string> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QCheckBox" name="buddy"> + <property name="text"> + <string>Buddy</string> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QCheckBox" name="rating"> + <property name="text"> + <string>Rating</string> + </property> + </widget> + </item> + <item row="5" column="0"> + <widget class="QCheckBox" name="visibility"> + <property name="text"> + <string>Visibility</string> + </property> + </widget> + </item> + <item row="6" column="0"> + <widget class="QCheckBox" name="notes"> + <property name="text"> + <string>Notes</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QCheckBox" name="tags"> + <property name="text"> + <string>Tags</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QCheckBox" name="weights"> + <property name="text"> + <string>Weights</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QCheckBox" name="cylinders"> + <property name="text"> + <string>Cylinders</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources> + <include location="../subsurface.qrc"/> + </resources> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>DiveComponentSelectionDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>DiveComponentSelectionDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/qt-ui/divelistview.cpp b/qt-ui/divelistview.cpp index d58f31284..8d771b5df 100644 --- a/qt-ui/divelistview.cpp +++ b/qt-ui/divelistview.cpp @@ -383,7 +383,7 @@ void DiveListView::reloadHeaderActions() QString settingName = QString("showColumn%1").arg(i); QAction *a = new QAction(title, header()); bool showHeaderFirstRun = !( - i == DiveTripModel::MAXCNS || i == DiveTripModel::NITROX || i == DiveTripModel::OTU || i == DiveTripModel::TEMPERATURE || i == DiveTripModel::TOTALWEIGHT || i == DiveTripModel::SUIT || i == DiveTripModel::CYLINDER || i == DiveTripModel::SAC); + i == DiveTripModel::MAXCNS || i == DiveTripModel::GAS || i == DiveTripModel::OTU || i == DiveTripModel::TEMPERATURE || i == DiveTripModel::TOTALWEIGHT || i == DiveTripModel::SUIT || i == DiveTripModel::CYLINDER || i == DiveTripModel::SAC); bool shown = s.value(settingName, showHeaderFirstRun).toBool(); a->setCheckable(true); a->setChecked(shown); diff --git a/qt-ui/divelogexportdialog.cpp b/qt-ui/divelogexportdialog.cpp index 03bdaf9cb..99669d6d4 100644 --- a/qt-ui/divelogexportdialog.cpp +++ b/qt-ui/divelogexportdialog.cpp @@ -45,6 +45,15 @@ DiveLogExportDialog::DiveLogExportDialog(QWidget *parent) : QDialog(parent), if (settings.contains("themeSelection")) { ui->themeSelection->setCurrentIndex(settings.value("themeSelection").toInt()); } + if (settings.contains("subsurfaceNumbers")) { + ui->exportSubsurfaceNumber->setChecked(settings.value("subsurfaceNumbers").toBool()); + } + if (settings.contains("yearlyStatistics")) { + ui->exportStatistics->setChecked(settings.value("yearlyStatistics").toBool()); + } + if (settings.contains("listOnly")) { + ui->exportListOnly->setChecked(settings.value("listOnly").toBool()); + } settings.endGroup(); } @@ -86,6 +95,7 @@ void DiveLogExportDialog::exportHtmlInit(const QString &filename) QString json_dive_data = exportFiles + QDir::separator() + "file.json"; QString json_settings = exportFiles + QDir::separator() + "settings.json"; + QString translation = exportFiles + QDir::separator() + "translation.json"; QString stat_file = exportFiles + QDir::separator() + "stat.json"; QString photos_directory = exportFiles + QDir::separator() + "photos" + QDir::separator(); mainDir.mkdir(photos_directory); @@ -93,6 +103,8 @@ void DiveLogExportDialog::exportHtmlInit(const QString &filename) exportHTMLsettings(json_settings); exportHTMLstatistics(stat_file); + export_translation(translation.toUtf8().data()); + export_HTML(json_dive_data.toUtf8().data(), photos_directory.toUtf8().data(), ui->exportSelectedDives->isChecked(), ui->exportListOnly->isChecked()); QString searchPath = getSubsurfaceDataPath("theme"); @@ -121,6 +133,9 @@ void DiveLogExportDialog::exportHTMLsettings(const QString &filename) settings.setValue("fontSelection", ui->fontSelection->currentIndex()); settings.setValue("fontSizeSelection", ui->fontSizeSelection->currentIndex()); settings.setValue("themeSelection", ui->themeSelection->currentIndex()); + settings.setValue("subsurfaceNumbers", ui->exportSubsurfaceNumber->isChecked()); + settings.setValue("yearlyStatistics", ui->exportStatistics->isChecked()); + settings.setValue("listOnly", ui->exportListOnly->isChecked()); settings.endGroup(); QString fontSize = ui->fontSizeSelection->currentText(); @@ -138,32 +153,64 @@ void DiveLogExportDialog::exportHTMLstatistics(const QString &filename) QFile file(filename); file.open(QIODevice::WriteOnly | QIODevice::Text); QTextStream out(&file); + + stats_t total_stats; + + total_stats.selection_size = 0; + total_stats.total_time.seconds = 0; + int i = 0; out << "divestat=["; - while (stats_yearly != NULL && stats_yearly[i].period) { - out << "{"; - out << "\"YEAR\":\"" << stats_yearly[i].period << "\","; - out << "\"DIVES\":\"" << stats_yearly[i].selection_size << "\","; - out << "\"TOTAL_TIME\":\"" << get_time_string(stats_yearly[i].total_time.seconds, 0) << "\","; - out << "\"AVERAGE_TIME\":\"" << get_minutes(stats_yearly[i].total_time.seconds / stats_yearly[i].selection_size) << "\","; - out << "\"SHORTEST_TIME\":\"" << get_minutes(stats_yearly[i].shortest_time.seconds) << "\","; - out << "\"LONGEST_TIME\":\"" << get_minutes(stats_yearly[i].longest_time.seconds) << "\","; - out << "\"AVG_DEPTH\":\"" << get_depth_string(stats_yearly[i].avg_depth) << "\","; - out << "\"MIN_DEPTH\":\"" << get_depth_string(stats_yearly[i].min_depth) << "\","; - out << "\"MAX_DEPTH\":\"" << get_depth_string(stats_yearly[i].max_depth) << "\","; - out << "\"AVG_SAC\":\"" << get_volume_string(stats_yearly[i].avg_sac) << "\","; - out << "\"MIN_SAC\":\"" << get_volume_string(stats_yearly[i].min_sac) << "\","; - out << "\"MAX_SAC\":\"" << get_volume_string(stats_yearly[i].max_sac) << "\","; - out << "\"AVG_TEMP\":\"" << QString::number(stats_yearly[i].combined_temp / stats_yearly[i].combined_count, 'f', 1) << "\","; - out << "\"MIN_TEMP\":\"" << get_temp_units(stats_yearly[i].min_temp, NULL) << "\","; - out << "\"MAX_TEMP\":\"" << get_temp_units(stats_yearly[i].max_temp, NULL) << "\","; - out << "},"; - i++; + if (ui->exportStatistics->isChecked()) { + while (stats_yearly != NULL && stats_yearly[i].period) { + out << "{"; + out << "\"YEAR\":\"" << stats_yearly[i].period << "\","; + out << "\"DIVES\":\"" << stats_yearly[i].selection_size << "\","; + out << "\"TOTAL_TIME\":\"" << get_time_string(stats_yearly[i].total_time.seconds, 0) << "\","; + out << "\"AVERAGE_TIME\":\"" << get_minutes(stats_yearly[i].total_time.seconds / stats_yearly[i].selection_size) << "\","; + out << "\"SHORTEST_TIME\":\"" << get_minutes(stats_yearly[i].shortest_time.seconds) << "\","; + out << "\"LONGEST_TIME\":\"" << get_minutes(stats_yearly[i].longest_time.seconds) << "\","; + out << "\"AVG_DEPTH\":\"" << get_depth_string(stats_yearly[i].avg_depth) << "\","; + out << "\"MIN_DEPTH\":\"" << get_depth_string(stats_yearly[i].min_depth) << "\","; + out << "\"MAX_DEPTH\":\"" << get_depth_string(stats_yearly[i].max_depth) << "\","; + out << "\"AVG_SAC\":\"" << get_volume_string(stats_yearly[i].avg_sac) << "\","; + out << "\"MIN_SAC\":\"" << get_volume_string(stats_yearly[i].min_sac) << "\","; + out << "\"MAX_SAC\":\"" << get_volume_string(stats_yearly[i].max_sac) << "\","; + out << "\"AVG_TEMP\":\"" << QString::number(stats_yearly[i].combined_temp / stats_yearly[i].combined_count, 'f', 1) << "\","; + out << "\"MIN_TEMP\":\"" << get_temp_units(stats_yearly[i].min_temp, NULL) << "\","; + out << "\"MAX_TEMP\":\"" << get_temp_units(stats_yearly[i].max_temp, NULL) << "\","; + out << "},"; + total_stats.selection_size += stats_yearly[i].selection_size; + total_stats.total_time.seconds += stats_yearly[i].total_time.seconds; + i++; + } + exportHTMLstatisticsTotal(out, &total_stats); } out << "]"; file.close(); } +void exportHTMLstatisticsTotal(QTextStream &out, stats_t *total_stats) +{ + out << "{"; + out << "\"YEAR\":\"Total\","; + out << "\"DIVES\":\"" << total_stats->selection_size << "\","; + out << "\"TOTAL_TIME\":\"" << get_time_string(total_stats->total_time.seconds, 0) << "\","; + out << "\"AVERAGE_TIME\":\"--\","; + out << "\"SHORTEST_TIME\":\"--\","; + out << "\"LONGEST_TIME\":\"--\","; + out << "\"AVG_DEPTH\":\"--\","; + out << "\"MIN_DEPTH\":\"--\","; + out << "\"MAX_DEPTH\":\"--\","; + out << "\"AVG_SAC\":\"--\","; + out << "\"MIN_SAC\":\"--\","; + out << "\"MAX_SAC\":\"--\","; + out << "\"AVG_TEMP\":\"--\","; + out << "\"MIN_TEMP\":\"--\","; + out << "\"MAX_TEMP\":\"--\","; + out << "},"; +} + void DiveLogExportDialog::on_exportGroup_buttonClicked(QAbstractButton *button) { showExplanation(); diff --git a/qt-ui/divelogexportdialog.h b/qt-ui/divelogexportdialog.h index e398a5a05..8472f8075 100644 --- a/qt-ui/divelogexportdialog.h +++ b/qt-ui/divelogexportdialog.h @@ -2,12 +2,18 @@ #define DIVELOGEXPORTDIALOG_H #include <QDialog> +#include <QTextStream> +#include "helpers.h" +#include "statistics.h" + class QAbstractButton; namespace Ui { class DiveLogExportDialog; } +void exportHTMLstatisticsTotal(QTextStream &out, stats_t *total_stats); + class DiveLogExportDialog : public QDialog { Q_OBJECT diff --git a/qt-ui/divelogexportdialog.ui b/qt-ui/divelogexportdialog.ui index aeed08895..ce1513efb 100644 --- a/qt-ui/divelogexportdialog.ui +++ b/qt-ui/divelogexportdialog.ui @@ -194,6 +194,9 @@ <property name="text"> <string>Subsurface numbers</string> </property> + <property name="checked"> + <bool>true</bool> + </property> </widget> </item> <item row="0" column="1"> @@ -216,9 +219,12 @@ </widget> </item> <item row="1" column="0"> - <widget class="QCheckBox" name="checkBox_2"> + <widget class="QCheckBox" name="exportStatistics"> <property name="text"> - <string>Minimum Javascript</string> + <string>Export Yearly Statistics</string> + </property> + <property name="checked"> + <bool>true</bool> </property> </widget> </item> diff --git a/qt-ui/diveplanner.cpp b/qt-ui/diveplanner.cpp index 09320cafe..e51400ac0 100644 --- a/qt-ui/diveplanner.cpp +++ b/qt-ui/diveplanner.cpp @@ -416,8 +416,8 @@ PlannerSettingsWidget::PlannerSettingsWidget(QWidget *parent, Qt::WindowFlags f) connect(ui.gflow, SIGNAL(editingFinished()), plannerModel, SLOT(triggerGFLow())); connect(ui.backgasBreaks, SIGNAL(toggled(bool)), this, SLOT(setBackgasBreaks(bool))); - ui.bottomSAC->setValue(prefs.bottomsac / 1000.0); - ui.decoStopSAC->setValue(prefs.decosac / 1000.0); + ui.bottomSAC->setValue(rint(get_volume_units(prefs.bottomsac, NULL, NULL))); + ui.decoStopSAC->setValue(rint(get_volume_units(prefs.decosac, NULL, NULL))); ui.gflow->setValue(prefs.gflow); ui.gfhigh->setValue(prefs.gfhigh); @@ -466,6 +466,21 @@ void PlannerSettingsWidget::settingsChanged() ui.asc50to6->setText(tr("50% avg. depth to 6m")); ui.asc6toSurf->setText(tr("6m to surface")); } + if(get_units()->volume == units::CUFT) { + ui.bottomSAC->setSuffix(tr("cuft/min")); + ui.decoStopSAC->setSuffix(tr("cuft/min")); + ui.bottomSAC->setPrefix("."); + ui.decoStopSAC->setPrefix("."); + ui.bottomSAC->setValue(rint(ml_to_cuft(prefs.bottomsac) * 100.0)); + ui.decoStopSAC->setValue(rint(ml_to_cuft(prefs.decosac) * 100.0)); + } else { + ui.bottomSAC->setSuffix(tr("ℓ/min")); + ui.decoStopSAC->setSuffix(tr("ℓ/min")); + ui.bottomSAC->setPrefix(""); + ui.decoStopSAC->setPrefix(""); + ui.bottomSAC->setValue(rint((double) prefs.bottomsac / 1000.0)); + ui.decoStopSAC->setValue(rint((double) prefs.decosac / 1000.0)); + } updateUnitsUI(); ui.ascRate75->setSuffix(vs); ui.ascRate50->setSuffix(vs); @@ -690,14 +705,17 @@ void DivePlannerPointsModel::emitDataChanged() void DivePlannerPointsModel::setBottomSac(int sac) { - diveplan.bottomsac = sac * 1000; + volume_t newSAC; + newSAC.mliter = units_to_sac(sac); + diveplan.bottomsac = newSAC.mliter; prefs.bottomsac = diveplan.bottomsac; emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, COLUMNS - 1)); } void DivePlannerPointsModel::setDecoSac(int sac) { - diveplan.decosac = sac * 1000; + volume_t newSAC; + diveplan.decosac = units_to_sac(sac); prefs.decosac = diveplan.decosac; emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, COLUMNS - 1)); } diff --git a/qt-ui/downloadfromdivecomputer.cpp b/qt-ui/downloadfromdivecomputer.cpp index d2a5fae1c..fe5d4ee9a 100644 --- a/qt-ui/downloadfromdivecomputer.cpp +++ b/qt-ui/downloadfromdivecomputer.cpp @@ -284,6 +284,8 @@ void DownloadFromDCWidget::on_ok_clicked() data.descriptor = descriptorLookup[ui.vendor->currentText() + ui.product->currentText()]; data.force_download = ui.forceDownload->isChecked(); + data.create_new_trip = ui.createNewTrip->isChecked(); + data.trip = NULL; data.deviceid = data.diveid = 0; set_default_dive_computer(data.vendor, data.product); set_default_dive_computer_device(data.devname); @@ -413,6 +415,7 @@ void DownloadFromDCWidget::markChildrenAsDisabled() ui.vendor->setDisabled(true); ui.product->setDisabled(true); ui.forceDownload->setDisabled(true); + ui.createNewTrip->setDisabled(true); ui.preferDownloaded->setDisabled(true); ui.ok->setDisabled(true); ui.search->setDisabled(true); @@ -428,6 +431,7 @@ void DownloadFromDCWidget::markChildrenAsEnabled() ui.vendor->setDisabled(false); ui.product->setDisabled(false); ui.forceDownload->setDisabled(false); + ui.createNewTrip->setDisabled(false); ui.preferDownloaded->setDisabled(false); ui.ok->setDisabled(false); ui.cancel->setDisabled(false); diff --git a/qt-ui/downloadfromdivecomputer.ui b/qt-ui/downloadfromdivecomputer.ui index e99782ee8..9ea85afc8 100644 --- a/qt-ui/downloadfromdivecomputer.ui +++ b/qt-ui/downloadfromdivecomputer.ui @@ -74,7 +74,7 @@ </property> </widget> </item> - <item row="8" column="0" colspan="3"> + <item row="9" column="0" colspan="3"> <layout class="QHBoxLayout" name="horizontalLayout"> <item> <spacer name="horizontalSpacer"> @@ -105,41 +105,48 @@ </item> </layout> </item> - <item row="9" column="0" colspan="3"> + <item row="10" column="0" colspan="3"> <widget class="QProgressBar" name="progressBar"> <property name="value"> <number>24</number> </property> </widget> </item> - <item row="6" column="0" colspan="2"> + <item row="7" column="0" colspan="2"> <widget class="QCheckBox" name="logToFile"> <property name="text"> <string>Save libdivecomputer logfile</string> </property> </widget> </item> - <item row="7" column="0" colspan="2"> + <item row="8" column="0" colspan="2"> <widget class="QCheckBox" name="dumpToFile"> <property name="text"> <string>Save libdivecomputer dumpfile</string> </property> </widget> </item> - <item row="6" column="2"> + <item row="7" column="2"> <widget class="QToolButton" name="chooseLogFile"> <property name="text"> <string>...</string> </property> </widget> </item> - <item row="7" column="2"> + <item row="8" column="2"> <widget class="QToolButton" name="chooseDumpFile"> <property name="text"> <string>...</string> </property> </widget> </item> + <item row="6" column="0"> + <widget class="QCheckBox" name="createNewTrip"> + <property name="text"> + <string>Download into new trip</string> + </property> + </widget> + </item> </layout> </widget> <resources/> diff --git a/qt-ui/maintab.cpp b/qt-ui/maintab.cpp index e8087c7cc..6bb6f14f8 100644 --- a/qt-ui/maintab.cpp +++ b/qt-ui/maintab.cpp @@ -771,10 +771,7 @@ void MainTab::acceptChanges() } if (editMode == ADD || editMode == MANUALLY_ADDED_DIVE) { fixup_dive(current_dive); - if (dive_table.nr == 1) - current_dive->number = 1; - else if (selected_dive == dive_table.nr - 1 && get_dive(dive_table.nr - 2)->number) - current_dive->number = get_dive(dive_table.nr - 2)->number + 1; + set_dive_nr_for_current_dive(); MainWindow::instance()->showProfile(); mark_divelist_changed(true); DivePlannerPointsModel::instance()->setPlanMode(DivePlannerPointsModel::NOTHING); @@ -1026,9 +1023,12 @@ void MainTab::on_location_textChanged(const QString &text) // If we have GPS data for the location entered, add it. void MainTab::on_location_editingFinished() { + // if we have a location and no GPS data, look up the GPS data; + // but if the GPS data was intentionally cleared then don't if (!currentTrip && !same_string(displayed_dive.location, "") && - ui.coordinates->text().trimmed().isEmpty()) { + ui.coordinates->text().trimmed().isEmpty() && + !(editMode == DIVE && dive_has_gps_location(current_dive))) { struct dive *dive; int i = 0; for_each_dive (i, dive) { @@ -1170,3 +1170,43 @@ void MainTab::removeSelectedPhotos() QString fileUrl = photoIndex.data(Qt::DisplayPropertyRole).toString(); DivePictureModel::instance()->removePicture(fileUrl); } + +#define SHOW_SELECTIVE(_component) \ + if (what._component) \ + ui._component->setText(displayed_dive._component); + +void MainTab::showAndTriggerEditSelective(struct dive_components what) +{ + // take the data in our copyPasteDive and apply it to selected dives + enableEdition(); + SHOW_SELECTIVE(location); + SHOW_SELECTIVE(buddy); + SHOW_SELECTIVE(divemaster); + SHOW_SELECTIVE(suit); + if (what.notes) { + QString tmp(displayed_dive.notes); + if (tmp.contains("<table")) + ui.notes->setHtml(tmp); + else + ui.notes->setPlainText(tmp); + } + if (what.rating) + ui.rating->setCurrentStars(displayed_dive.rating); + if (what.visibility) + ui.visibility->setCurrentStars(displayed_dive.visibility); + if (what.gps) + updateGpsCoordinates(&displayed_dive); + if (what.tags) { + char buf[1024]; + taglist_get_tagstring(displayed_dive.tag_list, buf, 1024); + ui.tagWidget->setText(QString(buf)); + } + if (what.cylinders) { + cylindersModel->updateDive(); + cylindersModel->changed = true; + } + if (what.weights) { + weightModel->updateDive(); + weightModel->changed = true; + } +} diff --git a/qt-ui/maintab.h b/qt-ui/maintab.h index f3aec5458..93bb1c949 100644 --- a/qt-ui/maintab.h +++ b/qt-ui/maintab.h @@ -49,6 +49,8 @@ public: bool isEditing(); void updateCoordinatesText(qreal lat, qreal lon); void nextInputField(QKeyEvent *event); + void showAndTriggerEditSelective(struct dive_components what); + signals: void addDiveFinished(); diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 85b31608c..74015c73f 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -115,6 +115,8 @@ MainWindow::MainWindow() : QMainWindow(), #ifdef NO_PRINTING ui.menuFile->removeAction(ui.actionPrint); #endif + memset(©PasteDive, 0, sizeof(copyPasteDive)); + memset(&what, 0, sizeof(what)); } MainWindow::~MainWindow() @@ -243,6 +245,7 @@ void MainWindow::setToolButtonsEnabled(bool enabled) ui.profScaled->setEnabled(enabled); ui.profHR->setEnabled(enabled); ui.profTogglePicture->setEnabled(enabled); + ui.profTankbar->setEnabled(enabled); } bool MainWindow::okToClose(QString message) @@ -400,6 +403,12 @@ void MainWindow::planCanceled() void MainWindow::planCreated() { + // get the new dive selected and assign a number if reasonable + dive_list()->unselectDives(); + select_dive(dive_table.nr - 1); + dive_list()->selectDive(selected_dive); + set_dive_nr_for_current_dive(); + showProfile(); refreshDisplay(); } @@ -797,6 +806,7 @@ void MainWindow::readSettings() TOOLBOX_PREF_BUTTON(rulergraph, rulergraph, profRuler); TOOLBOX_PREF_BUTTON(show_sac, show_sac, profSAC); TOOLBOX_PREF_BUTTON(show_pictures_in_profile, show_pictures_in_profile, profTogglePicture); + TOOLBOX_PREF_BUTTON(tankbar, tankbar, profTankbar); s.endGroup(); s.beginGroup("DiveComputer"); default_dive_computer_vendor = getSetting(s, "dive_computer_vendor"); @@ -1271,6 +1281,7 @@ TOOLBOX_PREF_PROFILE(profRuler, rulergraph, rulergraph); TOOLBOX_PREF_PROFILE(profSAC, show_sac, show_sac); TOOLBOX_PREF_PROFILE(profScaled, zoomed_plot, zoomed_plot); TOOLBOX_PREF_PROFILE(profTogglePicture, show_pictures_in_profile, show_pictures_in_profile); +TOOLBOX_PREF_PROFILE(profTankbar, tankbar, tankbar); void MainWindow::turnOffNdlTts() { @@ -1296,10 +1307,26 @@ void MainWindow::on_actionConfigure_Dive_Computer_triggered() void MainWindow::setEnabledToolbar(bool arg1) { - QList<QToolButton*> toolBar; toolBar << ui.profCalcAllTissues << ui.profCalcCeiling - << ui.profDcCeiling << ui.profEad << ui.profHR << ui.profIncrement3m - << ui.profMod << ui.profNdl_tts << ui.profNdl_tts << ui.profPhe << ui.profPn2 - << ui.profPO2 << ui.profRuler << ui.profSAC << ui.profScaled << ui.profTogglePicture; + QList<QToolButton*> toolBar; + toolBar << ui.profCalcAllTissues << ui.profCalcCeiling << ui.profDcCeiling << ui.profEad << + ui.profHR << ui.profIncrement3m << ui.profMod << ui.profNdl_tts << ui.profNdl_tts << + ui.profPhe << ui.profPn2 << ui.profPO2 << ui.profRuler << ui.profSAC << ui.profScaled << + ui.profTogglePicture << ui.profTankbar; Q_FOREACH(QToolButton *b, toolBar) b->setEnabled(arg1); } + +void MainWindow::on_copy_triggered() +{ + // open dialog to select what gets copied + // copy the displayed dive + DiveComponentSelection dialog(this, ©PasteDive, &what); + dialog.exec(); +} + +void MainWindow::on_paste_triggered() +{ + // take the data in our copyPasteDive and apply it to selected dives + selective_copy_dive(©PasteDive, &displayed_dive, what, false); + ui.InfoWidget->showAndTriggerEditSelective(what); +} diff --git a/qt-ui/mainwindow.h b/qt-ui/mainwindow.h index b9c8e4682..5ef5eed4a 100644 --- a/qt-ui/mainwindow.h +++ b/qt-ui/mainwindow.h @@ -141,7 +141,10 @@ slots: void on_profSAC_clicked(bool triggered); void on_profScaled_clicked(bool triggered); void on_profTogglePicture_clicked(bool triggered); + void on_profTankbar_clicked(bool triggered); void on_actionExport_triggered(); + void on_copy_triggered(); + void on_paste_triggered(); void on_actionConfigure_Dive_Computer_triggered(); @@ -186,6 +189,8 @@ private: bool plannerStateClean(); void setupForAddAndPlan(const char *model); QDialog *survey; + struct dive copyPasteDive; + struct dive_components what; }; #endif // MAINWINDOW_H diff --git a/qt-ui/mainwindow.ui b/qt-ui/mainwindow.ui index bd5581f64..dca59e706 100644 --- a/qt-ui/mainwindow.ui +++ b/qt-ui/mainwindow.ui @@ -109,7 +109,7 @@ <property name="spacing"> <number>0</number> </property> - <item row="15" column="0"> + <item row="17" column="0"> <spacer name="verticalSpacer"> <property name="orientation"> <enum>Qt::Vertical</enum> @@ -440,6 +440,9 @@ </property> </widget> </item> + <item row="0" column="1" rowspan="19"> + <widget class="ProfileWidget2" name="newProfile"/> + </item> <item row="0" column="0"> <widget class="QToolButton" name="profPO2"> <property name="toolTip"> @@ -466,9 +469,6 @@ </property> </widget> </item> - <item row="0" column="1" rowspan="16"> - <widget class="ProfileWidget2" name="newProfile"/> - </item> <item row="1" column="0"> <widget class="QToolButton" name="profPn2"> <property name="toolTip"> @@ -495,6 +495,32 @@ </property> </widget> </item> + <item row="15" column="0"> + <widget class="QToolButton" name="profTankbar"> + <property name="toolTip"> + <string>Toggle Cylinder Bar</string> + </property> + <property name="text"> + <string>...</string> + </property> + <property name="icon"> + <iconset resource="../subsurface.qrc"> + <normaloff>:/gaschange</normaloff>:/gaschange</iconset> + </property> + <property name="iconSize"> + <size> + <width>24</width> + <height>24</height> + </size> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="autoRaise"> + <bool>true</bool> + </property> + </widget> + </item> <item row="14" column="0"> <widget class="QToolButton" name="profTogglePicture"> <property name="toolTip"> @@ -664,7 +690,7 @@ p, li { white-space: pre-wrap; } <x>0</x> <y>0</y> <width>1682</width> - <height>27</height> + <height>19</height> </rect> </property> <widget class="QMenu" name="menuFile"> @@ -696,6 +722,8 @@ p, li { white-space: pre-wrap; } </property> <addaction name="actionAddDive"/> <addaction name="actionDivePlanner"/> + <addaction name="copy"/> + <addaction name="paste"/> <addaction name="separator"/> <addaction name="actionRenumber"/> <addaction name="actionAutoGroup"/> @@ -855,6 +883,22 @@ p, li { white-space: pre-wrap; } <string>Ctrl++</string> </property> </action> + <action name="copy"> + <property name="text"> + <string>&Copy dive components</string> + </property> + <property name="shortcut"> + <string>Ctrl+C</string> + </property> + </action> + <action name="paste"> + <property name="text"> + <string>&Paste dive components</string> + </property> + <property name="shortcut"> + <string>Ctrl+V</string> + </property> + </action> <action name="actionRenumber"> <property name="text"> <string>&Renumber</string> diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index 11e84c9c2..82fdbdc43 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -1102,7 +1102,7 @@ QVariant DiveItem::data(int column, int role) const case CYLINDER: retVal = QString(dive->cylinder[0].type.description); break; - case NITROX: + case GAS: retVal = nitrox_sort_value(dive); break; case SAC: @@ -1146,8 +1146,8 @@ QVariant DiveItem::data(int column, int role) const case CYLINDER: retVal = QString(dive->cylinder[0].type.description); break; - case NITROX: - retVal = QString(get_nitrox_string(dive)); + case GAS: + retVal = QString(get_dive_gas_string(dive)); break; case SAC: retVal = displaySac(); @@ -1338,8 +1338,8 @@ QVariant DiveTripModel::headerData(int section, Qt::Orientation orientation, int case CYLINDER: ret = tr("Cyl"); break; - case NITROX: - ret = QString("O%1%").arg(UTF8_SUBSCRIPT_2); + case GAS: + ret = tr("Gas"); break; case SAC: ret = tr("SAC"); diff --git a/qt-ui/models.h b/qt-ui/models.h index aa314f119..c4a3a077d 100644 --- a/qt-ui/models.h +++ b/qt-ui/models.h @@ -191,7 +191,7 @@ struct DiveItem : public TreeItem { TOTALWEIGHT, SUIT, CYLINDER, - NITROX, + GAS, SAC, OTU, MAXCNS, @@ -245,7 +245,7 @@ public: TOTALWEIGHT, SUIT, CYLINDER, - NITROX, + GAS, SAC, OTU, MAXCNS, diff --git a/qt-ui/plannerSettings.ui b/qt-ui/plannerSettings.ui index 0ad0f5b70..a28453c01 100644 --- a/qt-ui/plannerSettings.ui +++ b/qt-ui/plannerSettings.ui @@ -30,8 +30,8 @@ <rect> <x>0</x> <y>0</y> - <width>1089</width> - <height>404</height> + <width>1078</width> + <height>418</height> </rect> </property> <layout class="QHBoxLayout" name="horizontalLayout"> @@ -271,6 +271,9 @@ </item> <item row="0" column="1"> <widget class="QSpinBox" name="gflow"> + <property name="suffix"> + <string>%</string> + </property> <property name="minimum"> <number>1</number> </property> @@ -288,6 +291,9 @@ </item> <item row="1" column="1"> <widget class="QSpinBox" name="gfhigh"> + <property name="suffix"> + <string>%</string> + </property> <property name="minimum"> <number>1</number> </property> @@ -362,7 +368,11 @@ </widget> </item> <item row="0" column="1"> - <widget class="QSpinBox" name="bottomSAC"/> + <widget class="QSpinBox" name="bottomSAC"> + <property name="suffix"> + <string>ℓ/min</string> + </property> + </widget> </item> <item row="1" column="0"> <widget class="QLabel" name="label_14"> @@ -372,7 +382,11 @@ </widget> </item> <item row="1" column="1"> - <widget class="QSpinBox" name="decoStopSAC"/> + <widget class="QSpinBox" name="decoStopSAC"> + <property name="suffix"> + <string>ℓ/min</string> + </property> + </widget> </item> <item row="2" column="0"> <widget class="QLabel" name="label_6"> diff --git a/qt-ui/preferences.cpp b/qt-ui/preferences.cpp index 6000f453a..edd95d838 100644 --- a/qt-ui/preferences.cpp +++ b/qt-ui/preferences.cpp @@ -315,6 +315,7 @@ void PreferencesDialog::loadSettings() GET_BOOL("calcndltts", calcndltts); GET_BOOL("calcalltissues", calcalltissues); GET_BOOL("hrgraph", hrgraph); + GET_BOOL("tankbar", tankbar); GET_INT("gflow", gflow); GET_INT("gfhigh", gfhigh); GET_BOOL("gf_low_at_maxdepth", gf_low_at_maxdepth); diff --git a/qt-ui/preferences.ui b/qt-ui/preferences.ui index 48592ae1d..c021ae872 100644 --- a/qt-ui/preferences.ui +++ b/qt-ui/preferences.ui @@ -612,6 +612,9 @@ <property name="enabled"> <bool>true</bool> </property> + <property name="singleStep"> + <double>0.100000000000000</double> + </property> </widget> </item> </layout> @@ -633,6 +636,9 @@ <property name="enabled"> <bool>true</bool> </property> + <property name="singleStep"> + <double>0.100000000000000</double> + </property> </widget> </item> </layout> @@ -654,6 +660,9 @@ <property name="enabled"> <bool>true</bool> </property> + <property name="singleStep"> + <double>0.100000000000000</double> + </property> </widget> </item> </layout> @@ -675,6 +684,9 @@ <property name="enabled"> <bool>true</bool> </property> + <property name="singleStep"> + <double>0.100000000000000</double> + </property> </widget> </item> </layout> diff --git a/qt-ui/profile/profilewidget2.cpp b/qt-ui/profile/profilewidget2.cpp index 413414ec8..409328918 100644 --- a/qt-ui/profile/profilewidget2.cpp +++ b/qt-ui/profile/profilewidget2.cpp @@ -13,6 +13,7 @@ #include "planner.h" #include "device.h" #include "ruleritem.h" +#include "tankitem.h" #include "dive.h" #include "pref.h" #include <libdivecomputer/parser.h> @@ -52,8 +53,10 @@ static struct _ItemPos { }; _Pos background; _Pos dcLabel; + _Pos tankBar; _Axis depth; _Axis partialPressure; + _Axis partialPressureWithTankBar; _Axis time; _Axis cylinder; _Axis temperature; @@ -89,6 +92,7 @@ ProfileWidget2::ProfileWidget2(QWidget *parent) : QGraphicsView(parent), mouseFollowerVertical(new DiveLineItem()), mouseFollowerHorizontal(new DiveLineItem()), rulerItem(new RulerItem2()), + tankItem(new TankItem()), isGrayscale(false), printMode(false), shouldCalculateMaxTime(true), @@ -160,6 +164,7 @@ void ProfileWidget2::addItemsToScene() scene()->addItem(rulerItem); scene()->addItem(rulerItem->sourceNode()); scene()->addItem(rulerItem->destNode()); + scene()->addItem(tankItem); scene()->addItem(mouseFollowerHorizontal); scene()->addItem(mouseFollowerVertical); QPen pen(QColor(Qt::red).lighter()); @@ -177,6 +182,7 @@ void ProfileWidget2::setupItemOnScene() toolTipItem->setZValue(9998); toolTipItem->setTimeAxis(timeAxis); rulerItem->setZValue(9997); + tankItem->setZValue(100); profileYAxis->setOrientation(DiveCartesianAxis::TopToBottom); profileYAxis->setMinimum(0); @@ -219,6 +225,7 @@ void ProfileWidget2::setupItemOnScene() diveComputerText->setBrush(getColor(TIME_TEXT, isGrayscale)); rulerItem->setAxis(timeAxis, profileYAxis); + tankItem->setHorizontalAxis(timeAxis); setupItem(reportedCeiling, timeAxis, profileYAxis, dataModel, DivePlotDataModel::CEILING, DivePlotDataModel::TIME, 1); setupItem(diveCeiling, timeAxis, profileYAxis, dataModel, DivePlotDataModel::CEILING, DivePlotDataModel::TIME, 1); @@ -301,6 +308,8 @@ void ProfileWidget2::setupItemSizes() itemPos.partialPressure.pos.off.setY(63); itemPos.partialPressure.expanded.setP1(QPointF(0, 0)); itemPos.partialPressure.expanded.setP2(QPointF(0, 30)); + itemPos.partialPressureWithTankBar = itemPos.partialPressure; + itemPos.partialPressureWithTankBar.expanded.setP2(QPointF(0, 27)); // cylinder axis config itemPos.cylinder.pos.on.setX(3); @@ -331,6 +340,9 @@ void ProfileWidget2::setupItemSizes() itemPos.dcLabel.on.setY(100); itemPos.dcLabel.off.setX(-10); itemPos.dcLabel.off.setY(100); + + itemPos.tankBar.on.setX(0); + itemPos.tankBar.on.setY(92); } void ProfileWidget2::setupItem(AbstractProfilePolygonItem *item, DiveCartesianAxis *hAxis, @@ -409,6 +421,14 @@ void ProfileWidget2::plotDive(struct dive *d, bool force) // reset some item visibility on printMode changes toolTipItem->setVisible(!printMode); rulerItem->setVisible(prefs.rulergraph && !printMode); + tankItem->setVisible(prefs.tankbar); + if (prefs.tankbar) { + gasYAxis->setPos(itemPos.partialPressureWithTankBar.pos.on); + gasYAxis->setLine(itemPos.partialPressureWithTankBar.expanded); + } else { + gasYAxis->setPos(itemPos.partialPressure.pos.on); + gasYAxis->setLine(itemPos.partialPressure.expanded); + } if (currentState == EMPTY) setProfileState(); @@ -487,6 +507,7 @@ void ProfileWidget2::plotDive(struct dive *d, bool force) cylinderPressureAxis->setMaximum(pInfo.maxpressure); rulerItem->setPlotInfo(pInfo); + tankItem->setData(dataModel, &pInfo, &displayed_dive); meanDepth->setVisible(prefs.show_average_depth); meanDepth->setMeanDepth(pInfo.meandepth); meanDepth->setLine(0, 0, timeAxis->posAtValue(currentdc->duration.seconds), 0); @@ -552,6 +573,13 @@ void ProfileWidget2::settingsChanged() temperatureAxis->animateChangeLine(itemPos.temperature.expanded); cylinderPressureAxis->animateChangeLine(itemPos.cylinder.expanded); } + if (prefs.tankbar) { + gasYAxis->setPos(itemPos.partialPressureWithTankBar.pos.on); + gasYAxis->animateChangeLine(itemPos.partialPressureWithTankBar.expanded); + } else { + gasYAxis->setPos(itemPos.partialPressure.pos.on); + gasYAxis->animateChangeLine(itemPos.partialPressure.expanded); + } if (prefs.zoomed_plot != isPlotZoomed) { isPlotZoomed = prefs.zoomed_plot; needReplot = true; @@ -727,6 +755,7 @@ void ProfileWidget2::setEmptyState() diveCeiling->setVisible(false); reportedCeiling->setVisible(false); rulerItem->setVisible(false); + tankItem->setVisible(false); pn2GasItem->setVisible(false); po2GasItem->setVisible(false); pheGasItem->setVisible(false); @@ -784,9 +813,13 @@ void ProfileWidget2::setProfileState() po2GasItem->setVisible(prefs.pp_graphs.po2); pheGasItem->setVisible(prefs.pp_graphs.phe); - gasYAxis->setPos(itemPos.partialPressure.pos.on); - gasYAxis->setLine(itemPos.partialPressure.expanded); - + if (prefs.tankbar) { + gasYAxis->setPos(itemPos.partialPressureWithTankBar.pos.on); + gasYAxis->setLine(itemPos.partialPressureWithTankBar.expanded); + } else { + gasYAxis->setPos(itemPos.partialPressure.pos.on); + gasYAxis->setLine(itemPos.partialPressure.expanded); + } timeAxis->setPos(itemPos.time.pos.on); timeAxis->setLine(itemPos.time.expanded); @@ -809,6 +842,9 @@ void ProfileWidget2::setProfileState() } } rulerItem->setVisible(prefs.rulergraph); + tankItem->setVisible(prefs.tankbar); + tankItem->setPos(itemPos.tankBar.on); + #define HIDE_ALL(TYPE, CONTAINER) \ Q_FOREACH (TYPE *item, CONTAINER) item->setVisible(false); HIDE_ALL(DiveHandler, handles); diff --git a/qt-ui/profile/profilewidget2.h b/qt-ui/profile/profilewidget2.h index c3db9845c..fa5e53e26 100644 --- a/qt-ui/profile/profilewidget2.h +++ b/qt-ui/profile/profilewidget2.h @@ -40,6 +40,7 @@ class DiveCalculatedTissue; class PartialPressureGasItem; class PartialGasPressureAxis; class AbstractProfilePolygonItem; +class TankItem; class DiveHandler; class QGraphicsSimpleTextItem; class QModelIndex; @@ -164,6 +165,7 @@ private: DiveLineItem *mouseFollowerVertical; DiveLineItem *mouseFollowerHorizontal; RulerItem2 *rulerItem; + TankItem *tankItem; bool isGrayscale; bool printMode; diff --git a/qt-ui/profile/tankitem.cpp b/qt-ui/profile/tankitem.cpp new file mode 100644 index 000000000..a5ec5050c --- /dev/null +++ b/qt-ui/profile/tankitem.cpp @@ -0,0 +1,105 @@ +#include "tankitem.h" +#include "diveplotdatamodel.h" +#include "divetextitem.h" +#include "profile.h" +#include <QGradient> +#include <QDebug> +#include <QPen> + +TankItem::TankItem(QObject *parent) : + QGraphicsRectItem(), + dataModel(0), + dive(0), + pInfo(0) +{ + height = 3; + QColor red(PERSIANRED1); + QColor blue(AIR_BLUE); + QColor yellow(NITROX_YELLOW); + QColor green(NITROX_GREEN); + QLinearGradient nitroxGradient(QPointF(0, 0), QPointF(0, height)); + nitroxGradient.setColorAt(0.0, green); + nitroxGradient.setColorAt(0.49, green); + nitroxGradient.setColorAt(0.5, yellow); + nitroxGradient.setColorAt(1.0, yellow); + nitrox = nitroxGradient; + QLinearGradient trimixGradient(QPointF(0, 0), QPointF(0, height)); + trimixGradient.setColorAt(0.0, green); + trimixGradient.setColorAt(0.49, green); + trimixGradient.setColorAt(0.5, red); + trimixGradient.setColorAt(1.0, red); + trimix = trimixGradient; + air = blue; +} + +void TankItem::setData(DivePlotDataModel *model, struct plot_info *plotInfo, struct dive *d) +{ + pInfo = plotInfo; + dive = d; + dataModel = model; + connect(dataModel, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SLOT(modelDataChanged(QModelIndex, QModelIndex))); + modelDataChanged(); +} + +void TankItem::createBar(qreal x, qreal w, struct gasmix *gas) +{ + // pick the right gradient, size, position and text + QGraphicsRectItem *rect = new QGraphicsRectItem(x, 0, w, height, this); + if (gasmix_is_air(gas)) + rect->setBrush(air); + else if (gas->he.permille) + rect->setBrush(trimix); + else + rect->setBrush(nitrox); + rect->setPen(QPen(QBrush(), 0.0)); // get rid of the thick line around the rectangle + rects.push_back(rect); + DiveTextItem *label = new DiveTextItem(rect); + label->setText(gasname(gas)); + label->setBrush(Qt::black); + label->setPos(x + 1, 0); + label->setAlignment(Qt::AlignBottom | Qt::AlignRight); + label->setZValue(101); +} + +void TankItem::modelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) +{ + // We don't have enougth data to calculate things, quit. + + if (!dive || !dataModel || !pInfo || !pInfo->nr) + return; + + // remove the old rectangles + foreach (QGraphicsRectItem *r, rects) { + delete(r); + } + rects.clear(); + + // walk the list and figure out which tanks go where + struct plot_data *entry = pInfo->entry; + int cylIdx = entry->cylinderindex; + int i = -1; + int startTime = 0; + struct gasmix *gas = &dive->cylinder[cylIdx].gasmix; + qreal width, left; + while (++i < pInfo->nr) { + entry = &pInfo->entry[i]; + if (entry->cylinderindex == cylIdx) + continue; + width = hAxis->posAtValue(entry->sec) - hAxis->posAtValue(startTime); + left = hAxis->posAtValue(startTime); + createBar(left, width, gas); + cylIdx = entry->cylinderindex; + gas = &dive->cylinder[cylIdx].gasmix; + startTime = entry->sec; + } + width = hAxis->posAtValue(entry->sec) - hAxis->posAtValue(startTime); + left = hAxis->posAtValue(startTime); + createBar(left, width, gas); +} + +void TankItem::setHorizontalAxis(DiveCartesianAxis *horizontal) +{ + hAxis = horizontal; + connect(hAxis, SIGNAL(sizeChanged()), this, SLOT(modelDataChanged())); + modelDataChanged(); +} diff --git a/qt-ui/profile/tankitem.h b/qt-ui/profile/tankitem.h new file mode 100644 index 000000000..615a7017d --- /dev/null +++ b/qt-ui/profile/tankitem.h @@ -0,0 +1,38 @@ +#ifndef TANKITEM_H +#define TANKITEM_H + +#include <QGraphicsItem> +#include <QModelIndex> +#include <QBrush> +#include "divelineitem.h" +#include "divecartesianaxis.h" +#include "dive.h" + +class TankItem : public QObject, public QGraphicsRectItem +{ + Q_OBJECT + Q_INTERFACES(QGraphicsItem) + +public: + explicit TankItem(QObject *parent = 0); + void setHorizontalAxis(DiveCartesianAxis *horizontal); + void setData(DivePlotDataModel *model, struct plot_info *plotInfo, struct dive *d); + +signals: + +public slots: + virtual void modelDataChanged(const QModelIndex &topLeft = QModelIndex(), const QModelIndex &bottomRight = QModelIndex()); + +private: + void createBar(qreal x, qreal w, struct gasmix *gas); + DivePlotDataModel *dataModel; + DiveCartesianAxis *hAxis; + int hDataColumn; + struct dive *dive; + struct plot_info *pInfo; + qreal yPos, height; + QBrush air, nitrox, trimix; + QList<QGraphicsRectItem *> rects; +}; + +#endif // TANKITEM_H diff --git a/qt-ui/simplewidgets.cpp b/qt-ui/simplewidgets.cpp index 217291277..772c8d1b9 100644 --- a/qt-ui/simplewidgets.cpp +++ b/qt-ui/simplewidgets.cpp @@ -411,3 +411,47 @@ void DateWidget::keyPressEvent(QKeyEvent *event) QWidget::keyPressEvent(event); } } + +#define COMPONENT_FROM_UI(_component) what->_component = ui._component->isChecked() +#define UI_FROM_COMPONENT(_component) ui._component->setChecked(what->_component) + +DiveComponentSelection::DiveComponentSelection(QWidget *parent, struct dive *target, struct dive_components *_what): + targetDive(target) +{ + ui.setupUi(this); + what = _what; + UI_FROM_COMPONENT(location); + UI_FROM_COMPONENT(gps); + UI_FROM_COMPONENT(divemaster); + UI_FROM_COMPONENT(buddy); + UI_FROM_COMPONENT(rating); + UI_FROM_COMPONENT(visibility); + UI_FROM_COMPONENT(notes); + UI_FROM_COMPONENT(suit); + UI_FROM_COMPONENT(tags); + UI_FROM_COMPONENT(cylinders); + UI_FROM_COMPONENT(weights); + connect(ui.buttonBox, SIGNAL(clicked(QAbstractButton *)), this, SLOT(buttonClicked(QAbstractButton *))); + QShortcut *close = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_W), this); + connect(close, SIGNAL(activated()), this, SLOT(close())); + QShortcut *quit = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q), this); + connect(quit, SIGNAL(activated()), parent, SLOT(close())); +} + +void DiveComponentSelection::buttonClicked(QAbstractButton *button) +{ + if (ui.buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole) { + COMPONENT_FROM_UI(location); + COMPONENT_FROM_UI(gps); + COMPONENT_FROM_UI(divemaster); + COMPONENT_FROM_UI(buddy); + COMPONENT_FROM_UI(rating); + COMPONENT_FROM_UI(visibility); + COMPONENT_FROM_UI(notes); + COMPONENT_FROM_UI(suit); + COMPONENT_FROM_UI(tags); + COMPONENT_FROM_UI(cylinders); + COMPONENT_FROM_UI(weights); + selective_copy_dive(&displayed_dive, targetDive, *what, true); + } +} diff --git a/qt-ui/simplewidgets.h b/qt-ui/simplewidgets.h index 58c9199a5..b41189fe5 100644 --- a/qt-ui/simplewidgets.h +++ b/qt-ui/simplewidgets.h @@ -11,6 +11,7 @@ class QAbstractButton; #include "ui_renumber.h" #include "ui_shifttimes.h" #include "ui_shiftimagetimes.h" +#include "ui_divecomponentselection.h" #include "exif.h" class MinMaxAvgWidget : public QWidget { @@ -112,6 +113,19 @@ private: QCalendarWidget *calendarWidget; }; +class DiveComponentSelection : public QDialog { + Q_OBJECT +public: + explicit DiveComponentSelection(QWidget *parent, struct dive *target, struct dive_components *_what); +private +slots: + void buttonClicked(QAbstractButton *button); +private: + Ui::DiveComponentSelectionDialog ui; + struct dive *targetDive; + struct dive_components *what; +}; + bool isGnome3Session(); QImage grayImage(const QImage& coloredImg); diff --git a/qt-ui/usersurvey.cpp b/qt-ui/usersurvey.cpp index f436fce47..a8916c990 100644 --- a/qt-ui/usersurvey.cpp +++ b/qt-ui/usersurvey.cpp @@ -15,6 +15,7 @@ UserSurvey::UserSurvey(QWidget *parent) : QDialog(parent), ui(new Ui::UserSurvey) { ui->setupUi(this); + ui->buttonBox->buttons().first()->setText(tr("Send")); this->adjustSize(); QShortcut *closeKey = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_W), this); connect(closeKey, SIGNAL(activated()), this, SLOT(close())); diff --git a/qthelper.cpp b/qthelper.cpp index 98fa785a1..e481bc9e9 100644 --- a/qthelper.cpp +++ b/qthelper.cpp @@ -284,19 +284,17 @@ picture_load_exit: return; } -extern "C" const char* get_file_name(const char *fileName) +extern "C" char *get_file_name(const char *fileName) { - QFile file(fileName); - QFileInfo fileInfo(file.fileName()); - QString filename(fileInfo.fileName()); - return filename.toStdString().c_str(); + QFileInfo fileInfo(fileName); + return strdup(fileInfo.fileName().toUtf8()); } extern "C" void copy_image_and_overwrite(const char *cfileName, const char *cnewName) { QString fileName = QString::fromUtf8(cfileName); QString newName = QString::fromUtf8(cnewName); - newName += get_file_name(cfileName); + newName += QFileInfo(cfileName).fileName(); QFile file(newName); if (file.exists()) file.remove(); diff --git a/save-html.c b/save-html.c index 652ffb411..c3d5073b2 100644 --- a/save-html.c +++ b/save-html.c @@ -16,7 +16,9 @@ void save_photos(struct membuffer *b, const char *photos_dir, struct dive *dive) struct picture *pic = dive->picture_list; put_string(b, "\"photos\":["); while (pic) { - put_format(b, "{\"filename\":\"%s\"},", get_file_name(pic->filename)); + char *fname = get_file_name(pic->filename); + put_format(b, "{\"filename\":\"%s\"},", fname); + free(fname); copy_image_and_overwrite(pic->filename, photos_dir); pic = pic->next; } @@ -99,7 +101,7 @@ static void put_cylinder_HTML(struct membuffer *b, struct dive *dive) if (cylinder->gasmix.o2.permille) { put_format(b, "\"O2\":\"%u.%u%%\",", FRACTION(cylinder->gasmix.o2.permille, 10)); } else { - write_attribute(b, "O2", "--"); + write_attribute(b, "O2", "Air"); } put_string(b, "},"); } @@ -122,6 +124,21 @@ void put_HTML_samples(struct membuffer *b, struct dive *dive) put_string(b, "],"); } +void put_HTML_coordinates(struct membuffer *b, struct dive *dive) +{ + degrees_t latitude = dive->latitude; + degrees_t longitude = dive->longitude; + + //don't put coordinates if in (0,0) + if (!latitude.udeg && !longitude.udeg) + return; + + put_string(b, "\"coordinates\":{"); + put_degrees(b, latitude, "\"lat\":\"", "\","); + put_degrees(b, longitude, "\"lon\":\"", "\","); + put_string(b, "},"); +} + void put_HTML_date(struct membuffer *b, struct dive *dive, const char *pre, const char *post) { struct tm tm; @@ -207,8 +224,11 @@ void write_one_dive(struct membuffer *b, struct dive *dive, const char *photos_d put_HTML_date(b, dive, "\"date\":\"", "\","); put_HTML_time(b, dive, "\"time\":\"", "\","); write_attribute(b, "location", dive->location); + put_HTML_coordinates(b, dive); put_format(b, "\"rating\":%d,", dive->rating); put_format(b, "\"visibility\":%d,", dive->visibility); + put_format(b, "\"dive_duration\":\"%u:%02u min\",", + FRACTION(dive->duration.seconds, 60)); put_string(b, "\"temperature\":{"); put_HTML_airtemp(b, dive, "\"air\":\"", "\","); put_HTML_watertemp(b, dive, "\"water\":\"", "\","); @@ -230,35 +250,51 @@ void write_one_dive(struct membuffer *b, struct dive *dive, const char *photos_d (*dive_no)++; } -void write_no_trip(struct membuffer *b, int *dive_no, const char *photos_dir, const bool list_only) +void write_no_trip(struct membuffer *b, int *dive_no, bool selected_only, const char *photos_dir, const bool list_only) { int i; struct dive *dive; - - put_format(b, "{"); - put_format(b, "\"name\":\"Other\","); - put_format(b, "\"dives\":["); + bool found_sel_dive = 0; for_each_dive (i, dive) { - if (!dive->divetrip) + // write dive if it doesn't belong to any trip and the dive is selected + // or we are in exporting all dives mode. + if (!dive->divetrip && (dive->selected || !selected_only)) { + if (!found_sel_dive) { + put_format(b, "{"); + put_format(b, "\"name\":\"Other\","); + put_format(b, "\"dives\":["); + found_sel_dive = 1; + } write_one_dive(b, dive, photos_dir, dive_no, list_only); + } } - put_format(b, "]},\n\n"); + if (found_sel_dive) + put_format(b, "]},\n\n"); } -void write_trip(struct membuffer *b, dive_trip_t *trip, int *dive_no, const char *photos_dir, const bool list_only) +void write_trip(struct membuffer *b, dive_trip_t *trip, int *dive_no, bool selected_only, const char *photos_dir, const bool list_only) { struct dive *dive; - - put_format(b, "{"); - put_format(b, "\"name\":\"%s\",", trip->location); - put_format(b, "\"dives\":["); + bool found_sel_dive = 0; for (dive = trip->dives; dive != NULL; dive = dive->next) { + if (!dive->selected && selected_only) + continue; + + // save trip if found at least one selected dive. + if (!found_sel_dive) { + found_sel_dive = 1; + put_format(b, "{"); + put_format(b, "\"name\":\"%s\",", trip->location); + put_format(b, "\"dives\":["); + } write_one_dive(b, dive, photos_dir, dive_no, list_only); } - put_format(b, "]},\n\n"); + // close the trip object if contain dives. + if (found_sel_dive) + put_format(b, "]},\n\n"); } void write_trips(struct membuffer *b, const char *photos_dir, bool selected_only, const bool list_only) @@ -270,34 +306,20 @@ void write_trips(struct membuffer *b, const char *photos_dir, bool selected_only for (trip = dive_trip_list; trip != NULL; trip = trip->next) trip->index = 0; - if (selected_only) { - put_format(b, "{"); - put_format(b, "\"name\":\"Other\","); - put_format(b, "\"dives\":["); - - for_each_dive (i, dive) { - if (!dive->selected) - continue; - write_one_dive(b, dive, photos_dir, &dive_no, list_only); - } - put_format(b, "]},\n\n"); - } else { - - for_each_dive (i, dive) { - trip = dive->divetrip; - - /*Continue if the dive have no trips or we have seen this trip before*/ - if (!trip || trip->index) - continue; + for_each_dive (i, dive) { + trip = dive->divetrip; - /* We haven't seen this trip before - save it and all dives */ - trip->index = 1; - write_trip(b, trip, &dive_no, photos_dir, list_only); - } + /*Continue if the dive have no trips or we have seen this trip before*/ + if (!trip || trip->index) + continue; - /*Save all remaining trips into Others*/ - write_no_trip(b, &dive_no, photos_dir, list_only); + /* We haven't seen this trip before - save it and all dives */ + trip->index = 1; + write_trip(b, trip, &dive_no, selected_only, photos_dir, list_only); } + + /*Save all remaining trips into Others*/ + write_no_trip(b, &dive_no, selected_only, photos_dir, list_only); } void export_list(struct membuffer *b, const char *photos_dir, bool selected_only, const bool list_only) @@ -322,3 +344,86 @@ void export_HTML(const char *file_name, const char *photos_dir, const bool selec free_buffer(&buf); fclose(f); } + +void export_translation(const char *file_name) +{ + FILE *f; + + struct membuffer buf = { 0 }; + struct membuffer *b = &buf; + + //export translated words here + put_format(b, "translate={"); + + //Dive list view + write_attribute(b, "Number", translate("gettextFromC", "Number")); + write_attribute(b, "Date", translate("gettextFromC", "Date")); + write_attribute(b, "Time", translate("gettextFromC", "Time")); + write_attribute(b, "Location", translate("gettextFromC", "Location")); + write_attribute(b, "Air_Temp", translate("gettextFromC", "Air Temp")); + write_attribute(b, "Water_Temp", translate("gettextFromC", "Water Temp")); + write_attribute(b, "dives", translate("gettextFromC", "dives")); + write_attribute(b, "Expand_All", translate("gettextFromC", "Expand All")); + write_attribute(b, "Collapse_All", translate("gettextFromC", "Collapse All")); + write_attribute(b, "trips", translate("gettextFromC", "trips")); + write_attribute(b, "Statistics", translate("gettextFromC", "Statistics")); + write_attribute(b, "Advanced_Search", translate("gettextFromC", "Advanced Search")); + + //Dive expanded view + write_attribute(b, "Rating", translate("gettextFromC", "Rating")); + write_attribute(b, "Visibility", translate("gettextFromC", "Visibility")); + write_attribute(b, "Duration", translate("gettextFromC", "Duration")); + write_attribute(b, "DiveMaster", translate("gettextFromC", "DiveMaster")); + write_attribute(b, "Buddy", translate("gettextFromC", "Buddy")); + write_attribute(b, "Suit", translate("gettextFromC", "Suit")); + write_attribute(b, "Tags", translate("gettextFromC", "Tags")); + write_attribute(b, "Notes", translate("gettextFromC", "Notes")); + write_attribute(b, "Show_more_details", translate("gettextFromC", "Show more details")); + + //Yearly statistics view + write_attribute(b, "Yearly_statistics", translate("gettextFromC", "Yearly statistics")); + write_attribute(b, "Year", translate("gettextFromC", "Year")); + write_attribute(b, "Total_Time", translate("gettextFromC", "Total Time")); + write_attribute(b, "Average_Time", translate("gettextFromC", "Average Time")); + write_attribute(b, "Shortest_Time", translate("gettextFromC", "Shortest Time")); + write_attribute(b, "Longest_Time", translate("gettextFromC", "Longest Time")); + write_attribute(b, "Average_Depth", translate("gettextFromC", "Average Depth")); + write_attribute(b, "Min_Depth", translate("gettextFromC", "Min Depth")); + write_attribute(b, "Max_Depth", translate("gettextFromC", "Max Depth")); + write_attribute(b, "Average_SAC", translate("gettextFromC", "Average SAC")); + write_attribute(b, "Min_SAC", translate("gettextFromC", "Min SAC")); + write_attribute(b, "Max_SAC", translate("gettextFromC", "Max SAC")); + write_attribute(b, "Average_Temp", translate("gettextFromC", "Average Temp")); + write_attribute(b, "Min_Temp", translate("gettextFromC", "Min Temp")); + write_attribute(b, "Max_Temp", translate("gettextFromC", "Max Temp")); + write_attribute(b, "Back_to_List", translate("gettextFromC", "Back to List")); + + //dive detailed view + write_attribute(b, "Dive_No", translate("gettextFromC", "Dive No.")); + write_attribute(b, "Dive_profile", translate("gettextFromC", "Dive profile")); + write_attribute(b, "Dive_information", translate("gettextFromC", "Dive information")); + write_attribute(b, "Dive_equipments", translate("gettextFromC", "Dive equipments")); + write_attribute(b, "Type", translate("gettextFromC", "Type")); + write_attribute(b, "Size", translate("gettextFromC", "Size")); + write_attribute(b, "Work_Pressure", translate("gettextFromC", "Work Pressure")); + write_attribute(b, "Start_Pressure", translate("gettextFromC", "Start Pressure")); + write_attribute(b, "End_Pressure", translate("gettextFromC", "End Pressure")); + write_attribute(b, "Gas", translate("gettextFromC", "Gas")); + write_attribute(b, "Weight", translate("gettextFromC", "Weight")); + write_attribute(b, "Type", translate("gettextFromC", "Type")); + write_attribute(b, "Events", translate("gettextFromC", "Events")); + write_attribute(b, "Name", translate("gettextFromC", "Name")); + write_attribute(b, "Value", translate("gettextFromC", "Value")); + write_attribute(b, "Coordinates", translate("gettextFromC", "Coordinates")); + write_attribute(b, "Dive_Status", translate("gettextFromC", "Dive Status")); + + put_format(b, "}"); + + f = subsurface_fopen(file_name, "w+"); + if (!f) + report_error(translate("gettextFromC", "Can't open file %s"), file_name); + + flush_buffer(&buf, f); /*check for writing errors? */ + free_buffer(&buf); + fclose(f); +} diff --git a/save-html.h b/save-html.h index 298196108..f47ae3ba6 100644 --- a/save-html.h +++ b/save-html.h @@ -16,9 +16,10 @@ void put_HTML_notes(struct membuffer *b, struct dive *dive, const char *pre, con void put_HTML_quoted(struct membuffer *b, const char *text); void export_HTML(const char *file_name, const char *photos_dir, const bool selected_only, const bool list_only); +void export_translation(const char *file_name); extern void copy_image_and_overwrite(const char *cfileName, const char *cnewName); -extern const char* get_file_name(const char *fileName); +extern char *get_file_name(const char *fileName); #ifdef __cplusplus } #endif diff --git a/scripts/parse-descriptor.pl b/scripts/parse-descriptor.pl index 19d4085e8..26df4028a 100755 --- a/scripts/parse-descriptor.pl +++ b/scripts/parse-descriptor.pl @@ -36,12 +36,12 @@ foreach (@sortedDescriptors) { printf(", %s", $mod); } else { if ($lastVend lt "Uemis" && $vend gt "Uemis") { - printf("</li>\n\t</ul>\n </dd>\n <dt>Uemis</dt><dd>\n\t<ul>\n\t <li>Zürich SDA"); + printf("</li></ul>\n </dd>\n <dt>Uemis</dt><dd><ul>\n\t <li>Zürich SDA"); } if ($lastVend eq "") { - printf("<dl><dt>%s</dt><dd>\n\t<ul>\n\t <li>%s", $vend, $mod); + printf("<dl><dt>%s</dt><dd><ul>\n\t <li>%s", $vend, $mod); } else { - printf("</li>\n\t</ul>\n </dd>\n <dt>%s</dt><dd>\n\t<ul>\n\t <li>%s", $vend, $mod); + printf("</li></ul>\n </dd>\n <dt>%s</dt><dd><ul>\n\t <li>%s", $vend, $mod); } } } else { diff --git a/subsurface.pro b/subsurface.pro index 723513d0e..af3f6e1b8 100644 --- a/subsurface.pro +++ b/subsurface.pro @@ -6,6 +6,7 @@ QT = core gui network svg concurrent lessThan(QT_MAJOR_VERSION, 5) { QT += webkit } else { + QT += printsupport !android: QT += webkitwidgets webkit android: QT += androidextras } @@ -83,6 +84,7 @@ HEADERS = \ qt-ui/profile/diveeventitem.h \ qt-ui/profile/divetooltipitem.h \ qt-ui/profile/ruleritem.h \ + qt-ui/profile/tankitem.h \ qt-ui/updatemanager.h \ qt-ui/divelogexportdialog.h \ qt-ui/usersurvey.h \ @@ -166,6 +168,7 @@ SOURCES = \ qt-ui/profile/diveeventitem.cpp \ qt-ui/profile/divetooltipitem.cpp \ qt-ui/profile/ruleritem.cpp \ + qt-ui/profile/tankitem.cpp \ qt-ui/updatemanager.cpp \ qt-ui/divelogexportdialog.cpp \ qt-ui/usersurvey.cpp \ @@ -205,6 +208,7 @@ FORMS = \ qt-ui/divelogexportdialog.ui \ qt-ui/plannerSettings.ui \ qt-ui/usersurvey.ui \ + qt-ui/divecomponentselection.ui \ qt-ui/configuredivecomputerdialog.ui # Nether usermanual or printing is supported on android right now diff --git a/subsurfacestartup.c b/subsurfacestartup.c index b7b1b6504..8edc409be 100644 --- a/subsurfacestartup.c +++ b/subsurfacestartup.c @@ -44,7 +44,8 @@ struct preferences default_prefs = { .drop_stone_mode = false, .bottomsac = 20000, .decosac = 17000, - .show_pictures_in_profile = true + .show_pictures_in_profile = true, + .tankbar = false }; int run_survey; diff --git a/theme/dive_export.html b/theme/dive_export.html index 91b0ef749..697c16ca3 100644 --- a/theme/dive_export.html +++ b/theme/dive_export.html @@ -39,6 +39,10 @@ function load_scripts() document.getElementsByTagName("head")[0].appendChild(fileref); fileref=document.createElement('script'); + fileref.setAttribute("src", location.pathname + "_files/translation.json"); + document.getElementsByTagName("head")[0].appendChild(fileref); + + fileref=document.createElement('script'); fileref.setAttribute("src", location.pathname + "_files/settings.json"); document.getElementsByTagName("head")[0].appendChild(fileref); @@ -121,9 +125,16 @@ window.onload=function(){ showAllDives(); document.getElementById("divePanel").style.display='none'; document.getElementById("diveStat").style.display='none'; + if (divestat.length <= 0) + document.getElementById("stats_button").style.display='none'; + document.body.style.visibility='visible'; document.onkeydown = switchDives; + + //translate Page + translate_page(); + getDefaultColor(); } function changeAdvSearch(e){ @@ -142,7 +153,8 @@ function changeAdvSearch(e){ </center> <div id="diveListPanel"> <div id="controlbox"> - <input id="search_input" oninput="SearchModules(this.value, null)" placeholder="search"/><a id="adv_srch_sp" onClick="showdiv()" >Advanced Search</a> + <input id="search_input" oninput="SearchModules(this.value, null)" placeholder="search"/> + <a id="adv_srch_sp" onClick="showdiv()" >Advanced Search</a> <div id="advanced_search"> <input type="checkbox" onchange="changeAdvSearch(this)" value="location" checked>Location<br> <input type="checkbox" onchange="changeAdvSearch(this)" value="divemaster" checked>Divemaster<br> @@ -157,19 +169,19 @@ function changeAdvSearch(e){ <option>50</option> <option>100</option> </select> - <button onClick="expandAll()"> Expand All </button> - <button onClick="collapseAll()"> Collapse All </button> + <button id="expnd_all_btn" onClick="expandAll()"> Expand All </button> + <button id="claps_all_btn" onClick="collapseAll()"> Collapse All </button> <button id="trip_button" onclick="toggleTrips();">trips</button> <button id="stats_button" onclick="toggleStats();">Stats</button> </div> </div> <div id="header"> - <div onClick="change_sort_col('1')" class="item">Number</div> - <div onClick="change_sort_col('2')" class="item">Date</div> - <div onClick="change_sort_col('3')" class="item">Time</div> - <div onClick="change_sort_col('6')" class="item_large">Location</div> - <div onClick="change_sort_col('4')" class="item">Air Temp</div> - <div onClick="change_sort_col('5')" class="item">Water Temp</div> + <div id="number_header" onClick="change_sort_col('1')" class="item">Number</div> + <div id="date_header" onClick="change_sort_col('2')" class="item">Date</div> + <div id="time_header" onClick="change_sort_col('3')" class="item">Time</div> + <div id="location_header" onClick="change_sort_col('6')" class="item_large">Location</div> + <div id="air_temp_header" onClick="change_sort_col('4')" class="item">Air Temp</div> + <div id="water_temp_header" onClick="change_sort_col('5')" class="item">Water Temp</div> </div> <div id="diveslist"> </div> @@ -185,7 +197,7 @@ function changeAdvSearch(e){ <center> <div id="but"> <button onClick="prevDetailedDive()"><-</button> - <button onClick="unshowDiveDetails()">Back to List</button> + <button id="bk_to_ls_lbl" onClick="unshowDiveDetails()">Back to List</button> <button onClick="nextDetailedDive()">-></button> </div> </center> @@ -194,7 +206,7 @@ function changeAdvSearch(e){ <h2 id="dive_no"></h2> <h3 id="dive_location"></h3> </center> - <h2 class="det_hed">Dive profile</h2> + <h2 id="div_profil_lbl" class="det_hed">Dive profile</h2> <div style="width:100%" id="chart1"></div> </div> <div id="diveinfo"> @@ -221,7 +233,7 @@ function changeAdvSearch(e){ </div> <div id="diveStat"> <center> - <button onClick="toggleStats()">Back to List</button> + <button id="bk_to_ls_lbl2" onClick="toggleStats()">Back to List</button> </center> <div id="diveStatsData"> </div> diff --git a/theme/light.css b/theme/light.css index a1acbfa65..baa04b62b 100644 --- a/theme/light.css +++ b/theme/light.css @@ -129,7 +129,6 @@ input[type=checkbox]{ font-weight:bold; } - #controller{ min-width:200px; padding:10px; @@ -159,8 +158,14 @@ input[type=checkbox]{ } .statscell{ - min-width:100px; + min-width:120px; + height: 25px; margin:0px; + text-align:center; +} + +.stats_row{ + background-color:rgba(125,125,125,0.3); } #stats_header{ diff --git a/theme/list_lib.js b/theme/list_lib.js index 684d92e13..c66be6414 100644 --- a/theme/list_lib.js +++ b/theme/list_lib.js @@ -126,7 +126,7 @@ function next_page() function view_pagging(start, end) { var page = document.getElementById("pagging"); - page.innerHTML = (start + 1) + ' to ' + (end + 1) + ' of ' + (itemsToShow.length) + ' dives'; + page.innerHTML = (start + 1) + ' to ' + (end + 1) + ' of ' + (itemsToShow.length) + '' + translate.dives; } /** @@ -134,12 +134,14 @@ function view_pagging(start, end) */ function expandAll() { + console.time('expnadAll'); for (var i = start; i < start + sizeofpage; i++) { if (i >= itemsToShow.length) break; unexpand(document.getElementById(itemsToShow[i])); items[itemsToShow[i]].expanded = false; } + console.timeEnd('expnadAll'); } /** @@ -204,21 +206,22 @@ function getlimited(dive) function getExpanded(dive) { - var res = '<table><tr><td class="words">Date: </td><td>' + dive.date + - '</td><td class="words"> Time: </td><td>' + dive.time + - '</td><td class="words"> Location: </td><td>' + '<a onclick=\"Search_list_Modules(\'' + dive.location + '\', {location:true, divemaster:false, buddy:false, notes:false, tags:false,})\">' + dive.location + '</a>' + - '</td></tr></table><table><tr><td class="words">Rating:</td><td>' + putRating(dive.rating) + - '</td><td class="words"> Visibilty:</td><td>' + putRating(dive.visibility) + + var res = '<table><tr><td class="words">' + translate.Date + ': </td><td>' + dive.date + + '</td><td class="words"> ' + translate.Time + ': </td><td>' + dive.time + + '</td><td class="words"> ' + translate.Location + ': </td><td>' + '<a onclick=\"Search_list_Modules(\'' + dive.location + '\', {location:true, divemaster:false, buddy:false, notes:false, tags:false,})\">' + dive.location + '</a>' + getDiveCoor(dive) + + '</td></tr></table><table><tr><td class="words">' + translate.Rating + ':</td><td>' + putRating(dive.rating) + + '</td><td class="words"> ' + translate.Visibility + ':</td><td>' + putRating(dive.visibility) + '</td></tr></table>' + - '<table><tr><td class="words">Air temp: </td><td>' + dive.temperature.air + - '</td><td class="words"> Water temp: </td><td>' + dive.temperature.water + - '</td></tr></table><table><tr><td class="words">DiveMaster: </td><td>' + dive.divemaster + - '</td></tr><tr><td class="words"><p>Buddy: </p></td><td>' + dive.buddy + - '</td></tr><tr><td class="words">Suit: </td><td>' + dive.suit + - '</td></tr><tr><td class="words">Tags: </td><td>' + putTags(dive.tags) + - '</td></tr></table><div style="margin:10px;"><p class="words">Notes: </p>' + dive.notes + '</div>'; + '<table><tr><td class="words">' + translate.Air_Temp + ': </td><td>' + dive.temperature.air + + '</td><td class="words"> ' + translate.Water_Temp + ': </td><td>' + dive.temperature.water + + '</td></tr></table><table><tr><td class="words">' + translate.Duration + ': </td><td>' + dive.dive_duration + + '</td></tr><tr><td class="words">' + translate.DiveMaster + ': </td><td>' + dive.divemaster + + '</td></tr><tr><td class="words"><p>' + translate.Buddy + ': </p></td><td>' + dive.buddy + + '</td></tr><tr><td class="words">' + translate.Suit + ': </td><td>' + dive.suit + + '</td></tr><tr><td class="words">' + translate.Tags + ': </td><td>' + putTags(dive.tags) + + '</td></tr></table><div style="margin:10px;"><p class="words">' + translate.Notes + ': </p>' + dive.notes + '</div>'; if (settings.listOnly === '0') { - res += '<center><a onclick="showDiveDetails(' + dive.number + ')">show more details</a></center>'; + res += '<center><a onclick="showDiveDetails(' + dive.number + ')">' + translate.Show_more_details + '</a></center>'; } return res; }; @@ -271,7 +274,7 @@ var locat = true; This variable keep the state of the col. which is sorted upon it. */ -var sort_based_on = 1; // sorting is based on number by default. +var sort_based_on = '1'; // sorting is based on number by default. function change_sort_col(sortOn) { @@ -371,9 +374,14 @@ function cmpNumAsc(j, iSmaller) return items[j].subsurface_number < items[iSmaller].subsurface_number; } +function cmpNumAsc(j, iSmaller) +{ + return settings.subsurfaceNumbers === '0' ? items[j].number < items[iSmaller].number : items[j].subsurface_number < items[iSmaller].subsurface_number; +} + function cmpNumDes(j, iSmaller) { - return items[j].subsurface_number > items[iSmaller].subsurface_number; + return settings.subsurfaceNumbers === '0' ? items[j].number > items[iSmaller].number : items[j].subsurface_number > items[iSmaller].subsurface_number; } function cmpTimeAsc(j, iSmaller) @@ -398,22 +406,22 @@ function cmpDateDes(j, iSmaller) function cmpAtempAsc(j, iSmaller) { - return parseInt(items[j].temperature.air, 10) < parseInt(items[iSmaller].temperature.air, 10); + return parseFloat(items[j].temperature.air, 10) < parseFloat(items[iSmaller].temperature.air, 10); } function cmpAtempDes(j, iSmaller) { - return parseInt(items[j].temperature.air, 10) > parseInt(items[iSmaller].temperature.air, 10); + return parseFloat(items[j].temperature.air, 10) > parseFloat(items[iSmaller].temperature.air, 10); } function cmpWtempAsc(j, iSmaller) { - return parseInt(items[j].temperature.water, 10) < parseInt(items[iSmaller].temperature.water, 10); + return parseFloat(items[j].temperature.water, 10) < parseFloat(items[iSmaller].temperature.water, 10); } function cmpWtempDes(j, iSmaller) { - return parseInt(items[j].temperature.water, 10) > parseInt(items[iSmaller].temperature.water, 10); + return parseFloat(items[j].temperature.water, 10) > parseFloat(items[iSmaller].temperature.water, 10); } function sort_it(sortOn, function_) @@ -491,7 +499,7 @@ Set.prototype.intersect = function(another_set) } var result = new Array(); for (var i = 0; i < another_set.keys.length; i++) { - if(this.contains(another_set.keys[i])) { + if (this.contains(another_set.keys[i])) { result.push(another_set.keys[i]); } }; @@ -658,13 +666,11 @@ function toggleStats() var stats_button = document.getElementById('stats_button'); if (statsShows) { statsShows = false; - stats_button.style.backgroundColor = "#dfdfdf"; - document.getElementById('diveListPanel').style.display='block'; - document.getElementById('diveStat').style.display='none'; + document.getElementById('diveListPanel').style.display = 'block'; + document.getElementById('diveStat').style.display = 'none'; } else { - document.getElementById('diveListPanel').style.display='none'; - document.getElementById('diveStat').style.display='block'; - stats_button.style.backgroundColor = "#5f7f8f"; + document.getElementById('diveListPanel').style.display = 'none'; + document.getElementById('diveStat').style.display = 'block'; statsShows = true; showStats(); } @@ -676,28 +682,48 @@ function showStats() document.getElementById('diveStatsData').innerHTML += getDiveStats(); } -function getDiveStats(){ +function getDiveStats() +{ var res = ""; res += '<table><tr id="stats_header">'; - res += '<td class="statscell">Year</td><td class="statscell">#</td><td class="statscell">Total Time</td><td class="statscell">Avarage Time</td><td class="statscell">Shortest Time</td><td class="statscell">Longest Time</td><td class="statscell">Avarage Depth</td><td class="statscell">Min Depth</td><td class="statscell">Max Depth</td><td class="statscell">Average SAC</td><td class="statscell">Min SAC</td><td class="statscell">Max SAC</td><td class="statscell">Average Temp</td><td class="statscell">Min Temp</td><td class="statscell">Max Temp</td>'; + res += '<td class="statscell">' + translate.Year + '</td><td class="statscell">#</td><td class="statscell">' + translate.Total_Time + '</td><td class="statscell">' + translate.Average_Time + '</td><td class="statscell">' + translate.Shortest_Time + '</td><td class="statscell">' + translate.Longest_Time + '</td><td class="statscell">' + translate.Average_Depth + '</td><td class="statscell">' + translate.Min_Depth + '</td><td class="statscell">' + translate.Max_Depth + '</td><td class="statscell">' + translate.Average_SAC + '</td><td class="statscell">' + translate.Min_SAC + '</td><td class="statscell">' + translate.Max_SAC + '</td><td class="statscell">' + translate.Average_Temp + '</td><td class="statscell">' + translate.Min_Temp + '</td><td class="statscell">' + translate.Max_Temp + '</td>'; res += '</tr>'; res += getStatsRows(); res += '</table>'; return res; } -function getStatsRows(){ +function getStatsRows() +{ var res = ""; - for(var i = 0; i < divestat.length ; i++) { - res += '<tr><td class="statscell">'+divestat[i].YEAR+'</td><td class="statscell">'+divestat[i].DIVES+'</td><td class="statscell">'+divestat[i].TOTAL_TIME+'</td><td class="statscell">'+divestat[i].AVERAGE_TIME+'</td><td class="statscell">'+divestat[i].SHORTEST_TIME+'</td><td class="statscell">'+divestat[i].LONGEST_TIME+'</td><td class="statscell">'+divestat[i].AVG_DEPTH+'</td><td class="statscell">'+divestat[i].MIN_DEPTH+'</td><td class="statscell">'+divestat[i].MAX_DEPTH+'</td><td class="statscell">'+divestat[i].AVG_SAC+'</td><td class="statscell">'+divestat[i].MIN_SAC+'</td><td class="statscell">'+divestat[i].MAX_SAC+'</td><td class="statscell">'+divestat[i].AVG_TEMP+'</td><td class="statscell">'+divestat[i].MIN_TEMP+'</td><td class="statscell">'+divestat[i].MAX_TEMP+'</td></tr>'; + for (var i = 0; i < divestat.length; i++) { + res += '<tr onmouseout="stats_row_unhighlight(this)" onmouseover="stats_row_highlight(this)" class="stats_row"><td class="statscell">' + divestat[i].YEAR + '</td><td class="statscell">' + divestat[i].DIVES + '</td><td class="statscell">' + divestat[i].TOTAL_TIME + '</td><td class="statscell">' + divestat[i].AVERAGE_TIME + '</td><td class="statscell">' + divestat[i].SHORTEST_TIME + '</td><td class="statscell">' + divestat[i].LONGEST_TIME + '</td><td class="statscell">' + divestat[i].AVG_DEPTH + '</td><td class="statscell">' + divestat[i].MIN_DEPTH + '</td><td class="statscell">' + divestat[i].MAX_DEPTH + '</td><td class="statscell">' + divestat[i].AVG_SAC + '</td><td class="statscell">' + divestat[i].MIN_SAC + '</td><td class="statscell">' + divestat[i].MAX_SAC + '</td><td class="statscell">' + divestat[i].AVG_TEMP + '</td><td class="statscell">' + divestat[i].MIN_TEMP + '</td><td class="statscell">' + divestat[i].MAX_TEMP + '</td></tr>'; } return res; } +function stats_row_highlight(row) +{ + row.style.backgroundColor = "rgba(125,125,125,0.7)"; +} + +function stats_row_unhighlight(row) +{ + row.style.backgroundColor = "rgba(125,125,125,0.3)"; +} + //trips var tripsShown; +var HEADER_COLOR; +var BACKGROUND_COLOR; +function getDefaultColor(){ + var header = document.getElementById('header'); + var button = document.getElementById('no_dives_selector'); + HEADER_COLOR = document.defaultView.getComputedStyle(header).getPropertyValue('background-color'); + BACKGROUND_COLOR = document.defaultView.getComputedStyle(button).getPropertyValue('background-color'); +} /** *This is the main function called to show/hide trips @@ -707,11 +733,11 @@ function toggleTrips() var trip_button = document.getElementById('trip_button'); if (tripsShown) { tripsShown = false; - trip_button.style.backgroundColor = "#dfdfdf"; + trip_button.style.backgroundColor = BACKGROUND_COLOR; viewInPage(); } else { showtrips(); - trip_button.style.backgroundColor = "#5f7f8f"; + trip_button.style.backgroundColor = HEADER_COLOR; tripsShown = true; } } @@ -796,8 +822,11 @@ function get_weight_HTML(weight) */ function get_weights_HTML(dive) { + if (!dive.Weights.length) + return ""; + var result = ""; - result += '<table><tr><td class="words">Weight</td><td class="words">Type</td></tr>'; + result += '<table><tr><td class="words">' + translate.Weight + '</td><td class="words">' + translate.Type + '</td></tr>'; for (var i in dive.Weights) { result += get_weight_HTML(dive.Weights[i]); } @@ -814,12 +843,12 @@ function get_cylinder_HTML(cylinder) var cEPressure = cylinder.EPressure; if (cSPressure === "--") { - cSPressure = mbar_to_bar(items[dive_id].samples[0][2]) + " bar"; + cSPressure = Math.round(mbar_to_bar(items[dive_id].samples[0][2])).toFixed(1) + " bar"; } if (cEPressure === "--") { var nonZeroCEPressure = lastNonZero(); - cEPressure = mbar_to_bar(nonZeroCEPressure) + " bar"; + cEPressure = Math.round(mbar_to_bar(nonZeroCEPressure)).toFixed(1) + " bar"; } return '<tr><td class="Cyl">' + cylinder.Type + '</td><td class="Cyl">' + cylinder.Size + '</td><td class="Cyl">' + cylinder.WPressure + '</td>' + '<td class="Cyl">' + cSPressure + '</td><td class="Cyl">' + cEPressure + '</td><td class="Cyl">' + cylinder.O2 + '</td></tr>'; @@ -830,8 +859,11 @@ function get_cylinder_HTML(cylinder) */ function get_cylinders_HTML(dive) { + if (!dive.Cylinders.length) + return ""; + var result = ""; - result += '<h2 class="det_hed">Dive equipments</h2><table><tr><td class="words">Type</td><td class="words">Size</td><td class="words">Work Pressure</td><td class="words">Start Pressure</td><td class="words">End Pressure</td><td class="words">O2</td></tr>'; + result += '<h2 class="det_hed">' + translate.Dive_equipments + '</h2><table><tr><td class="words">' + translate.Type + '</td><td class="words">' + translate.Size + '</td><td class="words">' + translate.Work_Pressure + '</td><td class="words">' + translate.Start_Pressure + '</td><td class="words">' + translate.End_Pressure + '</td><td class="words">'+translate.Gas+'</td></tr>'; for (var i in dive.Cylinders) { result += get_cylinder_HTML(dive.Cylinders[i]); } @@ -868,7 +900,7 @@ function get_bookmarks_HTML(dive) if (dive.events <= 0) return ""; var result = ""; - result += '<h2 class="det_hed">Events</h2><table><tr><td class="words">Name</td><td class="words">Time</td><td class="words">Value</td></tr>'; + result += '<h2 class="det_hed">' + translate.Events + '</h2><table><tr><td class="words">' + translate.Name + '</td><td class="words">' + translate.Time + '</td><td class="words">' + translate.Value + '</td></tr>'; for (var i in dive.events) { result += get_bookmark_HTML(dive.events[i]); } @@ -876,25 +908,41 @@ function get_bookmarks_HTML(dive) return result; } +function getDiveCoorString(coordinates){ + res = ""; + lat = coordinates.lat; + lon = coordinates.lon; + res += float_to_deg(lat) + ' , ' + float_to_deg(lon); + return res; +} + +function getDiveCoor(dive) +{ + if (!dive.coordinates) + return ""; + return '<td class="words"> ' + translate.Coordinates + ': </td><td>' + '<a href="http://www.google.com/maps/@' + dive.coordinates.lat + ',' + dive.coordinates.lon + ',13z" target="_blank">' + getDiveCoorString(dive.coordinates) + '</a></td>'; +} + /** *Return HTML main data of a dive */ function get_dive_HTML(dive) { - return '<h2 class="det_hed">Dive Information</h2><table><tr><td class="words">Date: </td><td>' + dive.date + - '</td><td class="words"> Time: </td><td>' + dive.time + - '</td><td class="words"> Location: </td><td>' + '<a onclick=\"Search_list_Modules(\'' + dive.location + '\', {location:true, divemaster:false, buddy:false, notes:false, tags:false,})\">' + - dive.location + '</a>' + - '</td></tr></table><table><tr><td class="words">Rating:</td><td>' + putRating(dive.rating) + - '</td><td class="words"> Visibilty:</td><td>' + putRating(dive.visibility) + - '</td></tr></table>' + - '<table><tr><td class="words">Air temp: </td><td>' + dive.temperature.air + - '</td><td class="words"> Water temp: </td><td>' + dive.temperature.water + - '</td></tr></table><table><tr><td class="words">DiveMaster: </td><td>' + dive.divemaster + - '</td></tr><tr><td class="words"><p>Buddy: </p></td><td>' + dive.buddy + - '</td></tr><tr><td class="words">Suit: </td><td>' + dive.suit + - '</td></tr><tr><td class="words">Tags: </td><td>' + putTags(dive.tags) + - '</td></tr></table><div style="margin:10px;"><p class="words">Notes: </p>' + dive.notes + '</div>'; + var res = '<h2 class="det_hed">' + translate.Dive_information + '</h2><table><tr><td class="words">' + translate.Date + ': </td><td>' + dive.date + + '</td><td class="words"> ' + translate.Time + ': </td><td>' + dive.time + + '</td><td class="words"> ' + translate.Location + ': </td><td>' + '<a onclick=\"Search_list_Modules(\'' + dive.location + '\', {location:true, divemaster:false, buddy:false, notes:false, tags:false,})\">' + dive.location + '</a></td>' + getDiveCoor(dive) + + '</tr></table><table><tr><td class="words">' + translate.Rating + ':</td><td>' + putRating(dive.rating) + + '</td><td class="words"> ' + translate.Visibility + ':</td><td>' + putRating(dive.visibility) + + '</td></tr></table>' + + '<table><tr><td class="words">' + translate.Air_Temp + ': </td><td>' + dive.temperature.air + + '</td><td class="words"> ' + translate.Water_Temp + ': </td><td>' + dive.temperature.water + + '</td></tr></table><table><tr><td class="words">' + translate.Duration + ': </td><td>' + dive.dive_duration + + '</td></tr><tr><td class="words">' + translate.DiveMaster + ': </td><td>' + dive.divemaster + + '</td></tr><tr><td class="words"><p>' + translate.Buddy + ': </p></td><td>' + dive.buddy + + '</td></tr><tr><td class="words">' + translate.Suit + ': </td><td>' + dive.suit + + '</td></tr><tr><td class="words">' + translate.Tags + ': </td><td>' + putTags(dive.tags) + + '</td></tr></table><div style="margin:10px;"><p class="words">' + translate.Notes + ': </p>' + dive.notes + '</div>'; + return res; }; /** @@ -902,10 +950,9 @@ function get_dive_HTML(dive) */ function get_status_HTML(dive) { - return '<h2 class="det_hed">Dive Status</h2><table><tr><td class="words">Sac: </td><td>' + ml_to_litre(dive.sac) + - ' l/min' + '</td><td class="words"> Otu: </td><td>' + dive.otu + - '</td><td class="words"> Cns: </td><td>' + dive.cns + - '</td></tr></table>'; + return '<h2 class="det_hed">' + translate.Dive_Status + '</h2><table><tr><td class="words">SAC: </td><td>' + ml_to_litre(dive.sac) + + ' l/min' + '</td><td class="words"> OTU: </td><td>' + dive.otu + + '</td><td class="words"> CNS: </td><td>' + dive.cns + '</td></tr></table>'; }; function get_dive_photos(dive) @@ -917,8 +964,8 @@ function get_dive_photos(dive) var slider = ""; document.getElementById("divephotos").style.display = 'block'; for (var i = 0; i < dive.photos.length; i++) { - slider += '<img src="'+location.pathname + - '_files/photos/'+dive.photos[i].filename+'" alt="" height="240" width="240">'; + slider += '<img src="' + location.pathname + + '_files/photos/' + dive.photos[i].filename + '" alt="" height="240" width="240">'; } return slider; } @@ -962,7 +1009,7 @@ function mm_to_meter(mm) function gram_to_km(gram) { - return gram / 1000; + return (gram / 1000).toFixed(1); } function ml_to_litre(ml) @@ -980,6 +1027,14 @@ function int_to_time(n) return Math.floor((n) / 60) + ":" + format_two_digit((n) % (60)) + " min"; } +function float_to_deg(flt){ + var deg = 0 | flt; + flt = (flt < 0 ? flt =- flt : flt); + var min = 0 | flt % 1 * 60; + var sec = (0 | flt * 60 % 1 * 6000) / 100; + return deg + "° " + min + "' " + sec + "\""; +} + /** *Main canvas draw function *this calls the axis and grid initialization functions. @@ -1024,8 +1079,7 @@ function canvas_draw() 0 ]); } - if (plot1) - { + if (plot1) { $('chart1').unbind(); plot1.destroy(); } @@ -1047,7 +1101,7 @@ function canvas_draw() if(seriesIndex===2) return items[dive_id].events[pointIndex].name; else - return str; + return str.replace(",", " : "); } }, seriesDefaults : { @@ -1090,7 +1144,7 @@ function canvas_draw() tickRenderer : $.jqplot.CanvasAxisTickRenderer, tickOptions : { showGridline : false, - formatString : '%i' + formatString : '%imin' }, label:'Time (min)' }, @@ -1152,11 +1206,12 @@ function showDiveDetails(dive) document.getElementById("diveListPanel").style.display = 'none'; document.getElementById("divePanel").style.display = 'block'; canvas_draw(); + scrollToTheTop(); } function setDiveTitle(dive) { - document.getElementById("dive_no").innerHTML = "Dive No. " + (settings.subsurfaceNumbers === '0' ? + document.getElementById("dive_no").innerHTML = translate.Dive_No + (settings.subsurfaceNumbers === '0' ? dive.number + 1 : dive.subsurface_number); document.getElementById("dive_location").innerHTML = dive.location; } @@ -1167,7 +1222,6 @@ function setDiveTitle(dive) */ function unshowDiveDetails(dive) { - start = dive_id; viewInPage(); plot1 = null; document.getElementById("diveListPanel").style.display = 'block'; @@ -1204,7 +1258,33 @@ function switchDives(e) } } -window.onresize = function(event) { - if (plot1) - plot1.replot( { resetAxes: true } ); +function scrollToTheTop() +{ + window.scrollTo(0, 0); +} + +window.onresize = function(event) +{ + if (plot1) + plot1.replot({ + resetAxes : false + }); }; + +function translate_page() +{ + document.getElementById("number_header").innerHTML = translate.Number; + document.getElementById("date_header").innerHTML = translate.Date; + document.getElementById("time_header").innerHTML = translate.Time; + document.getElementById("location_header").innerHTML = translate.Location; + document.getElementById("air_temp_header").innerHTML = translate.Air_Temp; + document.getElementById("water_temp_header").innerHTML = translate.Water_Temp; + document.getElementById("adv_srch_sp").innerHTML = translate.Advanced_Search; + document.getElementById("expnd_all_btn").innerHTML = translate.Expand_All; + document.getElementById("claps_all_btn").innerHTML = translate.Collapse_All; + document.getElementById("trip_button").innerHTML = translate.trips; + document.getElementById("stats_button").innerHTML = translate.Statistics; + document.getElementById("div_profil_lbl").innerHTML = translate.Dive_profile; + document.getElementById("bk_to_ls_lbl").innerHTML = translate.Back_to_List; + document.getElementById("bk_to_ls_lbl2").innerHTML = translate.Back_to_List; +} diff --git a/theme/sand.css b/theme/sand.css index befa861b7..b2f35bbb1 100644 --- a/theme/sand.css +++ b/theme/sand.css @@ -147,7 +147,7 @@ input[type=checkbox]{ width:90%; margin:0% 5% 0% 5%; margin-bottom:50px; - background-color: rgba(88,121,139,0.3); + background-color: rgba(253, 195, 141,0.3); box-shadow: 10px 10px 5px #888888; } @@ -156,9 +156,18 @@ input[type=checkbox]{ overflow-y:hidden; } .statscell{ - border-style:solid; - padding-right:100px; + min-width:120px; + height: 25px; margin:0px; + text-align:center; +} + +.stats_row{ + background-color:rgba(125,125,125,0.3); +} + +#stats_header{ + background-color:#EFC15F; } button,#no_dives_selector{ diff --git a/uemis-downloader.c b/uemis-downloader.c index 5d8b07c01..608d3c6ea 100644 --- a/uemis-downloader.c +++ b/uemis-downloader.c @@ -340,6 +340,11 @@ static bool next_file(int max) return true; } +/* try and do a quick decode - without trying to get to fancy in case the data + * straddles a block boundary... + * we are parsing something that looks like this: + * object_id{int{2{date{ts{2011-04-05T12:38:04{duration{float{12.000... + */ static char *first_object_id_val(char *buf) { char *object, *bufend; @@ -349,7 +354,7 @@ static char *first_object_id_val(char *buf) object = strstr(buf, "object_id"); if (object && object + 14 < bufend) { /* get the value */ - char tmp[10]; + char tmp[36]; char *p = object + 14; char *t = tmp; @@ -362,6 +367,18 @@ static char *first_object_id_val(char *buf) while (p < bufend && *p != '{' && t < tmp + 9) *t++ = *p++; if (*p == '{') { + /* found the object_id - let's quickly look for the date */ + if (strncmp(p, "{date{ts{", 9) == 0 && strstr(p, "{duration{") != NULL) { + /* cool - that was easy */ + *t++ = ','; + *t++ = ' '; + /* skip the 9 characters we just found and take the date, ignoring the seconds + * and replace the silly 'T' in the middle with a space */ + strncpy(t, p + 9, 16); + if (*(t + 10) == 'T') + *(t + 10) = ' '; + t += 16; + } *t = '\0'; return strdup(tmp); } @@ -378,9 +395,9 @@ static void show_progress(char *buf, const char *what) if (val) { /* let the user know what we are working on */ #if UEMIS_DEBUG & 2 - fprintf(debugfile, "reading %s %s\n", what, val); + fprintf(stderr, "reading %s %s %s\n", what, val, buf); #endif - uemis_info(translate("gettextFromC", "Reading %s %s"), what, val); + uemis_info(translate("gettextFromC", "%s %s"), what, val); free(val); } } @@ -421,11 +438,11 @@ static bool uemis_get_answer(const char *path, char *request, int n_param_in, answer_in_mbuf = true; str_append_with_delim(sb, ""); if (!strcmp(request, "getDivelogs")) - what = translate("gettextFromC", "divelog entry id"); + what = translate("gettextFromC", "divelog #"); else if (!strcmp(request, "getDivespot")) - what = translate("gettextFromC", "divespot data id"); + what = translate("gettextFromC", "divespot #"); else if (!strcmp(request, "getDive")) - what = translate("gettextFromC", "more data dive id"); + what = translate("gettextFromC", "details for #"); } str_append_with_delim(sb, ""); file_length = strlen(sb); diff --git a/xslt/divelogs-export.xslt b/xslt/divelogs-export.xslt index 3de8c60b9..ec39ba243 100644 --- a/xslt/divelogs-export.xslt +++ b/xslt/divelogs-export.xslt @@ -69,8 +69,22 @@ <CYLINDERDESCRIPTION> <xsl:value-of select="cylinder[position() = $cylinder]/@description"/> </CYLINDERDESCRIPTION> + + <xsl:variable name="double"> + <xsl:choose> + <xsl:when test="substring(cylinder[position() = $cylinder]/@description, 1, 1) = 'D' and substring-before(substring(cylinder[position() = $cylinder]/@description, 2), ' ') * 2 = substring-before(cylinder[position() = $cylinder]/@size, ' ')"> + <xsl:value-of select="'2'"/> + </xsl:when> + <xsl:otherwise> + <xsl:value-of select="'1'"/> + </xsl:otherwise> + </xsl:choose> + </xsl:variable> + <DBLTANK> + <xsl:value-of select="$double - 1"/> + </DBLTANK> <CYLINDERSIZE> - <xsl:value-of select="substring-before(cylinder[position() = $cylinder]/@size, ' ')"/> + <xsl:value-of select="substring-before(cylinder[position() = $cylinder]/@size, ' ') div $double"/> </CYLINDERSIZE> <CYLINDERSTARTPRESSURE> <xsl:choose> |