diff options
-rw-r--r-- | dive.h | 4 | ||||
-rw-r--r-- | icons/ruler.svg | 91 | ||||
-rw-r--r-- | icons/scale.svg | 71 | ||||
-rw-r--r-- | profile.c | 134 | ||||
-rw-r--r-- | profile.h | 2 | ||||
-rw-r--r-- | qt-ui/mainwindow.cpp | 3 | ||||
-rw-r--r-- | qt-ui/profilegraphics.cpp | 274 | ||||
-rw-r--r-- | qt-ui/profilegraphics.h | 56 | ||||
-rw-r--r-- | subsurface.qrc | 2 |
9 files changed, 626 insertions, 11 deletions
@@ -658,6 +658,10 @@ const char *weekday(int wday); const char *monthname(int mon); #define UTF8_DEGREE "\xc2\xb0" +#define UTF8_DELTA "\xce\x94" +#define UTF8_UPWARDS_ARROW "\xE2\x86\x91" +#define UTF8_DOWNWARDS_ARROW "\xE2\x86\x93" +#define UTF8_AVERAGE "\xc3\xb8" #define UCS4_DEGREE 0xb0 #define UTF8_SUBSCRIPT_2 "\xe2\x82\x82" #define UTF8_WHITESTAR "\xe2\x98\x86" diff --git a/icons/ruler.svg b/icons/ruler.svg new file mode 100644 index 000000000..27e88bb44 --- /dev/null +++ b/icons/ruler.svg @@ -0,0 +1,91 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="256" + height="256" + id="svg2" + version="1.1" + inkscape:version="0.48.4 r9939" + sodipodi:docname="Neues Dokument 1"> + <defs + id="defs4" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="1.979899" + inkscape:cx="82.200797" + inkscape:cy="132.66914" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="false" + inkscape:window-width="1920" + inkscape:window-height="1060" + inkscape:window-x="1917" + inkscape:window-y="-3" + inkscape:window-maximized="1" /> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Ebene 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(0,-796.36218)"> + <path + style="fill:none;stroke:#000000;stroke-width:26;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + d="m 25,855.87869 0,136.96699 206,0 0,-136.96699" + id="path2985" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cccc" /> + <path + sodipodi:type="arc" + style="fill:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:none" + id="path3755" + sodipodi:cx="109.60155" + sodipodi:cy="121.64971" + sodipodi:rx="24.748737" + sodipodi:ry="24.748737" + d="m 134.35028,121.64971 a 24.748737,24.748737 0 1 1 -49.49747,0 24.748737,24.748737 0 1 1 49.49747,0 z" + transform="matrix(0.80812204,0,0,0.80812204,142.42857,894.53737)" /> + <path + sodipodi:type="arc" + style="fill:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:none" + id="path3755-9" + sodipodi:cx="109.60155" + sodipodi:cy="121.64971" + sodipodi:rx="24.748737" + sodipodi:ry="24.748737" + d="m 134.35028,121.64971 a 24.748737,24.748737 0 1 1 -49.49747,0 24.748737,24.748737 0 1 1 49.49747,0 z" + transform="matrix(0.80812204,0,0,0.80812204,-63.571426,894.53737)" /> + <g + transform="scale(0.93424041,1.0703883)" + style="font-size:167.5866394px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans" + id="text3793"> + <path + d="m 128.45853,792.84841 17.75698,0 41.733,103.7597 0,11.53795 -101.877616,0 0.08183,-11.53795 42.305806,-103.7597 m 43.1241,102.53226 -27.65834,-70.53696 c -1.63665,-4.03683 -3.16413,-8.2374 -4.58245,-12.60173 -1.36388,-4.41868 -2.10034,-6.95539 -2.20939,-7.61013 l -0.73647,2.61854 c -1.69119,6.05547 -3.70965,11.97446 -6.05537,17.75698 l -27.74017,70.3733 68.98219,0" + style="" + id="path3817" /> + </g> + </g> +</svg> diff --git a/icons/scale.svg b/icons/scale.svg new file mode 100644 index 000000000..143e870e9 --- /dev/null +++ b/icons/scale.svg @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="256" + height="256" + id="svg3838" + version="1.1" + inkscape:version="0.48.4 r9939" + sodipodi:docname="Neues Dokument 7"> + <defs + id="defs3840" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="0.98994949" + inkscape:cx="33.080739" + inkscape:cy="132.08746" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="false" + inkscape:window-width="1920" + inkscape:window-height="1060" + inkscape:window-x="1917" + inkscape:window-y="-3" + inkscape:window-maximized="1" /> + <metadata + id="metadata3843"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Ebene 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(0,-796.36218)"> + <path + style="fill:#009aff;fill-opacity:1;stroke:#bebebe;stroke-width:6.52823304999999987;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + d="m 3.8355452,827.43445 c 18.0403128,23.04384 34.6170948,48.35081 45.4644808,77.56 14.699861,49.43778 21.075276,130.56465 70.722524,130.56465 35.82631,0 76.39924,-153.38199 131.14189,-208.12465" + id="path3848" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cccc" /> + <g + style="font-size:233.09231567px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#333333;fill-opacity:1;stroke:none;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans" + id="text4358" + transform="matrix(1.2570811,0,0,1.2570811,-32.90638,-237.63608)"> + <path + d="m 195.09371,960.32762 c -1.4e-4,14.79593 -5.80468,26.25325 -17.41364,34.37201 -11.53332,8.11877 -27.8088,12.17817 -48.82646,12.17817 -39.07639,0 -61.725474,-13.58187 -67.947322,-40.74564 l 21.055702,-4.21114 c 2.428011,9.63634 7.587601,16.73078 15.478787,21.28334 7.891093,4.47672 18.627593,6.71507 32.209533,6.71506 14.03704,1e-5 24.84942,-2.39009 32.43716,-7.17032 7.6634,-4.85607 11.49516,-11.9505 11.49528,-21.28333 -1.2e-4,-5.23543 -1.21415,-9.4845 -3.64207,-12.74724 -2.35228,-3.26263 -5.69084,-5.95624 -10.01568,-8.08084 -4.32506,-2.12447 -9.48465,-3.90756 -15.47879,-5.34928 -5.99432,-1.44159 -12.6335,-2.99705 -19.91756,-4.6664 -12.67142,-2.80735 -22.30771,-5.61478 -28.908907,-8.42228 -6.52541,-2.80735 -11.685,-5.91828 -15.478787,-9.3328 -3.793846,-3.49022 -6.715084,-7.5496 -8.763724,-12.17816 -1.972806,-4.62836 -2.959198,-9.9397 -2.95918,-15.93405 -1.8e-5,-13.7335 5.235449,-24.31824 15.706416,-31.75428 10.546776,-7.43572 25.608222,-11.15366 45.184402,-11.15383 18.21024,1.7e-4 32.13354,2.80759 41.76996,8.42228 9.63617,5.53913 16.38916,15.02367 20.259,28.45366 l -21.39715,3.75588 c -2.35228,-8.49803 -6.82899,-14.64401 -13.43012,-18.43797 -6.60135,-3.86955 -15.74445,-5.8044 -27.42932,-5.80454 -12.82318,1.4e-4 -22.61123,2.12468 -29.364171,6.37361 -6.753043,4.24922 -10.129539,10.58489 -10.1295,19.00704 -3.9e-5,4.93209 1.289858,9.02941 3.869697,12.29198 2.655628,3.18692 6.449444,5.91846 11.381464,8.19465 4.9319,2.20052 14.75789,4.93207 29.47798,8.19465 4.93188,1.13824 9.8259,2.31433 14.68208,3.52826 4.93187,1.13823 9.6362,2.54194 14.11302,4.21114 4.47659,1.59349 8.64978,3.49039 12.5196,5.69073 3.94545,2.20049 7.35988,4.8941 10.24332,8.08084 2.88316,3.18687 5.12151,6.94275 6.71506,11.26764 1.66914,4.32501 2.50378,9.40873 2.50392,15.25116" + id="path4382" + inkscape:connector-curvature="0" /> + </g> + </g> +</svg> @@ -3,6 +3,7 @@ * uses cairo to draw it */ #include <glib/gi18n.h> +#include <limits.h> #include "dive.h" #include "display.h" @@ -389,7 +390,8 @@ static struct plot_info *analyze_plot_info(struct plot_info *pi) /* vertical velocity in mm/sec */ /* Linus wants to smooth this - let's at least look at the samples that aren't FAST or CRAZY */ if (entry[0].sec - entry[-1].sec) { - entry->velocity = velocity((entry[0].depth - entry[-1].depth) / (entry[0].sec - entry[-1].sec)); + entry->speed = (entry[0].depth - entry[-1].depth) / (entry[0].sec - entry[-1].sec); + entry->velocity = velocity(entry->speed); /* if our samples are short and we aren't too FAST*/ if (entry[0].sec - entry[-1].sec < 15 && entry->velocity < FAST) { int past = -2; @@ -400,6 +402,7 @@ static struct plot_info *analyze_plot_info(struct plot_info *pi) } } else { entry->velocity = STABLE; + entry->speed = 0; } } @@ -849,8 +852,8 @@ static struct plot_data *populate_plot_entries(struct dive *dive, struct divecom } /* Add two final surface events */ - plot_data[idx++].sec = lasttime+10; - plot_data[idx++].sec = lasttime+20; + plot_data[idx++].sec = lasttime+1; + plot_data[idx++].sec = lasttime+2; pi->nr = idx; return plot_data; @@ -1212,14 +1215,16 @@ static void plot_string(struct plot_data *entry, char *buf, int bufsize, int pressurevalue, mod, ead, end, eadd; const char *depth_unit, *pressure_unit, *temp_unit; char *buf2 = malloc(bufsize); - double depthvalue, tempvalue; + double depthvalue, tempvalue, speedvalue; depthvalue = get_depth_units(depth, NULL, &depth_unit); snprintf(buf, bufsize, _("D:%.1f %s"), depthvalue, depth_unit); + if (prefs.show_time) { memcpy(buf2, buf, bufsize); snprintf(buf, bufsize, _("%s\nT:%d:%02d"), buf2, FRACTION(entry->sec, 60)); } + if (pressure) { pressurevalue = get_pressure_units(pressure, &pressure_unit); memcpy(buf2, buf, bufsize); @@ -1230,6 +1235,14 @@ static void plot_string(struct plot_data *entry, char *buf, int bufsize, memcpy(buf2, buf, bufsize); snprintf(buf, bufsize, _("%s\nT:%.1f %s"), buf2, tempvalue, temp_unit); } + + speedvalue = get_depth_units(abs(entry->speed), NULL, &depth_unit); + memcpy(buf2, buf, bufsize); + /* Ascending speeds are positive, descending are negative */ + if (entry->speed > 0) + speedvalue *= -1; + snprintf(buf, bufsize, _("%s\nV:%.2f %s/s"), buf2, speedvalue, depth_unit); + if (entry->ceiling) { depthvalue = get_depth_units(entry->ceiling, NULL, &depth_unit); memcpy(buf2, buf, bufsize); @@ -1330,3 +1343,116 @@ void get_plot_details(struct graphics_context *gc, int time, char *buf, int bufs if (entry) plot_string(entry, buf, bufsize, entry->depth, pressure, temp, pi->has_ndl); } + +/* Compare two plot_data entries and writes the results into a string */ +void compare_samples(struct plot_data *e1, struct plot_data *e2, char *buf, int bufsize, int sum) +{ + struct plot_data *start, *stop, *data; + const char *depth_unit, *pressure_unit; + char *buf2 = malloc(bufsize); + int avg_speed, max_speed, min_speed; + int delta_depth, avg_depth, max_depth, min_depth; + int bar_used, last_pressure, pressurevalue; + int count, last_sec, delta_time; + + double depthvalue, speedvalue; + + if (bufsize > 0) + buf[0] = '\0'; + if (e1 == NULL || e2 == NULL) + return; + + if (e1->sec < e2->sec) { + start = e1; + stop = e2; + } else if (e1->sec > e2->sec) { + start = e2; + stop = e1; + } else { + return; + } + count = 0; + max_speed = 0; + min_speed = INT_MAX; + + delta_depth = abs(start->depth-stop->depth); + delta_time = abs(start->sec-stop->sec); + avg_depth = 0; + max_depth = 0; + min_depth = INT_MAX; + bar_used = 0; + + last_sec = start->sec; + last_pressure = GET_PRESSURE(start); + + while (data != stop) { + data = start+count; + if (sum) + avg_speed += abs(data->speed)*(data->sec-last_sec); + else + avg_speed += data->speed*(data->sec-last_sec); + avg_depth += data->depth*(data->sec-last_sec); + + if (abs(data->speed) < min_speed) + min_speed = abs(data->speed); + if (abs(data->speed) > max_speed) + max_speed = abs(data->speed); + + if (data->depth < min_depth) + min_depth = data->depth; + if (data->depth > max_depth) + max_depth = data->depth; + /* Try to detect gas changes */ + if (GET_PRESSURE(data) > last_pressure+2000) + last_pressure = GET_PRESSURE(data); + else + bar_used += last_pressure-GET_PRESSURE(data); + + + count+=1; + last_sec = data->sec; + last_pressure = GET_PRESSURE(data); + } + avg_depth /= stop->sec-start->sec; + avg_speed /= stop->sec-start->sec; + + snprintf(buf, bufsize, _("%sT: %d:%02d min"), UTF8_DELTA, delta_time/60, delta_time%60); + memcpy(buf2, buf, bufsize); + + depthvalue = get_depth_units(delta_depth, NULL, &depth_unit); + snprintf(buf, bufsize, _("%s %sD:%.1f%s"), buf2, UTF8_DELTA, depthvalue, depth_unit); + memcpy(buf2, buf, bufsize); + + depthvalue = get_depth_units(min_depth, NULL, &depth_unit); + snprintf(buf, bufsize, _("%s %sD:%.1f%s"), buf2, UTF8_DOWNWARDS_ARROW, depthvalue, depth_unit); + memcpy(buf2, buf, bufsize); + + depthvalue = get_depth_units(max_depth, NULL, &depth_unit); + snprintf(buf, bufsize, _("%s %sD:%.1f %s"), buf2, UTF8_UPWARDS_ARROW, depthvalue, depth_unit); + memcpy(buf2, buf, bufsize); + + depthvalue = get_depth_units(avg_depth, NULL, &depth_unit); + snprintf(buf, bufsize, _("%s %sD:%.1f%s\n"), buf2, UTF8_AVERAGE, depthvalue, depth_unit); + memcpy(buf2, buf, bufsize); + + speedvalue = get_depth_units(min_speed, NULL, &depth_unit); + snprintf(buf, bufsize, _("%s%sV:%.2f%s/s"), buf2, UTF8_DOWNWARDS_ARROW, speedvalue, depth_unit); + memcpy(buf2, buf, bufsize); + + speedvalue = get_depth_units(max_speed, NULL, &depth_unit); + snprintf(buf, bufsize, _("%s %sV:%.2f%s/s"), buf2, UTF8_UPWARDS_ARROW, speedvalue, depth_unit); + memcpy(buf2, buf, bufsize); + + speedvalue = get_depth_units(avg_speed, NULL, &depth_unit); + snprintf(buf, bufsize, _("%s %sV:%.2f%s/s"), buf2, UTF8_AVERAGE, speedvalue, depth_unit); + memcpy(buf2, buf, bufsize); + + /* Only print if gas has been used */ + if (bar_used) { + pressurevalue = get_pressure_units(bar_used, &pressure_unit); + memcpy(buf2, buf, bufsize); + snprintf(buf, bufsize, _("%s %sP:%d %s"), buf2, UTF8_DELTA, pressurevalue, pressure_unit); + } + + free(buf2); +} @@ -33,6 +33,7 @@ struct plot_data { double po2, pn2, phe; double mod, ead, end, eadd; velocity_t velocity; + int speed; struct plot_data *min[3]; struct plot_data *max[3]; int avg[3]; @@ -42,6 +43,7 @@ void calculate_max_limits(struct dive *dive, struct divecomputer *dc, struct gra struct plot_info *create_plot_info(struct dive *dive, struct divecomputer *dc, struct graphics_context *gc); int setup_temperature_limits(struct graphics_context *gc); int get_cylinder_pressure_range(struct graphics_context *gc); +void compare_samples(struct plot_data *e1, struct plot_data *e2, char *buf, int bufsize, int sum); struct ev_select { char *ev_name; diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 214e41bf0..8f2f8233d 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -294,7 +294,8 @@ void MainWindow::on_actionAutoGroup_triggered() void MainWindow::on_actionToggleZoom_triggered() { - qDebug("actionToggleZoom"); + zoomed_plot = !zoomed_plot; + ui->ProfileWidget->refresh(); } void MainWindow::on_actionYearlyStatistics_triggered() diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 07326f475..17598a6af 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -15,7 +15,9 @@ #include <QPropertyAnimation> #include <QGraphicsSceneHoverEvent> #include <QMouseEvent> +#include <QToolBar> #include <qtextdocument.h> +#include <limits> #include "../color.h" #include "../display.h" @@ -43,10 +45,11 @@ extern struct ev_select *ev_namelist; extern int evn_allocated; extern int evn_used; -ProfileGraphicsView::ProfileGraphicsView(QWidget* parent) : QGraphicsView(parent), toolTip(0) , dive(0), diveDC(0) +ProfileGraphicsView::ProfileGraphicsView(QWidget* parent) : QGraphicsView(parent), toolTip(0) , dive(0), diveDC(0), rulerItem(0), toolBarProxy(0) { printMode = false; isGrayscale = false; + rulerEnabled = false; gc.printer = false; fill_profile_color(); setScene(new QGraphicsScene()); @@ -175,11 +178,23 @@ void ProfileGraphicsView::clear() { resetTransform(); zoomLevel = 0; - if(toolTip){ + if(toolTip) { scene()->removeItem(toolTip); toolTip->deleteLater(); toolTip = 0; } + if(toolBarProxy) { + scene()->removeItem(toolBarProxy); + toolBarProxy->deleteLater(); + toolBarProxy = 0; + } + if(rulerItem) { + remove_ruler(); + rulerItem->destNode()->deleteLater(); + rulerItem->sourceNode()->deleteLater(); + rulerItem->deleteLater(); + rulerItem=0; + } scene()->clear(); } @@ -214,7 +229,7 @@ void ProfileGraphicsView::plot(struct dive *d, bool forceRedraw) dive = d; diveDC = d ? dc : NULL; - if (!isVisible() || !dive) { + if (!isVisible() || !dive || !mainWindow()) { return; } setBackgroundBrush(getColor(BACKGROUND)); @@ -274,6 +289,9 @@ void ProfileGraphicsView::plot(struct dive *d, bool forceRedraw) plot_events(dc); + if (rulerEnabled && !printMode) + create_ruler(); + /* Temperature profile */ plot_temperature_profile(); @@ -336,6 +354,12 @@ void ProfileGraphicsView::plot(struct dive *d, bool forceRedraw) connect(timeEditor, SIGNAL(editingFinished(QString)), this, SLOT(edit_dive_time(QString))); scene()->addItem(timeEditor); } + + if (!printMode) + addControlItems(); + + if (rulerEnabled && !printMode) + add_ruler(); } void ProfileGraphicsView::plot_depth_scale() @@ -368,11 +392,29 @@ void ProfileGraphicsView::plot_depth_scale() depthMarkers->setPos(depthMarkers->pos().x() - 10, 0); } +void ProfileGraphicsView::addControlItems() +{ + QAction *scaleAction = new QAction(QIcon(":scale"), tr("Scale"), this); + QAction *rulerAction = new QAction(QIcon(":ruler"), tr("Ruler"), this); + QToolBar *toolBar = new QToolBar("", 0); + toolBar->addAction(rulerAction); + toolBar->addAction(scaleAction); + //make toolbar transparent + toolBar->setStyleSheet(QString::fromUtf8 ("background-color: rgba(255,255,255,0);")); + + connect(scaleAction, SIGNAL(triggered()), this, SLOT(on_scaleAction())); + connect(rulerAction, SIGNAL(triggered()), this, SLOT(on_rulerAction())); + toolBarProxy = scene()->addWidget(toolBar); + //Put it into the lower right corner of the profile + toolBarProxy->setPos(gc.maxx-toolBar->width(), gc.maxy-toolBar->height()); +} + void ProfileGraphicsView::plot_pp_text() { double pp, dpp, m; int hpos; static text_render_options_t tro = {PP_TEXT_SIZE, PP_LINES, LEFT, MIDDLE}; + QGraphicsRectItem *pressureMarkers = new QGraphicsRectItem(); setup_pp_limits(&gc); pp = floor(gc.pi.maxpp * 10.0) / 10.0 + 0.2; @@ -386,8 +428,10 @@ void ProfileGraphicsView::plot_pp_text() pen.setColor(c); item->setPen(pen); scene()->addItem(item); - plot_text(&tro, QPointF(hpos + 30, m), QString::number(m)); + plot_text(&tro, QPointF(hpos, m), QString::number(m), pressureMarkers); } + scene()->addItem(pressureMarkers); + pressureMarkers->setPos(pressureMarkers->pos().x() + 10, 0); } void ProfileGraphicsView::plot_add_line(int sec, double val, QColor c, QPointF &from) @@ -840,6 +884,57 @@ void ProfileGraphicsView::plot_one_event(struct event *ev) item->setToolTip(name); } +void ProfileGraphicsView::create_ruler() +{ + int x,y; + struct plot_info *pi = &gc.pi; + struct plot_data *data = pi->entry; + + RulerNodeItem *first = new RulerNodeItem(0, gc); + RulerNodeItem *second = new RulerNodeItem(0, gc); + + x = SCALEXGC(data->sec); + y = data->depth; + + first->setPos(x,y); + + data = pi->entry+(pi->nr-1); + x = SCALEXGC(data->sec); + y = data->depth; + + second->setPos(x,y); + //Make sure that both points already have their entries + first->recalculate(); + second->recalculate(); + + rulerItem = new RulerItem(0, first, second); + first->setRuler(rulerItem); + second->setRuler(rulerItem); +} + +void ProfileGraphicsView::add_ruler() +{ + if (! scene()->items().contains(rulerItem)) { + scene()->addItem(rulerItem->sourceNode()); + scene()->addItem(rulerItem->destNode()); + scene()->addItem(rulerItem); + rulerItem->recalculate(); + } +} + +void ProfileGraphicsView::remove_ruler() +{ + if (rulerItem) { + if (scene()->items().contains(rulerItem)) + scene()->removeItem(rulerItem); + if (scene()->items().contains(rulerItem->sourceNode())) + scene()->removeItem(rulerItem->sourceNode()); + if (scene()->items().contains(rulerItem->destNode())) + scene()->removeItem(rulerItem->destNode()); + } +} + + void ProfileGraphicsView::plot_depth_profile() { int i, incr; @@ -888,7 +983,7 @@ void ProfileGraphicsView::plot_depth_profile() if (maxtime < 600) { /* Be a bit more verbose with shorter dives */ for (i = incr; i < maxtime; i += incr) - plot_text(&tro, QPointF(i, 0), QString("%1:%2").arg(i/60).arg(i%60), timeMarkers); + plot_text(&tro, QPointF(i, 0), QString("%1:%2").arg(i/60).arg(i%60, 2, 10, QChar('0')), timeMarkers); } else { /* Only render the time on every second marker for normal dives */ for (i = incr; i < maxtime; i += 2 * incr) @@ -1131,7 +1226,7 @@ QGraphicsItemGroup *ProfileGraphicsView::plot_text(text_render_options_t *tro,co void ProfileGraphicsView::resizeEvent(QResizeEvent *event) { - fitInView(sceneRect()); + refresh(); } void ProfileGraphicsView::plot_temperature_profile() @@ -1175,6 +1270,18 @@ void ProfileGraphicsView::edit_dive_time(const QString& time) refresh(); } +void ProfileGraphicsView::on_rulerAction() +{ + rulerEnabled = !rulerEnabled; + refresh(); +} + +void ProfileGraphicsView::on_scaleAction() +{ + zoomed_plot = !zoomed_plot; + refresh(); +} + void ToolTipItem::addToolTip(const QString& toolTip, const QIcon& icon) { QGraphicsPixmapItem *iconItem = 0; @@ -1430,6 +1537,161 @@ EventItem::EventItem(QGraphicsItem* parent, bool grayscale): QGraphicsPolygonIte ball->setPen(QPen(getColor(ALERT_FG))); } + + +RulerNodeItem::RulerNodeItem(QGraphicsItem *parent, graphics_context context) : QGraphicsEllipseItem(parent), gc(context), entry(NULL) , ruler(NULL) +{ + setRect(QRect(QPoint(-8,8),QPoint(8,-8))); + setBrush(QColor(0xff, 0, 0, 127)); + setPen(QColor("#FF0000")); + setFlag(QGraphicsItem::ItemIsMovable); + setFlag(ItemSendsGeometryChanges); + setFlag(ItemIgnoresTransformations); +} + +void RulerNodeItem::setRuler(RulerItem *r) +{ + ruler = r; +} + +void RulerNodeItem::recalculate() +{ + struct plot_info *pi = &gc.pi; + struct plot_data *data = pi->entry+(pi->nr-1); + uint16_t count = 0; + if (x() < 0) { + setPos(0, y()); + } + else if (x() > SCALEXGC(data->sec)) { + setPos(SCALEXGC(data->sec), y()); + } + else { + data = pi->entry; + count=0; + while (SCALEXGC(data->sec) < x() && count < pi->nr) { + data = pi->entry+count; + count++; + } + setPos(SCALEGC(data->sec, data->depth)); + entry=data; + } +} + +QVariant RulerNodeItem::itemChange(GraphicsItemChange change, const QVariant &value) +{ + if(change == ItemPositionHasChanged) { + recalculate(); + if(ruler != NULL) + ruler->recalculate(); + if (scene()) { + scene()->update(); + } + } + return QGraphicsEllipseItem::itemChange(change, value); +} + +RulerItem::RulerItem(QGraphicsItem *parent, RulerNodeItem *sourceNode, RulerNodeItem *destNode) : QGraphicsObject(parent), source(sourceNode), dest(destNode) +{ + recalculate(); +} + +void RulerItem::recalculate() +{ + char buffer[500]; + QPointF tmp; + QFont font; + QFontMetrics fm(font); + + if (source == NULL || dest == NULL) + return; + + prepareGeometryChange(); + startPoint = mapFromItem(source, 0, 0); + endPoint = mapFromItem(dest, 0, 0); + if (startPoint.x() > endPoint.x()) { + tmp = endPoint; + endPoint = startPoint; + startPoint = tmp; + } + QLineF line(startPoint, endPoint); + + compare_samples(source->entry, dest->entry, buffer, 500, 1); + text = QString(buffer); + + QRect r = fm.boundingRect(QRect(QPoint(10,-1*INT_MAX), QPoint(line.length()-10, 0)), Qt::TextWordWrap, text); + if (r.height() < 10) + height = 10; + else + height = r.height(); + + QLineF line_n = line.normalVector(); + line_n.setLength(height); + if (scene()) { + /* Determine whether we draw down or upwards */ + if (scene()->sceneRect().contains(line_n.p2()) && + scene()->sceneRect().contains(endPoint+QPointF(line_n.dx(),line_n.dy()))) + paint_direction = -1; + else + paint_direction = 1; + } +} + +RulerNodeItem *RulerItem::sourceNode() const +{ + return source; +} + +RulerNodeItem *RulerItem::destNode() const +{ + return dest; +} + +void RulerItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) +{ + QLineF line(startPoint, endPoint); + QLineF line_n = line.normalVector(); + painter->setPen(QColor(Qt::black)); + painter->setBrush(Qt::NoBrush); + line_n.setLength(height); + + if (paint_direction == 1) + line_n.setAngle(line_n.angle()+180); + painter->drawLine(line); + painter->drawLine(line_n); + painter->drawLine(line_n.p1() + QPointF(line.dx(), line.dy()), line_n.p2() + QPointF(line.dx(), line.dy())); + + //Draw Text + painter->save(); + painter->translate(startPoint.x(), startPoint.y()); + painter->rotate(line.angle()*-1); + if (paint_direction == 1) + painter->translate(0, height); + painter->setPen(Qt::black); + painter->drawText(QRectF(QPointF(10,-1*height), QPointF(line.length()-10, 0)), Qt::TextWordWrap, text); + painter->restore(); +} + +QRectF RulerItem::boundingRect() const +{ + return shape().controlPointRect(); +} + +QPainterPath RulerItem::shape() const +{ + QPainterPath path; + QLineF line(startPoint, endPoint); + QLineF line_n = line.normalVector(); + line_n.setLength(height); + if (paint_direction == 1) + line_n.setAngle(line_n.angle()+180); + path.moveTo(startPoint); + path.lineTo(line_n.p2()); + path.lineTo(line_n.p2() + QPointF(line.dx(), line.dy())); + path.lineTo(endPoint); + path.lineTo(startPoint); + return path; +} + GraphicsTextEditor::GraphicsTextEditor(QGraphicsItem* parent): QGraphicsTextItem(parent) { } diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h index 0bcbf7529..7cbc53e15 100644 --- a/qt-ui/profilegraphics.h +++ b/qt-ui/profilegraphics.h @@ -57,6 +57,49 @@ private: QRectF nextRectangle; }; +class RulerItem; + +class RulerNodeItem : public QObject, public QGraphicsEllipseItem +{ + Q_OBJECT + friend class RulerItem; +public: + explicit RulerNodeItem(QGraphicsItem* parent, graphics_context gc); + void setRuler(RulerItem *r); + void recalculate(); + +protected: + QVariant itemChange(GraphicsItemChange change, const QVariant & value ); + +private: + graphics_context gc; + struct plot_data *entry; + RulerItem* ruler; +}; + +class RulerItem : public QGraphicsObject +{ + Q_OBJECT +public: + explicit RulerItem(QGraphicsItem* parent, + RulerNodeItem *sourceMarker, + RulerNodeItem *destMarker); + void recalculate(); + + RulerNodeItem* sourceNode() const; + RulerNodeItem* destNode() const; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget * widget = 0); + QRectF boundingRect() const; + QPainterPath shape() const; + +private: + QPointF startPoint, endPoint; + RulerNodeItem *source, *dest; + QString text; + int height; + int paint_direction; +}; + class EventItem : public QGraphicsPolygonItem { public: @@ -106,6 +149,8 @@ protected: public slots: void refresh(); void edit_dive_time(const QString& time); + void on_rulerAction(); + void on_scaleAction(); private: void plot_depth_profile(); @@ -128,6 +173,13 @@ private: void plot_pp_text(); void plot_depth_scale(); + + void addControlItems(); + + void create_ruler(); + void add_ruler(); + void remove_ruler(); + QColor getColor(const color_indice_t i); QColor get_sac_color(int sac, int avg_sac); void scrollViewTo(const QPoint pos); @@ -139,6 +191,8 @@ private: struct dive *dive; struct divecomputer *diveDC; int zoomLevel; + + bool rulerEnabled; bool printMode; bool isGrayscale; @@ -147,6 +201,8 @@ private: QGraphicsItem* timeMarkers; QGraphicsItem* depthMarkers; QGraphicsItem* diveComputer; + RulerItem *rulerItem; + QGraphicsProxyWidget *toolBarProxy; // For 'Plan' mode.: GraphicsTextEditor *depthEditor; diff --git a/subsurface.qrc b/subsurface.qrc index 4e9e8ff84..cda15944c 100644 --- a/subsurface.qrc +++ b/subsurface.qrc @@ -12,5 +12,7 @@ <file alias="average">icons/average.svg</file> <file alias="warning">icons/warning.png</file> <file alias="table-css">qt-ui/css/tableviews.css</file> + <file alias="scale">icons/scale.svg</file> + <file alias="ruler">icons/ruler.svg</file> </qresource> </RCC> |