From ebfefbc736622a3001f95a1cce28d63f97edafa5 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Fri, 15 Jul 2022 17:13:16 +0200 Subject: [PATCH] core: move formatting of plot info to string-format.cpp Arguably, string handling in C is painful. Move the function that formats the "tooltip" of the current spot on the profile to the C++ string-format.cpp. Note: the actual code stays C with a few necessary adaptions concerning the translation machinery. Signed-off-by: Berthold Stoeger --- core/membuffer.h | 2 +- core/profile.c | 173 ---------------------------- core/profile.h | 2 +- core/string-format.cpp | 178 +++++++++++++++++++++++++++++ core/string-format.h | 3 + profile-widget/divetooltipitem.cpp | 10 +- 6 files changed, 187 insertions(+), 181 deletions(-) diff --git a/core/membuffer.h b/core/membuffer.h index 821b0cecb..fcb4ec5ee 100644 --- a/core/membuffer.h +++ b/core/membuffer.h @@ -48,7 +48,7 @@ struct membuffer { #ifdef __cplusplus -// In C++ code use this - it automatically frees the buffer, when going out of scope. +// In C++ code use this - it automatically frees the buffer when going out of scope. struct membufferpp : public membuffer { membufferpp(); ~membufferpp(); diff --git a/core/profile.c b/core/profile.c index 9b004b2df..2b76e72d6 100644 --- a/core/profile.c +++ b/core/profile.c @@ -26,8 +26,6 @@ //#define DEBUG_GAS 1 -#define MAX_PROFILE_DECO 7200 - extern int ascent_velocity(int depth, int avg_depth, int bottom_time); struct dive *current_dive = NULL; @@ -59,7 +57,6 @@ static void dump_pi(struct plot_info *pi) #endif #define ROUND_UP(x, y) ((((x) + (y) - 1) / (y)) * (y)) -#define DIV_UP(x, y) (((x) + (y) - 1) / (y)) /* * When showing dive profiles, we scale things to the @@ -1328,176 +1325,6 @@ void create_plot_info_new(const struct dive *dive, const struct divecomputer *dc analyze_plot_info(pi); } -static void plot_string(const struct dive *d, const struct plot_info *pi, int idx, struct membuffer *b) -{ - int pressurevalue, mod, ead, end, eadd; - const char *depth_unit, *pressure_unit, *temp_unit, *vertical_speed_unit; - double depthvalue, tempvalue, speedvalue, sacvalue; - int decimals, cyl; - const char *unit; - const struct plot_data *entry = pi->entry + idx; - - depthvalue = get_depth_units(entry->depth, NULL, &depth_unit); - put_format_loc(b, translate("gettextFromC", "@: %d:%02d\nD: %.1f%s\n"), FRACTION(entry->sec, 60), depthvalue, depth_unit); - for (cyl = 0; cyl < pi->nr_cylinders; cyl++) { - int mbar = get_plot_pressure(pi, idx, cyl); - if (!mbar) - continue; - struct gasmix mix = get_cylinder(d, cyl)->gasmix; - pressurevalue = get_pressure_units(mbar, &pressure_unit); - put_format_loc(b, translate("gettextFromC", "P: %d%s (%s)\n"), pressurevalue, pressure_unit, gasname(mix)); - } - if (entry->temperature) { - tempvalue = get_temp_units(entry->temperature, &temp_unit); - put_format_loc(b, translate("gettextFromC", "T: %.1f%s\n"), tempvalue, temp_unit); - } - speedvalue = get_vertical_speed_units(abs(entry->speed), NULL, &vertical_speed_unit); - /* Ascending speeds are positive, descending are negative */ - if (entry->speed > 0) - speedvalue *= -1; - put_format_loc(b, translate("gettextFromC", "V: %.1f%s\n"), speedvalue, vertical_speed_unit); - sacvalue = get_volume_units(entry->sac, &decimals, &unit); - if (entry->sac && prefs.show_sac) - put_format_loc(b, translate("gettextFromC", "SAC: %.*f%s/min\n"), decimals, sacvalue, unit); - if (entry->cns) - put_format_loc(b, translate("gettextFromC", "CNS: %u%%\n"), entry->cns); - if (prefs.pp_graphs.po2 && entry->pressures.o2 > 0) { - put_format_loc(b, translate("gettextFromC", "pO₂: %.2fbar\n"), entry->pressures.o2); - if (entry->scr_OC_pO2.mbar) - put_format_loc(b, translate("gettextFromC", "SCR ΔpO₂: %.2fbar\n"), entry->scr_OC_pO2.mbar/1000.0 - entry->pressures.o2); - } - if (prefs.pp_graphs.pn2 && entry->pressures.n2 > 0) - put_format_loc(b, translate("gettextFromC", "pN₂: %.2fbar\n"), entry->pressures.n2); - if (prefs.pp_graphs.phe && entry->pressures.he > 0) - put_format_loc(b, translate("gettextFromC", "pHe: %.2fbar\n"), entry->pressures.he); - if (prefs.mod && entry->mod > 0) { - mod = lrint(get_depth_units(entry->mod, NULL, &depth_unit)); - put_format_loc(b, translate("gettextFromC", "MOD: %d%s\n"), mod, depth_unit); - } - eadd = lrint(get_depth_units(entry->eadd, NULL, &depth_unit)); - - if (prefs.ead) { - switch (pi->dive_type) { - case NITROX: - if (entry->ead > 0) { - ead = lrint(get_depth_units(entry->ead, NULL, &depth_unit)); - put_format_loc(b, translate("gettextFromC", "EAD: %d%s\nEADD: %d%s / %.1fg/ℓ\n"), ead, depth_unit, eadd, depth_unit, entry->density); - break; - } - case TRIMIX: - if (entry->end > 0) { - end = lrint(get_depth_units(entry->end, NULL, &depth_unit)); - put_format_loc(b, translate("gettextFromC", "END: %d%s\nEADD: %d%s / %.1fg/ℓ\n"), end, depth_unit, eadd, depth_unit, entry->density); - break; - } - case AIR: - if (entry->density > 0) { - put_format_loc(b, translate("gettextFromC", "Density: %.1fg/ℓ\n"), entry->density); - } - case FREEDIVING: - /* nothing */ - break; - } - } - if (entry->stopdepth) { - depthvalue = get_depth_units(entry->stopdepth, NULL, &depth_unit); - if (entry->ndl > 0) { - /* this is a safety stop as we still have ndl */ - if (entry->stoptime) - put_format_loc(b, translate("gettextFromC", "Safety stop: %umin @ %.0f%s\n"), DIV_UP(entry->stoptime, 60), - depthvalue, depth_unit); - else - put_format_loc(b, translate("gettextFromC", "Safety stop: unknown time @ %.0f%s\n"), - depthvalue, depth_unit); - } else { - /* actual deco stop */ - if (entry->stoptime) - put_format_loc(b, translate("gettextFromC", "Deco: %umin @ %.0f%s\n"), DIV_UP(entry->stoptime, 60), - depthvalue, depth_unit); - else - put_format_loc(b, translate("gettextFromC", "Deco: unknown time @ %.0f%s\n"), - depthvalue, depth_unit); - } - } else if (entry->in_deco) { - put_string(b, translate("gettextFromC", "In deco\n")); - } else if (entry->ndl >= 0) { - put_format_loc(b, translate("gettextFromC", "NDL: %umin\n"), DIV_UP(entry->ndl, 60)); - } - if (entry->tts) - put_format_loc(b, translate("gettextFromC", "TTS: %umin\n"), DIV_UP(entry->tts, 60)); - if (entry->stopdepth_calc && entry->stoptime_calc) { - depthvalue = get_depth_units(entry->stopdepth_calc, NULL, &depth_unit); - put_format_loc(b, translate("gettextFromC", "Deco: %umin @ %.0f%s (calc)\n"), DIV_UP(entry->stoptime_calc, 60), - depthvalue, depth_unit); - } else if (entry->in_deco_calc) { - /* This means that we have no NDL left, - * and we have no deco stop, - * so if we just accend to the surface slowly - * (ascent_mm_per_step / ascent_s_per_step) - * everything will be ok. */ - put_string(b, translate("gettextFromC", "In deco (calc)\n")); - } else if (prefs.calcndltts && entry->ndl_calc != 0) { - if(entry->ndl_calc < MAX_PROFILE_DECO) - put_format_loc(b, translate("gettextFromC", "NDL: %umin (calc)\n"), DIV_UP(entry->ndl_calc, 60)); - else - put_string(b, translate("gettextFromC", "NDL: >2h (calc)\n")); - } - if (entry->tts_calc) { - if (entry->tts_calc < MAX_PROFILE_DECO) - put_format_loc(b, translate("gettextFromC", "TTS: %umin (calc)\n"), DIV_UP(entry->tts_calc, 60)); - else - put_string(b, translate("gettextFromC", "TTS: >2h (calc)\n")); - } - if (entry->rbt) - put_format_loc(b, translate("gettextFromC", "RBT: %umin\n"), DIV_UP(entry->rbt, 60)); - if (prefs.decoinfo) { - if (entry->current_gf > 0.0) - put_format(b, translate("gettextFromC", "GF %d%%\n"), (int)(100.0 * entry->current_gf)); - if (entry->surface_gf > 0.0) - put_format(b, translate("gettextFromC", "Surface GF %.0f%%\n"), entry->surface_gf); - if (entry->ceiling) { - depthvalue = get_depth_units(entry->ceiling, NULL, &depth_unit); - put_format_loc(b, translate("gettextFromC", "Calculated ceiling %.1f%s\n"), depthvalue, depth_unit); - if (prefs.calcalltissues) { - int k; - for (k = 0; k < 16; k++) { - if (entry->ceilings[k]) { - depthvalue = get_depth_units(entry->ceilings[k], NULL, &depth_unit); - put_format_loc(b, translate("gettextFromC", "Tissue %.0fmin: %.1f%s\n"), buehlmann_N2_t_halflife[k], depthvalue, depth_unit); - } - } - } - } - } - if (entry->icd_warning) - put_format(b, "%s", translate("gettextFromC", "ICD in leading tissue\n")); - if (entry->heartbeat && prefs.hrgraph) - put_format_loc(b, translate("gettextFromC", "heart rate: %d\n"), entry->heartbeat); - if (entry->bearing >= 0) - put_format_loc(b, translate("gettextFromC", "bearing: %d\n"), entry->bearing); - if (entry->running_sum) { - depthvalue = get_depth_units(entry->running_sum / entry->sec, NULL, &depth_unit); - put_format_loc(b, translate("gettextFromC", "mean depth to here %.1f%s\n"), depthvalue, depth_unit); - } - - strip_mb(b); -} - -int get_plot_details_new(const struct dive *d, const struct plot_info *pi, int time, struct membuffer *mb) -{ - int i; - - /* The two first and the two last plot entries do not have useful data */ - if (pi->nr <= 4) - return 0; - for (i = 2; i < pi->nr - 3; i++) { - if (pi->entry[i].sec >= time) - break; - } - plot_string(d, pi, i, mb); - return i; -} - /* Compare two plot_data entries and writes the results into a string */ void compare_samples(const struct dive *d, const struct plot_info *pi, int idx1, int idx2, char *buf, int bufsize, bool sum) { diff --git a/core/profile.h b/core/profile.h index 402b893cf..e9c8f7329 100644 --- a/core/profile.h +++ b/core/profile.h @@ -95,12 +95,12 @@ struct plot_info { }; #define AMB_PERCENTAGE 50.0 +#define MAX_PROFILE_DECO 7200 extern void compare_samples(const struct dive *d, const struct plot_info *pi, int idx1, int idx2, char *buf, int bufsize, bool sum); extern void init_plot_info(struct plot_info *pi); /* when planner_dc is non-null, this is called in planner mode. */ extern void create_plot_info_new(const struct dive *dive, const struct divecomputer *dc, struct plot_info *pi, const struct deco_state *planner_ds); -extern int get_plot_details_new(const struct dive *d, const struct plot_info *pi, int time, struct membuffer *); extern void free_plot_info_data(struct plot_info *pi); /* diff --git a/core/string-format.cpp b/core/string-format.cpp index 0526d3653..715c7c972 100644 --- a/core/string-format.cpp +++ b/core/string-format.cpp @@ -1,6 +1,9 @@ #include "string-format.h" +#include "deco.h" #include "dive.h" #include "divesite.h" +#include "membuffer.h" +#include "profile.h" #include "qthelper.h" #include "subsurface-string.h" #include "trip.h" @@ -300,3 +303,178 @@ QString formatTripTitleWithDives(const dive_trip *trip) return formatTripTitle(trip) + " " + gettextFromC::tr("(%n dive(s))", "", nr); } + +#define DIV_UP(x, y) (((x) + (y) - 1) / (y)) +#define translate(x,y) qPrintable(QCoreApplication::translate(x,y)) + +static QString formatPlotInfoInternal(const dive *d, const plot_info *pi, int idx) +{ + membufferpp b; + int pressurevalue, mod, ead, end, eadd; + const char *depth_unit, *pressure_unit, *temp_unit, *vertical_speed_unit; + double depthvalue, tempvalue, speedvalue, sacvalue; + int decimals, cyl; + const char *unit; + const struct plot_data *entry = pi->entry + idx; + + depthvalue = get_depth_units(entry->depth, NULL, &depth_unit); + put_format_loc(&b, translate("gettextFromC", "@: %d:%02d\nD: %.1f%s\n"), FRACTION(entry->sec, 60), depthvalue, depth_unit); + for (cyl = 0; cyl < pi->nr_cylinders; cyl++) { + int mbar = get_plot_pressure(pi, idx, cyl); + if (!mbar) + continue; + struct gasmix mix = get_cylinder(d, cyl)->gasmix; + pressurevalue = get_pressure_units(mbar, &pressure_unit); + put_format_loc(&b, translate("gettextFromC", "P: %d%s (%s)\n"), pressurevalue, pressure_unit, gasname(mix)); + } + if (entry->temperature) { + tempvalue = get_temp_units(entry->temperature, &temp_unit); + put_format_loc(&b, translate("gettextFromC", "T: %.1f%s\n"), tempvalue, temp_unit); + } + speedvalue = get_vertical_speed_units(abs(entry->speed), NULL, &vertical_speed_unit); + /* Ascending speeds are positive, descending are negative */ + if (entry->speed > 0) + speedvalue *= -1; + put_format_loc(&b, translate("gettextFromC", "V: %.1f%s\n"), speedvalue, vertical_speed_unit); + sacvalue = get_volume_units(entry->sac, &decimals, &unit); + if (entry->sac && prefs.show_sac) + put_format_loc(&b, translate("gettextFromC", "SAC: %.*f%s/min\n"), decimals, sacvalue, unit); + if (entry->cns) + put_format_loc(&b, translate("gettextFromC", "CNS: %u%%\n"), entry->cns); + if (prefs.pp_graphs.po2 && entry->pressures.o2 > 0) { + put_format_loc(&b, translate("gettextFromC", "pO₂: %.2fbar\n"), entry->pressures.o2); + if (entry->scr_OC_pO2.mbar) + put_format_loc(&b, translate("gettextFromC", "SCR ΔpO₂: %.2fbar\n"), entry->scr_OC_pO2.mbar/1000.0 - entry->pressures.o2); + } + if (prefs.pp_graphs.pn2 && entry->pressures.n2 > 0) + put_format_loc(&b, translate("gettextFromC", "pN₂: %.2fbar\n"), entry->pressures.n2); + if (prefs.pp_graphs.phe && entry->pressures.he > 0) + put_format_loc(&b, translate("gettextFromC", "pHe: %.2fbar\n"), entry->pressures.he); + if (prefs.mod && entry->mod > 0) { + mod = lrint(get_depth_units(entry->mod, NULL, &depth_unit)); + put_format_loc(&b, translate("gettextFromC", "MOD: %d%s\n"), mod, depth_unit); + } + eadd = lrint(get_depth_units(entry->eadd, NULL, &depth_unit)); + + if (prefs.ead) { + switch (pi->dive_type) { + case plot_info::NITROX: + if (entry->ead > 0) { + ead = lrint(get_depth_units(entry->ead, NULL, &depth_unit)); + put_format_loc(&b, translate("gettextFromC", "EAD: %d%s\nEADD: %d%s / %.1fg/ℓ\n"), ead, depth_unit, eadd, depth_unit, entry->density); + break; + } + case plot_info::TRIMIX: + if (entry->end > 0) { + end = lrint(get_depth_units(entry->end, NULL, &depth_unit)); + put_format_loc(&b, translate("gettextFromC", "END: %d%s\nEADD: %d%s / %.1fg/ℓ\n"), end, depth_unit, eadd, depth_unit, entry->density); + break; + } + case plot_info::AIR: + if (entry->density > 0) { + put_format_loc(&b, translate("gettextFromC", "Density: %.1fg/ℓ\n"), entry->density); + } + case plot_info::FREEDIVING: + /* nothing */ + break; + } + } + if (entry->stopdepth) { + depthvalue = get_depth_units(entry->stopdepth, NULL, &depth_unit); + if (entry->ndl > 0) { + /* this is a safety stop as we still have ndl */ + if (entry->stoptime) + put_format_loc(&b, translate("gettextFromC", "Safety stop: %umin @ %.0f%s\n"), DIV_UP(entry->stoptime, 60), + depthvalue, depth_unit); + else + put_format_loc(&b, translate("gettextFromC", "Safety stop: unknown time @ %.0f%s\n"), + depthvalue, depth_unit); + } else { + /* actual deco stop */ + if (entry->stoptime) + put_format_loc(&b, translate("gettextFromC", "Deco: %umin @ %.0f%s\n"), DIV_UP(entry->stoptime, 60), + depthvalue, depth_unit); + else + put_format_loc(&b, translate("gettextFromC", "Deco: unknown time @ %.0f%s\n"), + depthvalue, depth_unit); + } + } else if (entry->in_deco) { + put_string(&b, translate("gettextFromC", "In deco\n")); + } else if (entry->ndl >= 0) { + put_format_loc(&b, translate("gettextFromC", "NDL: %umin\n"), DIV_UP(entry->ndl, 60)); + } + if (entry->tts) + put_format_loc(&b, translate("gettextFromC", "TTS: %umin\n"), DIV_UP(entry->tts, 60)); + if (entry->stopdepth_calc && entry->stoptime_calc) { + depthvalue = get_depth_units(entry->stopdepth_calc, NULL, &depth_unit); + put_format_loc(&b, translate("gettextFromC", "Deco: %umin @ %.0f%s (calc)\n"), DIV_UP(entry->stoptime_calc, 60), + depthvalue, depth_unit); + } else if (entry->in_deco_calc) { + /* This means that we have no NDL left, + * and we have no deco stop, + * so if we just accend to the surface slowly + * (ascent_mm_per_step / ascent_s_per_step) + * everything will be ok. */ + put_string(&b, translate("gettextFromC", "In deco (calc)\n")); + } else if (prefs.calcndltts && entry->ndl_calc != 0) { + if(entry->ndl_calc < MAX_PROFILE_DECO) + put_format_loc(&b, translate("gettextFromC", "NDL: %umin (calc)\n"), DIV_UP(entry->ndl_calc, 60)); + else + put_string(&b, translate("gettextFromC", "NDL: >2h (calc)\n")); + } + if (entry->tts_calc) { + if (entry->tts_calc < MAX_PROFILE_DECO) + put_format_loc(&b, translate("gettextFromC", "TTS: %umin (calc)\n"), DIV_UP(entry->tts_calc, 60)); + else + put_string(&b, translate("gettextFromC", "TTS: >2h (calc)\n")); + } + if (entry->rbt) + put_format_loc(&b, translate("gettextFromC", "RBT: %umin\n"), DIV_UP(entry->rbt, 60)); + if (prefs.decoinfo) { + if (entry->current_gf > 0.0) + put_format(&b, translate("gettextFromC", "GF %d%%\n"), (int)(100.0 * entry->current_gf)); + if (entry->surface_gf > 0.0) + put_format(&b, translate("gettextFromC", "Surface GF %.0f%%\n"), entry->surface_gf); + if (entry->ceiling) { + depthvalue = get_depth_units(entry->ceiling, NULL, &depth_unit); + put_format_loc(&b, translate("gettextFromC", "Calculated ceiling %.1f%s\n"), depthvalue, depth_unit); + if (prefs.calcalltissues) { + int k; + for (k = 0; k < 16; k++) { + if (entry->ceilings[k]) { + depthvalue = get_depth_units(entry->ceilings[k], NULL, &depth_unit); + put_format_loc(&b, translate("gettextFromC", "Tissue %.0fmin: %.1f%s\n"), buehlmann_N2_t_halflife[k], depthvalue, depth_unit); + } + } + } + } + } + if (entry->icd_warning) + put_format(&b, "%s", translate("gettextFromC", "ICD in leading tissue\n")); + if (entry->heartbeat && prefs.hrgraph) + put_format_loc(&b, translate("gettextFromC", "heart rate: %d\n"), entry->heartbeat); + if (entry->bearing >= 0) + put_format_loc(&b, translate("gettextFromC", "bearing: %d\n"), entry->bearing); + if (entry->running_sum) { + depthvalue = get_depth_units(entry->running_sum / entry->sec, NULL, &depth_unit); + put_format_loc(&b, translate("gettextFromC", "mean depth to here %.1f%s\n"), depthvalue, depth_unit); + } + + strip_mb(&b); + return QString(mb_cstring(&b)); +} + +std::pair formatProfileInfo(const struct dive *d, const struct plot_info *pi, int time) +{ + int i; + + /* The two first and the two last plot entries do not have useful data */ + if (pi->nr <= 4) + return std::make_pair(QString(), 0); + for (i = 2; i < pi->nr - 3; i++) { + if (pi->entry[i].sec >= time) + break; + } + QString res = formatPlotInfoInternal(d, pi, i); + return std::make_pair(res, i); +} diff --git a/core/string-format.h b/core/string-format.h index 67221b540..34b46fc89 100644 --- a/core/string-format.h +++ b/core/string-format.h @@ -3,10 +3,12 @@ #ifndef STRING_FORMAT_H #define STRING_FORMAT_H +#include #include struct dive; struct dive_trip; +struct plot_info; QString formatSac(const dive *d); QString formatNotes(const dive *d); @@ -29,5 +31,6 @@ QString formatDiveDateTime(const dive *d); QString formatDayOfWeek(int day); QString formatTripTitle(const dive_trip *trip); QString formatTripTitleWithDives(const dive_trip *trip); +std::pair formatProfileInfo(const dive *d, const plot_info *pi, int time); #endif diff --git a/profile-widget/divetooltipitem.cpp b/profile-widget/divetooltipitem.cpp index de5f7f80f..70afaf057 100644 --- a/profile-widget/divetooltipitem.cpp +++ b/profile-widget/divetooltipitem.cpp @@ -1,12 +1,12 @@ // SPDX-License-Identifier: GPL-2.0 #include "profile-widget/divetooltipitem.h" #include "profile-widget/divecartesianaxis.h" -#include "core/membuffer.h" #include "core/metrics.h" #include "core/settings/qPrefDisplay.h" +#include "core/qthelper.h" +#include "core/string-format.h" #include #include -#include "core/qthelper.h" ToolTipItem::ToolTip ToolTipItem::makeToolTip(const QString &toolTip, const QPixmap &pixmap) { @@ -203,8 +203,6 @@ void ToolTipItem::setTimeAxis(DiveCartesianAxis *axis) void ToolTipItem::refresh(const dive *d, const QPointF &pos, bool inPlanner) { - struct membufferpp mb; - if(refreshTime.elapsed() < 40) return; refreshTime.start(); @@ -214,7 +212,7 @@ void ToolTipItem::refresh(const dive *d, const QPointF &pos, bool inPlanner) lastTime = time; clear(); - int idx = get_plot_details_new(d, &pInfo, time, &mb); + auto [text, idx] = formatProfileInfo(d, &pInfo, time); tissues.fill(); painter.setPen(QColor(0, 0, 0, 0)); @@ -234,7 +232,7 @@ void ToolTipItem::refresh(const dive *d, const QPointF &pos, bool inPlanner) painter.setPen(QColor(0, 0, 0, 127)); for (int i = 0; i < 16; i++) painter.drawLine(i, 60, i, 60 - entry->percentages[i] / 2); - entryToolTip.text->setText(QString::fromUtf8(mb.buffer, mb.len)); + entryToolTip.text->setText(text); } entryToolTip.pixmap->setPixmap(tissues);