From c96458cf3b2d07ecefc5a1e0a33e874c70068480 Mon Sep 17 00:00:00 2001 From: Bryant Date: Wed, 25 Jun 2014 18:55:39 -0700 Subject: [PATCH] Update Qwt to 6.1. We also document how we got the source code and keep our modifications out of the Qwt directory. --- libs/README.md | 9 + libs/qwt.pri | 149 ++ libs/qwt/qwt_abstract_legend.cpp | 38 + libs/qwt/qwt_abstract_legend.h | 71 + libs/qwt/qwt_abstract_scale.cpp | 362 +++-- libs/qwt/qwt_abstract_scale.h | 69 +- libs/qwt/qwt_abstract_scale_draw.cpp | 269 ++-- libs/qwt/qwt_abstract_scale_draw.h | 100 +- libs/qwt/qwt_abstract_slider.cpp | 1003 +++++++----- libs/qwt/qwt_abstract_slider.h | 218 ++- libs/qwt/qwt_analog_clock.cpp | 221 +-- libs/qwt/qwt_analog_clock.h | 50 +- libs/qwt/qwt_arrow_button.cpp | 289 ++-- libs/qwt/qwt_arrow_button.h | 20 +- libs/qwt/qwt_clipper.cpp | 731 +++++---- libs/qwt/qwt_clipper.h | 25 +- libs/qwt/qwt_color_map.cpp | 288 ++-- libs/qwt/qwt_color_map.h | 130 +- libs/qwt/qwt_column_symbol.cpp | 293 ++++ libs/qwt/qwt_column_symbol.h | 161 ++ libs/qwt/qwt_compass.cpp | 335 ++-- libs/qwt/qwt_compass.h | 64 +- libs/qwt/qwt_compass_rose.cpp | 253 ++- libs/qwt/qwt_compass_rose.h | 62 +- libs/qwt/qwt_compat.h | 42 + libs/qwt/qwt_counter.cpp | 950 ++++++----- libs/qwt/qwt_counter.h | 147 +- libs/qwt/qwt_curve_fitter.cpp | 393 ++++- libs/qwt/qwt_curve_fitter.h | 124 +- libs/qwt/qwt_date.cpp | 654 ++++++++ libs/qwt/qwt_date.h | 128 ++ libs/qwt/qwt_date_scale_draw.cpp | 269 ++++ libs/qwt/qwt_date_scale_draw.h | 77 + libs/qwt/qwt_date_scale_engine.cpp | 1300 +++++++++++++++ libs/qwt/qwt_date_scale_engine.h | 77 + libs/qwt/qwt_dial.cpp | 1283 ++++++--------- libs/qwt/qwt_dial.h | 175 +-- libs/qwt/qwt_dial_needle.cpp | 725 ++++----- libs/qwt/qwt_dial_needle.h | 140 +- libs/qwt/qwt_dyngrid_layout.cpp | 573 +++---- libs/qwt/qwt_dyngrid_layout.h | 60 +- libs/qwt/qwt_event_pattern.cpp | 212 ++- libs/qwt/qwt_event_pattern.h | 235 +-- libs/qwt/qwt_global.h | 24 +- libs/qwt/qwt_graphic.cpp | 986 ++++++++++++ libs/qwt/qwt_graphic.h | 172 ++ libs/qwt/qwt_interval.cpp | 354 +++++ libs/qwt/qwt_interval.h | 320 ++++ libs/qwt/qwt_interval_symbol.cpp | 319 ++++ libs/qwt/qwt_interval_symbol.h | 87 ++ libs/qwt/qwt_knob.cpp | 936 +++++++---- libs/qwt/qwt_knob.h | 164 +- libs/qwt/qwt_legend.cpp | 1000 +++++++----- libs/qwt/qwt_legend.h | 130 +- libs/qwt/qwt_legend_data.cpp | 129 ++ libs/qwt/qwt_legend_data.h | 87 ++ libs/qwt/qwt_legend_label.cpp | 421 +++++ libs/qwt/qwt_legend_label.h | 80 + libs/qwt/qwt_magnifier.cpp | 334 ++-- libs/qwt/qwt_magnifier.h | 48 +- libs/qwt/qwt_math.cpp | 47 +- libs/qwt/qwt_math.h | 199 +-- libs/qwt/qwt_matrix_raster_data.cpp | 298 ++++ libs/qwt/qwt_matrix_raster_data.h | 74 + libs/qwt/qwt_null_paintdevice.cpp | 593 +++++++ libs/qwt/qwt_null_paintdevice.h | 126 ++ libs/qwt/qwt_painter.cpp | 1475 +++++++++++------ libs/qwt/qwt_painter.h | 243 +-- libs/qwt/qwt_painter_command.cpp | 237 +++ libs/qwt/qwt_painter_command.h | 173 ++ libs/qwt/qwt_panner.cpp | 476 +++--- libs/qwt/qwt_panner.h | 58 +- libs/qwt/qwt_picker.cpp | 1464 +++++++++-------- libs/qwt/qwt_picker.h | 365 ++--- libs/qwt/qwt_picker_machine.cpp | 615 +++++--- libs/qwt/qwt_picker_machine.h | 116 +- libs/qwt/qwt_pixel_matrix.cpp | 51 + libs/qwt/qwt_pixel_matrix.h | 98 ++ libs/qwt/qwt_plot.cpp | 1240 +++++++++------ libs/qwt/qwt_plot.h | 290 ++-- libs/qwt/qwt_plot_abstract_barchart.cpp | 367 +++++ libs/qwt/qwt_plot_abstract_barchart.h | 97 ++ libs/qwt/qwt_plot_axis.cpp | 516 +++--- libs/qwt/qwt_plot_barchart.cpp | 459 ++++++ libs/qwt/qwt_plot_barchart.h | 118 ++ libs/qwt/qwt_plot_canvas.cpp | 1147 +++++++++++--- libs/qwt/qwt_plot_canvas.h | 162 +- libs/qwt/qwt_plot_curve.cpp | 1592 +++++++++---------- libs/qwt/qwt_plot_curve.h | 395 +++-- libs/qwt/qwt_plot_dict.cpp | 162 +- libs/qwt/qwt_plot_dict.h | 33 +- libs/qwt/qwt_plot_directpainter.cpp | 317 ++++ libs/qwt/qwt_plot_directpainter.h | 100 ++ libs/qwt/qwt_plot_glcanvas.cpp | 357 +++++ libs/qwt/qwt_plot_glcanvas.h | 136 ++ libs/qwt/qwt_plot_grid.cpp | 359 +++-- libs/qwt/qwt_plot_grid.h | 43 +- libs/qwt/qwt_plot_histogram.cpp | 690 ++++++++ libs/qwt/qwt_plot_histogram.h | 139 ++ libs/qwt/qwt_plot_intervalcurve.cpp | 603 +++++++ libs/qwt/qwt_plot_intervalcurve.h | 132 ++ libs/qwt/qwt_plot_item.cpp | 569 ++++--- libs/qwt/qwt_plot_item.h | 271 +++- libs/qwt/qwt_plot_layout.cpp | 1226 +++++++++------ libs/qwt/qwt_plot_layout.h | 99 +- libs/qwt/qwt_plot_legenditem.cpp | 865 ++++++++++ libs/qwt/qwt_plot_legenditem.h | 136 ++ libs/qwt/qwt_plot_magnifier.cpp | 81 +- libs/qwt/qwt_plot_magnifier.h | 13 +- libs/qwt/qwt_plot_marker.cpp | 571 +++++-- libs/qwt/qwt_plot_marker.h | 90 +- libs/qwt/qwt_plot_multi_barchart.cpp | 744 +++++++++ libs/qwt/qwt_plot_multi_barchart.h | 127 ++ libs/qwt/qwt_plot_panner.cpp | 204 ++- libs/qwt/qwt_plot_panner.h | 21 +- libs/qwt/qwt_plot_picker.cpp | 295 ++-- libs/qwt/qwt_plot_picker.h | 61 +- libs/qwt/qwt_plot_rasteritem.cpp | 873 +++++++++-- libs/qwt/qwt_plot_rasteritem.h | 124 +- libs/qwt/qwt_plot_renderer.cpp | 1014 ++++++++++++ libs/qwt/qwt_plot_renderer.h | 170 ++ libs/qwt/qwt_plot_rescaler.cpp | 631 ++++++++ libs/qwt/qwt_plot_rescaler.h | 142 ++ libs/qwt/qwt_plot_scaleitem.cpp | 291 ++-- libs/qwt/qwt_plot_scaleitem.h | 39 +- libs/qwt/qwt_plot_seriesitem.cpp | 112 ++ libs/qwt/qwt_plot_seriesitem.h | 66 + libs/qwt/qwt_plot_shapeitem.cpp | 491 ++++++ libs/qwt/qwt_plot_shapeitem.h | 111 ++ libs/qwt/qwt_plot_spectrocurve.cpp | 321 ++++ libs/qwt/qwt_plot_spectrocurve.h | 79 + libs/qwt/qwt_plot_spectrogram.cpp | 614 ++++---- libs/qwt/qwt_plot_spectrogram.h | 90 +- libs/qwt/qwt_plot_svgitem.cpp | 201 +-- libs/qwt/qwt_plot_svgitem.h | 33 +- libs/qwt/qwt_plot_textlabel.cpp | 261 ++++ libs/qwt/qwt_plot_textlabel.h | 75 + libs/qwt/qwt_plot_tradingcurve.cpp | 682 ++++++++ libs/qwt/qwt_plot_tradingcurve.h | 174 +++ libs/qwt/qwt_plot_xml.cpp | 23 +- libs/qwt/qwt_plot_zoneitem.cpp | 315 ++++ libs/qwt/qwt_plot_zoneitem.h | 65 + libs/qwt/qwt_plot_zoomer.cpp | 387 ++--- libs/qwt/qwt_plot_zoomer.h | 147 +- libs/qwt/qwt_point_3d.cpp | 22 + libs/qwt/qwt_point_3d.h | 189 +++ libs/qwt/qwt_point_data.cpp | 304 ++++ libs/qwt/qwt_point_data.h | 146 ++ libs/qwt/qwt_point_mapper.cpp | 721 +++++++++ libs/qwt/qwt_point_mapper.h | 89 ++ libs/qwt/qwt_point_polar.cpp | 121 ++ libs/qwt/qwt_point_polar.h | 201 +++ libs/qwt/qwt_raster_data.cpp | 389 +++-- libs/qwt/qwt_raster_data.h | 114 +- libs/qwt/qwt_round_scale_draw.cpp | 232 +-- libs/qwt/qwt_round_scale_draw.h | 40 +- libs/qwt/qwt_samples.h | 239 +++ libs/qwt/qwt_sampling_thread.cpp | 106 ++ libs/qwt/qwt_sampling_thread.h | 50 + libs/qwt/qwt_scale_div.cpp | 295 +++- libs/qwt/qwt_scale_div.h | 134 +- libs/qwt/qwt_scale_draw.cpp | 809 +++++----- libs/qwt/qwt_scale_draw.h | 93 +- libs/qwt/qwt_scale_engine.cpp | 1041 +++++++----- libs/qwt/qwt_scale_engine.h | 184 ++- libs/qwt/qwt_scale_map.cpp | 301 ++-- libs/qwt/qwt_scale_map.h | 152 +- libs/qwt/qwt_scale_widget.cpp | 777 ++++----- libs/qwt/qwt_scale_widget.h | 106 +- libs/qwt/qwt_series_data.cpp | 346 ++++ libs/qwt/qwt_series_data.h | 355 +++++ libs/qwt/qwt_series_store.h | 199 +++ libs/qwt/qwt_slider.cpp | 1332 +++++++++------- libs/qwt/qwt_slider.h | 132 +- libs/qwt/qwt_spline.cpp | 260 +-- libs/qwt/qwt_spline.h | 59 +- libs/qwt/qwt_symbol.cpp | 1912 ++++++++++++++++++++--- libs/qwt/qwt_symbol.h | 251 ++- libs/qwt/qwt_system_clock.cpp | 396 +++++ libs/qwt/qwt_system_clock.h | 47 + libs/qwt/qwt_text.cpp | 473 +++--- libs/qwt/qwt_text.h | 196 ++- libs/qwt/qwt_text_engine.cpp | 299 ++-- libs/qwt/qwt_text_engine.h | 82 +- libs/qwt/qwt_text_label.cpp | 163 +- libs/qwt/qwt_text_label.h | 34 +- libs/qwt/qwt_thermo.cpp | 1247 ++++++++------- libs/qwt/qwt_thermo.h | 195 ++- libs/qwt/qwt_transform.cpp | 165 ++ libs/qwt/qwt_transform.h | 137 ++ libs/qwt/qwt_wheel.cpp | 1469 ++++++++++++----- libs/qwt/qwt_wheel.h | 164 +- libs/qwt/qwt_widget_overlay.cpp | 373 +++++ libs/qwt/qwt_widget_overlay.h | 148 ++ 194 files changed, 47558 insertions(+), 17162 deletions(-) create mode 100644 libs/README.md create mode 100644 libs/qwt.pri create mode 100644 libs/qwt/qwt_abstract_legend.cpp create mode 100644 libs/qwt/qwt_abstract_legend.h create mode 100644 libs/qwt/qwt_column_symbol.cpp create mode 100644 libs/qwt/qwt_column_symbol.h create mode 100644 libs/qwt/qwt_compat.h create mode 100644 libs/qwt/qwt_date.cpp create mode 100644 libs/qwt/qwt_date.h create mode 100644 libs/qwt/qwt_date_scale_draw.cpp create mode 100644 libs/qwt/qwt_date_scale_draw.h create mode 100644 libs/qwt/qwt_date_scale_engine.cpp create mode 100644 libs/qwt/qwt_date_scale_engine.h create mode 100644 libs/qwt/qwt_graphic.cpp create mode 100644 libs/qwt/qwt_graphic.h create mode 100644 libs/qwt/qwt_interval.cpp create mode 100644 libs/qwt/qwt_interval.h create mode 100644 libs/qwt/qwt_interval_symbol.cpp create mode 100644 libs/qwt/qwt_interval_symbol.h create mode 100644 libs/qwt/qwt_legend_data.cpp create mode 100644 libs/qwt/qwt_legend_data.h create mode 100644 libs/qwt/qwt_legend_label.cpp create mode 100644 libs/qwt/qwt_legend_label.h create mode 100644 libs/qwt/qwt_matrix_raster_data.cpp create mode 100644 libs/qwt/qwt_matrix_raster_data.h create mode 100644 libs/qwt/qwt_null_paintdevice.cpp create mode 100644 libs/qwt/qwt_null_paintdevice.h create mode 100644 libs/qwt/qwt_painter_command.cpp create mode 100644 libs/qwt/qwt_painter_command.h create mode 100644 libs/qwt/qwt_pixel_matrix.cpp create mode 100644 libs/qwt/qwt_pixel_matrix.h create mode 100644 libs/qwt/qwt_plot_abstract_barchart.cpp create mode 100644 libs/qwt/qwt_plot_abstract_barchart.h create mode 100644 libs/qwt/qwt_plot_barchart.cpp create mode 100644 libs/qwt/qwt_plot_barchart.h create mode 100644 libs/qwt/qwt_plot_directpainter.cpp create mode 100644 libs/qwt/qwt_plot_directpainter.h create mode 100644 libs/qwt/qwt_plot_glcanvas.cpp create mode 100644 libs/qwt/qwt_plot_glcanvas.h create mode 100644 libs/qwt/qwt_plot_histogram.cpp create mode 100644 libs/qwt/qwt_plot_histogram.h create mode 100644 libs/qwt/qwt_plot_intervalcurve.cpp create mode 100644 libs/qwt/qwt_plot_intervalcurve.h create mode 100644 libs/qwt/qwt_plot_legenditem.cpp create mode 100644 libs/qwt/qwt_plot_legenditem.h create mode 100644 libs/qwt/qwt_plot_multi_barchart.cpp create mode 100644 libs/qwt/qwt_plot_multi_barchart.h create mode 100644 libs/qwt/qwt_plot_renderer.cpp create mode 100644 libs/qwt/qwt_plot_renderer.h create mode 100644 libs/qwt/qwt_plot_rescaler.cpp create mode 100644 libs/qwt/qwt_plot_rescaler.h create mode 100644 libs/qwt/qwt_plot_seriesitem.cpp create mode 100644 libs/qwt/qwt_plot_seriesitem.h create mode 100644 libs/qwt/qwt_plot_shapeitem.cpp create mode 100644 libs/qwt/qwt_plot_shapeitem.h create mode 100644 libs/qwt/qwt_plot_spectrocurve.cpp create mode 100644 libs/qwt/qwt_plot_spectrocurve.h create mode 100644 libs/qwt/qwt_plot_textlabel.cpp create mode 100644 libs/qwt/qwt_plot_textlabel.h create mode 100644 libs/qwt/qwt_plot_tradingcurve.cpp create mode 100644 libs/qwt/qwt_plot_tradingcurve.h create mode 100644 libs/qwt/qwt_plot_zoneitem.cpp create mode 100644 libs/qwt/qwt_plot_zoneitem.h create mode 100644 libs/qwt/qwt_point_3d.cpp create mode 100644 libs/qwt/qwt_point_3d.h create mode 100644 libs/qwt/qwt_point_data.cpp create mode 100644 libs/qwt/qwt_point_data.h create mode 100644 libs/qwt/qwt_point_mapper.cpp create mode 100644 libs/qwt/qwt_point_mapper.h create mode 100644 libs/qwt/qwt_point_polar.cpp create mode 100644 libs/qwt/qwt_point_polar.h create mode 100644 libs/qwt/qwt_samples.h create mode 100644 libs/qwt/qwt_sampling_thread.cpp create mode 100644 libs/qwt/qwt_sampling_thread.h create mode 100644 libs/qwt/qwt_series_data.cpp create mode 100644 libs/qwt/qwt_series_data.h create mode 100644 libs/qwt/qwt_series_store.h create mode 100644 libs/qwt/qwt_system_clock.cpp create mode 100644 libs/qwt/qwt_system_clock.h create mode 100644 libs/qwt/qwt_transform.cpp create mode 100644 libs/qwt/qwt_transform.h create mode 100644 libs/qwt/qwt_widget_overlay.cpp create mode 100644 libs/qwt/qwt_widget_overlay.h diff --git a/libs/README.md b/libs/README.md new file mode 100644 index 000000000..f0f9a48a2 --- /dev/null +++ b/libs/README.md @@ -0,0 +1,9 @@ +This folder contains the various required libraries for QGC to compile. They are distributed with the codebase to ease development. + +# Qwt +Qt Widgets for Technical Applications +Version: 6.1 +Source obtained: `svn checkout svn://svn.code.sf.net/p/qwt/code/branches/qwt-6.1 qwt` + +Contents of `/libs/qwt` is the contents of the `/src` directory from the Qwt repository. +qwt.pri file is custom made to compile all necessary Qwt code in with QGC. diff --git a/libs/qwt.pri b/libs/qwt.pri new file mode 100644 index 000000000..ecb19d61f --- /dev/null +++ b/libs/qwt.pri @@ -0,0 +1,149 @@ +###################################################################### +# Automatically generated by qmake (2.01a) Wed Feb 10 11:43:43 2010 +###################################################################### + +QWTSRCDIR = libs/qwt +DEPENDPATH += $$QWTSRCDIR +INCLUDEPATH += $$QWTSRCDIR + +# Input +HEADERS += $$QWTSRCDIR/qwt.h \ + $$QWTSRCDIR/qwt_abstract_legend.h \ + $$QWTSRCDIR/qwt_abstract_scale.h \ + $$QWTSRCDIR/qwt_abstract_scale_draw.h \ + $$QWTSRCDIR/qwt_abstract_slider.h \ + $$QWTSRCDIR/qwt_analog_clock.h \ + $$QWTSRCDIR/qwt_arrow_button.h \ + $$QWTSRCDIR/qwt_clipper.h \ + $$QWTSRCDIR/qwt_color_map.h \ + $$QWTSRCDIR/qwt_compass.h \ + $$QWTSRCDIR/qwt_compass_rose.h \ + $$QWTSRCDIR/qwt_counter.h \ + $$QWTSRCDIR/qwt_curve_fitter.h \ + $$QWTSRCDIR/qwt_dial.h \ + $$QWTSRCDIR/qwt_dial_needle.h \ + $$QWTSRCDIR/qwt_dyngrid_layout.h \ + $$QWTSRCDIR/qwt_event_pattern.h \ + $$QWTSRCDIR/qwt_global.h \ + $$QWTSRCDIR/qwt_graphic.h \ + $$QWTSRCDIR/qwt_interval.h \ + $$QWTSRCDIR/qwt_knob.h \ + $$QWTSRCDIR/qwt_legend.h \ + $$QWTSRCDIR/qwt_legend_data.h \ + $$QWTSRCDIR/qwt_legend_label.h \ + $$QWTSRCDIR/qwt_magnifier.h \ + $$QWTSRCDIR/qwt_math.h \ + $$QWTSRCDIR/qwt_null_paintdevice.h \ + $$QWTSRCDIR/qwt_painter.h \ + $$QWTSRCDIR/qwt_painter_command.h \ + $$QWTSRCDIR/qwt_panner.h \ + $$QWTSRCDIR/qwt_picker.h \ + $$QWTSRCDIR/qwt_picker_machine.h \ + $$QWTSRCDIR/qwt_pixel_matrix.h \ + $$QWTSRCDIR/qwt_plot.h \ + $$QWTSRCDIR/qwt_plot_canvas.h \ + $$QWTSRCDIR/qwt_plot_curve.h \ + $$QWTSRCDIR/qwt_plot_dict.h \ + $$QWTSRCDIR/qwt_plot_grid.h \ + $$QWTSRCDIR/qwt_plot_item.h \ + $$QWTSRCDIR/qwt_plot_layout.h \ + $$QWTSRCDIR/qwt_plot_magnifier.h \ + $$QWTSRCDIR/qwt_plot_marker.h \ + $$QWTSRCDIR/qwt_plot_panner.h \ + $$QWTSRCDIR/qwt_plot_picker.h \ + $$QWTSRCDIR/qwt_plot_rasteritem.h \ + $$QWTSRCDIR/qwt_plot_scaleitem.h \ + $$QWTSRCDIR/qwt_plot_seriesitem.h \ + $$QWTSRCDIR/qwt_plot_spectrogram.h \ + $$QWTSRCDIR/qwt_plot_svgitem.h \ + $$QWTSRCDIR/qwt_plot_zoomer.h \ + $$QWTSRCDIR/qwt_point_mapper.h \ + $$QWTSRCDIR/qwt_point_data.h \ + $$QWTSRCDIR/qwt_raster_data.h \ + $$QWTSRCDIR/qwt_round_scale_draw.h \ + $$QWTSRCDIR/qwt_scale_div.h \ + $$QWTSRCDIR/qwt_scale_draw.h \ + $$QWTSRCDIR/qwt_scale_engine.h \ + $$QWTSRCDIR/qwt_scale_map.h \ + $$QWTSRCDIR/qwt_scale_widget.h \ + $$QWTSRCDIR/qwt_series_data.h \ + $$QWTSRCDIR/qwt_slider.h \ + $$QWTSRCDIR/qwt_spline.h \ + $$QWTSRCDIR/qwt_symbol.h \ + $$QWTSRCDIR/qwt_text.h \ + $$QWTSRCDIR/qwt_text_engine.h \ + $$QWTSRCDIR/qwt_text_label.h \ + $$QWTSRCDIR/qwt_thermo.h \ + $$QWTSRCDIR/qwt_transform.h \ + $$QWTSRCDIR/qwt_wheel.h \ + $$QWTSRCDIR/qwt_widget_overlay.h +SOURCES += $$QWTSRCDIR/qwt_abstract_legend.cpp \ + $$QWTSRCDIR/qwt_abstract_scale.cpp \ + $$QWTSRCDIR/qwt_abstract_scale_draw.cpp \ + $$QWTSRCDIR/qwt_abstract_slider.cpp \ + $$QWTSRCDIR/qwt_analog_clock.cpp \ + $$QWTSRCDIR/qwt_arrow_button.cpp \ + $$QWTSRCDIR/qwt_clipper.cpp \ + $$QWTSRCDIR/qwt_color_map.cpp \ + $$QWTSRCDIR/qwt_compass.cpp \ + $$QWTSRCDIR/qwt_compass_rose.cpp \ + $$QWTSRCDIR/qwt_counter.cpp \ + $$QWTSRCDIR/qwt_curve_fitter.cpp \ + $$QWTSRCDIR/qwt_dial.cpp \ + $$QWTSRCDIR/qwt_dial_needle.cpp \ + $$QWTSRCDIR/qwt_dyngrid_layout.cpp \ + $$QWTSRCDIR/qwt_event_pattern.cpp \ + $$QWTSRCDIR/qwt_graphic.cpp \ + $$QWTSRCDIR/qwt_interval.cpp \ + $$QWTSRCDIR/qwt_knob.cpp \ + $$QWTSRCDIR/qwt_legend.cpp \ + $$QWTSRCDIR/qwt_legend_data.cpp \ + $$QWTSRCDIR/qwt_legend_label.cpp \ + $$QWTSRCDIR/qwt_magnifier.cpp \ + $$QWTSRCDIR/qwt_math.cpp \ + $$QWTSRCDIR/qwt_null_paintdevice.cpp \ + $$QWTSRCDIR/qwt_painter.cpp \ + $$QWTSRCDIR/qwt_painter_command.cpp \ + $$QWTSRCDIR/qwt_panner.cpp \ + $$QWTSRCDIR/qwt_picker.cpp \ + $$QWTSRCDIR/qwt_picker_machine.cpp \ + $$QWTSRCDIR/qwt_pixel_matrix.cpp \ + $$QWTSRCDIR/qwt_plot.cpp \ + $$QWTSRCDIR/qwt_plot_axis.cpp \ + $$QWTSRCDIR/qwt_plot_canvas.cpp \ + $$QWTSRCDIR/qwt_plot_curve.cpp \ + $$QWTSRCDIR/qwt_plot_dict.cpp \ + $$QWTSRCDIR/qwt_plot_grid.cpp \ + $$QWTSRCDIR/qwt_plot_item.cpp \ + $$QWTSRCDIR/qwt_plot_layout.cpp \ + $$QWTSRCDIR/qwt_plot_magnifier.cpp \ + $$QWTSRCDIR/qwt_plot_marker.cpp \ + $$QWTSRCDIR/qwt_plot_panner.cpp \ + $$QWTSRCDIR/qwt_plot_picker.cpp \ + $$QWTSRCDIR/qwt_plot_rasteritem.cpp \ + $$QWTSRCDIR/qwt_plot_scaleitem.cpp \ + $$QWTSRCDIR/qwt_plot_seriesitem.cpp \ + $$QWTSRCDIR/qwt_plot_spectrogram.cpp \ + $$QWTSRCDIR/qwt_plot_svgitem.cpp \ + $$QWTSRCDIR/qwt_plot_xml.cpp \ + $$QWTSRCDIR/qwt_plot_zoomer.cpp \ + $$QWTSRCDIR/qwt_point_mapper.cpp \ + $$QWTSRCDIR/qwt_point_data.cpp \ + $$QWTSRCDIR/qwt_raster_data.cpp \ + $$QWTSRCDIR/qwt_round_scale_draw.cpp \ + $$QWTSRCDIR/qwt_scale_div.cpp \ + $$QWTSRCDIR/qwt_scale_draw.cpp \ + $$QWTSRCDIR/qwt_scale_engine.cpp \ + $$QWTSRCDIR/qwt_scale_map.cpp \ + $$QWTSRCDIR/qwt_scale_widget.cpp \ + $$QWTSRCDIR/qwt_series_data.cpp \ + $$QWTSRCDIR/qwt_slider.cpp \ + $$QWTSRCDIR/qwt_spline.cpp \ + $$QWTSRCDIR/qwt_symbol.cpp \ + $$QWTSRCDIR/qwt_text.cpp \ + $$QWTSRCDIR/qwt_text_engine.cpp \ + $$QWTSRCDIR/qwt_text_label.cpp \ + $$QWTSRCDIR/qwt_thermo.cpp \ + $$QWTSRCDIR/qwt_transform.cpp \ + $$QWTSRCDIR/qwt_wheel.cpp \ + $$QWTSRCDIR/qwt_widget_overlay.cpp diff --git a/libs/qwt/qwt_abstract_legend.cpp b/libs/qwt/qwt_abstract_legend.cpp new file mode 100644 index 000000000..4ecaac61d --- /dev/null +++ b/libs/qwt/qwt_abstract_legend.cpp @@ -0,0 +1,38 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_abstract_legend.h" + +/*! + Constructor + + \param parent Parent widget +*/ +QwtAbstractLegend::QwtAbstractLegend( QWidget *parent ): + QFrame( parent ) +{ +} + +//! Destructor +QwtAbstractLegend::~QwtAbstractLegend() +{ +} + +/*! + Return the extent, that is needed for elements to scroll + the legend ( usually scrollbars ), + + \param orientation Orientation + \return Extent of the corresponding scroll element +*/ +int QwtAbstractLegend::scrollExtent( Qt::Orientation orientation ) const +{ + Q_UNUSED( orientation ); + return 0; +} diff --git a/libs/qwt/qwt_abstract_legend.h b/libs/qwt/qwt_abstract_legend.h new file mode 100644 index 000000000..18bd3f4b9 --- /dev/null +++ b/libs/qwt/qwt_abstract_legend.h @@ -0,0 +1,71 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_ABSTRACT_LEGEND_H +#define QWT_ABSTRACT_LEGEND_H + +#include "qwt_global.h" +#include "qwt_legend_data.h" +#include +#include + +class QVariant; + +/*! + \brief Abstract base class for legend widgets + + Legends, that need to be under control of the QwtPlot layout system + need to be derived from QwtAbstractLegend. + + \note Other type of legends can be implemented by connecting to + the QwtPlot::legendDataChanged() signal. But as these legends + are unknown to the plot layout system the layout code + ( on screen and for QwtPlotRenderer ) need to be organized + in application code. + + \sa QwtLegend + */ +class QWT_EXPORT QwtAbstractLegend : public QFrame +{ + Q_OBJECT + +public: + explicit QwtAbstractLegend( QWidget *parent = NULL ); + virtual ~QwtAbstractLegend(); + + /*! + Render the legend into a given rectangle. + + \param painter Painter + \param rect Bounding rectangle + \param fillBackground When true, fill rect with the widget background + + \sa renderLegend() is used by QwtPlotRenderer + */ + virtual void renderLegend( QPainter *painter, + const QRectF &rect, bool fillBackground ) const = 0; + + //! \return True, when no plot item is inserted + virtual bool isEmpty() const = 0; + + virtual int scrollExtent( Qt::Orientation ) const; + +public Q_SLOTS: + + /*! + \brief Update the entries for a plot item + + \param itemInfo Info about an item + \param data List of legend entry attributes for the item + */ + virtual void updateLegend( const QVariant &itemInfo, + const QList &data ) = 0; +}; + +#endif diff --git a/libs/qwt/qwt_abstract_scale.cpp b/libs/qwt/qwt_abstract_scale.cpp index 1f89a45a0..3018b854e 100644 --- a/libs/qwt/qwt_abstract_scale.cpp +++ b/libs/qwt/qwt_abstract_scale.cpp @@ -7,26 +7,27 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ +#include "qwt_abstract_scale.h" #include "qwt_scale_engine.h" #include "qwt_scale_draw.h" #include "qwt_scale_div.h" #include "qwt_scale_map.h" -#include "qwt_double_interval.h" -#include "qwt_abstract_scale.h" +#include "qwt_interval.h" class QwtAbstractScale::PrivateData { public: PrivateData(): - maxMajor(5), - maxMinor(3), - stepSize(0.0), - autoScale(true) { - scaleEngine = new QwtLinearScaleEngine; + maxMajor( 5 ), + maxMinor( 3 ), + stepSize( 0.0 ) + { + scaleEngine = new QwtLinearScaleEngine(); scaleDraw = new QwtScaleDraw(); } - ~PrivateData() { + ~PrivateData() + { delete scaleEngine; delete scaleDraw; } @@ -37,21 +38,25 @@ public: int maxMajor; int maxMinor; double stepSize; - - bool autoScale; }; /*! Constructor + \param parent Parent widget + Creates a default QwtScaleDraw and a QwtLinearScaleEngine. - Autoscaling is enabled, and the stepSize is initialized by 0.0. + The initial scale boundaries are set to [ 0.0, 100.0 ] + + The scaleStepSize() is initialized to 0.0, scaleMaxMajor() to 5 + and scaleMaxMajor to 3. */ -QwtAbstractScale::QwtAbstractScale() +QwtAbstractScale::QwtAbstractScale( QWidget *parent ): + QWidget( parent ) { d_data = new PrivateData; - rescale(0.0, 100.0); + rescale( 0.0, 100.0, d_data->stepSize ); } //! Destructor @@ -61,138 +66,166 @@ QwtAbstractScale::~QwtAbstractScale() } /*! - \brief Specify a scale. + Set the lower bound of the scale - Disable autoscaling and define a scale by an interval and a step size + \param value Lower bound - \param vmin lower limit of the scale interval - \param vmax upper limit of the scale interval - \param stepSize major step size - \sa setAutoScale() + \sa lowerBound(), setScale(), setUpperBound() + \note For inverted scales the lower bound + is greater than the upper bound */ -void QwtAbstractScale::setScale(double vmin, double vmax, double stepSize) +void QwtAbstractScale::setLowerBound( double value ) { - d_data->autoScale = false; - d_data->stepSize = stepSize; + setScale( value, upperBound() ); +} - rescale(vmin, vmax, stepSize); +/*! + \return Lower bound of the scale + \sa setLowerBound(), setScale(), upperBound() +*/ +double QwtAbstractScale::lowerBound() const +{ + return d_data->scaleDraw->scaleDiv().lowerBound(); } /*! - \brief Specify a scale. + Set the upper bound of the scale - Disable autoscaling and define a scale by an interval and a step size + \param value Upper bound - \param interval Interval - \param stepSize major step size - \sa setAutoScale() + \sa upperBound(), setScale(), setLowerBound() + \note For inverted scales the lower bound + is greater than the upper bound */ -void QwtAbstractScale::setScale(const QwtDoubleInterval &interval, - double stepSize) +void QwtAbstractScale::setUpperBound( double value ) { - setScale(interval.minValue(), interval.maxValue(), stepSize); + setScale( lowerBound(), value ); } +/*! + \return Upper bound of the scale + \sa setUpperBound(), setScale(), lowerBound() +*/ +double QwtAbstractScale::upperBound() const +{ + return d_data->scaleDraw->scaleDiv().upperBound(); +} /*! \brief Specify a scale. - Disable autoscaling and define a scale by a scale division + Define a scale by an interval - \param scaleDiv Scale division - \sa setAutoScale() + The ticks are calculated using scaleMaxMinor(), + scaleMaxMajor() and scaleStepSize(). + + \param lowerBound lower limit of the scale interval + \param upperBound upper limit of the scale interval + + \note For inverted scales the lower bound + is greater than the upper bound */ -void QwtAbstractScale::setScale(const QwtScaleDiv &scaleDiv) +void QwtAbstractScale::setScale( double lowerBound, double upperBound ) { - d_data->autoScale = false; - - if (scaleDiv != d_data->scaleDraw->scaleDiv()) { - d_data->scaleDraw->setScaleDiv(scaleDiv); - scaleChange(); - } + rescale( lowerBound, upperBound, d_data->stepSize ); } /*! - Recalculate the scale division and update the scale draw. + \brief Specify a scale. - \param vmin Lower limit of the scale interval - \param vmax Upper limit of the scale interval - \param stepSize Major step size + Define a scale by an interval - \sa scaleChange() + The ticks are calculated using scaleMaxMinor(), + scaleMaxMajor() and scaleStepSize(). + + \param interval Interval */ -void QwtAbstractScale::rescale(double vmin, double vmax, double stepSize) +void QwtAbstractScale::setScale( const QwtInterval &interval ) { - const QwtScaleDiv scaleDiv = d_data->scaleEngine->divideScale( - vmin, vmax, d_data->maxMajor, d_data->maxMinor, stepSize); - - if ( scaleDiv != d_data->scaleDraw->scaleDiv() ) { - d_data->scaleDraw->setTransformation( - d_data->scaleEngine->transformation()); - d_data->scaleDraw->setScaleDiv(scaleDiv); - scaleChange(); - } + setScale( interval.minValue(), interval.maxValue() ); } /*! - \brief Advise the widget to control the scale range internally. + \brief Specify a scale. - Autoscaling is on by default. - \sa setScale(), autoScale() + scaleMaxMinor(), scaleMaxMajor() and scaleStepSize() and have no effect. + + \param scaleDiv Scale division + \sa setAutoScale() */ -void QwtAbstractScale::setAutoScale() +void QwtAbstractScale::setScale( const QwtScaleDiv &scaleDiv ) { - if (!d_data->autoScale) { - d_data->autoScale = true; + if ( scaleDiv != d_data->scaleDraw->scaleDiv() ) + { +#if 1 + if ( d_data->scaleEngine ) + { + d_data->scaleDraw->setTransformation( + d_data->scaleEngine->transformation() ); + } +#endif + + d_data->scaleDraw->setScaleDiv( scaleDiv ); + scaleChange(); } } -/*! - \return \c true if autoscaling is enabled -*/ -bool QwtAbstractScale::autoScale() const -{ - return d_data->autoScale; -} - /*! \brief Set the maximum number of major tick intervals. The scale's major ticks are calculated automatically such that the number of major intervals does not exceed ticks. + The default value is 5. - \param ticks maximal number of major ticks. - \sa QwtAbstractScaleDraw + + \param ticks Maximal number of major ticks. + + \sa scaleMaxMajor(), setScaleMaxMinor(), + setScaleStepSize(), QwtScaleEngine::divideInterval() */ -void QwtAbstractScale::setScaleMaxMajor(int ticks) +void QwtAbstractScale::setScaleMaxMajor( int ticks ) { - if (ticks != d_data->maxMajor) { + if ( ticks != d_data->maxMajor ) + { d_data->maxMajor = ticks; updateScaleDraw(); } } +/*! + \return Maximal number of major tick intervals + \sa setScaleMaxMajor(), scaleMaxMinor() +*/ +int QwtAbstractScale::scaleMaxMajor() const +{ + return d_data->maxMajor; +} + /*! \brief Set the maximum number of minor tick intervals The scale's minor ticks are calculated automatically such that the number of minor intervals does not exceed ticks. The default value is 3. - \param ticks - \sa QwtAbstractScaleDraw + + \param ticks Maximal number of minor ticks. + + \sa scaleMaxMajor(), setScaleMaxMinor(), + setScaleStepSize(), QwtScaleEngine::divideInterval() */ -void QwtAbstractScale::setScaleMaxMinor(int ticks) +void QwtAbstractScale::setScaleMaxMinor( int ticks ) { - if ( ticks != d_data->maxMinor) { + if ( ticks != d_data->maxMinor ) + { d_data->maxMinor = ticks; updateScaleDraw(); } } /*! - \return Max. number of minor tick intervals - The default value is 3. + \return Maximal number of minor tick intervals + \sa setScaleMaxMinor(), scaleMaxMajor() */ int QwtAbstractScale::scaleMaxMinor() const { @@ -200,27 +233,52 @@ int QwtAbstractScale::scaleMaxMinor() const } /*! - \return Max. number of major tick intervals - The default value is 5. + \brief Set the step size used for calculating a scale division + + The step size is hint for calculating the intervals for + the major ticks of the scale. A value of 0.0 is interpreted + as no hint. + + \param stepSize Hint for the step size of the scale + + \sa scaleStepSize(), QwtScaleEngine::divideScale() + + \note Position and distance between the major ticks also + depends on scaleMaxMajor(). */ -int QwtAbstractScale::scaleMaxMajor() const +void QwtAbstractScale::setScaleStepSize( double stepSize ) { - return d_data->maxMajor; + if ( stepSize != d_data->stepSize ) + { + d_data->stepSize = stepSize; + updateScaleDraw(); + } +} + +/*! + \return Hint for the step size of the scale + \sa setScaleStepSize(), QwtScaleEngine::divideScale() +*/ +double QwtAbstractScale::scaleStepSize() const +{ + return d_data->stepSize; } /*! \brief Set a scale draw scaleDraw has to be created with new and will be deleted in - ~QwtAbstractScale or the next call of setAbstractScaleDraw. + the destructor or the next call of setAbstractScaleDraw(). + + \sa abstractScaleDraw() */ -void QwtAbstractScale::setAbstractScaleDraw(QwtAbstractScaleDraw *scaleDraw) +void QwtAbstractScale::setAbstractScaleDraw( QwtAbstractScaleDraw *scaleDraw ) { if ( scaleDraw == NULL || scaleDraw == d_data->scaleDraw ) return; if ( d_data->scaleDraw != NULL ) - scaleDraw->setScaleDiv(d_data->scaleDraw->scaleDiv()); + scaleDraw->setScaleDiv( d_data->scaleDraw->scaleDiv() ); delete d_data->scaleDraw; d_data->scaleDraw = scaleDraw; @@ -244,32 +302,27 @@ const QwtAbstractScaleDraw *QwtAbstractScale::abstractScaleDraw() const return d_data->scaleDraw; } -void QwtAbstractScale::updateScaleDraw() -{ - rescale( d_data->scaleDraw->scaleDiv().lBound(), - d_data->scaleDraw->scaleDiv().hBound(), d_data->stepSize); -} - /*! \brief Set a scale engine - The scale engine is responsible for calculating the scale division, - and in case of auto scaling how to align the scale. + The scale engine is responsible for calculating the scale division + and provides a transformation between scale and widget coordinates. scaleEngine has to be created with new and will be deleted in - ~QwtAbstractScale or the next call of setScaleEngine. + the destructor or the next call of setScaleEngine. */ -void QwtAbstractScale::setScaleEngine(QwtScaleEngine *scaleEngine) +void QwtAbstractScale::setScaleEngine( QwtScaleEngine *scaleEngine ) { - if ( scaleEngine != NULL && scaleEngine != d_data->scaleEngine ) { + if ( scaleEngine != NULL && scaleEngine != d_data->scaleEngine ) + { delete d_data->scaleEngine; d_data->scaleEngine = scaleEngine; } } /*! - \return Scale engine - \sa setScaleEngine() + \return Scale engine + \sa setScaleEngine() */ const QwtScaleEngine *QwtAbstractScale::scaleEngine() const { @@ -277,8 +330,8 @@ const QwtScaleEngine *QwtAbstractScale::scaleEngine() const } /*! - \return Scale engine - \sa setScaleEngine() + \return Scale engine + \sa setScaleEngine() */ QwtScaleEngine *QwtAbstractScale::scaleEngine() { @@ -286,18 +339,111 @@ QwtScaleEngine *QwtAbstractScale::scaleEngine() } /*! - \brief Notify changed scale + \return Scale boundaries and positions of the ticks - Dummy empty implementation, intended to be overloaded by derived classes -*/ -void QwtAbstractScale::scaleChange() + The scale division might have been assigned explicitly + or calculated implicitly by rescale(). + */ +const QwtScaleDiv &QwtAbstractScale::scaleDiv() const { + return d_data->scaleDraw->scaleDiv(); } /*! - \return abstractScaleDraw()->scaleMap() -*/ + \return Map to translate between scale and widget coordinates + */ const QwtScaleMap &QwtAbstractScale::scaleMap() const { return d_data->scaleDraw->scaleMap(); } + +/*! + Translate a scale value into a widget coordinate + + \param value Scale value + \return Corresponding widget coordinate for value + \sa scaleMap(), invTransform() + */ +int QwtAbstractScale::transform( double value ) const +{ + return qRound( d_data->scaleDraw->scaleMap().transform( value ) ); +} + +/*! + Translate a widget coordinate into a scale value + + \param value Widget coordinate + \return Corresponding scale coordinate for value + \sa scaleMap(), transform() + */ +double QwtAbstractScale::invTransform( int value ) const +{ + return d_data->scaleDraw->scaleMap().invTransform( value ); +} + +/*! + \return True, when the scale is increasing in opposite direction + to the widget coordinates + */ +bool QwtAbstractScale::isInverted() const +{ + return d_data->scaleDraw->scaleMap().isInverting(); +} + +/*! + \return The boundary with the smaller value + \sa maximum(), lowerBound(), upperBound() + */ +double QwtAbstractScale::minimum() const +{ + return qMin( d_data->scaleDraw->scaleDiv().lowerBound(), + d_data->scaleDraw->scaleDiv().upperBound() ); +} + +/*! + \return The boundary with the larger value + \sa minimum(), lowerBound(), upperBound() + */ +double QwtAbstractScale::maximum() const +{ + return qMax( d_data->scaleDraw->scaleDiv().lowerBound(), + d_data->scaleDraw->scaleDiv().upperBound() ); +} + +//! Notify changed scale +void QwtAbstractScale::scaleChange() +{ +} + +/*! + Recalculate the scale division and update the scale. + + \param lowerBound Lower limit of the scale interval + \param upperBound Upper limit of the scale interval + \param stepSize Major step size + + \sa scaleChange() +*/ +void QwtAbstractScale::rescale( + double lowerBound, double upperBound, double stepSize ) +{ + const QwtScaleDiv scaleDiv = d_data->scaleEngine->divideScale( + lowerBound, upperBound, d_data->maxMajor, d_data->maxMinor, stepSize ); + + if ( scaleDiv != d_data->scaleDraw->scaleDiv() ) + { +#if 1 + d_data->scaleDraw->setTransformation( + d_data->scaleEngine->transformation() ); +#endif + + d_data->scaleDraw->setScaleDiv( scaleDiv ); + scaleChange(); + } +} + +void QwtAbstractScale::updateScaleDraw() +{ + rescale( d_data->scaleDraw->scaleDiv().lowerBound(), + d_data->scaleDraw->scaleDiv().upperBound(), d_data->stepSize ); +} diff --git a/libs/qwt/qwt_abstract_scale.h b/libs/qwt/qwt_abstract_scale.h index dac0aa3f8..4ed6616aa 100644 --- a/libs/qwt/qwt_abstract_scale.h +++ b/libs/qwt/qwt_abstract_scale.h @@ -11,50 +11,85 @@ #define QWT_ABSTRACT_SCALE_H #include "qwt_global.h" +#include class QwtScaleEngine; class QwtAbstractScaleDraw; class QwtScaleDiv; class QwtScaleMap; -class QwtDoubleInterval; +class QwtInterval; /*! - \brief An abstract base class for classes containing a scale + \brief An abstract base class for widgets having a scale - QwtAbstractScale is used to provide classes with a QwtScaleDraw, - and a QwtScaleDiv. The QwtScaleDiv might be set explicitely - or calculated by a QwtScaleEngine. + The scale of an QwtAbstractScale is determined by a QwtScaleDiv + definition, that contains the boundaries and the ticks of the scale. + The scale is painted using a QwtScaleDraw object. + + The scale division might be assigned explicitly - but usually + it is calculated from the boundaries using a QwtScaleEngine. + + The scale engine also decides the type of transformation of the scale + ( linear, logarithmic ... ). */ -class QWT_EXPORT QwtAbstractScale +class QWT_EXPORT QwtAbstractScale: public QWidget { + Q_OBJECT + + Q_PROPERTY( double lowerBound READ lowerBound WRITE setLowerBound ) + Q_PROPERTY( double upperBound READ upperBound WRITE setUpperBound ) + + Q_PROPERTY( int scaleMaxMajor READ scaleMaxMajor WRITE setScaleMaxMajor ) + Q_PROPERTY( int scaleMaxMinor READ scaleMaxMinor WRITE setScaleMaxMinor ) + + Q_PROPERTY( double scaleStepSize READ scaleStepSize WRITE setScaleStepSize ) + public: - QwtAbstractScale(); + QwtAbstractScale( QWidget *parent = NULL ); virtual ~QwtAbstractScale(); - void setScale(double vmin, double vmax, double step = 0.0); - void setScale(const QwtDoubleInterval &, double step = 0.0); - void setScale(const QwtScaleDiv &s); + void setScale( double lowerBound, double upperBound ); + void setScale( const QwtInterval & ); + void setScale( const QwtScaleDiv & ); + + const QwtScaleDiv& scaleDiv() const; - void setAutoScale(); - bool autoScale() const; + void setLowerBound( double value ); + double lowerBound() const; - void setScaleMaxMajor( int ticks); + void setUpperBound( double value ); + double upperBound() const; + + void setScaleStepSize( double stepSize ); + double scaleStepSize() const; + + void setScaleMaxMajor( int ticks ); int scaleMaxMinor() const; - void setScaleMaxMinor( int ticks); + void setScaleMaxMinor( int ticks ); int scaleMaxMajor() const; - void setScaleEngine(QwtScaleEngine *); + void setScaleEngine( QwtScaleEngine * ); const QwtScaleEngine *scaleEngine() const; QwtScaleEngine *scaleEngine(); + int transform( double ) const; + double invTransform( int ) const; + + bool isInverted() const; + + double minimum() const; + double maximum() const; + const QwtScaleMap &scaleMap() const; protected: - void rescale(double vmin, double vmax, double step = 0.0); + void rescale( double lowerBound, + double upperBound, double stepSize ); + + void setAbstractScaleDraw( QwtAbstractScaleDraw * ); - void setAbstractScaleDraw(QwtAbstractScaleDraw *); const QwtAbstractScaleDraw *abstractScaleDraw() const; QwtAbstractScaleDraw *abstractScaleDraw(); diff --git a/libs/qwt/qwt_abstract_scale_draw.cpp b/libs/qwt/qwt_abstract_scale_draw.cpp index b04f2f5eb..201173303 100644 --- a/libs/qwt/qwt_abstract_scale_draw.cpp +++ b/libs/qwt/qwt_abstract_scale_draw.cpp @@ -7,39 +7,43 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ -// vim: expandtab - -#include -#include -#include -#include +#include "qwt_abstract_scale_draw.h" #include "qwt_math.h" #include "qwt_text.h" #include "qwt_painter.h" #include "qwt_scale_map.h" -#include "qwt_scale_draw.h" +#include +#include +#include +#include class QwtAbstractScaleDraw::PrivateData { public: PrivateData(): - components(Backbone | Ticks | Labels), - spacing(4), - minExtent(0) { - tickLength[QwtScaleDiv::MinorTick] = 4; - tickLength[QwtScaleDiv::MediumTick] = 6; - tickLength[QwtScaleDiv::MajorTick] = 8; + spacing( 4.0 ), + penWidth( 0 ), + minExtent( 0.0 ) + { + components = QwtAbstractScaleDraw::Backbone + | QwtAbstractScaleDraw::Ticks + | QwtAbstractScaleDraw::Labels; + + tickLength[QwtScaleDiv::MinorTick] = 4.0; + tickLength[QwtScaleDiv::MediumTick] = 6.0; + tickLength[QwtScaleDiv::MajorTick] = 8.0; } - int components; + ScaleComponents components; QwtScaleMap map; - QwtScaleDiv scldiv; + QwtScaleDiv scaleDiv; - int spacing; - int tickLength[QwtScaleDiv::NTickTypes]; + double spacing; + double tickLength[QwtScaleDiv::NTickTypes]; + int penWidth; - int minExtent; + double minExtent; QMap labelCache; }; @@ -56,23 +60,11 @@ QwtAbstractScaleDraw::QwtAbstractScaleDraw() d_data = new QwtAbstractScaleDraw::PrivateData; } -//! Copy constructor -QwtAbstractScaleDraw::QwtAbstractScaleDraw(const QwtAbstractScaleDraw &other) -{ - d_data = new QwtAbstractScaleDraw::PrivateData(*other.d_data); -} - //! Destructor QwtAbstractScaleDraw::~QwtAbstractScaleDraw() { delete d_data; } -//! Assignment operator -QwtAbstractScaleDraw &QwtAbstractScaleDraw::operator=(const QwtAbstractScaleDraw &other) -{ - *d_data = *other.d_data; - return *this; -} /*! En/Disable a component of the scale @@ -80,10 +72,10 @@ QwtAbstractScaleDraw &QwtAbstractScaleDraw::operator=(const QwtAbstractScaleDraw \param component Scale component \param enable On/Off - \sa QwtAbstractScaleDraw::hasComponent + \sa hasComponent() */ void QwtAbstractScaleDraw::enableComponent( - ScaleComponent component, bool enable) + ScaleComponent component, bool enable ) { if ( enable ) d_data->components |= component; @@ -93,21 +85,24 @@ void QwtAbstractScaleDraw::enableComponent( /*! Check if a component is enabled - \sa QwtAbstractScaleDraw::enableComponent + + \param component Component type + \return true, when component is enabled + \sa enableComponent() */ -bool QwtAbstractScaleDraw::hasComponent(ScaleComponent component) const +bool QwtAbstractScaleDraw::hasComponent( ScaleComponent component ) const { - return (d_data->components & component); + return ( d_data->components & component ); } /*! Change the scale division - \param sd New scale division + \param scaleDiv New scale division */ -void QwtAbstractScaleDraw::setScaleDiv(const QwtScaleDiv &sd) +void QwtAbstractScaleDraw::setScaleDiv( const QwtScaleDiv &scaleDiv ) { - d_data->scldiv = sd; - d_data->map.setScaleInterval(sd.lBound(), sd.hBound()); + d_data->scaleDiv = scaleDiv; + d_data->map.setScaleInterval( scaleDiv.lowerBound(), scaleDiv.upperBound() ); d_data->labelCache.clear(); } @@ -116,13 +111,13 @@ void QwtAbstractScaleDraw::setScaleDiv(const QwtScaleDiv &sd) \param transformation New scale transformation */ void QwtAbstractScaleDraw::setTransformation( - QwtScaleTransformation *transformation) + QwtTransform *transformation ) { - d_data->map.setTransformation(transformation); + d_data->map.setTransformation( transformation ); } //! \return Map how to translate between scale and pixel values -const QwtScaleMap &QwtAbstractScaleDraw::map() const +const QwtScaleMap &QwtAbstractScaleDraw::scaleMap() const { return d_data->map; } @@ -136,22 +131,31 @@ QwtScaleMap &QwtAbstractScaleDraw::scaleMap() //! \return scale division const QwtScaleDiv& QwtAbstractScaleDraw::scaleDiv() const { - return d_data->scldiv; + return d_data->scaleDiv; } -#if QT_VERSION < 0x040000 /*! - \brief Draw the scale + \brief Specify the width of the scale pen + \param width Pen width + \sa penWidth() +*/ +void QwtAbstractScaleDraw::setPenWidth( int width ) +{ + if ( width < 0 ) + width = 0; - \param painter The painter + if ( width != d_data->penWidth ) + d_data->penWidth = width; +} - \param colorGroup Color group, text color is used for the labels, - foreground color for ticks and backbone +/*! + \return Scale pen width + \sa setPenWidth() */ -void QwtAbstractScaleDraw::draw(QPainter *painter, - const QColorGroup& colorGroup) const - -#else +int QwtAbstractScaleDraw::penWidth() const +{ + return d_data->penWidth; +} /*! \brief Draw the scale @@ -161,70 +165,75 @@ void QwtAbstractScaleDraw::draw(QPainter *painter, \param palette Palette, text color is used for the labels, foreground color for ticks and backbone */ -void QwtAbstractScaleDraw::draw(QPainter *painter, - const QPalette& palette) const -#endif +void QwtAbstractScaleDraw::draw( QPainter *painter, + const QPalette& palette ) const { - if ( hasComponent(QwtAbstractScaleDraw::Labels) ) { - painter->save(); + painter->save(); -#if QT_VERSION < 0x040000 - painter->setPen(colorGroup.text()); // ignore pen style -#else - painter->setPen(palette.color(QPalette::Text)); // ignore pen style -#endif + QPen pen = painter->pen(); + pen.setWidth( d_data->penWidth ); + pen.setCosmetic( false ); + painter->setPen( pen ); - const QwtValueList &majorTicks = - d_data->scldiv.ticks(QwtScaleDiv::MajorTick); + if ( hasComponent( QwtAbstractScaleDraw::Labels ) ) + { + painter->save(); + painter->setPen( palette.color( QPalette::Text ) ); // ignore pen style + + const QList &majorTicks = + d_data->scaleDiv.ticks( QwtScaleDiv::MajorTick ); - for (int i = 0; i < (int)majorTicks.count(); i++) { + for ( int i = 0; i < majorTicks.count(); i++ ) + { const double v = majorTicks[i]; - if ( d_data->scldiv.contains(v) ) - drawLabel(painter, majorTicks[i]); + if ( d_data->scaleDiv.contains( v ) ) + drawLabel( painter, v ); } painter->restore(); } - if ( hasComponent(QwtAbstractScaleDraw::Ticks) ) { + if ( hasComponent( QwtAbstractScaleDraw::Ticks ) ) + { painter->save(); QPen pen = painter->pen(); -#if QT_VERSION < 0x040000 - pen.setColor(colorGroup.foreground()); -#else - pen.setColor(palette.color(QPalette::Foreground)); -#endif - painter->setPen(pen); + pen.setColor( palette.color( QPalette::WindowText ) ); + pen.setCapStyle( Qt::FlatCap ); + + painter->setPen( pen ); for ( int tickType = QwtScaleDiv::MinorTick; - tickType < QwtScaleDiv::NTickTypes; tickType++ ) { - const QwtValueList &ticks = d_data->scldiv.ticks(tickType); - for (int i = 0; i < (int)ticks.count(); i++) { + tickType < QwtScaleDiv::NTickTypes; tickType++ ) + { + const QList &ticks = d_data->scaleDiv.ticks( tickType ); + for ( int i = 0; i < ticks.count(); i++ ) + { const double v = ticks[i]; - if ( d_data->scldiv.contains(v) ) - drawTick(painter, v, d_data->tickLength[tickType]); + if ( d_data->scaleDiv.contains( v ) ) + drawTick( painter, v, d_data->tickLength[tickType] ); } } painter->restore(); } - if ( hasComponent(QwtAbstractScaleDraw::Backbone) ) { + if ( hasComponent( QwtAbstractScaleDraw::Backbone ) ) + { painter->save(); QPen pen = painter->pen(); -#if QT_VERSION < 0x040000 - pen.setColor(colorGroup.foreground()); -#else - pen.setColor(palette.color(QPalette::Foreground)); -#endif - painter->setPen(pen); + pen.setColor( palette.color( QPalette::WindowText ) ); + pen.setCapStyle( Qt::FlatCap ); + + painter->setPen( pen ); - drawBackbone(painter); + drawBackbone( painter ); painter->restore(); } + + painter->restore(); } /*! @@ -235,9 +244,9 @@ void QwtAbstractScaleDraw::draw(QPainter *painter, \param spacing Spacing - \sa QwtAbstractScaleDraw::spacing + \sa spacing() */ -void QwtAbstractScaleDraw::setSpacing(int spacing) +void QwtAbstractScaleDraw::setSpacing( double spacing ) { if ( spacing < 0 ) spacing = 0; @@ -251,9 +260,10 @@ void QwtAbstractScaleDraw::setSpacing(int spacing) The spacing is the distance between ticks and labels. The default spacing is 4 pixels. - \sa QwtAbstractScaleDraw::setSpacing + \return Spacing + \sa setSpacing() */ -int QwtAbstractScaleDraw::spacing() const +double QwtAbstractScaleDraw::spacing() const { return d_data->spacing; } @@ -261,7 +271,7 @@ int QwtAbstractScaleDraw::spacing() const /*! \brief Set a minimum for the extent - The extent is calculated from the coomponents of the + The extent is calculated from the components of the scale draw. In situations, where the labels are changing and the layout depends on the extent (f.e scrolling a scale), setting an upper limit as minimum extent will @@ -271,19 +281,20 @@ int QwtAbstractScaleDraw::spacing() const \sa extent(), minimumExtent() */ -void QwtAbstractScaleDraw::setMinimumExtent(int minExtent) +void QwtAbstractScaleDraw::setMinimumExtent( double minExtent ) { - if ( minExtent < 0 ) - minExtent = 0; + if ( minExtent < 0.0 ) + minExtent = 0.0; d_data->minExtent = minExtent; } /*! Get the minimum extent + \return Minimum extent \sa extent(), setMinimumExtent() */ -int QwtAbstractScaleDraw::minimumExtent() const +double QwtAbstractScaleDraw::minimumExtent() const { return d_data->minExtent; } @@ -297,33 +308,33 @@ int QwtAbstractScaleDraw::minimumExtent() const \warning the length is limited to [0..1000] */ void QwtAbstractScaleDraw::setTickLength( - QwtScaleDiv::TickType tickType, int length) + QwtScaleDiv::TickType tickType, double length ) { if ( tickType < QwtScaleDiv::MinorTick || - tickType > QwtScaleDiv::MajorTick ) { + tickType > QwtScaleDiv::MajorTick ) + { return; } - if ( length < 0 ) - length = 0; + if ( length < 0.0 ) + length = 0.0; - const int maxTickLen = 1000; + const double maxTickLen = 1000.0; if ( length > maxTickLen ) - length = 1000; + length = maxTickLen; d_data->tickLength[tickType] = length; } /*! - Return the length of the ticks - - \sa QwtAbstractScaleDraw::setTickLength, - QwtAbstractScaleDraw::majTickLength + \return Length of the ticks + \sa setTickLength(), maxTickLength() */ -int QwtAbstractScaleDraw::tickLength(QwtScaleDiv::TickType tickType) const +double QwtAbstractScaleDraw::tickLength( QwtScaleDiv::TickType tickType ) const { if ( tickType < QwtScaleDiv::MinorTick || - tickType > QwtScaleDiv::MajorTick ) { + tickType > QwtScaleDiv::MajorTick ) + { return 0; } @@ -331,27 +342,34 @@ int QwtAbstractScaleDraw::tickLength(QwtScaleDiv::TickType tickType) const } /*! - The same as QwtAbstractScaleDraw::tickLength(QwtScaleDiv::MajorTick). + \return Length of the longest tick + + Useful for layout calculations + \sa tickLength(), setTickLength() */ -int QwtAbstractScaleDraw::majTickLength() const +double QwtAbstractScaleDraw::maxTickLength() const { - return d_data->tickLength[QwtScaleDiv::MajorTick]; + double length = 0.0; + for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ ) + length = qMax( length, d_data->tickLength[i] ); + + return length; } /*! \brief Convert a value into its representing label The value is converted to a plain text using - QLocale::system().toString(value). + QLocale().toString(value). This method is often overloaded by applications to have individual labels. \param value Value \return Label string. */ -QwtText QwtAbstractScaleDraw::label(double value) const +QwtText QwtAbstractScaleDraw::label( double value ) const { - return QLocale::system().toString(value); + return QLocale().toString( value ); } /*! @@ -368,28 +386,29 @@ QwtText QwtAbstractScaleDraw::label(double value) const \return Tick label */ const QwtText &QwtAbstractScaleDraw::tickLabel( - const QFont &font, double value) const + const QFont &font, double value ) const { - QMap::const_iterator it = d_data->labelCache.find(value); - if ( it == d_data->labelCache.end() ) { - QwtText lbl = label(value); - lbl.setRenderFlags(0); - lbl.setLayoutAttribute(QwtText::MinimumLayout); + QMap::const_iterator it = d_data->labelCache.find( value ); + if ( it == d_data->labelCache.end() ) + { + QwtText lbl = label( value ); + lbl.setRenderFlags( 0 ); + lbl.setLayoutAttribute( QwtText::MinimumLayout ); - (void)lbl.textSize(font); // initialize the internal cache + ( void )lbl.textSize( font ); // initialize the internal cache - it = d_data->labelCache.insert(value, lbl); + it = d_data->labelCache.insert( value, lbl ); } - return (*it); + return ( *it ); } /*! - Invalidate the cache used by QwtAbstractScaleDraw::tickLabel + Invalidate the cache used by tickLabel() The cache is invalidated, when a new QwtScaleDiv is set. If the labels need to be changed. while the same QwtScaleDiv is set, - QwtAbstractScaleDraw::invalidateCache needs to be called manually. + invalidateCache() needs to be called manually. */ void QwtAbstractScaleDraw::invalidateCache() { diff --git a/libs/qwt/qwt_abstract_scale_draw.h b/libs/qwt/qwt_abstract_scale_draw.h index a2b7895f4..d0f1ec3c3 100644 --- a/libs/qwt/qwt_abstract_scale_draw.h +++ b/libs/qwt/qwt_abstract_scale_draw.h @@ -14,15 +14,10 @@ #include "qwt_scale_div.h" #include "qwt_text.h" - -#if QT_VERSION < 0x040000 -class QColorGroup; -#else class QPalette; -#endif class QPainter; class QFont; -class QwtScaleTransformation; +class QwtTransform; class QwtScaleMap; /*! @@ -31,8 +26,7 @@ class QwtScaleMap; QwtAbstractScaleDraw can be used to draw linear or logarithmic scales. After a scale division has been specified as a QwtScaleDiv object - using QwtAbstractScaleDraw::setScaleDiv(const QwtScaleDiv &s), - the scale can be drawn with the QwtAbstractScaleDraw::draw() member. + using setScaleDiv(), the scale can be drawn with the draw() member. */ class QWT_EXPORT QwtAbstractScaleDraw { @@ -40,66 +34,66 @@ public: /*! Components of a scale + \sa enableComponent(), hasComponent + */ + enum ScaleComponent + { + //! Backbone = the line where the ticks are located + Backbone = 0x01, - - Backbone - - Ticks - - Labels - - \sa QwtAbstractScaleDraw::enableComponent, - QwtAbstractScaleDraw::hasComponent - */ + //! Ticks + Ticks = 0x02, - enum ScaleComponent { - Backbone = 1, - Ticks = 2, - Labels = 4 + //! Labels + Labels = 0x04 }; + //! Scale components + typedef QFlags ScaleComponents; + QwtAbstractScaleDraw(); - QwtAbstractScaleDraw( const QwtAbstractScaleDraw & ); virtual ~QwtAbstractScaleDraw(); - QwtAbstractScaleDraw &operator=(const QwtAbstractScaleDraw &); - - void setScaleDiv(const QwtScaleDiv &s); + void setScaleDiv( const QwtScaleDiv &s ); const QwtScaleDiv& scaleDiv() const; - void setTransformation(QwtScaleTransformation *); - const QwtScaleMap &map() const; + void setTransformation( QwtTransform * ); + const QwtScaleMap &scaleMap() const; + QwtScaleMap &scaleMap(); - void enableComponent(ScaleComponent, bool enable = true); - bool hasComponent(ScaleComponent) const; + void enableComponent( ScaleComponent, bool enable = true ); + bool hasComponent( ScaleComponent ) const; - void setTickLength(QwtScaleDiv::TickType, int length); - int tickLength(QwtScaleDiv::TickType) const; - int majTickLength() const; + void setTickLength( QwtScaleDiv::TickType, double length ); + double tickLength( QwtScaleDiv::TickType ) const; + double maxTickLength() const; - void setSpacing(int margin); - int spacing() const; + void setSpacing( double margin ); + double spacing() const; -#if QT_VERSION < 0x040000 - virtual void draw(QPainter *, const QColorGroup &) const; -#else - virtual void draw(QPainter *, const QPalette &) const; -#endif + void setPenWidth( int width ); + int penWidth() const; + + virtual void draw( QPainter *, const QPalette & ) const; - virtual QwtText label(double) const; + virtual QwtText label( double ) const; /*! Calculate the extent - The extent is the distcance from the baseline to the outermost + The extent is the distance from the baseline to the outermost pixel of the scale draw in opposite to its orientation. It is at least minimumExtent() pixels. + \param font Font used for drawing the tick labels + \return Number of pixels + \sa setMinimumExtent(), minimumExtent() */ - virtual int extent(const QPen &, const QFont &) const = 0; + virtual double extent( const QFont &font ) const = 0; - void setMinimumExtent(int); - int minimumExtent() const; - - QwtScaleMap &scaleMap(); + void setMinimumExtent( double ); + double minimumExtent() const; protected: /*! @@ -107,11 +101,11 @@ protected: \param painter Painter \param value Value of the tick - \param len Lenght of the tick + \param len Length of the tick \sa drawBackbone(), drawLabel() */ - virtual void drawTick(QPainter *painter, double value, int len) const = 0; + virtual void drawTick( QPainter *painter, double value, double len ) const = 0; /*! Draws the baseline of the scale @@ -119,7 +113,7 @@ protected: \sa drawTick(), drawLabel() */ - virtual void drawBackbone(QPainter *painter) const = 0; + virtual void drawBackbone( QPainter *painter ) const = 0; /*! Draws the label for a major scale tick @@ -127,19 +121,21 @@ protected: \param painter Painter \param value Value - \sa drawTick, drawBackbone + \sa drawTick(), drawBackbone() */ - virtual void drawLabel(QPainter *painter, double value) const = 0; + virtual void drawLabel( QPainter *painter, double value ) const = 0; void invalidateCache(); - const QwtText &tickLabel(const QFont &, double value) const; + const QwtText &tickLabel( const QFont &, double value ) const; private: - int operator==(const QwtAbstractScaleDraw &) const; - int operator!=(const QwtAbstractScaleDraw &) const; + QwtAbstractScaleDraw( const QwtAbstractScaleDraw & ); + QwtAbstractScaleDraw &operator=( const QwtAbstractScaleDraw & ); class PrivateData; PrivateData *d_data; }; +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtAbstractScaleDraw::ScaleComponents ) + #endif diff --git a/libs/qwt/qwt_abstract_slider.cpp b/libs/qwt/qwt_abstract_slider.cpp index 0f4dcb2bb..7bac22e61 100644 --- a/libs/qwt/qwt_abstract_slider.cpp +++ b/libs/qwt/qwt_abstract_slider.cpp @@ -7,84 +7,149 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ -#include -#include #include "qwt_abstract_slider.h" +#include "qwt_abstract_scale_draw.h" #include "qwt_math.h" +#include "qwt_scale_map.h" +#include -#ifndef WHEEL_DELTA -#define WHEEL_DELTA 120 +#if QT_VERSION < 0x040601 +#define qFabs(x) ::fabs(x) #endif +static double qwtAlignToScaleDiv( + const QwtAbstractSlider *slider, double value ) +{ + const QwtScaleDiv &sd = slider->scaleDiv(); + + const int tValue = slider->transform( value ); + + if ( tValue == slider->transform( sd.lowerBound() ) ) + return sd.lowerBound(); + + if ( tValue == slider->transform( sd.lowerBound() ) ) + return sd.upperBound(); + + for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ ) + { + const QList ticks = sd.ticks( i ); + for ( int j = 0; j < ticks.size(); j++ ) + { + if ( slider->transform( ticks[ j ] ) == tValue ) + return ticks[ j ]; + } + } + + return value; +} + class QwtAbstractSlider::PrivateData { public: PrivateData(): - scrollMode(ScrNone), - mouseOffset(0.0), - tracking(true), - tmrID(0), - updTime(150), - mass(0.0), - readOnly(false) { + isScrolling( false ), + isTracking( true ), + pendingValueChanged( false ), + readOnly( false ), + totalSteps( 100 ), + singleSteps( 1 ), + pageSteps( 10 ), + stepAlignment( true ), + isValid( false ), + value( 0.0 ), + wrapping( false ), + invertedControls( false ) + { } - int scrollMode; - double mouseOffset; - int direction; - int tracking; - - int tmrID; - int updTime; - int timerTick; - QTime time; - double speed; - double mass; - Qt::Orientation orientation; + bool isScrolling; + bool isTracking; + bool pendingValueChanged; + bool readOnly; + + uint totalSteps; + uint singleSteps; + uint pageSteps; + bool stepAlignment; + + bool isValid; + double value; + + bool wrapping; + bool invertedControls; }; /*! - \brief Constructor + \brief Constructor + + The scale is initialized to [0.0, 100.0], the + number of steps is set to 100 with 1 and 10 and single + an page step sizes. Step alignment is enabled. + + The initial value is invalid. - \param orientation Orientation - \param parent Parent widget + \param parent Parent widget */ -QwtAbstractSlider::QwtAbstractSlider( - Qt::Orientation orientation, QWidget *parent): - QWidget(parent, NULL) +QwtAbstractSlider::QwtAbstractSlider( QWidget *parent ): + QwtAbstractScale( parent ) { d_data = new QwtAbstractSlider::PrivateData; - d_data->orientation = orientation; -#if QT_VERSION >= 0x040000 - using namespace Qt; -#endif - setFocusPolicy(TabFocus); + setScale( 0.0, 100.0 ); + setFocusPolicy( Qt::StrongFocus ); } //! Destructor QwtAbstractSlider::~QwtAbstractSlider() { - if(d_data->tmrID) - killTimer(d_data->tmrID); - delete d_data; } +/*! + Set the value to be valid/invalid + + \param on When true, the value is invalidated + + \sa setValue() +*/ +void QwtAbstractSlider::setValid( bool on ) +{ + if ( on != d_data->isValid ) + { + d_data->isValid = on; + sliderChange(); + + Q_EMIT valueChanged( d_data->value ); + } +} + +//! \return True, when the value is invalid +bool QwtAbstractSlider::isValid() const +{ + return d_data->isValid; +} + /*! En/Disable read only mode In read only mode the slider can't be controlled by mouse or keyboard. - \param readOnly Enables in case of true + \param on Enables in case of true \sa isReadOnly() + + \warning The focus policy is set to Qt::StrongFocus or Qt::NoFocus */ -void QwtAbstractSlider::setReadOnly(bool readOnly) +void QwtAbstractSlider::setReadOnly( bool on ) { - d_data->readOnly = readOnly; - update(); + if ( d_data->readOnly != on ) + { + d_data->readOnly = on; + setFocusPolicy( on ? Qt::StrongFocus : Qt::NoFocus ); + + update(); + } } /*! @@ -100,452 +165,658 @@ bool QwtAbstractSlider::isReadOnly() const } /*! - \brief Set the orientation. - \param o Orientation. Allowed values are - Qt::Horizontal and Qt::Vertical. -*/ -void QwtAbstractSlider::setOrientation(Qt::Orientation o) -{ - d_data->orientation = o; -} + \brief Enables or disables tracking. -/*! - \return Orientation - \sa setOrientation() -*/ -Qt::Orientation QwtAbstractSlider::orientation() const -{ - return d_data->orientation; -} + If tracking is enabled, the slider emits the valueChanged() + signal while the movable part of the slider is being dragged. + If tracking is disabled, the slider emits the valueChanged() signal + only when the user releases the slider. -//! Stop updating if automatic scrolling is active + Tracking is enabled by default. + \param on \c true (enable) or \c false (disable) tracking. -void QwtAbstractSlider::stopMoving() + \sa isTracking(), sliderMoved() +*/ +void QwtAbstractSlider::setTracking( bool on ) { - if(d_data->tmrID) { - killTimer(d_data->tmrID); - d_data->tmrID = 0; - } + d_data->isTracking = on; } /*! - \brief Specify the update interval for automatic scrolling - \param t update interval in milliseconds - \sa getScrollMode() + \return True, when tracking has been enabled + \sa setTracking() */ -void QwtAbstractSlider::setUpdateTime(int t) +bool QwtAbstractSlider::isTracking() const { - if (t < 50) - t = 50; - d_data->updTime = t; + return d_data->isTracking; } - -//! Mouse press event handler -void QwtAbstractSlider::mousePressEvent(QMouseEvent *e) +/*! + Mouse press event handler + \param event Mouse event +*/ +void QwtAbstractSlider::mousePressEvent( QMouseEvent *event ) { - if ( isReadOnly() ) { - e->ignore(); + if ( isReadOnly() ) + { + event->ignore(); return; } - if ( !isValid() ) - return; - - const QPoint &p = e->pos(); - d_data->timerTick = 0; - - getScrollMode(p, d_data->scrollMode, d_data->direction); - stopMoving(); + if ( !d_data->isValid || lowerBound() == upperBound() ) + return; - switch(d_data->scrollMode) { - case ScrPage: - case ScrTimer: - d_data->mouseOffset = 0; - d_data->tmrID = startTimer(qwtMax(250, 2 * d_data->updTime)); - break; + d_data->isScrolling = isScrollPosition( event->pos() ); - case ScrMouse: - d_data->time.start(); - d_data->speed = 0; - d_data->mouseOffset = getValue(p) - value(); - emit sliderPressed(); - break; + if ( d_data->isScrolling ) + { + d_data->pendingValueChanged = false; - default: - d_data->mouseOffset = 0; - d_data->direction = 0; - break; + Q_EMIT sliderPressed(); } } - -//! Emits a valueChanged() signal if necessary -void QwtAbstractSlider::buttonReleased() -{ - if ((!d_data->tracking) || (value() != prevValue())) - emit valueChanged(value()); -} - - -//! Mouse Release Event handler -void QwtAbstractSlider::mouseReleaseEvent(QMouseEvent *e) +/*! + Mouse Move Event handler + \param event Mouse event +*/ +void QwtAbstractSlider::mouseMoveEvent( QMouseEvent *event ) { - if ( isReadOnly() ) { - e->ignore(); + if ( isReadOnly() ) + { + event->ignore(); return; } - if ( !isValid() ) - return; - const double inc = step(); - - switch(d_data->scrollMode) { - case ScrMouse: { - setPosition(e->pos()); - d_data->direction = 0; - d_data->mouseOffset = 0; - if (d_data->mass > 0.0) { - const int ms = d_data->time.elapsed(); - if ((fabs(d_data->speed) > 0.0) && (ms < 50)) - d_data->tmrID = startTimer(d_data->updTime); - } else { - d_data->scrollMode = ScrNone; - buttonReleased(); - } - emit sliderReleased(); + if ( d_data->isValid && d_data->isScrolling ) + { + double value = scrolledTo( event->pos() ); + if ( value != d_data->value ) + { + value = boundedValue( value ); - break; - } + if ( d_data->stepAlignment ) + { + value = alignedValue( value ); + } + else + { + value = qwtAlignToScaleDiv( this, value ); + } - case ScrDirect: { - setPosition(e->pos()); - d_data->direction = 0; - d_data->mouseOffset = 0; - d_data->scrollMode = ScrNone; - buttonReleased(); - break; - } + if ( value != d_data->value ) + { + d_data->value = value; - case ScrPage: { - stopMoving(); - if (!d_data->timerTick) - QwtDoubleRange::incPages(d_data->direction); - d_data->timerTick = 0; - buttonReleased(); - d_data->scrollMode = ScrNone; - break; - } + sliderChange(); - case ScrTimer: { - stopMoving(); - if (!d_data->timerTick) - QwtDoubleRange::fitValue(value() + double(d_data->direction) * inc); - d_data->timerTick = 0; - buttonReleased(); - d_data->scrollMode = ScrNone; - break; - } + Q_EMIT sliderMoved( d_data->value ); - default: { - d_data->scrollMode = ScrNone; - buttonReleased(); - } + if ( d_data->isTracking ) + Q_EMIT valueChanged( d_data->value ); + else + d_data->pendingValueChanged = true; + } + } } } - /*! - Move the slider to a specified point, adjust the value - and emit signals if necessary. + Mouse Release Event handler + \param event Mouse event */ -void QwtAbstractSlider::setPosition(const QPoint &p) +void QwtAbstractSlider::mouseReleaseEvent( QMouseEvent *event ) { - QwtDoubleRange::fitValue(getValue(p) - d_data->mouseOffset); -} + if ( isReadOnly() ) + { + event->ignore(); + return; + } + if ( d_data->isScrolling && d_data->isValid ) + { + d_data->isScrolling = false; -/*! - \brief Enables or disables tracking. + if ( d_data->pendingValueChanged ) + Q_EMIT valueChanged( d_data->value ); - If tracking is enabled, the slider emits a - valueChanged() signal whenever its value - changes (the default behaviour). If tracking - is disabled, the value changed() signal will only - be emitted if:
    -
  • the user releases the mouse - button and the value has changed or -
  • at the end of automatic scrolling.
- Tracking is enabled by default. - \param enable \c true (enable) or \c false (disable) tracking. -*/ -void QwtAbstractSlider::setTracking(bool enable) -{ - d_data->tracking = enable; + Q_EMIT sliderReleased(); + } } /*! - Mouse Move Event handler - \param e Mouse event + Wheel Event handler + + In/decreases the value by s number of steps. The direction + depends on the invertedControls() property. + + When the control or shift modifier is pressed the wheel delta + ( divided by 120 ) is mapped to an increment according to + pageSteps(). Otherwise it is mapped to singleSteps(). + + \param event Wheel event */ -void QwtAbstractSlider::mouseMoveEvent(QMouseEvent *e) +void QwtAbstractSlider::wheelEvent( QWheelEvent *event ) { - if ( isReadOnly() ) { - e->ignore(); + if ( isReadOnly() ) + { + event->ignore(); return; } - if ( !isValid() ) + if ( !d_data->isValid || d_data->isScrolling ) return; - if (d_data->scrollMode == ScrMouse ) { - setPosition(e->pos()); - if (d_data->mass > 0.0) { - double ms = double(d_data->time.elapsed()); - if (ms < 1.0) - ms = 1.0; - d_data->speed = (exactValue() - exactPrevValue()) / ms; - d_data->time.start(); - } - if (value() != prevValue()) - emit sliderMoved(value()); - } -} + int numSteps = 0; -/*! - Wheel Event handler - \param e Whell event -*/ -void QwtAbstractSlider::wheelEvent(QWheelEvent *e) -{ - if ( isReadOnly() ) { - e->ignore(); - return; + if ( ( event->modifiers() & Qt::ControlModifier) || + ( event->modifiers() & Qt::ShiftModifier ) ) + { + // one page regardless of delta + numSteps = d_data->pageSteps; + if ( event->delta() < 0 ) + numSteps = -numSteps; + } + else + { + const int numTurns = ( event->delta() / 120 ); + numSteps = numTurns * d_data->singleSteps; } - if ( !isValid() ) - return; + if ( d_data->invertedControls ) + numSteps = -numSteps; - int mode = ScrNone, direction = 0; + const double value = incrementedValue( d_data->value, numSteps ); + if ( value != d_data->value ) + { + d_data->value = value; + sliderChange(); - // Give derived classes a chance to say ScrNone - getScrollMode(e->pos(), mode, direction); - if ( mode != ScrNone ) { - const int inc = e->delta() / WHEEL_DELTA; - QwtDoubleRange::incPages(inc); - if (value() != prevValue()) - emit sliderMoved(value()); + Q_EMIT sliderMoved( d_data->value ); + Q_EMIT valueChanged( d_data->value ); } } /*! Handles key events - - Key_Down, KeyLeft\n - Decrement by 1 - - Key_Up, Key_Right\n - Increment by 1 - - \param e Key event + QwtAbstractSlider handles the following keys: + + - Qt::Key_Left\n + Add/Subtract singleSteps() in direction to lowerBound(); + - Qt::Key_Right\n + Add/Subtract singleSteps() in direction to upperBound(); + - Qt::Key_Down\n + Subtract singleSteps(), when invertedControls() is false + - Qt::Key_Up\n + Add singleSteps(), when invertedControls() is false + - Qt::Key_PageDown\n + Subtract pageSteps(), when invertedControls() is false + - Qt::Key_PageUp\n + Add pageSteps(), when invertedControls() is false + - Qt::Key_Home\n + Set the value to the minimum() + - Qt::Key_End\n + Set the value to the maximum() + + \param event Key event \sa isReadOnly() */ -void QwtAbstractSlider::keyPressEvent(QKeyEvent *e) +void QwtAbstractSlider::keyPressEvent( QKeyEvent *event ) { - if ( isReadOnly() ) { - e->ignore(); + if ( isReadOnly() ) + { + event->ignore(); return; } - if ( !isValid() ) + if ( !d_data->isValid || d_data->isScrolling ) return; - int increment = 0; - switch ( e->key() ) { - case Qt::Key_Down: - if ( orientation() == Qt::Vertical ) - increment = -1; - break; - case Qt::Key_Up: - if ( orientation() == Qt::Vertical ) - increment = 1; - break; - case Qt::Key_Left: - if ( orientation() == Qt::Horizontal ) - increment = -1; - break; - case Qt::Key_Right: - if ( orientation() == Qt::Horizontal ) - increment = 1; - break; - default: - ; - e->ignore(); - } + int numSteps = 0; + double value = d_data->value; - if ( increment != 0 ) { - QwtDoubleRange::incValue(increment); - if (value() != prevValue()) - emit sliderMoved(value()); - } -} + switch ( event->key() ) + { + case Qt::Key_Left: + { + numSteps = -static_cast( d_data->singleSteps ); + if ( isInverted() ) + numSteps = -numSteps; -/*! - Qt timer event - \param e Timer event -*/ -void QwtAbstractSlider::timerEvent(QTimerEvent *) -{ - const double inc = step(); - - switch (d_data->scrollMode) { - case ScrMouse: { - if (d_data->mass > 0.0) { - d_data->speed *= exp( - double(d_data->updTime) * 0.001 / d_data->mass ); - const double newval = - exactValue() + d_data->speed * double(d_data->updTime); - QwtDoubleRange::fitValue(newval); - // stop if d_data->speed < one step per second - if (fabs(d_data->speed) < 0.001 * fabs(step())) { - d_data->speed = 0; - stopMoving(); - buttonReleased(); - } + break; + } + case Qt::Key_Right: + { + numSteps = d_data->singleSteps; + if ( isInverted() ) + numSteps = -numSteps; - } else - stopMoving(); - break; - } + break; + } + case Qt::Key_Down: + { + numSteps = -static_cast( d_data->singleSteps ); + if ( d_data->invertedControls ) + numSteps = -numSteps; + break; + } + case Qt::Key_Up: + { + numSteps = d_data->singleSteps; + if ( d_data->invertedControls ) + numSteps = -numSteps; - case ScrPage: { - QwtDoubleRange::incPages(d_data->direction); - if (!d_data->timerTick) { - killTimer(d_data->tmrID); - d_data->tmrID = startTimer(d_data->updTime); + break; } - break; - } - case ScrTimer: { - QwtDoubleRange::fitValue(value() + double(d_data->direction) * inc); - if (!d_data->timerTick) { - killTimer(d_data->tmrID); - d_data->tmrID = startTimer(d_data->updTime); + case Qt::Key_PageUp: + { + numSteps = d_data->pageSteps; + if ( d_data->invertedControls ) + numSteps = -numSteps; + break; + } + case Qt::Key_PageDown: + { + numSteps = -static_cast( d_data->pageSteps ); + if ( d_data->invertedControls ) + numSteps = -numSteps; + break; + } + case Qt::Key_Home: + { + value = minimum(); + break; + } + case Qt::Key_End: + { + value = maximum(); + break; + } + default:; + { + event->ignore(); } - break; } - default: { - stopMoving(); - break; + + if ( numSteps != 0 ) + { + value = incrementedValue( d_data->value, numSteps ); } + + if ( value != d_data->value ) + { + d_data->value = value; + sliderChange(); + + Q_EMIT sliderMoved( d_data->value ); + Q_EMIT valueChanged( d_data->value ); } +} - d_data->timerTick = 1; +/*! + \brief Set the number of steps + + The range of the slider is divided into a number of steps from + which the value increments according to user inputs depend. + + The default setting is 100. + + \param stepCount Number of steps + + \sa totalSteps(), setSingleSteps(), setPageSteps() + */ +void QwtAbstractSlider::setTotalSteps( uint stepCount ) +{ + d_data->totalSteps = stepCount; } +/*! + \return Number of steps + \sa setTotalSteps(), singleSteps(), pageSteps() + */ +uint QwtAbstractSlider::totalSteps() const +{ + return d_data->totalSteps; +} /*! - Notify change of value + \brief Set the number of steps for a single increment - This function can be reimplemented by derived classes - in order to keep track of changes, i.e. repaint the widget. - The default implementation emits a valueChanged() signal - if tracking is enabled. -*/ -void QwtAbstractSlider::valueChange() + The range of the slider is divided into a number of steps from + which the value increments according to user inputs depend. + + \param stepCount Number of steps + + \sa singleSteps(), setTotalSteps(), setPageSteps() + */ + +void QwtAbstractSlider::setSingleSteps( uint stepCount ) { - if (d_data->tracking) - emit valueChanged(value()); -} + d_data->singleSteps = stepCount; +} /*! - \brief Set the slider's mass for flywheel effect. + \return Number of steps + \sa setSingleSteps(), totalSteps(), pageSteps() + */ +uint QwtAbstractSlider::singleSteps() const +{ + return d_data->singleSteps; +} + +/*! + \brief Set the number of steps for a page increment + + The range of the slider is divided into a number of steps from + which the value increments according to user inputs depend. + + \param stepCount Number of steps + + \sa pageSteps(), setTotalSteps(), setSingleSteps() + */ + +void QwtAbstractSlider::setPageSteps( uint stepCount ) +{ + d_data->pageSteps = stepCount; +} - If the slider's mass is greater then 0, it will continue - to move after the mouse button has been released. Its speed - decreases with time at a rate depending on the slider's mass. - A large mass means that it will continue to move for a - long time. +/*! + \return Number of steps + \sa setPageSteps(), totalSteps(), singleSteps() + */ +uint QwtAbstractSlider::pageSteps() const +{ + return d_data->pageSteps; +} - Derived widgets may overload this function to make it public. +/*! + \brief Enable step alignment - \param val New mass in kg + When step alignment is enabled values resulting from slider + movements are aligned to the step size. - \bug If the mass is smaller than 1g, it is set to zero. - The maximal mass is limited to 100kg. - \sa mass() + \param on Enable step alignment when true + \sa stepAlignment() */ -void QwtAbstractSlider::setMass(double val) +void QwtAbstractSlider::setStepAlignment( bool on ) +{ + if ( on != d_data->stepAlignment ) + { + d_data->stepAlignment = on; + } +} + +/*! + \return True, when step alignment is enabled + \sa setStepAlignment() + */ +bool QwtAbstractSlider::stepAlignment() const { - if (val < 0.001) - d_data->mass = 0.0; - else if (val > 100.0) - d_data->mass = 100.0; - else - d_data->mass = val; + return d_data->stepAlignment; } /*! - \return mass - \sa setMass() + Set the slider to the specified value + + \param value New value + \sa setValid(), sliderChange(), valueChanged() */ -double QwtAbstractSlider::mass() const +void QwtAbstractSlider::setValue( double value ) { - return d_data->mass; + value = qBound( minimum(), value, maximum() ); + + const bool changed = ( d_data->value != value ) || !d_data->isValid; + + d_data->value = value; + d_data->isValid = true; + + if ( changed ) + { + sliderChange(); + Q_EMIT valueChanged( d_data->value ); + } } +//! Returns the current value. +double QwtAbstractSlider::value() const +{ + return d_data->value; +} /*! - \brief Move the slider to a specified value + If wrapping is true stepping up from upperBound() value will + take you to the minimum() value and vice versa. - This function can be used to move the slider to a value - which is not an integer multiple of the step size. - \param val new value - \sa fitValue() + \param on En/Disable wrapping + \sa wrapping() */ -void QwtAbstractSlider::setValue(double val) +void QwtAbstractSlider::setWrapping( bool on ) +{ + d_data->wrapping = on; +} + +/*! + \return True, when wrapping is set + \sa setWrapping() + */ +bool QwtAbstractSlider::wrapping() const { - if (d_data->scrollMode == ScrMouse) - stopMoving(); - QwtDoubleRange::setValue(val); + return d_data->wrapping; } +/*! + Invert wheel and key events + + Usually scrolling the mouse wheel "up" and using keys like page + up will increase the slider's value towards its maximum. + When invertedControls() is enabled the value is scrolled + towards its minimum. + + Inverting the controls might be f.e. useful for a vertical slider + with an inverted scale ( decreasing from top to bottom ). + + \param on Invert controls, when true + + \sa invertedControls(), keyEvent(), wheelEvent() + */ +void QwtAbstractSlider::setInvertedControls( bool on ) +{ + d_data->invertedControls = on; +} + +/*! + \return True, when the controls are inverted + \sa setInvertedControls() + */ +bool QwtAbstractSlider::invertedControls() const +{ + return d_data->invertedControls; +} /*! - \brief Set the slider's value to the nearest integer multiple - of the step size. + Increment the slider - \param valeu Value - \sa setValue(), incValue() -*/ -void QwtAbstractSlider::fitValue(double value) + The step size depends on the number of totalSteps() + + \param stepCount Number of steps + \sa setTotalSteps(), incrementedValue() + */ +void QwtAbstractSlider::incrementValue( int stepCount ) { - if (d_data->scrollMode == ScrMouse) - stopMoving(); - QwtDoubleRange::fitValue(value); + const double value = incrementedValue( + d_data->value, stepCount ); + + if ( value != d_data->value ) + { + d_data->value = value; + sliderChange(); + } } /*! - \brief Increment the value by a specified number of steps - \param steps number of steps - \sa setValue() -*/ -void QwtAbstractSlider::incValue(int steps) + Increment a value + + \param value Value + \param stepCount Number of steps + + \return Incremented value + */ +double QwtAbstractSlider::incrementedValue( + double value, int stepCount ) const { - if (d_data->scrollMode == ScrMouse) - stopMoving(); - QwtDoubleRange::incValue(steps); + if ( d_data->totalSteps == 0 ) + return value; + + const QwtTransform *transformation = + scaleMap().transformation(); + + if ( transformation == NULL ) + { + const double range = maximum() - minimum(); + value += stepCount * range / d_data->totalSteps; + } + else + { + QwtScaleMap map = scaleMap(); + map.setPaintInterval( 0, d_data->totalSteps ); + + // we need equidant steps according to + // paint device coordinates + const double range = transformation->transform( maximum() ) + - transformation->transform( minimum() ); + + const double stepSize = range / d_data->totalSteps; + + double v = transformation->transform( value ); + + v = qRound( v / stepSize ) * stepSize; + v += stepCount * range / d_data->totalSteps; + + value = transformation->invTransform( v ); + } + + value = boundedValue( value ); + + if ( d_data->stepAlignment ) + value = alignedValue( value ); + + return value; +} + +double QwtAbstractSlider::boundedValue( double value ) const +{ + const double vmin = minimum(); + const double vmax = maximum(); + + if ( d_data->wrapping && vmin != vmax ) + { + const int fullCircle = 360 * 16; + + const double pd = scaleMap().pDist(); + if ( int( pd / fullCircle ) * fullCircle == pd ) + { + // full circle scales: min and max are the same + const double range = vmax - vmin; + + if ( value < vmin ) + { + value += ::ceil( ( vmin - value ) / range ) * range; + } + else if ( value > vmax ) + { + value -= ::ceil( ( value - vmax ) / range ) * range; + } + } + else + { + if ( value < vmin ) + value = vmax; + else if ( value > vmax ) + value = vmin; + } + } + else + { + value = qBound( vmin, value, vmax ); + } + + return value; } -void QwtAbstractSlider::setMouseOffset(double offset) +double QwtAbstractSlider::alignedValue( double value ) const { - d_data->mouseOffset = offset; + if ( d_data->totalSteps == 0 ) + return value; + + double stepSize; + + if ( scaleMap().transformation() == NULL ) + { + stepSize = ( maximum() - minimum() ) / d_data->totalSteps; + if ( stepSize > 0.0 ) + { + value = lowerBound() + + qRound( ( value - lowerBound() ) / stepSize ) * stepSize; + } + } + else + { + stepSize = ( scaleMap().p2() - scaleMap().p1() ) / d_data->totalSteps; + + if ( stepSize > 0.0 ) + { + double v = scaleMap().transform( value ); + + v = scaleMap().p1() + + qRound( ( v - scaleMap().p1() ) / stepSize ) * stepSize; + + value = scaleMap().invTransform( v ); + } + } + + if ( qAbs( stepSize ) > 1e-12 ) + { + if ( qFuzzyCompare( value + 1.0, 1.0 ) ) + { + // correct rounding error if value = 0 + value = 0.0; + } + else + { + // correct rounding error at the border + if ( qFuzzyCompare( value, upperBound() ) ) + value = upperBound(); + else if ( qFuzzyCompare( value, lowerBound() ) ) + value = lowerBound(); + } + } + + return value; } -double QwtAbstractSlider::mouseOffset() const +/*! + Update the slider according to modifications of the scale + */ +void QwtAbstractSlider::scaleChange() { - return d_data->mouseOffset; + const double value = qBound( minimum(), d_data->value, maximum() ); + + const bool changed = ( value != d_data->value ); + if ( changed ) + { + d_data->value = value; + } + + if ( d_data->isValid || changed ) + Q_EMIT valueChanged( d_data->value ); + + updateGeometry(); + update(); } -int QwtAbstractSlider::scrollMode() const +//! Calling update() +void QwtAbstractSlider::sliderChange() { - return d_data->scrollMode; + update(); } diff --git a/libs/qwt/qwt_abstract_slider.h b/libs/qwt/qwt_abstract_slider.h index 3a4a48fe4..c91dcb604 100644 --- a/libs/qwt/qwt_abstract_slider.h +++ b/libs/qwt/qwt_abstract_slider.h @@ -10,112 +10,95 @@ #ifndef QWT_ABSTRACT_SLIDER_H #define QWT_ABSTRACT_SLIDER_H -#include #include "qwt_global.h" -#include "qwt_double_range.h" +#include "qwt_abstract_scale.h" /*! - \brief An abstract base class for slider widgets - - QwtAbstractSlider is a base class for - slider widgets. It handles mouse events - and updates the slider's value accordingly. Derived classes - only have to implement the getValue() and - getScrollMode() members, and should react to a - valueChange(), which normally requires repainting. + \brief An abstract base class for slider widgets with a scale + + A slider widget displays a value according to a scale. + The class is designed as a common super class for widgets like + QwtKnob, QwtDial and QwtSlider. + + When the slider is nor readOnly() its value can be modified + by keyboard, mouse and wheel inputs. + + The range of the slider is divided into a number of steps from + which the value increments according to user inputs depend. + Only for linear scales the number of steps correspond with + a fixed step size. */ -class QWT_EXPORT QwtAbstractSlider : public QWidget, public QwtDoubleRange +class QWT_EXPORT QwtAbstractSlider: public QwtAbstractScale { Q_OBJECT + + Q_PROPERTY( double value READ value WRITE setValue ) + + Q_PROPERTY( uint totalSteps READ totalSteps WRITE setTotalSteps ) + Q_PROPERTY( uint singleSteps READ singleSteps WRITE setSingleSteps ) + Q_PROPERTY( uint pageSteps READ pageSteps WRITE setPageSteps ) + Q_PROPERTY( bool stepAlignment READ stepAlignment WRITE setStepAlignment ) + Q_PROPERTY( bool readOnly READ isReadOnly WRITE setReadOnly ) - Q_PROPERTY( bool valid READ isValid WRITE setValid ) - Q_PROPERTY( double mass READ mass WRITE setMass ) -#ifndef Q_MOC_RUN // Qt3 moc -#define QWT_PROPERTY Q_PROPERTY - Q_PROPERTY( Orientation orientation - READ orientation WRITE setOrientation ) -#else // Qt4 moc -// MOC_SKIP_BEGIN - Q_PROPERTY( Qt::Orientation orientation - READ orientation WRITE setOrientation ) -// MOC_SKIP_END -#endif + Q_PROPERTY( bool tracking READ isTracking WRITE setTracking ) + Q_PROPERTY( bool wrapping READ wrapping WRITE setWrapping ) + + Q_PROPERTY( bool invertedControls READ invertedControls WRITE setInvertedControls ) public: - /*! - Scroll mode - \sa getScrollMode() - */ - enum ScrollMode { - ScrNone, - ScrMouse, - ScrTimer, - ScrDirect, - ScrPage - }; - - explicit QwtAbstractSlider(Qt::Orientation, QWidget *parent = NULL); + explicit QwtAbstractSlider( QWidget *parent = NULL ); virtual ~QwtAbstractSlider(); - void setUpdateTime(int t); - void stopMoving(); - void setTracking(bool enable); + void setValid( bool ); + bool isValid() const; - virtual void setMass(double val); - virtual double mass() const; + double value() const; -#if QT_VERSION >= 0x040000 - virtual void setOrientation(Qt::Orientation o); - Qt::Orientation orientation() const; -#else - virtual void setOrientation(Orientation o); - Orientation orientation() const; -#endif + void setWrapping( bool ); + bool wrapping() const; - bool isReadOnly() const; + void setTotalSteps( uint ); + uint totalSteps() const; - /* - Wrappers for QwtDblRange::isValid/QwtDblRange::setValid made - to be available as Q_PROPERTY in the designer. - */ + void setSingleSteps( uint ); + uint singleSteps() const; - /*! - \sa QwtDblRange::isValid - */ - bool isValid() const { - return QwtDoubleRange::isValid(); - } + void setPageSteps( uint ); + uint pageSteps() const; - /*! - \sa QwtDblRange::isValid - */ - void setValid(bool valid) { - QwtDoubleRange::setValid(valid); - } + void setStepAlignment( bool ); + bool stepAlignment() const; -public slots: - virtual void setValue(double val); - virtual void fitValue(double val); - virtual void incValue(int steps); + void setTracking( bool ); + bool isTracking() const; + + void setReadOnly( bool ); + bool isReadOnly() const; - virtual void setReadOnly(bool); + void setInvertedControls( bool ); + bool invertedControls() const; -signals: +public Q_SLOTS: + void setValue( double val ); + +Q_SIGNALS: /*! \brief Notify a change of value. - In the default setting - (tracking enabled), this signal will be emitted every - time the value changes ( see setTracking() ). - \param value new value + When tracking is enabled (default setting), + this signal will be emitted every time the value changes. + + \param value New value + + \sa setTracking(), sliderMoved() */ - void valueChanged(double value); + void valueChanged( double value ); /*! This signal is emitted when the user presses the - movable part of the slider (start ScrMouse Mode). + movable part of the slider. */ void sliderPressed(); @@ -123,70 +106,59 @@ signals: This signal is emitted when the user releases the movable part of the slider. */ - void sliderReleased(); + /*! This signal is emitted when the user moves the slider with the mouse. - \param value new value + + \param value New value + + \sa valueChanged() */ - void sliderMoved(double value); + void sliderMoved( double value ); protected: - virtual void setPosition(const QPoint &); - virtual void valueChange(); - - virtual void timerEvent(QTimerEvent *e); - virtual void mousePressEvent(QMouseEvent *e); - virtual void mouseReleaseEvent(QMouseEvent *e); - virtual void mouseMoveEvent(QMouseEvent *e); - virtual void keyPressEvent(QKeyEvent *e); - virtual void wheelEvent(QWheelEvent *e); + virtual void mousePressEvent( QMouseEvent * ); + virtual void mouseReleaseEvent( QMouseEvent * ); + virtual void mouseMoveEvent( QMouseEvent * ); + virtual void keyPressEvent( QKeyEvent * ); + virtual void wheelEvent( QWheelEvent * ); /*! - \brief Determine the value corresponding to a specified poind + \brief Determine what to do when the user presses a mouse button. + + \param pos Mouse position - This is an abstract virtual function which is called when - the user presses or releases a mouse button or moves the - mouse. It has to be implemented by the derived class. - \param p point + \retval True, when pos is a valid scroll position + \sa scrolledTo() */ - virtual double getValue(const QPoint & p) = 0; + virtual bool isScrollPosition( const QPoint &pos ) const = 0; + /*! - \brief Determine what to do when the user presses a mouse button. + \brief Determine the value for a new position of the + movable part of the slider - This function is abstract and has to be implemented by derived classes. - It is called on a mousePress event. The derived class can determine - what should happen next in dependence of the position where the mouse - was pressed by returning scrolling mode and direction. QwtAbstractSlider - knows the following modes:
-
QwtAbstractSlider::ScrNone -
Scrolling switched off. Don't change the value. -
QwtAbstractSlider::ScrMouse -
Change the value while the user keeps the - button pressed and moves the mouse. -
QwtAbstractSlider::ScrTimer -
Automatic scrolling. Increment the value - in the specified direction as long as - the user keeps the button pressed. -
QwtAbstractSlider::ScrPage -
Automatic scrolling. Same as ScrTimer, but - increment by page size.
- - \param p point where the mouse was pressed - \retval scrollMode The scrolling mode - \retval direction direction: 1, 0, or -1. + \param pos Mouse position + + \return Value for the mouse position + \sa isScrollPosition() */ - virtual void getScrollMode( const QPoint &p, - int &scrollMode, int &direction) = 0; + virtual double scrolledTo( const QPoint &pos ) const = 0; + + void incrementValue( int numSteps ); - void setMouseOffset(double); - double mouseOffset() const; + virtual void scaleChange(); + +protected: + virtual void sliderChange(); - int scrollMode() const; + double incrementedValue( + double value, int stepCount ) const; private: - void buttonReleased(); + double alignedValue( double ) const; + double boundedValue( double ) const; class PrivateData; PrivateData *d_data; diff --git a/libs/qwt/qwt_analog_clock.cpp b/libs/qwt/qwt_analog_clock.cpp index e42c6b661..1d44a95cb 100644 --- a/libs/qwt/qwt_analog_clock.cpp +++ b/libs/qwt/qwt_analog_clock.cpp @@ -8,69 +8,94 @@ *****************************************************************************/ #include "qwt_analog_clock.h" +#include "qwt_round_scale_draw.h" +#include +#include -/*! - Constructor - \param parent Parent widget -*/ -QwtAnalogClock::QwtAnalogClock(QWidget *parent): - QwtDial(parent) +class QwtAnalogClockScaleDraw: public QwtRoundScaleDraw { - initClock(); -} +public: + QwtAnalogClockScaleDraw() + { + setSpacing( 8 ); + + enableComponent( QwtAbstractScaleDraw::Backbone, false ); + + setTickLength( QwtScaleDiv::MinorTick, 2 ); + setTickLength( QwtScaleDiv::MediumTick, 4 ); + setTickLength( QwtScaleDiv::MajorTick, 8 ); + + setPenWidth( 1 ); + } + + virtual QwtText label( double value ) const + { + if ( qFuzzyCompare( value + 1.0, 1.0 ) ) + value = 60.0 * 60.0 * 12.0; + + return QLocale().toString( qRound( value / ( 60.0 * 60.0 ) ) ); + } +}; -#if QT_VERSION < 0x040000 /*! Constructor \param parent Parent widget - \param name Object name */ -QwtAnalogClock::QwtAnalogClock(QWidget* parent, const char *name): - QwtDial(parent, name) +QwtAnalogClock::QwtAnalogClock( QWidget *parent ): + QwtDial( parent ) { - initClock(); -} -#endif + setWrapping( true ); + setReadOnly( true ); -void QwtAnalogClock::initClock() -{ - setWrapping(true); - setReadOnly(true); + setOrigin( 270.0 ); + setScaleDraw( new QwtAnalogClockScaleDraw() ); + + setTotalSteps( 60 ); + + const int secondsPerHour = 60.0 * 60.0; - setOrigin(270.0); - setRange(0.0, 60.0 * 60.0 * 12.0); // seconds - setScale(-1, 5, 60.0 * 60.0); + QList majorTicks; + QList minorTicks; - setScaleOptions(ScaleTicks | ScaleLabel); - setScaleTicks(1, 0, 8); - scaleDraw()->setSpacing(8); + for ( int i = 0; i < 12; i++ ) + { + majorTicks += i * secondsPerHour; - QColor knobColor = -#if QT_VERSION < 0x040000 - palette().color(QPalette::Active, QColorGroup::Text); -#else - palette().color(QPalette::Active, QPalette::Text); -#endif - knobColor = knobColor.dark(120); + for ( int j = 1; j < 5; j++ ) + minorTicks += i * secondsPerHour + j * secondsPerHour / 5.0; + } + + QwtScaleDiv scaleDiv; + scaleDiv.setInterval( 0.0, 12.0 * secondsPerHour ); + scaleDiv.setTicks( QwtScaleDiv::MajorTick, majorTicks ); + scaleDiv.setTicks( QwtScaleDiv::MinorTick, minorTicks ); + setScale( scaleDiv ); + + QColor knobColor = palette().color( QPalette::Active, QPalette::Text ); + knobColor = knobColor.dark( 120 ); QColor handColor; int width; - for ( int i = 0; i < NHands; i++ ) { - if ( i == SecondHand ) { + for ( int i = 0; i < NHands; i++ ) + { + if ( i == SecondHand ) + { width = 2; - handColor = knobColor.dark(120); - } else { + handColor = knobColor.dark( 120 ); + } + else + { width = 8; handColor = knobColor; } QwtDialSimpleNeedle *hand = new QwtDialSimpleNeedle( - QwtDialSimpleNeedle::Arrow, true, handColor, knobColor); - hand->setWidth(width); + QwtDialSimpleNeedle::Arrow, true, handColor, knobColor ); + hand->setWidth( width ); d_hand[i] = NULL; - setHand((Hand)i, hand); + setHand( static_cast( i ), hand ); } } @@ -82,24 +107,25 @@ QwtAnalogClock::~QwtAnalogClock() } /*! - Nop method, use setHand instead - \sa QwtAnalogClock::setHand + Nop method, use setHand() instead + \sa setHand() */ -void QwtAnalogClock::setNeedle(QwtDialNeedle *) +void QwtAnalogClock::setNeedle( QwtDialNeedle * ) { // no op return; } /*! - Set a clockhand + Set a clock hand \param hand Specifies the type of hand \param needle Hand - \sa QwtAnalogClock::hand() + \sa hand() */ -void QwtAnalogClock::setHand(Hand hand, QwtDialNeedle *needle) +void QwtAnalogClock::setHand( Hand hand, QwtDialNeedle *needle ) { - if ( hand >= 0 || hand < NHands ) { + if ( hand >= 0 && hand < NHands ) + { delete d_hand[hand]; d_hand[hand] = needle; } @@ -108,9 +134,9 @@ void QwtAnalogClock::setHand(Hand hand, QwtDialNeedle *needle) /*! \return Clock hand \param hd Specifies the type of hand - \sa QwtAnalogClock::setHand + \sa setHand() */ -QwtDialNeedle *QwtAnalogClock::hand(Hand hd) +QwtDialNeedle *QwtAnalogClock::hand( Hand hd ) { if ( hd < 0 || hd >= NHands ) return NULL; @@ -121,81 +147,75 @@ QwtDialNeedle *QwtAnalogClock::hand(Hand hd) /*! \return Clock hand \param hd Specifies the type of hand - \sa QwtAnalogClock::setHand + \sa setHand() */ -const QwtDialNeedle *QwtAnalogClock::hand(Hand hd) const +const QwtDialNeedle *QwtAnalogClock::hand( Hand hd ) const { - return ((QwtAnalogClock *)this)->hand(hd); + return const_cast( this )->hand( hd ); } /*! \brief Set the current time - - This is the same as QwtAnalogClock::setTime(), but Qt < 3.0 - can't handle default parameters for slots. */ void QwtAnalogClock::setCurrentTime() { - setTime(QTime::currentTime()); + setTime( QTime::currentTime() ); } /*! Set a time \param time Time to display */ -void QwtAnalogClock::setTime(const QTime &time) -{ - if ( time.isValid() ) { - setValue((time.hour() % 12) * 60.0 * 60.0 - + time.minute() * 60.0 + time.second()); - } else - setValid(false); -} - -/*! - Find the scale label for a given value - - \param value Value - \return Label -*/ -QwtText QwtAnalogClock::scaleLabel(double value) const +void QwtAnalogClock::setTime( const QTime &time ) { - if ( value == 0.0 ) - value = 60.0 * 60.0 * 12.0; - - return QString::number(int(value / (60.0 * 60.0))); + if ( time.isValid() ) + { + setValue( ( time.hour() % 12 ) * 60.0 * 60.0 + + time.minute() * 60.0 + time.second() ); + } + else + setValid( false ); } /*! \brief Draw the needle - A clock has no single needle but three hands instead. drawNeedle + A clock has no single needle but three hands instead. drawNeedle() translates value() into directions for the hands and calls drawHand(). \param painter Painter \param center Center of the clock \param radius Maximum length for the hands - \param direction Dummy, not used. - \param cg ColorGroup + \param dir Dummy, not used. + \param colorGroup ColorGroup - \sa QwtAnalogClock::drawHand() + \sa drawHand() */ -void QwtAnalogClock::drawNeedle(QPainter *painter, const QPoint ¢er, - int radius, double, QPalette::ColorGroup cg) const +void QwtAnalogClock::drawNeedle( QPainter *painter, const QPointF ¢er, + double radius, double dir, QPalette::ColorGroup colorGroup ) const { - if ( isValid() ) { - const double hours = value() / (60.0 * 60.0); - const double minutes = (value() - (int)hours * 60.0 * 60.0) / 60.0; - const double seconds = value() - (int)hours * 60.0 * 60.0 - - (int)minutes * 60.0; - - drawHand(painter, HourHand, center, radius, - 360.0 - (origin() + 360.0 * hours / 12.0), cg); - drawHand(painter, MinuteHand, center, radius, - 360.0 - (origin() + 360.0 * minutes / 60.0), cg); - drawHand(painter, SecondHand, center, radius, - 360.0 - (origin() + 360.0 * seconds / 60.0), cg); + Q_UNUSED( dir ); + + if ( isValid() ) + { + const double hours = value() / ( 60.0 * 60.0 ); + const double minutes = + ( value() - qFloor(hours) * 60.0 * 60.0 ) / 60.0; + const double seconds = value() - qFloor(hours) * 60.0 * 60.0 + - qFloor(minutes) * 60.0; + + double angle[NHands]; + angle[HourHand] = 360.0 * hours / 12.0; + angle[MinuteHand] = 360.0 * minutes / 60.0; + angle[SecondHand] = 360.0 * seconds / 60.0; + + for ( int hand = 0; hand < NHands; hand++ ) + { + const double d = 360.0 - angle[hand] - origin(); + drawHand( painter, static_cast( hand ), + center, radius, d, colorGroup ); + } } } @@ -209,15 +229,16 @@ void QwtAnalogClock::drawNeedle(QPainter *painter, const QPoint ¢er, \param direction Direction of the hand in degrees, counter clockwise \param cg ColorGroup */ -void QwtAnalogClock::drawHand(QPainter *painter, Hand hd, - const QPoint ¢er, int radius, double direction, - QPalette::ColorGroup cg) const +void QwtAnalogClock::drawHand( QPainter *painter, Hand hd, + const QPointF ¢er, double radius, double direction, + QPalette::ColorGroup cg ) const { - const QwtDialNeedle *needle = hand(hd); - if ( needle ) { + const QwtDialNeedle *needle = hand( hd ); + if ( needle ) + { if ( hd == HourHand ) - radius = qRound(0.8 * radius); + radius = qRound( 0.8 * radius ); - needle->draw(painter, center, radius, direction, cg); + needle->draw( painter, center, radius, direction, cg ); } } diff --git a/libs/qwt/qwt_analog_clock.h b/libs/qwt/qwt_analog_clock.h index a660a9d4e..ffe27e2cd 100644 --- a/libs/qwt/qwt_analog_clock.h +++ b/libs/qwt/qwt_analog_clock.h @@ -10,10 +10,10 @@ #ifndef QWT_ANALOG_CLOCK_H #define QWT_ANALOG_CLOCK_H -#include #include "qwt_global.h" #include "qwt_dial.h" #include "qwt_dial_needle.h" +#include /*! \brief An analog clock @@ -21,7 +21,8 @@ \image html analogclock.png \par Example - \verbatim #include + \code + #include QwtAnalogClock *clock = new QwtAnalogClock(...); clock->scaleDraw()->setPenWidth(3); @@ -34,10 +35,7 @@ timer->connect(timer, SIGNAL(timeout()), clock, SLOT(setCurrentTime())); timer->start(1000); - \endverbatim - - Qwt is missing a set of good looking hands. - Contributions are very welcome. + \endcode \note The examples/dials example shows how to use QwtAnalogClock. */ @@ -51,41 +49,43 @@ public: Hand type \sa setHand(), hand() */ - - enum Hand { + enum Hand + { + //! Needle displaying the seconds SecondHand, + + //! Needle displaying the minutes MinuteHand, + + //! Needle displaying the hours HourHand, + //! Number of needles NHands }; - explicit QwtAnalogClock(QWidget* parent = NULL); -#if QT_VERSION < 0x040000 - explicit QwtAnalogClock(QWidget* parent, const char *name); -#endif + explicit QwtAnalogClock( QWidget* parent = NULL ); virtual ~QwtAnalogClock(); - virtual void setHand(Hand, QwtDialNeedle *); - const QwtDialNeedle *hand(Hand) const; - QwtDialNeedle *hand(Hand); + void setHand( Hand, QwtDialNeedle * ); -public slots: + const QwtDialNeedle *hand( Hand ) const; + QwtDialNeedle *hand( Hand ); + +public Q_SLOTS: void setCurrentTime(); - void setTime(const QTime & = QTime::currentTime()); + void setTime( const QTime & ); protected: - virtual QwtText scaleLabel(double) const; - - virtual void drawNeedle(QPainter *, const QPoint &, - int radius, double direction, QPalette::ColorGroup) const; + virtual void drawNeedle( QPainter *, const QPointF &, + double radius, double direction, QPalette::ColorGroup ) const; - virtual void drawHand(QPainter *, Hand, const QPoint &, - int radius, double direction, QPalette::ColorGroup) const; + virtual void drawHand( QPainter *, Hand, const QPointF &, + double radius, double direction, QPalette::ColorGroup ) const; private: - virtual void setNeedle(QwtDialNeedle *); - void initClock(); + // use setHand instead + void setNeedle( QwtDialNeedle * ); QwtDialNeedle *d_hand[NHands]; }; diff --git a/libs/qwt/qwt_arrow_button.cpp b/libs/qwt/qwt_arrow_button.cpp index d51c445ad..bd20f91e9 100644 --- a/libs/qwt/qwt_arrow_button.cpp +++ b/libs/qwt/qwt_arrow_button.cpp @@ -7,12 +7,13 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ +#include "qwt_arrow_button.h" +#include "qwt_math.h" #include #include +#include #include -#include "qwt_math.h" -#include "qwt_polygon.h" -#include "qwt_arrow_button.h" +#include static const int MaxNum = 3; static const int Margin = 2; @@ -25,56 +26,53 @@ public: Qt::ArrowType arrowType; }; - -#if QT_VERSION >= 0x040000 -#include -static QStyleOptionButton styleOpt(const QwtArrowButton* btn) +static QStyleOptionButton styleOpt( const QwtArrowButton* btn ) { QStyleOptionButton option; - option.init(btn); + option.init( btn ); option.features = QStyleOptionButton::None; - if (btn->isFlat()) + if ( btn->isFlat() ) option.features |= QStyleOptionButton::Flat; - if (btn->menu()) + if ( btn->menu() ) option.features |= QStyleOptionButton::HasMenu; - if (btn->autoDefault() || btn->isDefault()) + if ( btn->autoDefault() || btn->isDefault() ) option.features |= QStyleOptionButton::AutoDefaultButton; - if (btn->isDefault()) + if ( btn->isDefault() ) option.features |= QStyleOptionButton::DefaultButton; - if (btn->isDown()) + if ( btn->isDown() ) option.state |= QStyle::State_Sunken; - if (!btn->isFlat() && !btn->isDown()) + if ( !btn->isFlat() && !btn->isDown() ) option.state |= QStyle::State_Raised; return option; } -#endif /*! \param num Number of arrows - \param arrowType see Qt::ArowType in the Qt docs. + \param arrowType see Qt::ArrowType in the Qt docs. \param parent Parent widget */ -QwtArrowButton::QwtArrowButton(int num, - Qt::ArrowType arrowType, QWidget *parent): - QPushButton(parent) +QwtArrowButton::QwtArrowButton( int num, + Qt::ArrowType arrowType, QWidget *parent ): + QPushButton( parent ) { d_data = new PrivateData; - d_data->num = qwtLim(num, 1, MaxNum); + d_data->num = qBound( 1, num, MaxNum ); d_data->arrowType = arrowType; - setAutoRepeat(true); - setAutoDefault(false); - - switch(d_data->arrowType) { - case Qt::LeftArrow: - case Qt::RightArrow: - setSizePolicy(QSizePolicy::Expanding, - QSizePolicy::Fixed); - break; - default: - setSizePolicy(QSizePolicy::Fixed, - QSizePolicy::Expanding); + setAutoRepeat( true ); + setAutoDefault( false ); + + switch ( d_data->arrowType ) + { + case Qt::LeftArrow: + case Qt::RightArrow: + setSizePolicy( QSizePolicy::Expanding, + QSizePolicy::Fixed ); + break; + default: + setSizePolicy( QSizePolicy::Fixed, + QSizePolicy::Expanding ); } } @@ -102,60 +100,51 @@ int QwtArrowButton::num() const } /*! - \return the bounding rect for the label + \return the bounding rectangle for the label */ QRect QwtArrowButton::labelRect() const { const int m = Margin; QRect r = rect(); - r.setRect(r.x() + m, r.y() + m, - r.width() - 2 * m, r.height() - 2 * m); - - if ( isDown() ) { - int ph, pv; -#if QT_VERSION < 0x040000 - ph = style().pixelMetric( - QStyle::PM_ButtonShiftHorizontal, this); - pv = style().pixelMetric( - QStyle::PM_ButtonShiftVertical, this); - r.moveBy(ph, pv); -#else - QStyleOptionButton option = styleOpt(this); - ph = style()->pixelMetric( - QStyle::PM_ButtonShiftHorizontal, &option, this); - pv = style()->pixelMetric( - QStyle::PM_ButtonShiftVertical, &option, this); - r.translate(ph, pv); -#endif + r.setRect( r.x() + m, r.y() + m, + r.width() - 2 * m, r.height() - 2 * m ); + + if ( isDown() ) + { + QStyleOptionButton option = styleOpt( this ); + const int ph = style()->pixelMetric( + QStyle::PM_ButtonShiftHorizontal, &option, this ); + const int pv = style()->pixelMetric( + QStyle::PM_ButtonShiftVertical, &option, this ); + + r.translate( ph, pv ); } return r; } -#if QT_VERSION >= 0x040000 /*! Paint event handler \param event Paint event */ -void QwtArrowButton::paintEvent(QPaintEvent *event) +void QwtArrowButton::paintEvent( QPaintEvent *event ) { - QPushButton::paintEvent(event); - QPainter painter(this); - drawButtonLabel(&painter); + QPushButton::paintEvent( event ); + QPainter painter( this ); + drawButtonLabel( &painter ); } -#endif /*! \brief Draw the button label \param painter Painter - \sa The Qt Manual on QPushButton + \sa The Qt Manual for QPushButton */ -void QwtArrowButton::drawButtonLabel(QPainter *painter) +void QwtArrowButton::drawButtonLabel( QPainter *painter ) { const bool isVertical = d_data->arrowType == Qt::UpArrow || - d_data->arrowType == Qt::DownArrow; + d_data->arrowType == Qt::DownArrow; const QRect r = labelRect(); QSize boundingSize = labelRect().size(); @@ -163,32 +152,36 @@ void QwtArrowButton::drawButtonLabel(QPainter *painter) boundingSize.transpose(); const int w = - (boundingSize.width() - (MaxNum - 1) * Spacing) / MaxNum; + ( boundingSize.width() - ( MaxNum - 1 ) * Spacing ) / MaxNum; - QSize arrow = arrowSize(Qt::RightArrow, - QSize(w, boundingSize.height())); + QSize arrow = arrowSize( Qt::RightArrow, + QSize( w, boundingSize.height() ) ); if ( isVertical ) arrow.transpose(); QRect contentsSize; // aligned rect where to paint all arrows - if ( d_data->arrowType == Qt::LeftArrow || d_data->arrowType == Qt::RightArrow ) { - contentsSize.setWidth(d_data->num * arrow.width() - + (d_data->num - 1) * Spacing); - contentsSize.setHeight(arrow.height()); - } else { - contentsSize.setWidth(arrow.width()); - contentsSize.setHeight(d_data->num * arrow.height() - + (d_data->num - 1) * Spacing); + if ( d_data->arrowType == Qt::LeftArrow || d_data->arrowType == Qt::RightArrow ) + { + contentsSize.setWidth( d_data->num * arrow.width() + + ( d_data->num - 1 ) * Spacing ); + contentsSize.setHeight( arrow.height() ); + } + else + { + contentsSize.setWidth( arrow.width() ); + contentsSize.setHeight( d_data->num * arrow.height() + + ( d_data->num - 1 ) * Spacing ); } - QRect arrowRect(contentsSize); - arrowRect.moveCenter(r.center()); - arrowRect.setSize(arrow); + QRect arrowRect( contentsSize ); + arrowRect.moveCenter( r.center() ); + arrowRect.setSize( arrow ); painter->save(); - for (int i = 0; i < d_data->num; i++) { - drawArrow(painter, arrowRect, d_data->arrowType); + for ( int i = 0; i < d_data->num; i++ ) + { + drawArrow( painter, arrowRect, d_data->arrowType ); int dx = 0; int dy = 0; @@ -198,77 +191,66 @@ void QwtArrowButton::drawButtonLabel(QPainter *painter) else dx = arrow.width() + Spacing; -#if QT_VERSION >= 0x040000 - arrowRect.translate(dx, dy); -#else - arrowRect.moveBy(dx, dy); -#endif + arrowRect.translate( dx, dy ); } painter->restore(); - if ( hasFocus() ) { -#if QT_VERSION >= 0x040000 + if ( hasFocus() ) + { QStyleOptionFocusRect option; - option.init(this); - option.backgroundColor = palette().color(QPalette::Background); - - style()->drawPrimitive(QStyle::PE_FrameFocusRect, - &option, painter, this); -#else - const QRect focusRect = - style().subRect(QStyle::SR_PushButtonFocusRect, this); - style().drawPrimitive(QStyle::PE_FocusRect, painter, - focusRect, colorGroup()); -#endif + option.init( this ); + option.backgroundColor = palette().color( QPalette::Window ); + + style()->drawPrimitive( QStyle::PE_FrameFocusRect, + &option, painter, this ); } } /*! - Draw an arrow int a bounding rect + Draw an arrow int a bounding rectangle \param painter Painter \param r Rectangle where to paint the arrow \param arrowType Arrow type */ -void QwtArrowButton::drawArrow(QPainter *painter, - const QRect &r, Qt::ArrowType arrowType) const +void QwtArrowButton::drawArrow( QPainter *painter, + const QRect &r, Qt::ArrowType arrowType ) const { - QwtPolygon pa(3); - - switch(arrowType) { - case Qt::UpArrow: - pa.setPoint(0, r.bottomLeft()); - pa.setPoint(1, r.bottomRight()); - pa.setPoint(2, r.center().x(), r.top()); - break; - case Qt::DownArrow: - pa.setPoint(0, r.topLeft()); - pa.setPoint(1, r.topRight()); - pa.setPoint(2, r.center().x(), r.bottom()); - break; - case Qt::RightArrow: - pa.setPoint(0, r.topLeft()); - pa.setPoint(1, r.bottomLeft()); - pa.setPoint(2, r.right(), r.center().y()); - break; - case Qt::LeftArrow: - pa.setPoint(0, r.topRight()); - pa.setPoint(1, r.bottomRight()); - pa.setPoint(2, r.left(), r.center().y()); - break; - default: - break; + QPolygon pa( 3 ); + + switch ( arrowType ) + { + case Qt::UpArrow: + pa.setPoint( 0, r.bottomLeft() ); + pa.setPoint( 1, r.bottomRight() ); + pa.setPoint( 2, r.center().x(), r.top() ); + break; + case Qt::DownArrow: + pa.setPoint( 0, r.topLeft() ); + pa.setPoint( 1, r.topRight() ); + pa.setPoint( 2, r.center().x(), r.bottom() ); + break; + case Qt::RightArrow: + pa.setPoint( 0, r.topLeft() ); + pa.setPoint( 1, r.bottomLeft() ); + pa.setPoint( 2, r.right(), r.center().y() ); + break; + case Qt::LeftArrow: + pa.setPoint( 0, r.topRight() ); + pa.setPoint( 1, r.bottomRight() ); + pa.setPoint( 2, r.left(), r.center().y() ); + break; + default: + break; } painter->save(); -#if QT_VERSION < 0x040000 - painter->setPen(colorGroup().buttonText()); - painter->setBrush(colorGroup().brush(QColorGroup::ButtonText)); -#else - painter->setPen(palette().color(QPalette::ButtonText)); - painter->setBrush(palette().brush(QPalette::ButtonText)); -#endif - painter->drawPolygon(pa); + + painter->setRenderHint( QPainter::Antialiasing, true ); + painter->setPen( Qt::NoPen ); + painter->setBrush( palette().brush( QPalette::ButtonText ) ); + painter->drawPolygon( pa ); + painter->restore(); } @@ -277,7 +259,8 @@ void QwtArrowButton::drawArrow(QPainter *painter, */ QSize QwtArrowButton::sizeHint() const { - return minimumSizeHint(); + const QSize hint = minimumSizeHint(); + return hint.expandedTo( QApplication::globalStrut() ); } /*! @@ -285,43 +268,34 @@ QSize QwtArrowButton::sizeHint() const */ QSize QwtArrowButton::minimumSizeHint() const { - const QSize asz = arrowSize(Qt::RightArrow, QSize()); + const QSize asz = arrowSize( Qt::RightArrow, QSize() ); QSize sz( - 2 * Margin + (MaxNum - 1) * Spacing + MaxNum * asz.width(), + 2 * Margin + ( MaxNum - 1 ) * Spacing + MaxNum * asz.width(), 2 * Margin + asz.height() ); if ( d_data->arrowType == Qt::UpArrow || d_data->arrowType == Qt::DownArrow ) sz.transpose(); -#if QT_VERSION >= 0x040000 QStyleOption styleOption; - styleOption.init(this); - - const QSize hsz = style()->sizeFromContents(QStyle::CT_PushButton, - &styleOption, sz, this); -#if QT_VERSION < 0x040300 - if ( hsz.width() != 80 ) // avoid a bug in the Cleanlooks style -#endif - sz = hsz; + styleOption.init( this ); -#else - sz = style().sizeFromContents(QStyle::CT_PushButton, this, sz); -#endif + sz = style()->sizeFromContents( QStyle::CT_PushButton, + &styleOption, sz, this ); return sz; } /*! - Calculate the size for a arrow that fits into a rect of a given size + Calculate the size for a arrow that fits into a rectangle of a given size \param arrowType Arrow type \param boundingSize Bounding size \return Size of the arrow */ -QSize QwtArrowButton::arrowSize(Qt::ArrowType arrowType, - const QSize &boundingSize) const +QSize QwtArrowButton::arrowSize( Qt::ArrowType arrowType, + const QSize &boundingSize ) const { QSize bs = boundingSize; if ( arrowType == Qt::UpArrow || arrowType == Qt::DownArrow ) @@ -329,17 +303,18 @@ QSize QwtArrowButton::arrowSize(Qt::ArrowType arrowType, const int MinLen = 2; const QSize sz = bs.expandedTo( - QSize(MinLen, 2 * MinLen - 1)); // minimum + QSize( MinLen, 2 * MinLen - 1 ) ); // minimum int w = sz.width(); int h = 2 * w - 1; - if ( h > sz.height() ) { + if ( h > sz.height() ) + { h = sz.height(); - w = (h + 1) / 2; + w = ( h + 1 ) / 2; } - QSize arrSize(w, h); + QSize arrSize( w, h ); if ( arrowType == Qt::UpArrow || arrowType == Qt::DownArrow ) arrSize.transpose(); @@ -349,10 +324,10 @@ QSize QwtArrowButton::arrowSize(Qt::ArrowType arrowType, /*! \brief autoRepeat for the space keys */ -void QwtArrowButton::keyPressEvent(QKeyEvent *e) +void QwtArrowButton::keyPressEvent( QKeyEvent *event ) { - if ( e->isAutoRepeat() && e->key() == Qt::Key_Space ) - emit clicked(); + if ( event->isAutoRepeat() && event->key() == Qt::Key_Space ) + Q_EMIT clicked(); - QPushButton::keyPressEvent(e); + QPushButton::keyPressEvent( event ); } diff --git a/libs/qwt/qwt_arrow_button.h b/libs/qwt/qwt_arrow_button.h index a47d3236f..ae436fec0 100644 --- a/libs/qwt/qwt_arrow_button.h +++ b/libs/qwt/qwt_arrow_button.h @@ -10,8 +10,8 @@ #ifndef QWT_ARROW_BUTTON_H #define QWT_ARROW_BUTTON_H -#include #include "qwt_global.h" +#include /*! \brief Arrow Button @@ -23,7 +23,7 @@ class QWT_EXPORT QwtArrowButton : public QPushButton { public: - explicit QwtArrowButton (int num, Qt::ArrowType, QWidget *parent = NULL); + explicit QwtArrowButton ( int num, Qt::ArrowType, QWidget *parent = NULL ); virtual ~QwtArrowButton(); Qt::ArrowType arrowType() const; @@ -33,18 +33,16 @@ public: virtual QSize minimumSizeHint() const; protected: -#if QT_VERSION >= 0x040000 - virtual void paintEvent(QPaintEvent *event); -#endif + virtual void paintEvent( QPaintEvent *event ); - virtual void drawButtonLabel(QPainter *p); - virtual void drawArrow(QPainter *, - const QRect &, Qt::ArrowType) const; + virtual void drawButtonLabel( QPainter *p ); + virtual void drawArrow( QPainter *, + const QRect &, Qt::ArrowType ) const; virtual QRect labelRect() const; - virtual QSize arrowSize(Qt::ArrowType, - const QSize &boundingSize) const; + virtual QSize arrowSize( Qt::ArrowType, + const QSize &boundingSize ) const; - virtual void keyPressEvent(QKeyEvent *); + virtual void keyPressEvent( QKeyEvent * ); private: class PrivateData; diff --git a/libs/qwt/qwt_clipper.cpp b/libs/qwt/qwt_clipper.cpp index 7c2515789..51614aa37 100644 --- a/libs/qwt/qwt_clipper.cpp +++ b/libs/qwt/qwt_clipper.cpp @@ -7,373 +7,365 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ -#include -#include "qwt_math.h" #include "qwt_clipper.h" +#include "qwt_point_polar.h" +#include +#include +#include -static inline QwtDoubleRect boundingRect(const QwtPolygonF &polygon) -{ -#if QT_VERSION < 0x040000 - if (polygon.isEmpty()) - return QwtDoubleRect(0, 0, 0, 0); - - register const QwtDoublePoint *pd = polygon.data(); - - double minx, maxx, miny, maxy; - minx = maxx = pd->x(); - miny = maxy = pd->y(); - pd++; - - for (uint i = 1; i < polygon.size(); i++, pd++) { - if (pd->x() < minx) - minx = pd->x(); - else if (pd->x() > maxx) - maxx = pd->x(); - if (pd->y() < miny) - miny = pd->y(); - else if (pd->y() > maxy) - maxy = pd->y(); - } - return QwtDoubleRect(minx, miny, maxx - minx, maxy - miny); -#else - return polygon.boundingRect(); +#if QT_VERSION < 0x040601 +#define qAtan(x) ::atan(x) #endif -} -enum Edge { - Left, - Top, - Right, - Bottom, - NEdges -}; +namespace QwtClip +{ + // some templates used for inlining + template class LeftEdge; + template class RightEdge; + template class TopEdge; + template class BottomEdge; -class QwtPolygonClipper: public QRect + template class PointBuffer; +} + +template +class QwtClip::LeftEdge { public: - QwtPolygonClipper(const QRect &r); + inline LeftEdge( Value x1, Value, Value, Value ): + d_x1( x1 ) + { + } - QwtPolygon clipPolygon(const QwtPolygon &) const; + inline bool isInside( const Point &p ) const + { + return p.x() >= d_x1; + } + inline Point intersection( const Point &p1, const Point &p2 ) const + { + double dy = ( p1.y() - p2.y() ) / double( p1.x() - p2.x() ); + return Point( d_x1, static_cast< Value >( p2.y() + ( d_x1 - p2.x() ) * dy ) ); + } private: - void clipEdge(Edge, const QwtPolygon &, QwtPolygon &) const; - bool insideEdge(const QPoint &, Edge edge) const; - QPoint intersectEdge(const QPoint &p1, - const QPoint &p2, Edge edge) const; - - void addPoint(QwtPolygon &, uint pos, const QPoint &point) const; + const Value d_x1; }; -class QwtPolygonClipperF: public QwtDoubleRect +template +class QwtClip::RightEdge { public: - QwtPolygonClipperF(const QwtDoubleRect &r); - QwtPolygonF clipPolygon(const QwtPolygonF &) const; + inline RightEdge( Value, Value x2, Value, Value ): + d_x2( x2 ) + { + } -private: - void clipEdge(Edge, const QwtPolygonF &, QwtPolygonF &) const; - bool insideEdge(const QwtDoublePoint &, Edge edge) const; - QwtDoublePoint intersectEdge(const QwtDoublePoint &p1, - const QwtDoublePoint &p2, Edge edge) const; + inline bool isInside( const Point &p ) const + { + return p.x() <= d_x2; + } + + inline Point intersection( const Point &p1, const Point &p2 ) const + { + double dy = ( p1.y() - p2.y() ) / double( p1.x() - p2.x() ); + return Point( d_x2, static_cast( p2.y() + ( d_x2 - p2.x() ) * dy ) ); + } - void addPoint(QwtPolygonF &, uint pos, const QwtDoublePoint &point) const; +private: + const Value d_x2; }; -#if QT_VERSION >= 0x040000 -class QwtCircleClipper: public QwtDoubleRect +template +class QwtClip::TopEdge { public: - QwtCircleClipper(const QwtDoubleRect &r); - QwtArray clipCircle( - const QwtDoublePoint &, double radius) const; + inline TopEdge( Value, Value, Value y1, Value ): + d_y1( y1 ) + { + } + + inline bool isInside( const Point &p ) const + { + return p.y() >= d_y1; + } + + inline Point intersection( const Point &p1, const Point &p2 ) const + { + double dx = ( p1.x() - p2.x() ) / double( p1.y() - p2.y() ); + return Point( static_cast( p2.x() + ( d_y1 - p2.y() ) * dx ), d_y1 ); + } private: - QList cuttingPoints( - Edge, const QwtDoublePoint &pos, double radius) const; - double toAngle(const QwtDoublePoint &, const QwtDoublePoint &) const; + const Value d_y1; }; -#endif -QwtPolygonClipper::QwtPolygonClipper(const QRect &r): - QRect(r) +template +class QwtClip::BottomEdge { -} +public: + inline BottomEdge( Value, Value, Value, Value y2 ): + d_y2( y2 ) + { + } -inline void QwtPolygonClipper::addPoint( - QwtPolygon &pa, uint pos, const QPoint &point) const -{ - if ( uint(pa.size()) <= pos ) - pa.resize(pos + 5); + inline bool isInside( const Point &p ) const + { + return p.y() <= d_y2; + } - pa.setPoint(pos, point); -} + inline Point intersection( const Point &p1, const Point &p2 ) const + { + double dx = ( p1.x() - p2.x() ) / double( p1.y() - p2.y() ); + return Point( static_cast( p2.x() + ( d_y2 - p2.y() ) * dx ), d_y2 ); + } + +private: + const Value d_y2; +}; -//! Sutherland-Hodgman polygon clipping -QwtPolygon QwtPolygonClipper::clipPolygon(const QwtPolygon &pa) const +template +class QwtClip::PointBuffer { - if ( contains( pa.boundingRect() ) ) - return pa; +public: + PointBuffer( int capacity = 0 ): + m_capacity( 0 ), + m_size( 0 ), + m_buffer( NULL ) + { + if ( capacity > 0 ) + reserve( capacity ); + } - QwtPolygon cpa(pa.size()); + ~PointBuffer() + { + if ( m_buffer ) + ::free( m_buffer ); + } - clipEdge((Edge)0, pa, cpa); + inline void setPoints( int numPoints, const Point *points ) + { + reserve( numPoints ); - for ( uint edge = 1; edge < NEdges; edge++ ) { - const QwtPolygon rpa = cpa; -#if QT_VERSION < 0x040000 - cpa.detach(); -#endif - clipEdge((Edge)edge, rpa, cpa); + m_size = numPoints; + ::memcpy( m_buffer, points, m_size * sizeof( Point ) ); } - return cpa; -} - -bool QwtPolygonClipper::insideEdge(const QPoint &p, Edge edge) const -{ - switch(edge) { - case Left: - return p.x() > left(); - case Top: - return p.y() > top(); - case Right: - return p.x() < right(); - case Bottom: - return p.y() < bottom(); - default: - break; + inline void reset() + { + m_size = 0; } - return false; -} + inline int size() const + { + return m_size; + } -QPoint QwtPolygonClipper::intersectEdge(const QPoint &p1, - const QPoint &p2, Edge edge ) const -{ - int x=0, y=0; - double m = 0; - - const double dy = p2.y() - p1.y(); - const double dx = p2.x() - p1.x(); - - switch ( edge ) { - case Left: - x = left(); - m = double(qwtAbs(p1.x() - x)) / qwtAbs(dx); - y = p1.y() + int(dy * m); - break; - case Top: - y = top(); - m = double(qwtAbs(p1.y() - y)) / qwtAbs(dy); - x = p1.x() + int(dx * m); - break; - case Right: - x = right(); - m = double(qwtAbs(p1.x() - x)) / qwtAbs(dx); - y = p1.y() + int(dy * m); - break; - case Bottom: - y = bottom(); - m = double(qwtAbs(p1.y() - y)) / qwtAbs(dy); - x = p1.x() + int(dx * m); - break; - default: - break; + inline Point *data() const + { + return m_buffer; } - return QPoint(x,y); -} + inline Point &operator[]( int i ) + { + return m_buffer[i]; + } -void QwtPolygonClipper::clipEdge(Edge edge, - const QwtPolygon &pa, QwtPolygon &cpa) const -{ - if ( pa.count() == 0 ) { - cpa.resize(0); - return; + inline const Point &operator[]( int i ) const + { + return m_buffer[i]; } - unsigned int count = 0; - - QPoint p1 = pa.point(0); - if ( insideEdge(p1, edge) ) - addPoint(cpa, count++, p1); - - const uint nPoints = pa.size(); - for ( uint i = 1; i < nPoints; i++ ) { - const QPoint p2 = pa.point(i); - if ( insideEdge(p2, edge) ) { - if ( insideEdge(p1, edge) ) - addPoint(cpa, count++, p2); - else { - addPoint(cpa, count++, intersectEdge(p1, p2, edge)); - addPoint(cpa, count++, p2); - } - } else { - if ( insideEdge(p1, edge) ) - addPoint(cpa, count++, intersectEdge(p1, p2, edge)); - } - p1 = p2; + inline void add( const Point &point ) + { + if ( m_capacity <= m_size ) + reserve( m_size + 1 ); + + m_buffer[m_size++] = point; } - cpa.resize(count); -} -QwtPolygonClipperF::QwtPolygonClipperF(const QwtDoubleRect &r): - QwtDoubleRect(r) -{ -} +private: + inline void reserve( int size ) + { + if ( m_capacity == 0 ) + m_capacity = 1; -inline void QwtPolygonClipperF::addPoint(QwtPolygonF &pa, uint pos, const QwtDoublePoint &point) const -{ - if ( uint(pa.size()) <= pos ) - pa.resize(pos + 5); + while ( m_capacity < size ) + m_capacity *= 2; - pa[(int)pos] = point; -} + m_buffer = static_cast( + ::realloc( m_buffer, m_capacity * sizeof( Point ) ) ); + } -//! Sutherland-Hodgman polygon clipping -QwtPolygonF QwtPolygonClipperF::clipPolygon(const QwtPolygonF &pa) const -{ - if ( contains( ::boundingRect(pa) ) ) - return pa; + int m_capacity; + int m_size; + Point *m_buffer; +}; - QwtPolygonF cpa(pa.size()); +using namespace QwtClip; - clipEdge((Edge)0, pa, cpa); +template +class QwtPolygonClipper +{ +public: + QwtPolygonClipper( const Rect &clipRect ): + d_clipRect( clipRect ) + { + } - for ( uint edge = 1; edge < NEdges; edge++ ) { - const QwtPolygonF rpa = cpa; -#if QT_VERSION < 0x040000 - cpa.detach(); + Polygon clipPolygon( const Polygon &polygon, bool closePolygon ) const + { +#if 0 + if ( d_clipRect.contains( polygon.boundingRect() ) ) + return polygon; #endif - clipEdge((Edge)edge, rpa, cpa); - } - return cpa; -} + PointBuffer points1; + PointBuffer points2( qMin( 256, polygon.size() ) ); -bool QwtPolygonClipperF::insideEdge(const QwtDoublePoint &p, Edge edge) const -{ - switch(edge) { - case Left: - return p.x() > left(); - case Top: - return p.y() > top(); - case Right: - return p.x() < right(); - case Bottom: - return p.y() < bottom(); - default: - break; - } + points1.setPoints( polygon.size(), polygon.data() ); - return false; -} + clipEdge< LeftEdge >( closePolygon, points1, points2 ); + clipEdge< RightEdge >( closePolygon, points2, points1 ); + clipEdge< TopEdge >( closePolygon, points1, points2 ); + clipEdge< BottomEdge >( closePolygon, points2, points1 ); -QwtDoublePoint QwtPolygonClipperF::intersectEdge(const QwtDoublePoint &p1, - const QwtDoublePoint &p2, Edge edge ) const -{ - double x=0.0, y=0.0; - double m = 0; - - const double dy = p2.y() - p1.y(); - const double dx = p2.x() - p1.x(); - - switch ( edge ) { - case Left: - x = left(); - m = double(qwtAbs(p1.x() - x)) / qwtAbs(dx); - y = p1.y() + int(dy * m); - break; - case Top: - y = top(); - m = double(qwtAbs(p1.y() - y)) / qwtAbs(dy); - x = p1.x() + int(dx * m); - break; - case Right: - x = right(); - m = double(qwtAbs(p1.x() - x)) / qwtAbs(dx); - y = p1.y() + int(dy * m); - break; - case Bottom: - y = bottom(); - m = double(qwtAbs(p1.y() - y)) / qwtAbs(dy); - x = p1.x() + int(dx * m); - break; - default: - break; + Polygon p; + p.resize( points1.size() ); + ::memcpy( p.data(), points1.data(), points1.size() * sizeof( Point ) ); + + return p; } - return QwtDoublePoint(x,y); -} +private: + template + inline void clipEdge( bool closePolygon, + PointBuffer &points, PointBuffer &clippedPoints ) const + { + clippedPoints.reset(); + + if ( points.size() < 2 ) + { + if ( points.size() == 1 ) + clippedPoints.add( points[0] ); + return; + } -void QwtPolygonClipperF::clipEdge(Edge edge, - const QwtPolygonF &pa, QwtPolygonF &cpa) const -{ - if ( pa.count() == 0 ) { - cpa.resize(0); - return; - } + const Edge edge( d_clipRect.x(), d_clipRect.x() + d_clipRect.width(), + d_clipRect.y(), d_clipRect.y() + d_clipRect.height() ); + + int lastPos, start; + if ( closePolygon ) + { + start = 0; + lastPos = points.size() - 1; + } + else + { + start = 1; + lastPos = 0; - unsigned int count = 0; - - QwtDoublePoint p1 = pa[0]; - if ( insideEdge(p1, edge) ) - addPoint(cpa, count++, p1); - - const uint nPoints = pa.size(); - for ( uint i = 1; i < nPoints; i++ ) { - const QwtDoublePoint p2 = pa[(int)i]; - if ( insideEdge(p2, edge) ) { - if ( insideEdge(p1, edge) ) - addPoint(cpa, count++, p2); - else { - addPoint(cpa, count++, intersectEdge(p1, p2, edge)); - addPoint(cpa, count++, p2); + if ( edge.isInside( points[0] ) ) + clippedPoints.add( points[0] ); + } + + const uint nPoints = points.size(); + for ( uint i = start; i < nPoints; i++ ) + { + const Point &p1 = points[i]; + const Point &p2 = points[lastPos]; + + if ( edge.isInside( p1 ) ) + { + if ( edge.isInside( p2 ) ) + { + clippedPoints.add( p1 ); + } + else + { + clippedPoints.add( edge.intersection( p1, p2 ) ); + clippedPoints.add( p1 ); + } + } + else + { + if ( edge.isInside( p2 ) ) + { + clippedPoints.add( edge.intersection( p1, p2 ) ); + } } - } else { - if ( insideEdge(p1, edge) ) - addPoint(cpa, count++, intersectEdge(p1, p2, edge)); + lastPos = i; } - p1 = p2; } - cpa.resize(count); -} -#if QT_VERSION >= 0x040000 + const Rect d_clipRect; +}; + +class QwtCircleClipper +{ +public: + QwtCircleClipper( const QRectF &r ); + QVector clipCircle( const QPointF &, double radius ) const; + +private: + enum Edge + { + Left, + Top, + Right, + Bottom, + + NEdges + }; + + QList cuttingPoints( + Edge, const QPointF &pos, double radius ) const; + + double toAngle( const QPointF &, const QPointF & ) const; + + const QRectF d_rect; +}; + -QwtCircleClipper::QwtCircleClipper(const QwtDoubleRect &r): - QwtDoubleRect(r) +QwtCircleClipper::QwtCircleClipper( const QRectF &r ): + d_rect( r ) { } -QwtArray QwtCircleClipper::clipCircle( - const QwtDoublePoint &pos, double radius) const +QVector QwtCircleClipper::clipCircle( + const QPointF &pos, double radius ) const { - QList points; + QList points; for ( int edge = 0; edge < NEdges; edge++ ) - points += cuttingPoints((Edge)edge, pos, radius); - - QwtArray intv; - if ( points.size() <= 0 ) { - QwtDoubleRect cRect(0, 0, 2 * radius, 2* radius); - cRect.moveCenter(pos); - if ( contains(cRect) ) - intv += QwtDoubleInterval(0.0, 2 * M_PI); - } else { + points += cuttingPoints( static_cast(edge), pos, radius ); + + QVector intv; + if ( points.size() <= 0 ) + { + QRectF cRect( 0, 0, 2 * radius, 2 * radius ); + cRect.moveCenter( pos ); + if ( d_rect.contains( cRect ) ) + intv += QwtInterval( 0.0, 2 * M_PI ); + } + else + { QList angles; for ( int i = 0; i < points.size(); i++ ) - angles += toAngle(pos, points[i]); - qSort(angles); - - const int in = contains(qwtPolar2Pos(pos, radius, - angles[0] + (angles[1] - angles[0]) / 2)); - if ( in ) { - for ( int i = 0; i < angles.size() - 1; i += 2) - intv += QwtDoubleInterval(angles[i], angles[i+1]); - } else { - for ( int i = 1; i < angles.size() - 1; i += 2) - intv += QwtDoubleInterval(angles[i], angles[i+1]); - intv += QwtDoubleInterval(angles.last(), angles.first()); + angles += toAngle( pos, points[i] ); + qSort( angles ); + + const int in = d_rect.contains( qwtPolar2Pos( pos, radius, + angles[0] + ( angles[1] - angles[0] ) / 2 ) ); + + if ( in ) + { + for ( int i = 0; i < angles.size() - 1; i += 2 ) + intv += QwtInterval( angles[i], angles[i+1] ); + } + else + { + for ( int i = 1; i < angles.size() - 1; i += 2 ) + intv += QwtInterval( angles[i], angles[i+1] ); + intv += QwtInterval( angles.last(), angles.first() ); } } @@ -381,18 +373,21 @@ QwtArray QwtCircleClipper::clipCircle( } double QwtCircleClipper::toAngle( - const QwtDoublePoint &from, const QwtDoublePoint &to) const + const QPointF &from, const QPointF &to ) const { if ( from.x() == to.x() ) return from.y() <= to.y() ? M_PI / 2.0 : 3 * M_PI / 2.0; - const double m = qwtAbs((to.y() - from.y()) / (to.x() - from.x()) ); + const double m = qAbs( ( to.y() - from.y() ) / ( to.x() - from.x() ) ); - double angle = ::atan(m); - if ( to.x() > from.x() ) { + double angle = qAtan( m ); + if ( to.x() > from.x() ) + { if ( to.y() > from.y() ) angle = 2 * M_PI - angle; - } else { + } + else + { if ( to.y() > from.y() ) angle = M_PI + angle; else @@ -402,58 +397,114 @@ double QwtCircleClipper::toAngle( return angle; } -QList QwtCircleClipper::cuttingPoints( - Edge edge, const QwtDoublePoint &pos, double radius) const +QList QwtCircleClipper::cuttingPoints( + Edge edge, const QPointF &pos, double radius ) const { - QList points; - - if ( edge == Left || edge == Right ) { - const double x = (edge == Left) ? left() : right(); - if ( qwtAbs(pos.x() - x) < radius ) { - const double off = ::sqrt(qwtSqr(radius) - qwtSqr(pos.x() - x)); - const double y1 = pos.y() + off; - if ( y1 >= top() && y1 <= bottom() ) - points += QwtDoublePoint(x, y1); - const double y2 = pos.y() - off; - if ( y2 >= top() && y2 <= bottom() ) - points += QwtDoublePoint(x, y2); + QList points; + + if ( edge == Left || edge == Right ) + { + const double x = ( edge == Left ) ? d_rect.left() : d_rect.right(); + if ( qAbs( pos.x() - x ) < radius ) + { + const double off = qSqrt( qwtSqr( radius ) - qwtSqr( pos.x() - x ) ); + const double m_y1 = pos.y() + off; + if ( m_y1 >= d_rect.top() && m_y1 <= d_rect.bottom() ) + points += QPointF( x, m_y1 ); + + const double m_y2 = pos.y() - off; + if ( m_y2 >= d_rect.top() && m_y2 <= d_rect.bottom() ) + points += QPointF( x, m_y2 ); } - } else { - const double y = (edge == Top) ? top() : bottom(); - if ( qwtAbs(pos.y() - y) < radius ) { - const double off = ::sqrt(qwtSqr(radius) - qwtSqr(pos.y() - y)); + } + else + { + const double y = ( edge == Top ) ? d_rect.top() : d_rect.bottom(); + if ( qAbs( pos.y() - y ) < radius ) + { + const double off = qSqrt( qwtSqr( radius ) - qwtSqr( pos.y() - y ) ); const double x1 = pos.x() + off; - if ( x1 >= left() && x1 <= right() ) - points += QwtDoublePoint(x1, y); - const double x2 = pos.x() - off; - if ( x2 >= left() && x2 <= right() ) - points += QwtDoublePoint(x2, y); + if ( x1 >= d_rect.left() && x1 <= d_rect.right() ) + points += QPointF( x1, y ); + + const double m_x2 = pos.x() - off; + if ( m_x2 >= d_rect.left() && m_x2 <= d_rect.right() ) + points += QPointF( m_x2, y ); } } return points; } -#endif -QwtPolygon QwtClipper::clipPolygon( - const QRect &clipRect, const QwtPolygon &polygon) +/*! + Sutherland-Hodgman polygon clipping + + \param clipRect Clip rectangle + \param polygon Polygon + \param closePolygon True, when the polygon is closed + + \return Clipped polygon +*/ +QPolygon QwtClipper::clipPolygon( + const QRectF &clipRect, const QPolygon &polygon, bool closePolygon ) +{ + const int minX = qCeil( clipRect.left() ); + const int maxX = qFloor( clipRect.right() ); + const int minY = qCeil( clipRect.top() ); + const int maxY = qFloor( clipRect.bottom() ); + + const QRect r( minX, minY, maxX - minX, maxY - minY ); + + QwtPolygonClipper clipper( r ); + return clipper.clipPolygon( polygon, closePolygon ); +} +/*! + Sutherland-Hodgman polygon clipping + + \param clipRect Clip rectangle + \param polygon Polygon + \param closePolygon True, when the polygon is closed + + \return Clipped polygon +*/ +QPolygon QwtClipper::clipPolygon( + const QRect &clipRect, const QPolygon &polygon, bool closePolygon ) { - QwtPolygonClipper clipper(clipRect); - return clipper.clipPolygon(polygon); + QwtPolygonClipper clipper( clipRect ); + return clipper.clipPolygon( polygon, closePolygon ); } -QwtPolygonF QwtClipper::clipPolygonF( - const QwtDoubleRect &clipRect, const QwtPolygonF &polygon) +/*! + Sutherland-Hodgman polygon clipping + + \param clipRect Clip rectangle + \param polygon Polygon + \param closePolygon True, when the polygon is closed + + \return Clipped polygon +*/ +QPolygonF QwtClipper::clipPolygonF( + const QRectF &clipRect, const QPolygonF &polygon, bool closePolygon ) { - QwtPolygonClipperF clipper(clipRect); - return clipper.clipPolygon(polygon); + QwtPolygonClipper clipper( clipRect ); + return clipper.clipPolygon( polygon, closePolygon ); } -#if QT_VERSION >= 0x040000 -QwtArray QwtClipper::clipCircle( - const QwtDoubleRect &clipRect, - const QwtDoublePoint ¢er, double radius) +/*! + Circle clipping + + clipCircle() divides a circle into intervals of angles representing arcs + of the circle. When the circle is completely inside the clip rectangle + an interval [0.0, 2 * M_PI] is returned. + + \param clipRect Clip rectangle + \param center Center of the circle + \param radius Radius of the circle + + \return Arcs of the circle +*/ +QVector QwtClipper::clipCircle( const QRectF &clipRect, + const QPointF ¢er, double radius ) { - QwtCircleClipper clipper(clipRect); - return clipper.clipCircle(center, radius); + QwtCircleClipper clipper( clipRect ); + return clipper.clipCircle( center, radius ); } -#endif diff --git a/libs/qwt/qwt_clipper.h b/libs/qwt/qwt_clipper.h index b3fe16f90..1b1820bb0 100644 --- a/libs/qwt/qwt_clipper.h +++ b/libs/qwt/qwt_clipper.h @@ -11,27 +11,30 @@ #define QWT_CLIPPER_H #include "qwt_global.h" -#include "qwt_array.h" -#include "qwt_polygon.h" -#include "qwt_double_rect.h" -#include "qwt_double_interval.h" +#include "qwt_interval.h" +#include +#include class QRect; +class QRectF; /*! - \brief Some clipping algos + \brief Some clipping algorithms */ class QWT_EXPORT QwtClipper { public: - static QwtPolygon clipPolygon(const QRect &, const QwtPolygon &); - static QwtPolygonF clipPolygonF(const QwtDoubleRect &, const QwtPolygonF &); + static QPolygon clipPolygon( const QRect &, + const QPolygon &, bool closePolygon = false ); + static QPolygon clipPolygon( const QRectF &, + const QPolygon &, bool closePolygon = false ); -#if QT_VERSION >= 0x040000 - static QwtArray clipCircle( - const QwtDoubleRect &, const QwtDoublePoint &, double radius); -#endif + static QPolygonF clipPolygonF( const QRectF &, + const QPolygonF &, bool closePolygon = false ); + + static QVector clipCircle( + const QRectF &, const QPointF &, double radius ); }; #endif diff --git a/libs/qwt/qwt_color_map.cpp b/libs/qwt/qwt_color_map.cpp index 3ea3b129e..571c1380d 100644 --- a/libs/qwt/qwt_color_map.cpp +++ b/libs/qwt/qwt_color_map.cpp @@ -7,31 +7,23 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ -#include "qwt_array.h" -#include "qwt_math.h" -#include "qwt_double_interval.h" #include "qwt_color_map.h" - -#if QT_VERSION < 0x040000 -#include -typedef QValueVector QwtColorTable; -#else -typedef QVector QwtColorTable; -#endif +#include "qwt_math.h" +#include "qwt_interval.h" +#include class QwtLinearColorMap::ColorStops { public: - ColorStops() { -#if QT_VERSION >= 0x040000 - _stops.reserve(256); -#endif + ColorStops() + { + _stops.reserve( 256 ); } - void insert(double pos, const QColor &color); - QRgb rgb(QwtLinearColorMap::Mode, double pos) const; + void insert( double pos, const QColor &color ); + QRgb rgb( QwtLinearColorMap::Mode, double pos ) const; - QwtArray stops() const; + QVector stops() const; private: @@ -39,16 +31,18 @@ private: { public: ColorStop(): - pos(0.0), - rgb(0) { + pos( 0.0 ), + rgb( 0 ) + { }; - ColorStop(double p, const QColor &c): - pos(p), - rgb(c.rgb()) { - r = qRed(rgb); - g = qGreen(rgb); - b = qBlue(rgb); + ColorStop( double p, const QColor &c ): + pos( p ), + rgb( c.rgb() ) + { + r = qRed( rgb ); + g = qGreen( rgb ); + b = qBlue( rgb ); } double pos; @@ -56,11 +50,11 @@ private: int r, g, b; }; - inline int findUpper(double pos) const; - QwtArray _stops; + inline int findUpper( double pos ) const; + QVector _stops; }; -void QwtLinearColorMap::ColorStops::insert(double pos, const QColor &color) +void QwtLinearColorMap::ColorStops::insert( double pos, const QColor &color ) { // Lookups need to be very fast, insertions are not so important. // Anyway, a balanced tree is what we need here. TODO ... @@ -69,53 +63,52 @@ void QwtLinearColorMap::ColorStops::insert(double pos, const QColor &color) return; int index; - if ( _stops.size() == 0 ) { + if ( _stops.size() == 0 ) + { index = 0; -#if QT_VERSION < 0x040000 - _stops.resize(1, QGArray::SpeedOptim); -#else - _stops.resize(1); -#endif - } else { - index = findUpper(pos); - if ( index == (int)_stops.size() || - qwtAbs(_stops[index].pos - pos) >= 0.001 ) { -#if QT_VERSION < 0x040000 - _stops.resize(_stops.size() + 1, QGArray::SpeedOptim); -#else - _stops.resize(_stops.size() + 1); -#endif + _stops.resize( 1 ); + } + else + { + index = findUpper( pos ); + if ( index == _stops.size() || + qAbs( _stops[index].pos - pos ) >= 0.001 ) + { + _stops.resize( _stops.size() + 1 ); for ( int i = _stops.size() - 1; i > index; i-- ) _stops[i] = _stops[i-1]; } } - _stops[index] = ColorStop(pos, color); + _stops[index] = ColorStop( pos, color ); } -inline QwtArray QwtLinearColorMap::ColorStops::stops() const +inline QVector QwtLinearColorMap::ColorStops::stops() const { - QwtArray positions(_stops.size()); - for ( int i = 0; i < (int)_stops.size(); i++ ) + QVector positions( _stops.size() ); + for ( int i = 0; i < _stops.size(); i++ ) positions[i] = _stops[i].pos; return positions; } -inline int QwtLinearColorMap::ColorStops::findUpper(double pos) const +inline int QwtLinearColorMap::ColorStops::findUpper( double pos ) const { int index = 0; int n = _stops.size(); const ColorStop *stops = _stops.data(); - while (n > 0) { + while ( n > 0 ) + { const int half = n >> 1; const int middle = index + half; - if ( stops[middle].pos <= pos ) { + if ( stops[middle].pos <= pos ) + { index = middle + 1; n -= half + 1; - } else + } + else n = half; } @@ -123,33 +116,36 @@ inline int QwtLinearColorMap::ColorStops::findUpper(double pos) const } inline QRgb QwtLinearColorMap::ColorStops::rgb( - QwtLinearColorMap::Mode mode, double pos) const + QwtLinearColorMap::Mode mode, double pos ) const { if ( pos <= 0.0 ) return _stops[0].rgb; if ( pos >= 1.0 ) - return _stops[(int)(_stops.size() - 1)].rgb; + return _stops[ _stops.size() - 1 ].rgb; - const int index = findUpper(pos); - if ( mode == FixedColors ) { + const int index = findUpper( pos ); + if ( mode == FixedColors ) + { return _stops[index-1].rgb; - } else { + } + else + { const ColorStop &s1 = _stops[index-1]; const ColorStop &s2 = _stops[index]; - const double ratio = (pos - s1.pos) / (s2.pos - s1.pos); + const double ratio = ( pos - s1.pos ) / ( s2.pos - s1.pos ); - const int r = s1.r + qRound(ratio * (s2.r - s1.r)); - const int g = s1.g + qRound(ratio * (s2.g - s1.g)); - const int b = s1.b + qRound(ratio * (s2.b - s1.b)); + const int r = s1.r + qRound( ratio * ( s2.r - s1.r ) ); + const int g = s1.g + qRound( ratio * ( s2.g - s1.g ) ); + const int b = s1.b + qRound( ratio * ( s2.b - s1.b ) ); - return qRgb(r, g, b); + return qRgb( r, g, b ); } } //! Constructor -QwtColorMap::QwtColorMap(Format format): - d_format(format) +QwtColorMap::QwtColorMap( Format format ): + d_format( format ) { } @@ -167,15 +163,15 @@ QwtColorMap::~QwtColorMap() \param interval Range for the values \return A color table, that can be used for a QImage */ -QwtColorTable QwtColorMap::colorTable( - const QwtDoubleInterval &interval) const +QVector QwtColorMap::colorTable( const QwtInterval &interval ) const { - QwtColorTable table(256); + QVector table( 256 ); - if ( interval.isValid() ) { - const double step = interval.width() / (table.size() - 1); - for ( int i = 0; i < (int) table.size(); i++ ) - table[i] = rgb(interval, interval.minValue() + step * i); + if ( interval.isValid() ) + { + const double step = interval.width() / ( table.size() - 1 ); + for ( int i = 0; i < table.size(); i++ ) + table[i] = rgb( interval, interval.minValue() + step * i ); } return table; @@ -194,21 +190,13 @@ public: \param format Preferred format of the color map */ -QwtLinearColorMap::QwtLinearColorMap(QwtColorMap::Format format): - QwtColorMap(format) +QwtLinearColorMap::QwtLinearColorMap( QwtColorMap::Format format ): + QwtColorMap( format ) { d_data = new PrivateData; d_data->mode = ScaledColors; - setColorInterval( Qt::blue, Qt::yellow); -} - -//! Copy constructor -QwtLinearColorMap::QwtLinearColorMap(const QwtLinearColorMap &other): - QwtColorMap(other) -{ - d_data = new PrivateData; - *this = other; + setColorInterval( Qt::blue, Qt::yellow ); } /*! @@ -216,15 +204,15 @@ QwtLinearColorMap::QwtLinearColorMap(const QwtLinearColorMap &other): \param color1 Color used for the minimum value of the value interval \param color2 Color used for the maximum value of the value interval - \param format Preferred format of the coor map + \param format Preferred format for the color map */ -QwtLinearColorMap::QwtLinearColorMap(const QColor &color1, - const QColor &color2, QwtColorMap::Format format): - QwtColorMap(format) +QwtLinearColorMap::QwtLinearColorMap( const QColor &color1, + const QColor &color2, QwtColorMap::Format format ): + QwtColorMap( format ) { d_data = new PrivateData; d_data->mode = ScaledColors; - setColorInterval(color1, color2); + setColorInterval( color1, color2 ); } //! Destructor @@ -233,24 +221,6 @@ QwtLinearColorMap::~QwtLinearColorMap() delete d_data; } -//! Assignment operator -QwtLinearColorMap &QwtLinearColorMap::operator=( - const QwtLinearColorMap &other) -{ - QwtColorMap::operator=(other); - *d_data = *other.d_data; - return *this; -} - -//! Clone the color map -QwtColorMap *QwtLinearColorMap::copy() const -{ - QwtLinearColorMap* map = new QwtLinearColorMap(); - *map = *this; - - return map; -} - /*! \brief Set the mode of the color map @@ -260,7 +230,7 @@ QwtColorMap *QwtLinearColorMap::copy() const \sa mode() */ -void QwtLinearColorMap::setMode(Mode mode) +void QwtLinearColorMap::setMode( Mode mode ) { d_data->mode = mode; } @@ -285,11 +255,11 @@ QwtLinearColorMap::Mode QwtLinearColorMap::mode() const \sa color1(), color2() */ void QwtLinearColorMap::setColorInterval( - const QColor &color1, const QColor &color2) + const QColor &color1, const QColor &color2 ) { d_data->colorStops = ColorStops(); - d_data->colorStops.insert(0.0, color1); - d_data->colorStops.insert(1.0, color2); + d_data->colorStops.insert( 0.0, color1 ); + d_data->colorStops.insert( 1.0, color2 ); } /*! @@ -302,16 +272,16 @@ void QwtLinearColorMap::setColorInterval( \param value Value between [0.0, 1.0] \param color Color stop */ -void QwtLinearColorMap::addColorStop(double value, const QColor& color) +void QwtLinearColorMap::addColorStop( double value, const QColor& color ) { if ( value >= 0.0 && value <= 1.0 ) - d_data->colorStops.insert(value, color); + d_data->colorStops.insert( value, color ); } /*! - Return all positions of color stops in increasing order + \return Positions of color stops in increasing order */ -QwtArray QwtLinearColorMap::colorStops() const +QVector QwtLinearColorMap::colorStops() const { return d_data->colorStops.stops(); } @@ -322,7 +292,7 @@ QwtArray QwtLinearColorMap::colorStops() const */ QColor QwtLinearColorMap::color1() const { - return QColor(d_data->colorStops.rgb(d_data->mode, 0.0)); + return QColor( d_data->colorStops.rgb( d_data->mode, 0.0 ) ); } /*! @@ -331,51 +301,58 @@ QColor QwtLinearColorMap::color1() const */ QColor QwtLinearColorMap::color2() const { - return QColor(d_data->colorStops.rgb(d_data->mode, 1.0)); + return QColor( d_data->colorStops.rgb( d_data->mode, 1.0 ) ); } /*! - Map a value of a given interval into a rgb value + Map a value of a given interval into a RGB value \param interval Range for all values - \param value Value to map into a rgb value + \param value Value to map into a RGB value + + \return RGB value for value */ QRgb QwtLinearColorMap::rgb( - const QwtDoubleInterval &interval, double value) const + const QwtInterval &interval, double value ) const { + if ( qIsNaN(value) ) + return qRgba(0, 0, 0, 0); + const double width = interval.width(); double ratio = 0.0; if ( width > 0.0 ) - ratio = (value - interval.minValue()) / width; + ratio = ( value - interval.minValue() ) / width; - return d_data->colorStops.rgb(d_data->mode, ratio); + return d_data->colorStops.rgb( d_data->mode, ratio ); } /*! - Map a value of a given interval into a color index, between 0 and 255 + \brief Map a value of a given interval into a color index \param interval Range for all values \param value Value to map into a color index + + \return Index, between 0 and 255 */ unsigned char QwtLinearColorMap::colorIndex( - const QwtDoubleInterval &interval, double value) const + const QwtInterval &interval, double value ) const { const double width = interval.width(); - if ( width <= 0.0 || value <= interval.minValue() ) + if ( qIsNaN(value) || width <= 0.0 || value <= interval.minValue() ) return 0; if ( value >= interval.maxValue() ) - return (unsigned char)255; + return 255; - const double ratio = (value - interval.minValue()) / width; + const double ratio = ( value - interval.minValue() ) / width; unsigned char index; if ( d_data->mode == FixedColors ) - index = (unsigned char)(ratio * 255); // always floor + index = static_cast( ratio * 255 ); // always floor else - index = (unsigned char)qRound(ratio * 255); + index = static_cast( qRound( ratio * 255 ) ); return index; } @@ -392,23 +369,12 @@ public: Constructor \param color Color of the map */ -QwtAlphaColorMap::QwtAlphaColorMap(const QColor &color): - QwtColorMap(QwtColorMap::RGB) +QwtAlphaColorMap::QwtAlphaColorMap( const QColor &color ): + QwtColorMap( QwtColorMap::RGB ) { d_data = new PrivateData; d_data->color = color; - d_data->rgb = color.rgb() & qRgba(255, 255, 255, 0); -} - -/*! - Copy constructor - \param other Other color map -*/ -QwtAlphaColorMap::QwtAlphaColorMap(const QwtAlphaColorMap &other): - QwtColorMap(other) -{ - d_data = new PrivateData; - *this = other; + d_data->rgb = color.rgb() & qRgba( 255, 255, 255, 0 ); } //! Destructor @@ -417,35 +383,13 @@ QwtAlphaColorMap::~QwtAlphaColorMap() delete d_data; } -/*! - Assignment operator - \param other Other color map - \return *this -*/ -QwtAlphaColorMap &QwtAlphaColorMap::operator=( - const QwtAlphaColorMap &other) -{ - QwtColorMap::operator=(other); - *d_data = *other.d_data; - return *this; -} - -//! Clone the color map -QwtColorMap *QwtAlphaColorMap::copy() const -{ - QwtAlphaColorMap* map = new QwtAlphaColorMap(); - *map = *this; - - return map; -} - /*! Set the color \param color Color \sa color() */ -void QwtAlphaColorMap::setColor(const QColor &color) +void QwtAlphaColorMap::setColor( const QColor &color ) { d_data->color = color; d_data->rgb = color.rgb(); @@ -466,22 +410,22 @@ QColor QwtAlphaColorMap::color() const alpha := (value - interval.minValue()) / interval.width(); \param interval Range for all values - \param value Value to map into a rgb value - \return rgb value, with an alpha value + \param value Value to map into a RGB value + \return RGB value, with an alpha value */ -QRgb QwtAlphaColorMap::rgb(const QwtDoubleInterval &interval, - double value) const +QRgb QwtAlphaColorMap::rgb( const QwtInterval &interval, double value ) const { const double width = interval.width(); - if ( width >= 0.0 ) { - const double ratio = (value - interval.minValue()) / width; - int alpha = qRound(255 * ratio); + if ( !qIsNaN(value) && width >= 0.0 ) + { + const double ratio = ( value - interval.minValue() ) / width; + int alpha = qRound( 255 * ratio ); if ( alpha < 0 ) alpha = 0; if ( alpha > 255 ) alpha = 255; - return d_data->rgb | (alpha << 24); + return d_data->rgb | ( alpha << 24 ); } return d_data->rgb; } @@ -494,7 +438,7 @@ QRgb QwtAlphaColorMap::rgb(const QwtDoubleInterval &interval, \return Always 0 */ unsigned char QwtAlphaColorMap::colorIndex( - const QwtDoubleInterval &, double) const + const QwtInterval &, double ) const { return 0; } diff --git a/libs/qwt/qwt_color_map.h b/libs/qwt/qwt_color_map.h index 759ac5a5b..91a92bd0c 100644 --- a/libs/qwt/qwt_color_map.h +++ b/libs/qwt/qwt_color_map.h @@ -10,21 +10,10 @@ #ifndef QWT_COLOR_MAP_H #define QWT_COLOR_MAP_H -#include +#include "qwt_global.h" +#include "qwt_interval.h" #include -#if QT_VERSION < 0x040000 -#include -#else #include -#endif -#include "qwt_array.h" -#include "qwt_double_interval.h" - -#if defined(QWT_TEMPLATEDLL) -// MOC_SKIP_BEGIN -template class QWT_EXPORT QwtArray; -// MOC_SKIP_END -#endif /*! \brief QwtColorMap is used to map values into colors. @@ -45,67 +34,60 @@ class QWT_EXPORT QwtColorMap { public: /*! - - RGB\n - The map is intended to map into QRgb values. - - Indexed\n - The map is intended to map into 8 bit values, that - are indices into the color table. - + Format for color mapping \sa rgb(), colorIndex(), colorTable() */ - enum Format { + enum Format + { + //! The map is intended to map into RGB values. RGB, + + /*! + The map is intended to map into 8 bit values, that + are indices into the color table. + */ Indexed }; - QwtColorMap(Format = QwtColorMap::RGB ); + QwtColorMap( Format = QwtColorMap::RGB ); virtual ~QwtColorMap(); - inline Format format() const; - - //! Clone the color map - virtual QwtColorMap *copy() const = 0; + Format format() const; /*! - Map a value of a given interval into a rgb value. + Map a value of a given interval into a RGB value. + \param interval Range for the values \param value Value - \return rgb value, corresponding to value + \return RGB value, corresponding to value */ - virtual QRgb rgb( - const QwtDoubleInterval &interval, double value) const = 0; + virtual QRgb rgb( const QwtInterval &interval, + double value ) const = 0; /*! Map a value of a given interval into a color index + \param interval Range for the values \param value Value \return color index, corresponding to value */ virtual unsigned char colorIndex( - const QwtDoubleInterval &interval, double value) const = 0; + const QwtInterval &interval, double value ) const = 0; - QColor color(const QwtDoubleInterval &, double value) const; -#if QT_VERSION < 0x040000 - virtual QValueVector colorTable(const QwtDoubleInterval &) const; -#else - virtual QVector colorTable(const QwtDoubleInterval &) const; -#endif + QColor color( const QwtInterval &, double value ) const; + virtual QVector colorTable( const QwtInterval & ) const; private: Format d_format; }; - /*! \brief QwtLinearColorMap builds a color map from color stops. A color stop is a color at a specific position. The valid range for the positions is [0.0, 1.0]. When mapping a value - into a color it is translated into this interval. If - mode() == FixedColors the color is calculated from the next lower - color stop. If mode() == ScaledColors the color is calculated - by interpolating the colors of the adjacent stops. + into a color it is translated into this interval according to mode(). */ class QWT_EXPORT QwtLinearColorMap: public QwtColorMap { @@ -114,67 +96,66 @@ public: Mode of color map \sa setMode(), mode() */ - enum Mode { + enum Mode + { + //! Return the color from the next lower color stop FixedColors, + + //! Interpolating the colors of the adjacent stops. ScaledColors }; - QwtLinearColorMap(QwtColorMap::Format = QwtColorMap::RGB); + QwtLinearColorMap( QwtColorMap::Format = QwtColorMap::RGB ); QwtLinearColorMap( const QColor &from, const QColor &to, - QwtColorMap::Format = QwtColorMap::RGB); - - QwtLinearColorMap(const QwtLinearColorMap &); + QwtColorMap::Format = QwtColorMap::RGB ); virtual ~QwtLinearColorMap(); - QwtLinearColorMap &operator=(const QwtLinearColorMap &); - - virtual QwtColorMap *copy() const; - - void setMode(Mode); + void setMode( Mode ); Mode mode() const; - void setColorInterval(const QColor &color1, const QColor &color2); - void addColorStop(double value, const QColor&); - QwtArray colorStops() const; + void setColorInterval( const QColor &color1, const QColor &color2 ); + void addColorStop( double value, const QColor& ); + QVector colorStops() const; QColor color1() const; QColor color2() const; - virtual QRgb rgb(const QwtDoubleInterval &, double value) const; + virtual QRgb rgb( const QwtInterval &, double value ) const; virtual unsigned char colorIndex( - const QwtDoubleInterval &, double value) const; + const QwtInterval &, double value ) const; class ColorStops; private: + // Disabled copy constructor and operator= + QwtLinearColorMap( const QwtLinearColorMap & ); + QwtLinearColorMap &operator=( const QwtLinearColorMap & ); + class PrivateData; PrivateData *d_data; }; /*! - \brief QwtAlphaColorMap variies the alpha value of a color + \brief QwtAlphaColorMap varies the alpha value of a color */ class QWT_EXPORT QwtAlphaColorMap: public QwtColorMap { public: - QwtAlphaColorMap(const QColor & = QColor(Qt::gray)); - QwtAlphaColorMap(const QwtAlphaColorMap &); - + QwtAlphaColorMap( const QColor & = QColor( Qt::gray ) ); virtual ~QwtAlphaColorMap(); - QwtAlphaColorMap &operator=(const QwtAlphaColorMap &); - - virtual QwtColorMap *copy() const; - - void setColor(const QColor &); + void setColor( const QColor & ); QColor color() const; - virtual QRgb rgb(const QwtDoubleInterval &, double value) const; + virtual QRgb rgb( const QwtInterval &, double value ) const; private: + QwtAlphaColorMap( const QwtAlphaColorMap & ); + QwtAlphaColorMap &operator=( const QwtAlphaColorMap & ); + virtual unsigned char colorIndex( - const QwtDoubleInterval &, double value) const; + const QwtInterval &, double value ) const; class PrivateData; PrivateData *d_data; @@ -194,13 +175,16 @@ private: color table once and find the color using colorIndex(). */ inline QColor QwtColorMap::color( - const QwtDoubleInterval &interval, double value) const + const QwtInterval &interval, double value ) const { - if ( d_format == RGB ) { - return QColor( rgb(interval, value) ); - } else { - const unsigned int index = colorIndex(interval, value); - return colorTable(interval)[index]; // slow + if ( d_format == RGB ) + { + return QColor( rgb( interval, value ) ); + } + else + { + const unsigned int index = colorIndex( interval, value ); + return colorTable( interval )[index]; // slow } } diff --git a/libs/qwt/qwt_column_symbol.cpp b/libs/qwt/qwt_column_symbol.cpp new file mode 100644 index 000000000..d6f0f1a63 --- /dev/null +++ b/libs/qwt/qwt_column_symbol.cpp @@ -0,0 +1,293 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_column_symbol.h" +#include "qwt_math.h" +#include "qwt_painter.h" +#include +#include + +static void qwtDrawBox( QPainter *p, const QRectF &rect, + const QPalette &pal, double lw ) +{ + if ( lw > 0.0 ) + { + if ( rect.width() == 0.0 ) + { + p->setPen( pal.dark().color() ); + p->drawLine( rect.topLeft(), rect.bottomLeft() ); + return; + } + + if ( rect.height() == 0.0 ) + { + p->setPen( pal.dark().color() ); + p->drawLine( rect.topLeft(), rect.topRight() ); + return; + } + + lw = qMin( lw, rect.height() / 2.0 - 1.0 ); + lw = qMin( lw, rect.width() / 2.0 - 1.0 ); + + const QRectF outerRect = rect.adjusted( 0, 0, 1, 1 ); + QPolygonF polygon( outerRect ); + + if ( outerRect.width() > 2 * lw && + outerRect.height() > 2 * lw ) + { + const QRectF innerRect = outerRect.adjusted( lw, lw, -lw, -lw ); + polygon = polygon.subtracted( innerRect ); + } + + p->setPen( Qt::NoPen ); + + p->setBrush( pal.dark() ); + p->drawPolygon( polygon ); + } + + const QRectF windowRect = rect.adjusted( lw, lw, -lw + 1, -lw + 1 ); + if ( windowRect.isValid() ) + p->fillRect( windowRect, pal.window() ); +} + +static void qwtDrawPanel( QPainter *painter, const QRectF &rect, + const QPalette &pal, double lw ) +{ + if ( lw > 0.0 ) + { + if ( rect.width() == 0.0 ) + { + painter->setPen( pal.window().color() ); + painter->drawLine( rect.topLeft(), rect.bottomLeft() ); + return; + } + + if ( rect.height() == 0.0 ) + { + painter->setPen( pal.window().color() ); + painter->drawLine( rect.topLeft(), rect.topRight() ); + return; + } + + lw = qMin( lw, rect.height() / 2.0 - 1.0 ); + lw = qMin( lw, rect.width() / 2.0 - 1.0 ); + + const QRectF outerRect = rect.adjusted( 0, 0, 1, 1 ); + const QRectF innerRect = outerRect.adjusted( lw, lw, -lw, -lw ); + + QPolygonF lines[2]; + + lines[0] += outerRect.bottomLeft(); + lines[0] += outerRect.topLeft(); + lines[0] += outerRect.topRight(); + lines[0] += innerRect.topRight(); + lines[0] += innerRect.topLeft(); + lines[0] += innerRect.bottomLeft(); + + lines[1] += outerRect.topRight(); + lines[1] += outerRect.bottomRight(); + lines[1] += outerRect.bottomLeft(); + lines[1] += innerRect.bottomLeft(); + lines[1] += innerRect.bottomRight(); + lines[1] += innerRect.topRight(); + + painter->setPen( Qt::NoPen ); + + painter->setBrush( pal.light() ); + painter->drawPolygon( lines[0] ); + painter->setBrush( pal.dark() ); + painter->drawPolygon( lines[1] ); + } + + painter->fillRect( rect.adjusted( lw, lw, -lw + 1, -lw + 1 ), pal.window() ); +} + +class QwtColumnSymbol::PrivateData +{ +public: + PrivateData(): + style( QwtColumnSymbol::Box ), + frameStyle( QwtColumnSymbol::Raised ), + lineWidth( 2 ) + { + palette = QPalette( Qt::gray ); + } + + QwtColumnSymbol::Style style; + QwtColumnSymbol::FrameStyle frameStyle; + + QPalette palette; + int lineWidth; +}; + +/*! + Constructor + + \param style Style of the symbol + \sa setStyle(), style(), Style +*/ +QwtColumnSymbol::QwtColumnSymbol( Style style ) +{ + d_data = new PrivateData(); + d_data->style = style; +} + +//! Destructor +QwtColumnSymbol::~QwtColumnSymbol() +{ + delete d_data; +} + +/*! + Specify the symbol style + + \param style Style + \sa style(), setPalette() +*/ +void QwtColumnSymbol::setStyle( Style style ) +{ + d_data->style = style; +} + +/*! + \return Current symbol style + \sa setStyle() +*/ +QwtColumnSymbol::Style QwtColumnSymbol::style() const +{ + return d_data->style; +} + +/*! + Assign a palette for the symbol + + \param palette Palette + \sa palette(), setStyle() +*/ +void QwtColumnSymbol::setPalette( const QPalette &palette ) +{ + d_data->palette = palette; +} + +/*! + \return Current palette + \sa setPalette() +*/ +const QPalette& QwtColumnSymbol::palette() const +{ + return d_data->palette; +} + +/*! + Set the frame, that is used for the Box style. + + \param frameStyle Frame style + \sa frameStyle(), setLineWidth(), setStyle() +*/ +void QwtColumnSymbol::setFrameStyle( FrameStyle frameStyle ) +{ + d_data->frameStyle = frameStyle; +} + +/*! + \return Current frame style, that is used for the Box style. + \sa setFrameStyle(), lineWidth(), setStyle() +*/ +QwtColumnSymbol::FrameStyle QwtColumnSymbol::frameStyle() const +{ + return d_data->frameStyle; +} + +/*! + Set the line width of the frame, that is used for the Box style. + + \param width Width + \sa lineWidth(), setFrameStyle() +*/ +void QwtColumnSymbol::setLineWidth( int width ) +{ + if ( width < 0 ) + width = 0; + + d_data->lineWidth = width; +} + +/*! + \return Line width of the frame, that is used for the Box style. + \sa setLineWidth(), frameStyle(), setStyle() +*/ +int QwtColumnSymbol::lineWidth() const +{ + return d_data->lineWidth; +} + +/*! + Draw the symbol depending on its style. + + \param painter Painter + \param rect Directed rectangle + + \sa drawBox() +*/ +void QwtColumnSymbol::draw( QPainter *painter, + const QwtColumnRect &rect ) const +{ + painter->save(); + + switch ( d_data->style ) + { + case QwtColumnSymbol::Box: + { + drawBox( painter, rect ); + break; + } + default:; + } + + painter->restore(); +} + +/*! + Draw the symbol when it is in Box style. + + \param painter Painter + \param rect Directed rectangle + + \sa draw() +*/ +void QwtColumnSymbol::drawBox( QPainter *painter, + const QwtColumnRect &rect ) const +{ + QRectF r = rect.toRect(); + if ( QwtPainter::roundingAlignment( painter ) ) + { + r.setLeft( qRound( r.left() ) ); + r.setRight( qRound( r.right() ) ); + r.setTop( qRound( r.top() ) ); + r.setBottom( qRound( r.bottom() ) ); + } + + switch ( d_data->frameStyle ) + { + case QwtColumnSymbol::Raised: + { + qwtDrawPanel( painter, r, d_data->palette, d_data->lineWidth ); + break; + } + case QwtColumnSymbol::Plain: + { + qwtDrawBox( painter, r, d_data->palette, d_data->lineWidth ); + break; + } + default: + { + painter->fillRect( r, d_data->palette.window() ); + } + } +} diff --git a/libs/qwt/qwt_column_symbol.h b/libs/qwt/qwt_column_symbol.h new file mode 100644 index 000000000..918fe4a3c --- /dev/null +++ b/libs/qwt/qwt_column_symbol.h @@ -0,0 +1,161 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_COLUMN_SYMBOL_H +#define QWT_COLUMN_SYMBOL_H + +#include "qwt_global.h" +#include "qwt_interval.h" +#include +#include +#include + +class QPainter; +class QPalette; +class QRect; +class QwtText; + +/*! + \brief Directed rectangle representing bounding rectangle and orientation + of a column. +*/ +class QWT_EXPORT QwtColumnRect +{ +public: + //! Direction of the column + enum Direction + { + //! From left to right + LeftToRight, + + //! From right to left + RightToLeft, + + //! From bottom to top + BottomToTop, + + //! From top to bottom + TopToBottom + }; + + //! Build an rectangle with invalid intervals directed BottomToTop. + QwtColumnRect(): + direction( BottomToTop ) + { + } + + //! \return A normalized QRect built from the intervals + QRectF toRect() const + { + QRectF r( hInterval.minValue(), vInterval.minValue(), + hInterval.maxValue() - hInterval.minValue(), + vInterval.maxValue() - vInterval.minValue() ); + r = r.normalized(); + + if ( hInterval.borderFlags() & QwtInterval::ExcludeMinimum ) + r.adjust( 1, 0, 0, 0 ); + if ( hInterval.borderFlags() & QwtInterval::ExcludeMaximum ) + r.adjust( 0, 0, -1, 0 ); + if ( vInterval.borderFlags() & QwtInterval::ExcludeMinimum ) + r.adjust( 0, 1, 0, 0 ); + if ( vInterval.borderFlags() & QwtInterval::ExcludeMaximum ) + r.adjust( 0, 0, 0, -1 ); + + return r; + } + + //! \return Orientation + Qt::Orientation orientation() const + { + if ( direction == LeftToRight || direction == RightToLeft ) + return Qt::Horizontal; + + return Qt::Vertical; + } + + //! Interval for the horizontal coordinates + QwtInterval hInterval; + + //! Interval for the vertical coordinates + QwtInterval vInterval; + + //! Direction + Direction direction; +}; + +//! A drawing primitive for columns +class QWT_EXPORT QwtColumnSymbol +{ +public: + /*! + Style + \sa setStyle(), style() + */ + enum Style + { + //! No Style, the symbol draws nothing + NoStyle = -1, + + /*! + The column is painted with a frame depending on the frameStyle() + and lineWidth() using the palette(). + */ + Box, + + /*! + Styles >= QwtColumnSymbol::UserStyle are reserved for derived + classes of QwtColumnSymbol that overload draw() with + additional application specific symbol types. + */ + UserStyle = 1000 + }; + + /*! + Frame Style used in Box style(). + \sa Style, setFrameStyle(), frameStyle(), setStyle(), setPalette() + */ + enum FrameStyle + { + //! No frame + NoFrame, + + //! A plain frame style + Plain, + + //! A raised frame style + Raised + }; + +public: + QwtColumnSymbol( Style = NoStyle ); + virtual ~QwtColumnSymbol(); + + void setFrameStyle( FrameStyle style ); + FrameStyle frameStyle() const; + + void setLineWidth( int width ); + int lineWidth() const; + + void setPalette( const QPalette & ); + const QPalette &palette() const; + + void setStyle( Style ); + Style style() const; + + virtual void draw( QPainter *, const QwtColumnRect & ) const; + +protected: + void drawBox( QPainter *, const QwtColumnRect & ) const; + +private: + class PrivateData; + PrivateData* d_data; +}; + +#endif diff --git a/libs/qwt/qwt_compass.cpp b/libs/qwt/qwt_compass.cpp index e2ce65040..4e2c9ffdf 100644 --- a/libs/qwt/qwt_compass.cpp +++ b/libs/qwt/qwt_compass.cpp @@ -7,33 +7,126 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ -// vim: expandtab - -#include -#include -#include -#include +#include "qwt_compass.h" +#include "qwt_compass_rose.h" #include "qwt_math.h" #include "qwt_scale_draw.h" -#include "qwt_paint_buffer.h" #include "qwt_painter.h" #include "qwt_dial_needle.h" -#include "qwt_compass_rose.h" -#include "qwt_compass.h" +#include +#include +#include + +/*! + \brief Constructor + + Initializes a label map for multiples of 45 degrees + */ +QwtCompassScaleDraw::QwtCompassScaleDraw() +{ + enableComponent( QwtAbstractScaleDraw::Backbone, false ); + enableComponent( QwtAbstractScaleDraw::Ticks, false ); + + d_labelMap.insert( 0.0, QString::fromLatin1( "N" ) ); + d_labelMap.insert( 45.0, QString::fromLatin1( "NE" ) ); + d_labelMap.insert( 90.0, QString::fromLatin1( "E" ) ); + d_labelMap.insert( 135.0, QString::fromLatin1( "SE" ) ); + d_labelMap.insert( 180.0, QString::fromLatin1( "S" ) ); + d_labelMap.insert( 225.0, QString::fromLatin1( "SW" ) ); + d_labelMap.insert( 270.0, QString::fromLatin1( "W" ) ); + d_labelMap.insert( 315.0, QString::fromLatin1( "NW" ) ); + +#if 0 + d_labelMap.insert( 22.5, QString::fromLatin1( "NNE" ) ); + d_labelMap.insert( 67.5, QString::fromLatin1( "NEE" ) ); + d_labelMap.insert( 112.5, QString::fromLatin1( "SEE" ) ); + d_labelMap.insert( 157.5, QString::fromLatin1( "SSE" ) ); + d_labelMap.insert( 202.5, QString::fromLatin1( "SSW" ) ); + d_labelMap.insert( 247.5, QString::fromLatin1( "SWW" ) ); + d_labelMap.insert( 292.5, QString::fromLatin1( "NWW" ) ); + d_labelMap.insert( 337.5, QString::fromLatin1( "NNW" ) ); +#endif +} + +/*! + \brief Constructor + + \param map Value to label map + */ +QwtCompassScaleDraw::QwtCompassScaleDraw( const QMap &map ): + d_labelMap( map ) +{ + enableComponent( QwtAbstractScaleDraw::Backbone, false ); + enableComponent( QwtAbstractScaleDraw::Ticks, false ); +} + +/*! + \brief Set a map, mapping values to labels + \param map Value to label map + + The values of the major ticks are found by looking into this + map. The default map consists of the labels N, NE, E, SE, S, SW, W, NW. + + \warning The map will have no effect for values that are no major + tick values. Major ticks can be changed by QwtScaleDraw::setScale + + \sa labelMap(), scaleDraw(), setScale() +*/ +void QwtCompassScaleDraw::setLabelMap( const QMap &map ) +{ + d_labelMap = map; +} + + +/*! + \return map, mapping values to labels + \sa setLabelMap() +*/ +QMap QwtCompassScaleDraw::labelMap() const +{ + return d_labelMap; +} + +/*! + Map a value to a corresponding label + + \param value Value that will be mapped + + label() looks in the labelMap() for a corresponding label for value + or returns an null text. + + \return Label, or QString::null + \sa labelMap(), setLabelMap() +*/ + +QwtText QwtCompassScaleDraw::label( double value ) const +{ + if ( qFuzzyCompare( value + 1.0, 1.0 ) ) + value = 0.0; + + if ( value < 0.0 ) + value += 360.0; + + if ( d_labelMap.contains( value ) ) + return d_labelMap[value]; + + return QwtText(); +} class QwtCompass::PrivateData { public: PrivateData(): - rose(NULL) { + rose( NULL ) + { } - ~PrivateData() { + ~PrivateData() + { delete rose; } QwtCompassRose *rose; - QMap labelMap; }; /*! @@ -45,31 +138,22 @@ public: mouse and keyboard inputs and has no step size. The default mode is QwtDial::RotateNeedle. */ -QwtCompass::QwtCompass(QWidget* parent): - QwtDial(parent) +QwtCompass::QwtCompass( QWidget* parent ): + QwtDial( parent ) { - initCompass(); -} + d_data = new PrivateData; -#if QT_VERSION < 0x040000 + setScaleDraw( new QwtCompassScaleDraw() ); -/*! - \brief Constructor - \param parent Parent widget - \param name Object name + setOrigin( 270.0 ); + setWrapping( true ); - Create a compass widget with a scale, no needle and no rose. - The default origin is 270.0 with no valid value. It accepts - mouse and keyboard inputs and has no step size. The default mode - is QwtDial::RotateNeedle. -*/ -QwtCompass::QwtCompass(QWidget* parent, const char *name): - QwtDial(parent, name) -{ - initCompass(); -} + setScaleMaxMajor( 36 ); + setScaleMaxMinor( 10 ); -#endif + setScale( 0.0, 360.0 ); // degrees as default + setTotalSteps( 360 ); +} //! Destructor QwtCompass::~QwtCompass() @@ -77,40 +161,16 @@ QwtCompass::~QwtCompass() delete d_data; } -void QwtCompass::initCompass() -{ - d_data = new PrivateData; - - setScaleOptions(ScaleLabel); // Only labels, no backbone, no ticks - - setOrigin(270.0); - setWrapping(true); - - d_data->labelMap.insert(0.0, QString::fromLatin1("N")); - d_data->labelMap.insert(45.0, QString::fromLatin1("NE")); - d_data->labelMap.insert(90.0, QString::fromLatin1("E")); - d_data->labelMap.insert(135.0, QString::fromLatin1("SE")); - d_data->labelMap.insert(180.0, QString::fromLatin1("S")); - d_data->labelMap.insert(225.0, QString::fromLatin1("SW")); - d_data->labelMap.insert(270.0, QString::fromLatin1("W")); - d_data->labelMap.insert(315.0, QString::fromLatin1("NW")); - -#if 0 - d_data->labelMap.insert(22.5, QString::fromLatin1("NNE")); - d_data->labelMap.insert(67.5, QString::fromLatin1("NEE")); - d_data->labelMap.insert(112.5, QString::fromLatin1("SEE")); - d_data->labelMap.insert(157.5, QString::fromLatin1("SSE")); - d_data->labelMap.insert(202.5, QString::fromLatin1("SSW")); - d_data->labelMap.insert(247.5, QString::fromLatin1("SWW")); - d_data->labelMap.insert(292.5, QString::fromLatin1("NWW")); - d_data->labelMap.insert(337.5, QString::fromLatin1("NNW")); -#endif -} +/*! + Draw the contents of the scale -//! Draw the contents of the scale -void QwtCompass::drawScaleContents(QPainter *painter, - const QPoint ¢er, int radius) const + \param painter Painter + \param center Center of the content circle + \param radius Radius of the content circle +*/ +void QwtCompass::drawScaleContents( QPainter *painter, + const QPointF ¢er, double radius ) const { QPalette::ColorGroup cg; if ( isEnabled() ) @@ -119,13 +179,14 @@ void QwtCompass::drawScaleContents(QPainter *painter, cg = QPalette::Disabled; double north = origin(); - if ( isValid() ) { + if ( isValid() ) + { if ( mode() == RotateScale ) north -= value(); } const int margin = 4; - drawRose(painter, center, radius - margin, 360.0 - north, cg); + drawRose( painter, center, radius - margin, 360.0 - north, cg ); } /*! @@ -137,11 +198,11 @@ void QwtCompass::drawScaleContents(QPainter *painter, \param north Direction pointing north, in degrees counter clockwise \param cg Color group */ -void QwtCompass::drawRose(QPainter *painter, const QPoint ¢er, - int radius, double north, QPalette::ColorGroup cg) const +void QwtCompass::drawRose( QPainter *painter, const QPointF ¢er, + double radius, double north, QPalette::ColorGroup cg ) const { if ( d_data->rose ) - d_data->rose->draw(painter, center, radius, north, cg); + d_data->rose->draw( painter, center, radius, north, cg ); } /*! @@ -151,9 +212,10 @@ void QwtCompass::drawRose(QPainter *painter, const QPoint ¢er, set or in ~QwtCompass \sa rose() */ -void QwtCompass::setRose(QwtCompassRose *rose) +void QwtCompass::setRose( QwtCompassRose *rose ) { - if ( rose != d_data->rose ) { + if ( rose != d_data->rose ) + { if ( d_data->rose ) delete d_data->rose; @@ -189,13 +251,14 @@ QwtCompassRose *QwtCompass::rose() \sa isReadOnly() */ -void QwtCompass::keyPressEvent(QKeyEvent *kev) +void QwtCompass::keyPressEvent( QKeyEvent *kev ) { - if (isReadOnly()) + if ( isReadOnly() ) return; #if 0 - if ( kev->key() == Key_5 ) { + if ( kev->key() == Key_5 ) + { invalidate(); // signal ??? return; } @@ -203,101 +266,43 @@ void QwtCompass::keyPressEvent(QKeyEvent *kev) double newValue = value(); - if ( kev->key() >= Qt::Key_1 && kev->key() <= Qt::Key_9 ) { + if ( kev->key() >= Qt::Key_1 && kev->key() <= Qt::Key_9 ) + { if ( mode() != RotateNeedle || kev->key() == Qt::Key_5 ) return; - switch (kev->key()) { - case Qt::Key_6: - newValue = 180.0 * 0.0; - break; - case Qt::Key_3: - newValue = 180.0 * 0.25; - break; - case Qt::Key_2: - newValue = 180.0 * 0.5; - break; - case Qt::Key_1: - newValue = 180.0 * 0.75; - break; - case Qt::Key_4: - newValue = 180.0 * 1.0; - break; - case Qt::Key_7: - newValue = 180.0 * 1.25; - break; - case Qt::Key_8: - newValue = 180.0 * 1.5; - break; - case Qt::Key_9: - newValue = 180.0 * 1.75; - break; + switch ( kev->key() ) + { + case Qt::Key_6: + newValue = 180.0 * 0.0; + break; + case Qt::Key_3: + newValue = 180.0 * 0.25; + break; + case Qt::Key_2: + newValue = 180.0 * 0.5; + break; + case Qt::Key_1: + newValue = 180.0 * 0.75; + break; + case Qt::Key_4: + newValue = 180.0 * 1.0; + break; + case Qt::Key_7: + newValue = 180.0 * 1.25; + break; + case Qt::Key_8: + newValue = 180.0 * 1.5; + break; + case Qt::Key_9: + newValue = 180.0 * 1.75; + break; } newValue -= origin(); - setValue(newValue); - } else { - QwtDial::keyPressEvent(kev); + setValue( newValue ); + } + else + { + QwtDial::keyPressEvent( kev ); } -} - -/*! - \return map, mapping values to labels - \sa setLabelMap() -*/ -const QMap &QwtCompass::labelMap() const -{ - return d_data->labelMap; -} - -/*! - \return map, mapping values to labels - \sa setLabelMap() -*/ -QMap &QwtCompass::labelMap() -{ - return d_data->labelMap; -} - -/*! - \brief Set a map, mapping values to labels - \param map value to label map - - The values of the major ticks are found by looking into this - map. The default map consists of the labels N, NE, E, SE, S, SW, W, NW. - - \warning The map will have no effect for values that are no major - tick values. Major ticks can be changed by QwtScaleDraw::setScale - - \sa labelMap(), scaleDraw(), setScale() -*/ -void QwtCompass::setLabelMap(const QMap &map) -{ - d_data->labelMap = map; -} - -/*! - Map a value to a corresponding label - \param value Value that will be mapped - \return Label, or QString::null - - label() looks in a map for a corresponding label for value - or return an null text. - \sa labelMap(), setLabelMap() -*/ - -QwtText QwtCompass::scaleLabel(double value) const -{ -#if 0 - // better solution ??? - if ( value == -0 ) - value = 0.0; -#endif - - if ( value < 0.0 ) - value += 360.0; - - if ( d_data->labelMap.contains(value) ) - return d_data->labelMap[value]; - - return QwtText(); } diff --git a/libs/qwt/qwt_compass.h b/libs/qwt/qwt_compass.h index ed6d06d0a..b9a3c95bf 100644 --- a/libs/qwt/qwt_compass.h +++ b/libs/qwt/qwt_compass.h @@ -10,29 +10,38 @@ #ifndef QWT_COMPASS_H #define QWT_COMPASS_H 1 +#include "qwt_global.h" +#include "qwt_dial.h" +#include "qwt_round_scale_draw.h" #include #include -#include "qwt_dial.h" -#if defined(QWT_TEMPLATEDLL) +class QwtCompassRose; + +/*! + \brief A special scale draw made for QwtCompass -#if defined(QT_NO_STL) || QT_VERSION < 0x040000 || QT_VERSION > 0x040001 -/* - Unfortunately Qt 4.0.0/Qt 4.0.1 contains uncompilable - code in the STL adaptors of qmap.h. The declaration below - instantiates this code resulting in compiler errors. - If you really need the map to be exported, remove the condition above - and fix the qmap.h + QwtCompassScaleDraw maps values to strings using + a special map, that can be modified by the application + + The default map consists of the labels N, NE, E, SE, S, SW, W, NW. + + \sa QwtCompass */ -// MOC_SKIP_BEGIN -template class QWT_EXPORT QMap; -// MOC_SKIP_END -#endif +class QWT_EXPORT QwtCompassScaleDraw: public QwtRoundScaleDraw +{ +public: + explicit QwtCompassScaleDraw(); + explicit QwtCompassScaleDraw( const QMap &map ); -#endif + void setLabelMap( const QMap &map ); + QMap labelMap() const; + virtual QwtText label( double value ) const; -class QwtCompassRose; +private: + QMap d_labelMap; +}; /*! \brief A Compass Widget @@ -50,34 +59,23 @@ class QWT_EXPORT QwtCompass: public QwtDial Q_OBJECT public: - explicit QwtCompass( QWidget* parent = NULL); -#if QT_VERSION < 0x040000 - explicit QwtCompass(QWidget* parent, const char *name); -#endif + explicit QwtCompass( QWidget* parent = NULL ); virtual ~QwtCompass(); - void setRose(QwtCompassRose *rose); + void setRose( QwtCompassRose *rose ); const QwtCompassRose *rose() const; QwtCompassRose *rose(); - const QMap &labelMap() const; - QMap &labelMap(); - void setLabelMap(const QMap &map); - protected: - virtual QwtText scaleLabel(double value) const; + virtual void drawRose( QPainter *, const QPointF ¢er, + double radius, double north, QPalette::ColorGroup ) const; - virtual void drawRose(QPainter *, const QPoint ¢er, - int radius, double north, QPalette::ColorGroup) const; + virtual void drawScaleContents( QPainter *, + const QPointF ¢er, double radius ) const; - virtual void drawScaleContents(QPainter *, - const QPoint ¢er, int radius) const; - - virtual void keyPressEvent(QKeyEvent *); + virtual void keyPressEvent( QKeyEvent * ); private: - void initCompass(); - class PrivateData; PrivateData *d_data; }; diff --git a/libs/qwt/qwt_compass_rose.cpp b/libs/qwt/qwt_compass_rose.cpp index 6e412e4ce..21a35f244 100644 --- a/libs/qwt/qwt_compass_rose.cpp +++ b/libs/qwt/qwt_compass_rose.cpp @@ -7,48 +7,40 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ -#include -#include -#include "qwt_math.h" -#include "qwt_painter.h" #include "qwt_compass_rose.h" +#include "qwt_point_polar.h" +#include "qwt_painter.h" +#include -static QPoint cutPoint(QPoint p11, QPoint p12, QPoint p21, QPoint p22) +static QPointF qwtIntersection( + QPointF p11, QPointF p12, QPointF p21, QPointF p22 ) { - double dx1 = p12.x() - p11.x(); - double dy1 = p12.y() - p11.y(); - double dx2 = p22.x() - p21.x(); - double dy2 = p22.y() - p21.y(); - - if ( dx1 == 0.0 && dx2 == 0.0 ) - return QPoint(); - - if ( dx1 == 0.0 ) { - const double m = dy2 / dx2; - const double t = p21.y() - m * p21.x(); - return QPoint(p11.x(), qRound(m * p11.x() + t)); - } + const QLineF line1( p11, p12 ); + const QLineF line2( p21, p22 ); - if ( dx2 == 0 ) { - const double m = dy1 / dx1; - const double t = p11.y() - m * p11.x(); - return QPoint(p21.x(), qRound(m * p21.x() + t)); - } - - const double m1 = dy1 / dx1; - const double t1 = p11.y() - m1 * p11.x(); + QPointF pos; + if ( line1.intersect( line2, &pos ) == QLineF::NoIntersection ) + return QPointF(); - const double m2 = dy2 / dx2; - const double t2 = p21.y() - m2 * p21.x(); - - if ( m1 == m2 ) - return QPoint(); + return pos; +} - const double x = ( t2 - t1 ) / ( m1 - m2 ); - const double y = t1 + m1 * x; +class QwtSimpleCompassRose::PrivateData +{ +public: + PrivateData(): + width( 0.2 ), + numThorns( 8 ), + numThornLevels( -1 ), + shrinkFactor( 0.9 ) + { + } - return QPoint(qRound(x), qRound(y)); -} + double width; + int numThorns; + int numThornLevels; + double shrinkFactor; +}; /*! Constructor @@ -56,31 +48,48 @@ static QPoint cutPoint(QPoint p11, QPoint p12, QPoint p21, QPoint p22) \param numThorns Number of thorns \param numThornLevels Number of thorn levels */ -QwtSimpleCompassRose::QwtSimpleCompassRose(int numThorns, int numThornLevels): - d_width(0.2), - d_numThorns(numThorns), - d_numThornLevels(numThornLevels), - d_shrinkFactor(0.9) +QwtSimpleCompassRose::QwtSimpleCompassRose( + int numThorns, int numThornLevels ) { - const QColor dark(128,128,255); - const QColor light(192,255,255); + d_data = new PrivateData(); + d_data->numThorns = numThorns; + d_data->numThornLevels = numThornLevels; + + const QColor dark( 128, 128, 255 ); + const QColor light( 192, 255, 255 ); QPalette palette; - for ( int i = 0; i < QPalette::NColorGroups; i++ ) { -#if QT_VERSION < 0x040000 - palette.setColor((QPalette::ColorGroup)i, - QColorGroup::Dark, dark); - palette.setColor((QPalette::ColorGroup)i, - QColorGroup::Light, light); -#else - palette.setColor((QPalette::ColorGroup)i, - QPalette::Dark, dark); - palette.setColor((QPalette::ColorGroup)i, - QPalette::Light, light); -#endif - } + palette.setColor( QPalette::Dark, dark ); + palette.setColor( QPalette::Light, light ); - setPalette(palette); + setPalette( palette ); +} + +//! Destructor +QwtSimpleCompassRose::~QwtSimpleCompassRose() +{ + delete d_data; +} + +/*! + Set the Factor how to shrink the thorns with each level + The default value is 0.9. + + \param factor Shrink factor + \sa shrinkFactor() +*/ +void QwtSimpleCompassRose::setShrinkFactor( double factor ) +{ + d_data->shrinkFactor = factor; +} + +/*! + \return Factor how to shrink the thorns with each level + \sa setShrinkFactor() +*/ +double QwtSimpleCompassRose::shrinkFactor() const +{ + return d_data->shrinkFactor; } /*! @@ -92,28 +101,14 @@ QwtSimpleCompassRose::QwtSimpleCompassRose(int numThorns, int numThornLevels): \param north Position \param cg Color group */ -void QwtSimpleCompassRose::draw(QPainter *painter, const QPoint ¢er, - int radius, double north, QPalette::ColorGroup cg) const +void QwtSimpleCompassRose::draw( QPainter *painter, const QPointF ¢er, + double radius, double north, QPalette::ColorGroup cg ) const { -#if QT_VERSION < 0x040000 - QColorGroup colorGroup; - switch(cg) { - case QPalette::Disabled: - colorGroup = palette().disabled(); - case QPalette::Inactive: - colorGroup = palette().inactive(); - default: - colorGroup = palette().active(); - } - - drawRose(painter, colorGroup, center, radius, north, d_width, - d_numThorns, d_numThornLevels, d_shrinkFactor); -#else QPalette pal = palette(); - pal.setCurrentColorGroup(cg); - drawRose(painter, pal, center, radius, north, d_width, - d_numThorns, d_numThornLevels, d_shrinkFactor); -#endif + pal.setCurrentColorGroup( cg ); + + drawRose( painter, pal, center, radius, north, d_data->width, + d_data->numThorns, d_data->numThornLevels, d_data->shrinkFactor ); } /*! @@ -131,13 +126,9 @@ void QwtSimpleCompassRose::draw(QPainter *painter, const QPoint ¢er, */ void QwtSimpleCompassRose::drawRose( QPainter *painter, -#if QT_VERSION < 0x040000 - const QColorGroup &cg, -#else const QPalette &palette, -#endif - const QPoint ¢er, int radius, double north, double width, - int numThorns, int numThornLevels, double shrinkFactor) + const QPointF ¢er, double radius, double north, double width, + int numThorns, int numThornLevels, double shrinkFactor ) { if ( numThorns < 4 ) numThorns = 4; @@ -156,15 +147,17 @@ void QwtSimpleCompassRose::drawRose( painter->save(); - painter->setPen(Qt::NoPen); + painter->setPen( Qt::NoPen ); - for ( int j = 1; j <= numThornLevels; j++ ) { - double step = pow(2.0, j) * M_PI / (double)numThorns; + for ( int j = 1; j <= numThornLevels; j++ ) + { + double step = qPow( 2.0, j ) * M_PI / numThorns; if ( step > M_PI_2 ) break; double r = radius; - for ( int k = 0; k < 3; k++ ) { + for ( int k = 0; k < 3; k++ ) + { if ( j + k < numThornLevels ) r *= shrinkFactor; } @@ -173,37 +166,31 @@ void QwtSimpleCompassRose::drawRose( if ( 2.0 * M_PI / step > 32 ) leafWidth = 16; - const double origin = north / 180.0 * M_PI; + const double origin = qwtRadians( north ); for ( double angle = origin; - angle < 2.0 * M_PI + origin; angle += step) { - const QPoint p = qwtPolar2Pos(center, r, angle); - QPoint p1 = qwtPolar2Pos(center, leafWidth, angle + M_PI_2); - QPoint p2 = qwtPolar2Pos(center, leafWidth, angle - M_PI_2); - - QwtPolygon pa(3); - pa.setPoint(0, center); - pa.setPoint(1, p); - - QPoint p3 = qwtPolar2Pos(center, r, angle + step / 2.0); - p1 = cutPoint(center, p3, p1, p); - pa.setPoint(2, p1); -#if QT_VERSION < 0x040000 - painter->setBrush(cg.brush(QColorGroup::Dark)); -#else - painter->setBrush(palette.brush(QPalette::Dark)); -#endif - painter->drawPolygon(pa); - - QPoint p4 = qwtPolar2Pos(center, r, angle - step / 2.0); - p2 = cutPoint(center, p4, p2, p); - - pa.setPoint(2, p2); -#if QT_VERSION < 0x040000 - painter->setBrush(cg.brush(QColorGroup::Light)); -#else - painter->setBrush(palette.brush(QPalette::Light)); -#endif - painter->drawPolygon(pa); + angle < 2.0 * M_PI + origin; angle += step ) + { + const QPointF p = qwtPolar2Pos( center, r, angle ); + const QPointF p1 = qwtPolar2Pos( center, leafWidth, angle + M_PI_2 ); + const QPointF p2 = qwtPolar2Pos( center, leafWidth, angle - M_PI_2 ); + const QPointF p3 = qwtPolar2Pos( center, r, angle + step / 2.0 ); + const QPointF p4 = qwtPolar2Pos( center, r, angle - step / 2.0 ); + + QPainterPath darkPath; + darkPath.moveTo( center ); + darkPath.lineTo( p ); + darkPath.lineTo( qwtIntersection( center, p3, p1, p ) ); + + painter->setBrush( palette.brush( QPalette::Dark ) ); + painter->drawPath( darkPath ); + + QPainterPath lightPath; + lightPath.moveTo( center ); + lightPath.lineTo( p ); + lightPath.lineTo( qwtIntersection( center, p4, p2, p ) ); + + painter->setBrush( palette.brush( QPalette::Light ) ); + painter->drawPath( lightPath ); } } painter->restore(); @@ -215,15 +202,23 @@ void QwtSimpleCompassRose::drawRose( \param width Width */ - -void QwtSimpleCompassRose::setWidth(double width) +void QwtSimpleCompassRose::setWidth( double width ) { - d_width = width; - if (d_width < 0.03) - d_width = 0.03; + d_data->width = width; + if ( d_data->width < 0.03 ) + d_data->width = 0.03; + + if ( d_data->width > 0.4 ) + d_data->width = 0.4; +} - if (d_width > 0.4) - d_width = 0.4; +/*! + \return Width of the rose + \sa setWidth() + */ +double QwtSimpleCompassRose::width() const +{ + return d_data->width; } /*! @@ -233,7 +228,7 @@ void QwtSimpleCompassRose::setWidth(double width) \param numThorns Number of thorns \sa numThorns(), setNumThornLevels() */ -void QwtSimpleCompassRose::setNumThorns(int numThorns) +void QwtSimpleCompassRose::setNumThorns( int numThorns ) { if ( numThorns < 4 ) numThorns = 4; @@ -241,7 +236,7 @@ void QwtSimpleCompassRose::setNumThorns(int numThorns) if ( numThorns % 4 ) numThorns += 4 - numThorns % 4; - d_numThorns = numThorns; + d_data->numThorns = numThorns; } /*! @@ -250,7 +245,7 @@ void QwtSimpleCompassRose::setNumThorns(int numThorns) */ int QwtSimpleCompassRose::numThorns() const { - return d_numThorns; + return d_data->numThorns; } /*! @@ -259,9 +254,9 @@ int QwtSimpleCompassRose::numThorns() const \param numThornLevels Number of thorns levels \sa setNumThorns(), numThornLevels() */ -void QwtSimpleCompassRose::setNumThornLevels(int numThornLevels) +void QwtSimpleCompassRose::setNumThornLevels( int numThornLevels ) { - d_numThornLevels = numThornLevels; + d_data->numThornLevels = numThornLevels; } /*! @@ -270,5 +265,5 @@ void QwtSimpleCompassRose::setNumThornLevels(int numThornLevels) */ int QwtSimpleCompassRose::numThornLevels() const { - return d_numThornLevels; + return d_data->numThornLevels; } diff --git a/libs/qwt/qwt_compass_rose.h b/libs/qwt/qwt_compass_rose.h index a87c4da38..9b715dfe0 100644 --- a/libs/qwt/qwt_compass_rose.h +++ b/libs/qwt/qwt_compass_rose.h @@ -10,8 +10,8 @@ #ifndef QWT_COMPASS_ROSE_H #define QWT_COMPASS_ROSE_H 1 -#include #include "qwt_global.h" +#include class QPainter; @@ -21,12 +21,18 @@ class QPainter; class QWT_EXPORT QwtCompassRose { public: + //! Destructor virtual ~QwtCompassRose() {}; - virtual void setPalette(const QPalette &p) { + //! Assign a palette + virtual void setPalette( const QPalette &p ) + { d_palette = p; } - const QPalette &palette() const { + + //! \return Current palette + const QPalette &palette() const + { return d_palette; } @@ -39,9 +45,9 @@ public: \param north Position \param colorGroup Color group */ - virtual void draw(QPainter *painter, const QPoint ¢er, - int radius, double north, - QPalette::ColorGroup colorGroup = QPalette::Active) const = 0; + virtual void draw( QPainter *painter, + const QPointF ¢er, double radius, double north, + QPalette::ColorGroup colorGroup = QPalette::Active ) const = 0; private: QPalette d_palette; @@ -53,43 +59,31 @@ private: class QWT_EXPORT QwtSimpleCompassRose: public QwtCompassRose { public: - QwtSimpleCompassRose(int numThorns = 8, int numThornLevels = -1); + QwtSimpleCompassRose( int numThorns = 8, int numThornLevels = -1 ); + virtual ~QwtSimpleCompassRose(); - void setWidth(double w); - double width() const { - return d_width; - } + void setWidth( double w ); + double width() const; - void setNumThorns(int count); + void setNumThorns( int count ); int numThorns() const; - void setNumThornLevels(int count); + void setNumThornLevels( int count ); int numThornLevels() const; - void setShrinkFactor(double factor) { - d_shrinkFactor = factor; - } - double shrinkFactor() const { - return d_shrinkFactor; - } + void setShrinkFactor( double factor ); + double shrinkFactor() const; - virtual void draw(QPainter *, const QPoint ¢er, int radius, - double north, QPalette::ColorGroup = QPalette::Active) const; + virtual void draw( QPainter *, const QPointF ¢er, double radius, + double north, QPalette::ColorGroup = QPalette::Active ) const; - static void drawRose(QPainter *, -#if QT_VERSION < 0x040000 - const QColorGroup &, -#else - const QPalette &, -#endif - const QPoint ¢er, int radius, double origin, double width, - int numThorns, int numThornLevels, double shrinkFactor); + static void drawRose( QPainter *, const QPalette &, + const QPointF ¢er, double radius, double origin, double width, + int numThorns, int numThornLevels, double shrinkFactor ); private: - double d_width; - int d_numThorns; - int d_numThornLevels; - double d_shrinkFactor; + class PrivateData; + PrivateData *d_data; }; -#endif // QWT_COMPASS_ROSE_H +#endif diff --git a/libs/qwt/qwt_compat.h b/libs/qwt/qwt_compat.h new file mode 100644 index 000000000..c97cf6b9c --- /dev/null +++ b/libs/qwt/qwt_compat.h @@ -0,0 +1,42 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef _QWT_COMPAT_H_ +#define _QWT_COMPAT_H_ + +#include "qwt_global.h" +#include "qwt_interval.h" +#include "qwt_point_3d.h" +#include +#include +#include +#include +#include +#include + +// A couple of definition for Qwt5 compatibility + +#define qwtMax qMax +#define qwtMin qMin +#define qwtAbs qAbs +#define qwtRound qRound + +#define QwtArray QVector + +typedef QList QwtValueList; +typedef QPointF QwtDoublePoint; +typedef QSizeF QwtDoubleSize; +typedef QRectF QwtDoubleRect; + +typedef QPolygon QwtPolygon; +typedef QPolygonF QwtPolygonF; +typedef QwtInterval QwtDoubleInterval; +typedef QwtPoint3D QwtDoublePoint3D; + +#endif diff --git a/libs/qwt/qwt_counter.cpp b/libs/qwt/qwt_counter.cpp index c3236d36c..31c05c8df 100644 --- a/libs/qwt/qwt_counter.cpp +++ b/libs/qwt/qwt_counter.cpp @@ -7,22 +7,26 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ -// vim: expandtab - +#include "qwt_arrow_button.h" +#include "qwt_math.h" +#include "qwt_counter.h" #include #include #include #include #include -#include "qwt_math.h" -#include "qwt_counter.h" -#include "qwt_arrow_button.h" class QwtCounter::PrivateData { public: PrivateData(): - editable(true) { + minimum( 0.0 ), + maximum( 0.0 ), + singleStep( 1.0 ), + isValid( false ), + value( 0.0 ), + wrapping( false ) + { increment[Button1] = 1; increment[Button2] = 10; increment[Button3] = 100; @@ -33,27 +37,22 @@ public: QLineEdit *valueEdit; int increment[ButtonCnt]; - int nButtons; + int numButtons; - bool editable; -}; + double minimum; + double maximum; + double singleStep; -/*! - The default number of buttons is set to 2. The default increments are: - \li Button 1: 1 step - \li Button 2: 10 steps - \li Button 3: 100 steps + bool isValid; + double value; - \param parent - */ -QwtCounter::QwtCounter(QWidget *parent): - QWidget(parent) -{ - initCounter(); -} + bool wrapping; +}; -#if QT_VERSION < 0x040000 /*! + The counter is initialized with a range is set to [0.0, 1.0] with + 0.01 as single step size. The value is invalid. + The default number of buttons is set to 2. The default increments are: \li Button 1: 1 step \li Button 2: 10 steps @@ -61,79 +60,68 @@ QwtCounter::QwtCounter(QWidget *parent): \param parent */ -QwtCounter::QwtCounter(QWidget *parent, const char *name): - QWidget(parent, name) +QwtCounter::QwtCounter( QWidget *parent ): + QWidget( parent ) { initCounter(); } -#endif void QwtCounter::initCounter() { d_data = new PrivateData; -#if QT_VERSION >= 0x040000 - using namespace Qt; -#endif - - QHBoxLayout *layout = new QHBoxLayout(this); - layout->setSpacing(0); - layout->setMargin(0); + QHBoxLayout *layout = new QHBoxLayout( this ); + layout->setSpacing( 0 ); + layout->setMargin( 0 ); - int i; - for(i = ButtonCnt - 1; i >= 0; i--) { + for ( int i = ButtonCnt - 1; i >= 0; i-- ) + { QwtArrowButton *btn = - new QwtArrowButton(i+1, Qt::DownArrow,this); - btn->setFocusPolicy(NoFocus); - btn->installEventFilter(this); - layout->addWidget(btn); + new QwtArrowButton( i + 1, Qt::DownArrow, this ); + btn->setFocusPolicy( Qt::NoFocus ); + btn->installEventFilter( this ); + layout->addWidget( btn ); - connect(btn, SIGNAL(released()), SLOT(btnReleased())); - connect(btn, SIGNAL(clicked()), SLOT(btnClicked())); + connect( btn, SIGNAL( released() ), SLOT( btnReleased() ) ); + connect( btn, SIGNAL( clicked() ), SLOT( btnClicked() ) ); d_data->buttonDown[i] = btn; } - d_data->valueEdit = new QLineEdit(this); - d_data->valueEdit->setReadOnly(false); - d_data->valueEdit->setValidator(new QDoubleValidator(d_data->valueEdit)); - layout->addWidget(d_data->valueEdit); - -#if QT_VERSION >= 0x040000 - connect( d_data->valueEdit, SIGNAL(editingFinished()), - SLOT(textChanged()) ); -#else - connect( d_data->valueEdit, SIGNAL(returnPressed()), SLOT(textChanged()) ); - connect( d_data->valueEdit, SIGNAL(lostFocus()), SLOT(textChanged()) ); -#endif + d_data->valueEdit = new QLineEdit( this ); + d_data->valueEdit->setReadOnly( false ); + d_data->valueEdit->setValidator( new QDoubleValidator( d_data->valueEdit ) ); + layout->addWidget( d_data->valueEdit ); - layout->setStretchFactor(d_data->valueEdit, 10); + connect( d_data->valueEdit, SIGNAL( editingFinished() ), + SLOT( textChanged() ) ); - for(i = 0; i < ButtonCnt; i++) { -#if QT_VERSION >= 0x040000 - using namespace Qt; -#endif + layout->setStretchFactor( d_data->valueEdit, 10 ); + + for ( int i = 0; i < ButtonCnt; i++ ) + { QwtArrowButton *btn = - new QwtArrowButton(i+1, Qt::UpArrow, this); - btn->setFocusPolicy(NoFocus); - btn->installEventFilter(this); - layout->addWidget(btn); + new QwtArrowButton( i + 1, Qt::UpArrow, this ); + btn->setFocusPolicy( Qt::NoFocus ); + btn->installEventFilter( this ); + layout->addWidget( btn ); - connect(btn, SIGNAL(released()), SLOT(btnReleased())); - connect(btn, SIGNAL(clicked()), SLOT(btnClicked())); + connect( btn, SIGNAL( released() ), SLOT( btnReleased() ) ); + connect( btn, SIGNAL( clicked() ), SLOT( btnClicked() ) ); d_data->buttonUp[i] = btn; } - setNumButtons(2); - setRange(0.0,1.0,0.001); - setValue(0.0); + setNumButtons( 2 ); + setRange( 0.0, 1.0 ); + setSingleStep( 0.001 ); + setValue( 0.0 ); setSizePolicy( - QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed)); + QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed ) ); - setFocusProxy(d_data->valueEdit); - setFocusPolicy(StrongFocus); + setFocusProxy( d_data->valueEdit ); + setFocusPolicy( Qt::StrongFocus ); } //! Destructor @@ -142,193 +130,265 @@ QwtCounter::~QwtCounter() delete d_data; } +/*! + Set the counter to be in valid/invalid state + + When the counter is set to invalid, no numbers are displayed and + the buttons are disabled. + + \param on If true the counter will be set as valid + + \sa setValue(), isValid() +*/ +void QwtCounter::setValid( bool on ) +{ + if ( on != d_data->isValid ) + { + d_data->isValid = on; + + updateButtons(); + + if ( d_data->isValid ) + { + showNumber( value() ); + Q_EMIT valueChanged( value() ); + } + else + { + d_data->valueEdit->setText( QString::null ); + } + } +} + +/*! + \return True, if the value is valid + \sa setValid(), setValue() + */ +bool QwtCounter::isValid() const +{ + return d_data->isValid; +} + +/*! + \brief Allow/disallow the user to manually edit the value + + \param on True disable editing + \sa isReadOnly() +*/ +void QwtCounter::setReadOnly( bool on ) +{ + d_data->valueEdit->setReadOnly( on ); +} + +/*! + \return True, when the line line edit is read only. (default is no) + \sa setReadOnly() + */ +bool QwtCounter::isReadOnly() const +{ + return d_data->valueEdit->isReadOnly(); +} + /*! - Sets the minimum width for the buttons + \brief Set a new value without adjusting to the step raster + + The state of the counter is set to be valid. + + \param value New value + + \sa isValid(), value(), valueChanged() + \warning The value is clipped when it lies outside the range. */ -void QwtCounter::polish() + +void QwtCounter::setValue( double value ) { - const int w = d_data->valueEdit->fontMetrics().width("W") + 8; + const double vmin = qMin( d_data->minimum, d_data->maximum ); + const double vmax = qMax( d_data->minimum, d_data->maximum ); + + value = qBound( vmin, value, vmax ); + + if ( !d_data->isValid || value != d_data->value ) + { + d_data->isValid = true; + d_data->value = value; + + showNumber( value ); + updateButtons(); - for ( int i = 0; i < ButtonCnt; i++ ) { - d_data->buttonDown[i]->setMinimumWidth(w); - d_data->buttonUp[i]->setMinimumWidth(w); + Q_EMIT valueChanged( value ); } +} -#if QT_VERSION < 0x040000 - QWidget::polish(); -#endif +/*! + \return Current value of the counter + \sa setValue(), valueChanged() + */ +double QwtCounter::value() const +{ + return d_data->value; } -//! Set from lineedit -void QwtCounter::textChanged() +/*! + \brief Set the minimum and maximum values + + The maximum is adjusted if necessary to ensure that the range remains valid. + The value might be modified to be inside of the range. + + \param min Minimum value + \param max Maximum value + + \sa minimum(), maximum() + */ +void QwtCounter::setRange( double min, double max ) { - if ( !d_data->editable ) + max = qMax( min, max ); + + if ( d_data->maximum == max && d_data->minimum == min ) return; - bool converted = false; + d_data->minimum = min; + d_data->maximum = max; - const double value = d_data->valueEdit->text().toDouble(&converted); - if ( converted ) - setValue( value ); + setSingleStep( singleStep() ); + + const double value = qBound( min, d_data->value, max ); + + if ( value != d_data->value ) + { + d_data->value = value; + + if ( d_data->isValid ) + { + showNumber( value ); + Q_EMIT valueChanged( value ); + } + } + + updateButtons(); } -/** - \brief Allow/disallow the user to manually edit the value +/*! + Set the minimum value of the range - \param editable true enables editing - \sa editable() + \param value Minimum value + \sa setRange(), setMaximum(), minimum() + + \note The maximum is adjusted if necessary to ensure that the range remains valid. */ -void QwtCounter::setEditable(bool editable) +void QwtCounter::setMinimum( double value ) { -#if QT_VERSION >= 0x040000 - using namespace Qt; -#endif - if ( editable == d_data->editable ) - return; + setRange( value, maximum() ); +} - d_data->editable = editable; - d_data->valueEdit->setReadOnly(!editable); +/*! + \return The minimum of the range + \sa setRange(), setMinimum(), maximum() +*/ +double QwtCounter::minimum() const +{ + return d_data->minimum; } -//! returns whether the line edit is edatble. (default is yes) -bool QwtCounter::editable() const +/*! + Set the maximum value of the range + + \param value Maximum value + \sa setRange(), setMinimum(), maximum() +*/ +void QwtCounter::setMaximum( double value ) { - return d_data->editable; + setRange( minimum(), value ); } /*! - Handle PolishRequest events + \return The maximum of the range + \sa setRange(), setMaximum(), minimum() */ -bool QwtCounter::event ( QEvent * e ) +double QwtCounter::maximum() const { -#if QT_VERSION >= 0x040000 - if ( e->type() == QEvent::PolishRequest ) - polish(); -#endif - return QWidget::event(e); + return d_data->maximum; } /*! - Handles key events + \brief Set the step size of the counter - - Ctrl + Qt::Key_Home - Step to minValue() - - Ctrl + Qt::Key_End - Step to maxValue() - - Qt::Key_Up - Increment by incSteps(QwtCounter::Button1) - - Qt::Key_Down - Decrement by incSteps(QwtCounter::Button1) - - Qt::Key_PageUp - Increment by incSteps(QwtCounter::Button2) - - Qt::Key_PageDown - Decrement by incSteps(QwtCounter::Button2) - - Shift + Qt::Key_PageUp - Increment by incSteps(QwtCounter::Button3) - - Shift + Qt::Key_PageDown - Decrement by incSteps(QwtCounter::Button3) + A value <= 0.0 disables stepping + + \param stepSize Single step size + \sa singleStep() */ +void QwtCounter::setSingleStep( double stepSize ) +{ + d_data->singleStep = qMax( stepSize, 0.0 ); +} -void QwtCounter::keyPressEvent (QKeyEvent *e) +/*! + \return Single step size + \sa setSingleStep() + */ +double QwtCounter::singleStep() const { - bool accepted = true; + return d_data->singleStep; +} - switch ( e->key() ) { - case Qt::Key_Home: -#if QT_VERSION >= 0x040000 - if ( e->modifiers() & Qt::ControlModifier ) -#else - if ( e->state() & Qt::ControlButton ) -#endif - setValue(minValue()); - else - accepted = false; - break; - case Qt::Key_End: -#if QT_VERSION >= 0x040000 - if ( e->modifiers() & Qt::ControlModifier ) -#else - if ( e->state() & Qt::ControlButton ) -#endif - setValue(maxValue()); - else - accepted = false; - break; - case Qt::Key_Up: - incValue(d_data->increment[0]); - break; - case Qt::Key_Down: - incValue(-d_data->increment[0]); - break; - case Qt::Key_PageUp: - case Qt::Key_PageDown: { - int increment = d_data->increment[0]; - if ( d_data->nButtons >= 2 ) - increment = d_data->increment[1]; - if ( d_data->nButtons >= 3 ) { -#if QT_VERSION >= 0x040000 - if ( e->modifiers() & Qt::ShiftModifier ) -#else - if ( e->state() & Qt::ShiftButton ) -#endif - increment = d_data->increment[2]; - } - if ( e->key() == Qt::Key_PageDown ) - increment = -increment; - incValue(increment); - break; - } - default: - accepted = false; - } +/*! + \brief En/Disable wrapping - if ( accepted ) { - e->accept(); - return; - } + If wrapping is true stepping up from maximum() value will take + you to the minimum() value and vice versa. - QWidget::keyPressEvent (e); + \param on En/Disable wrapping + \sa wrapping() + */ +void QwtCounter::setWrapping( bool on ) +{ + d_data->wrapping = on; } -void QwtCounter::wheelEvent(QWheelEvent *e) +/*! + \return True, when wrapping is set + \sa setWrapping() + */ +bool QwtCounter::wrapping() const { - e->accept(); + return d_data->wrapping; +} - if ( d_data->nButtons <= 0 ) - return; +/*! + Specify the number of buttons on each side of the label - int increment = d_data->increment[0]; - if ( d_data->nButtons >= 2 ) { -#if QT_VERSION >= 0x040000 - if ( e->modifiers() & Qt::ControlModifier ) -#else - if ( e->state() & Qt::ControlButton ) -#endif - increment = d_data->increment[1]; - } - if ( d_data->nButtons >= 3 ) { -#if QT_VERSION >= 0x040000 - if ( e->modifiers() & Qt::ShiftModifier ) -#else - if ( e->state() & Qt::ShiftButton ) -#endif - increment = d_data->increment[2]; - } + \param numButtons Number of buttons + \sa numButtons() +*/ +void QwtCounter::setNumButtons( int numButtons ) +{ + if ( numButtons < 0 || numButtons > QwtCounter::ButtonCnt ) + return; - for ( int i = 0; i < d_data->nButtons; i++ ) { - if ( d_data->buttonDown[i]->geometry().contains(e->pos()) || - d_data->buttonUp[i]->geometry().contains(e->pos()) ) { - increment = d_data->increment[i]; + for ( int i = 0; i < QwtCounter::ButtonCnt; i++ ) + { + if ( i < numButtons ) + { + d_data->buttonDown[i]->show(); + d_data->buttonUp[i]->show(); + } + else + { + d_data->buttonDown[i]->hide(); + d_data->buttonUp[i]->hide(); } } - const int wheel_delta = 120; - - int delta = e->delta(); - if ( delta >= 2 * wheel_delta ) - delta /= 2; // Never saw an abs(delta) < 240 + d_data->numButtons = numButtons; +} - incValue(delta / wheel_delta * increment); +/*! + \return The number of buttons on each side of the widget. + \sa setNumButtons() +*/ +int QwtCounter::numButtons() const +{ + return d_data->numButtons; } /*! @@ -336,155 +396,359 @@ void QwtCounter::wheelEvent(QWheelEvent *e) is incremented or decremented when a specified button is pushed. - \param btn One of \c QwtCounter::Button1, \c QwtCounter::Button2, - \c QwtCounter::Button3 - \param nSteps Number of steps + \param button Button index + \param numSteps Number of steps + + \sa incSteps() */ -void QwtCounter::setIncSteps(QwtCounter::Button btn, int nSteps) +void QwtCounter::setIncSteps( QwtCounter::Button button, int numSteps ) { - if (( btn >= 0) && (btn < ButtonCnt)) - d_data->increment[btn] = nSteps; + if ( button >= 0 && button < QwtCounter::ButtonCnt ) + d_data->increment[ button ] = numSteps; } /*! - \return the number of steps by which a specified button increments the value - or 0 if the button is invalid. - \param btn One of \c QwtCounter::Button1, \c QwtCounter::Button2, - \c QwtCounter::Button3 + \return The number of steps by which a specified button increments the value + or 0 if the button is invalid. + \param button Button index + + \sa setIncSteps() */ -int QwtCounter::incSteps(QwtCounter::Button btn) const +int QwtCounter::incSteps( QwtCounter::Button button ) const { - if (( btn >= 0) && (btn < ButtonCnt)) - return d_data->increment[btn]; + if ( button >= 0 && button < QwtCounter::ButtonCnt ) + return d_data->increment[ button ]; return 0; } + /*! - \brief Set a new value - \param v new value - Calls QwtDoubleRange::setValue and does all visual updates. - \sa QwtDoubleRange::setValue + Set the number of increment steps for button 1 + \param nSteps Number of steps */ +void QwtCounter::setStepButton1( int nSteps ) +{ + setIncSteps( QwtCounter::Button1, nSteps ); +} -void QwtCounter::setValue(double v) +//! returns the number of increment steps for button 1 +int QwtCounter::stepButton1() const { - QwtDoubleRange::setValue(v); + return incSteps( QwtCounter::Button1 ); +} - showNum(value()); - updateButtons(); +/*! + Set the number of increment steps for button 2 + \param nSteps Number of steps +*/ +void QwtCounter::setStepButton2( int nSteps ) +{ + setIncSteps( QwtCounter::Button2, nSteps ); +} + +//! returns the number of increment steps for button 2 +int QwtCounter::stepButton2() const +{ + return incSteps( QwtCounter::Button2 ); } /*! - \brief Notify a change of value + Set the number of increment steps for button 3 + \param nSteps Number of steps */ -void QwtCounter::valueChange() +void QwtCounter::setStepButton3( int nSteps ) { - if ( isValid() ) - showNum(value()); - else - d_data->valueEdit->setText(QString::null); + setIncSteps( QwtCounter::Button3, nSteps ); +} - updateButtons(); +//! returns the number of increment steps for button 3 +int QwtCounter::stepButton3() const +{ + return incSteps( QwtCounter::Button3 ); +} + +//! Set from lineedit +void QwtCounter::textChanged() +{ + bool converted = false; + + const double value = d_data->valueEdit->text().toDouble( &converted ); + if ( converted ) + setValue( value ); +} - if ( isValid() ) - emit valueChanged(value()); +/*! + Handle QEvent::PolishRequest events + \param event Event + \return see QWidget::event() +*/ +bool QwtCounter::event( QEvent *event ) +{ + if ( event->type() == QEvent::PolishRequest ) + { + const int w = d_data->valueEdit->fontMetrics().width( "W" ) + 8; + for ( int i = 0; i < ButtonCnt; i++ ) + { + d_data->buttonDown[i]->setMinimumWidth( w ); + d_data->buttonUp[i]->setMinimumWidth( w ); + } + } + + return QWidget::event( event ); } /*! - \brief Update buttons according to the current value + Handle key events - When the QwtCounter under- or over-flows, the focus is set to the smallest - up- or down-button and counting is disabled. + - Ctrl + Qt::Key_Home\n + Step to minimum() + - Ctrl + Qt::Key_End\n + Step to maximum() + - Qt::Key_Up\n + Increment by incSteps(QwtCounter::Button1) + - Qt::Key_Down\n + Decrement by incSteps(QwtCounter::Button1) + - Qt::Key_PageUp\n + Increment by incSteps(QwtCounter::Button2) + - Qt::Key_PageDown\n + Decrement by incSteps(QwtCounter::Button2) + - Shift + Qt::Key_PageUp\n + Increment by incSteps(QwtCounter::Button3) + - Shift + Qt::Key_PageDown\n + Decrement by incSteps(QwtCounter::Button3) - Counting is re-enabled on a button release event (mouse or space bar). + \param event Key event */ -void QwtCounter::updateButtons() +void QwtCounter::keyPressEvent ( QKeyEvent *event ) { - if ( isValid() ) { - // 1. save enabled state of the smallest down- and up-button - // 2. change enabled state on under- or over-flow + bool accepted = true; - for ( int i = 0; i < ButtonCnt; i++ ) { - d_data->buttonDown[i]->setEnabled(value() > minValue()); - d_data->buttonUp[i]->setEnabled(value() < maxValue()); + switch ( event->key() ) + { + case Qt::Key_Home: + { + if ( event->modifiers() & Qt::ControlModifier ) + setValue( minimum() ); + else + accepted = false; + break; + } + case Qt::Key_End: + { + if ( event->modifiers() & Qt::ControlModifier ) + setValue( maximum() ); + else + accepted = false; + break; + } + case Qt::Key_Up: + { + incrementValue( d_data->increment[0] ); + break; } - } else { - for ( int i = 0; i < ButtonCnt; i++ ) { - d_data->buttonDown[i]->setEnabled(false); - d_data->buttonUp[i]->setEnabled(false); + case Qt::Key_Down: + { + incrementValue( -d_data->increment[0] ); + break; + } + case Qt::Key_PageUp: + case Qt::Key_PageDown: + { + int increment = d_data->increment[0]; + if ( d_data->numButtons >= 2 ) + increment = d_data->increment[1]; + if ( d_data->numButtons >= 3 ) + { + if ( event->modifiers() & Qt::ShiftModifier ) + increment = d_data->increment[2]; + } + if ( event->key() == Qt::Key_PageDown ) + increment = -increment; + incrementValue( increment ); + break; + } + default: + { + accepted = false; } } + + if ( accepted ) + { + event->accept(); + return; + } + + QWidget::keyPressEvent ( event ); } /*! - \brief Specify the number of buttons on each side of the label - \param n Number of buttons + Handle wheel events + \param event Wheel event */ -void QwtCounter::setNumButtons(int n) +void QwtCounter::wheelEvent( QWheelEvent *event ) { - if ( n<0 || n>ButtonCnt ) + event->accept(); + + if ( d_data->numButtons <= 0 ) return; - for ( int i = 0; i < ButtonCnt; i++ ) { - if ( i < n ) { - d_data->buttonDown[i]->show(); - d_data->buttonUp[i]->show(); - } else { - d_data->buttonDown[i]->hide(); - d_data->buttonUp[i]->hide(); + int increment = d_data->increment[0]; + if ( d_data->numButtons >= 2 ) + { + if ( event->modifiers() & Qt::ControlModifier ) + increment = d_data->increment[1]; + } + if ( d_data->numButtons >= 3 ) + { + if ( event->modifiers() & Qt::ShiftModifier ) + increment = d_data->increment[2]; + } + + for ( int i = 0; i < d_data->numButtons; i++ ) + { + if ( d_data->buttonDown[i]->geometry().contains( event->pos() ) || + d_data->buttonUp[i]->geometry().contains( event->pos() ) ) + { + increment = d_data->increment[i]; + } + } + + const int wheel_delta = 120; + +#if 1 + int delta = event->delta(); + if ( delta >= 2 * wheel_delta ) + delta /= 2; // Never saw an abs(delta) < 240 +#endif + + incrementValue( delta / wheel_delta * increment ); +} + +void QwtCounter::incrementValue( int numSteps ) +{ + const double min = d_data->minimum; + const double max = d_data->maximum; + double stepSize = d_data->singleStep; + + if ( !d_data->isValid || min >= max || stepSize <= 0.0 ) + return; + + +#if 1 + stepSize = qMax( stepSize, 1.0e-10 * ( max - min ) ); +#endif + + double value = d_data->value + numSteps * stepSize; + + if ( d_data->wrapping ) + { + const double range = max - min; + + if ( value < min ) + { + value += ::ceil( ( min - value ) / range ) * range; } + else if ( value > max ) + { + value -= ::ceil( ( value - max ) / range ) * range; + } + } + else + { + value = qBound( min, value, max ); } - d_data->nButtons = n; + value = min + qRound( ( value - min ) / stepSize ) * stepSize; + + if ( stepSize > 1e-12 ) + { + if ( qFuzzyCompare( value + 1.0, 1.0 ) ) + { + // correct rounding error if value = 0 + value = 0.0; + } + else if ( qFuzzyCompare( value, max ) ) + { + // correct rounding error at the border + value = max; + } + } + + if ( value != d_data->value ) + { + d_data->value = value; + showNumber( d_data->value ); + updateButtons(); + + Q_EMIT valueChanged( d_data->value ); + } } + /*! - \return The number of buttons on each side of the widget. + \brief Update buttons according to the current value + + When the QwtCounter under- or over-flows, the focus is set to the smallest + up- or down-button and counting is disabled. + + Counting is re-enabled on a button release event (mouse or space bar). */ -int QwtCounter::numButtons() const +void QwtCounter::updateButtons() { - return d_data->nButtons; + if ( d_data->isValid ) + { + // 1. save enabled state of the smallest down- and up-button + // 2. change enabled state on under- or over-flow + + for ( int i = 0; i < QwtCounter::ButtonCnt; i++ ) + { + d_data->buttonDown[i]->setEnabled( value() > minimum() ); + d_data->buttonUp[i]->setEnabled( value() < maximum() ); + } + } + else + { + for ( int i = 0; i < QwtCounter::ButtonCnt; i++ ) + { + d_data->buttonDown[i]->setEnabled( false ); + d_data->buttonUp[i]->setEnabled( false ); + } + } } +/*! + Display number string -//! Display number string -void QwtCounter::showNum(double d) + \param number Number +*/ +void QwtCounter::showNumber( double number ) { - QString v; - v.setNum(d); + QString text; + text.setNum( number ); const int cursorPos = d_data->valueEdit->cursorPosition(); - d_data->valueEdit->setText(v); - d_data->valueEdit->setCursorPosition(cursorPos); + d_data->valueEdit->setText( text ); + d_data->valueEdit->setCursorPosition( cursorPos ); } //! Button clicked void QwtCounter::btnClicked() { - for ( int i = 0; i < ButtonCnt; i++ ) { + for ( int i = 0; i < ButtonCnt; i++ ) + { if ( d_data->buttonUp[i] == sender() ) - incValue(d_data->increment[i]); + incrementValue( d_data->increment[i] ); if ( d_data->buttonDown[i] == sender() ) - incValue(-d_data->increment[i]); + incrementValue( -d_data->increment[i] ); } } //! Button released void QwtCounter::btnReleased() { - emit buttonReleased(value()); -} - -/*! - \brief Notify change of range - - This function updates the enabled property of - all buttons contained in QwtCounter. -*/ -void QwtCounter::rangeChange() -{ - updateButtons(); + Q_EMIT buttonReleased( value() ); } //! A size hint @@ -492,112 +756,30 @@ QSize QwtCounter::sizeHint() const { QString tmp; - int w = tmp.setNum(minValue()).length(); - int w1 = tmp.setNum(maxValue()).length(); + int w = tmp.setNum( minimum() ).length(); + int w1 = tmp.setNum( maximum() ).length(); if ( w1 > w ) w = w1; - w1 = tmp.setNum(minValue() + step()).length(); + w1 = tmp.setNum( minimum() + singleStep() ).length(); if ( w1 > w ) w = w1; - w1 = tmp.setNum(maxValue() - step()).length(); + w1 = tmp.setNum( maximum() - singleStep() ).length(); if ( w1 > w ) w = w1; - tmp.fill('9', w); + tmp.fill( '9', w ); - QFontMetrics fm(d_data->valueEdit->font()); - w = fm.width(tmp) + 2; -#if QT_VERSION >= 0x040000 + QFontMetrics fm( d_data->valueEdit->font() ); + w = fm.width( tmp ) + 2; if ( d_data->valueEdit->hasFrame() ) - w += 2 * style()->pixelMetric(QStyle::PM_DefaultFrameWidth); -#else - w += 2 * d_data->valueEdit->frameWidth(); -#endif + w += 2 * style()->pixelMetric( QStyle::PM_DefaultFrameWidth ); // Now we replace default sizeHint contribution of d_data->valueEdit by // what we really need. w += QWidget::sizeHint().width() - d_data->valueEdit->sizeHint().width(); - const int h = qwtMin(QWidget::sizeHint().height(), - d_data->valueEdit->minimumSizeHint().height()); - return QSize(w, h); -} - -//! returns the step size -double QwtCounter::step() const -{ - return QwtDoubleRange::step(); -} - -//! sets the step size -void QwtCounter::setStep(double s) -{ - QwtDoubleRange::setStep(s); -} - -//! returns the minimum value of the range -double QwtCounter::minVal() const -{ - return minValue(); -} - -//! sets the minimum value of the range -void QwtCounter::setMinValue(double m) -{ - setRange(m, maxValue(), step()); -} - -//! returns the maximum value of the range -double QwtCounter::maxVal() const -{ - return QwtDoubleRange::maxValue(); -} - -//! sets the maximum value of the range -void QwtCounter::setMaxValue(double m) -{ - setRange(minValue(), m, step()); -} - -//! set the number of increment steps for button 1 -void QwtCounter::setStepButton1(int nSteps) -{ - setIncSteps(Button1, nSteps); -} - -//! returns the number of increment steps for button 1 -int QwtCounter::stepButton1() const -{ - return incSteps(Button1); + const int h = qMin( QWidget::sizeHint().height(), + d_data->valueEdit->minimumSizeHint().height() ); + return QSize( w, h ); } - -//! set the number of increment steps for button 2 -void QwtCounter::setStepButton2(int nSteps) -{ - setIncSteps(Button2, nSteps); -} - -//! returns the number of increment steps for button 2 -int QwtCounter::stepButton2() const -{ - return incSteps(Button2); -} - -//! set the number of increment steps for button 3 -void QwtCounter::setStepButton3(int nSteps) -{ - setIncSteps(Button3, nSteps); -} - -//! returns the number of increment steps for button 3 -int QwtCounter::stepButton3() const -{ - return incSteps(Button3); -} - -double QwtCounter::value() const -{ - return QwtDoubleRange::value(); -} - diff --git a/libs/qwt/qwt_counter.h b/libs/qwt/qwt_counter.h index bda399cce..8799eddca 100644 --- a/libs/qwt/qwt_counter.h +++ b/libs/qwt/qwt_counter.h @@ -7,14 +7,11 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ -// vim: expandtab - #ifndef QWT_COUNTER_H #define QWT_COUNTER_H -#include #include "qwt_global.h" -#include "qwt_double_range.h" +#include /*! \brief The Counter Widget @@ -24,130 +21,138 @@ of the label which can be used to increment or decrement the counter's value. - A Counter has a range from a minimum value to a maximum value - and a step size. The range can be specified using - QwtDblRange::setRange(). - The counter's value is an integer multiple of the step size. - The number of steps by which a button increments or decrements - the value can be specified using QwtCounter::setIncSteps(). - The number of buttons can be changed with - QwtCounter::setNumButtons(). - - Holding the space bar down with focus on a button is the - fastest method to step through the counter values. - When the counter underflows/overflows, the focus is set - to the smallest up/down button and counting is disabled. - Counting is re-enabled on a button release event (mouse or - space bar). + A counter has a range from a minimum value to a maximum value + and a step size. When the wrapping property is set + the counter is circular. + + The number of steps by which a button increments or decrements the value + can be specified using setIncSteps(). The number of buttons can be + changed with setNumButtons(). Example: \code -#include "../include/qwt_counter.h> +#include -QwtCounter *cnt; +QwtCounter *counter = new QwtCounter(parent); -cnt = new QwtCounter(parent, name); +counter->setRange(0.0, 100.0); // From 0.0 to 100 +counter->setSingleStep( 1.0 ); // Step size 1.0 +counter->setNumButtons(2); // Two buttons each side +counter->setIncSteps(QwtCounter::Button1, 1); // Button 1 increments 1 step +counter->setIncSteps(QwtCounter::Button2, 20); // Button 2 increments 20 steps -cnt->setRange(0.0, 100.0, 1.0); // From 0.0 to 100, step 1.0 -cnt->setNumButtons(2); // Two buttons each side -cnt->setIncSteps(QwtCounter::Button1, 1); // Button 1 increments 1 step -cnt->setIncSteps(QwtCounter::Button2, 20); // Button 2 increments 20 steps - -connect(cnt, SIGNAL(valueChanged(double)), my_class, SLOT(newValue(double))); +connect(counter, SIGNAL(valueChanged(double)), myClass, SLOT(newValue(double))); \endcode */ -class QWT_EXPORT QwtCounter : public QWidget, public QwtDoubleRange +class QWT_EXPORT QwtCounter : public QWidget { Q_OBJECT + Q_PROPERTY( double value READ value WRITE setValue ) + Q_PROPERTY( double minimum READ minimum WRITE setMinimum ) + Q_PROPERTY( double maximum READ maximum WRITE setMaximum ) + Q_PROPERTY( double singleStep READ singleStep WRITE setSingleStep ) + Q_PROPERTY( int numButtons READ numButtons WRITE setNumButtons ) - Q_PROPERTY( double basicstep READ step WRITE setStep ) - Q_PROPERTY( double minValue READ minVal WRITE setMinValue ) - Q_PROPERTY( double maxValue READ maxVal WRITE setMaxValue ) Q_PROPERTY( int stepButton1 READ stepButton1 WRITE setStepButton1 ) Q_PROPERTY( int stepButton2 READ stepButton2 WRITE setStepButton2 ) Q_PROPERTY( int stepButton3 READ stepButton3 WRITE setStepButton3 ) - Q_PROPERTY( double value READ value WRITE setValue ) - Q_PROPERTY( bool editable READ editable WRITE setEditable ) -public: - /*! - Button index - */ + Q_PROPERTY( bool readOnly READ isReadOnly WRITE setReadOnly ) + Q_PROPERTY( bool wrapping READ wrapping WRITE setWrapping ) - enum Button { +public: + //! Button index + enum Button + { + //! Button intended for minor steps Button1, + + //! Button intended for medium steps Button2, + + //! Button intended for large steps Button3, + + //! Number of buttons ButtonCnt }; - explicit QwtCounter(QWidget *parent = NULL); -#if QT_VERSION < 0x040000 - explicit QwtCounter(QWidget *parent, const char *name); -#endif + explicit QwtCounter( QWidget *parent = NULL ); virtual ~QwtCounter(); - bool editable() const; - void setEditable(bool); + void setValid( bool ); + bool isValid() const; - void setNumButtons(int n); + void setWrapping( bool ); + bool wrapping() const; + + bool isReadOnly() const; + void setReadOnly( bool ); + + void setNumButtons( int n ); int numButtons() const; - void setIncSteps(QwtCounter::Button btn, int nSteps); - int incSteps(QwtCounter::Button btn) const; + void setIncSteps( QwtCounter::Button btn, int nSteps ); + int incSteps( QwtCounter::Button btn ) const; - virtual void setValue(double); virtual QSize sizeHint() const; - virtual void polish(); + double singleStep() const; + void setSingleStep( double s ); + + void setRange( double min, double max ); + + double minimum() const; + void setMinimum( double min ); - // a set of dummies to help the designer + double maximum() const; + void setMaximum( double max ); - double step() const; - void setStep(double s); - double minVal() const; - void setMinValue(double m); - double maxVal() const; - void setMaxValue(double m); - void setStepButton1(int nSteps); + void setStepButton1( int nSteps ); int stepButton1() const; - void setStepButton2(int nSteps); + + void setStepButton2( int nSteps ); int stepButton2() const; - void setStepButton3(int nSteps); + + void setStepButton3( int nSteps ); int stepButton3() const; - virtual double value() const; -signals: + double value() const; + +public Q_SLOTS: + void setValue( double ); + + +Q_SIGNALS: /*! This signal is emitted when a button has been released \param value The new value */ - void buttonReleased (double value); + void buttonReleased ( double value ); /*! This signal is emitted when the counter's value has changed \param value The new value */ - void valueChanged (double value); + void valueChanged ( double value ); protected: - virtual bool event(QEvent *); - virtual void wheelEvent(QWheelEvent *); - virtual void keyPressEvent(QKeyEvent *); - virtual void rangeChange(); + virtual bool event( QEvent * ); + virtual void wheelEvent( QWheelEvent * ); + virtual void keyPressEvent( QKeyEvent * ); -private slots: +private Q_SLOTS: void btnReleased(); void btnClicked(); void textChanged(); private: + void incrementValue( int numSteps ); void initCounter(); void updateButtons(); - void showNum(double); - virtual void valueChange(); + void showNumber( double ); class PrivateData; PrivateData *d_data; diff --git a/libs/qwt/qwt_curve_fitter.cpp b/libs/qwt/qwt_curve_fitter.cpp index 05cc82ce8..5f09d5d8d 100644 --- a/libs/qwt/qwt_curve_fitter.cpp +++ b/libs/qwt/qwt_curve_fitter.cpp @@ -7,9 +7,15 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ +#include "qwt_curve_fitter.h" #include "qwt_math.h" #include "qwt_spline.h" -#include "qwt_curve_fitter.h" +#include +#include + +#if QT_VERSION < 0x040601 +#define qFabs(x) ::fabs(x) +#endif //! Constructor QwtCurveFitter::QwtCurveFitter() @@ -25,8 +31,9 @@ class QwtSplineCurveFitter::PrivateData { public: PrivateData(): - fitMode(QwtSplineCurveFitter::Auto), - splineSize(250) { + fitMode( QwtSplineCurveFitter::Auto ), + splineSize( 250 ) + { } QwtSpline spline; @@ -34,71 +41,110 @@ public: int splineSize; }; +//! Constructor QwtSplineCurveFitter::QwtSplineCurveFitter() { d_data = new PrivateData; } +//! Destructor QwtSplineCurveFitter::~QwtSplineCurveFitter() { delete d_data; } -void QwtSplineCurveFitter::setFitMode(FitMode mode) +/*! + Select the algorithm used for building the spline + + \param mode Mode representing a spline algorithm + \sa fitMode() +*/ +void QwtSplineCurveFitter::setFitMode( FitMode mode ) { d_data->fitMode = mode; } +/*! + \return Mode representing a spline algorithm + \sa setFitMode() +*/ QwtSplineCurveFitter::FitMode QwtSplineCurveFitter::fitMode() const { return d_data->fitMode; } -void QwtSplineCurveFitter::setSpline(const QwtSpline &spline) +/*! + Assign a spline + + \param spline Spline + \sa spline() +*/ +void QwtSplineCurveFitter::setSpline( const QwtSpline &spline ) { d_data->spline = spline; d_data->spline.reset(); } +/*! + \return Spline + \sa setSpline() +*/ const QwtSpline &QwtSplineCurveFitter::spline() const { return d_data->spline; } +/*! + \return Spline + \sa setSpline() +*/ QwtSpline &QwtSplineCurveFitter::spline() { return d_data->spline; } -void QwtSplineCurveFitter::setSplineSize(int splineSize) +/*! + Assign a spline size ( has to be at least 10 points ) + + \param splineSize Spline size + \sa splineSize() +*/ +void QwtSplineCurveFitter::setSplineSize( int splineSize ) { - d_data->splineSize = qwtMax(splineSize, 10); + d_data->splineSize = qMax( splineSize, 10 ); } +/*! + \return Spline size + \sa setSplineSize() +*/ int QwtSplineCurveFitter::splineSize() const { return d_data->splineSize; } -#if QT_VERSION < 0x040000 -QwtArray QwtSplineCurveFitter::fitCurve( - const QwtArray & points) const -#else -QPolygonF QwtSplineCurveFitter::fitCurve( - const QPolygonF &points) const -#endif +/*! + Find a curve which has the best fit to a series of data points + + \param points Series of data points + \return Curve points +*/ +QPolygonF QwtSplineCurveFitter::fitCurve( const QPolygonF &points ) const { - const int size = (int)points.size(); + const int size = points.size(); if ( size <= 2 ) return points; FitMode fitMode = d_data->fitMode; - if ( fitMode == Auto ) { + if ( fitMode == Auto ) + { fitMode = Spline; - const QwtDoublePoint *p = points.data(); - for ( int i = 1; i < size; i++ ) { - if ( p[i].x() <= p[i-1].x() ) { + const QPointF *p = points.data(); + for ( int i = 1; i < size; i++ ) + { + if ( p[i].x() <= p[i-1].x() ) + { fitMode = ParametricSpline; break; } @@ -106,109 +152,302 @@ QPolygonF QwtSplineCurveFitter::fitCurve( } if ( fitMode == ParametricSpline ) - return fitParametric(points); + return fitParametric( points ); else - return fitSpline(points); + return fitSpline( points ); } -#if QT_VERSION < 0x040000 -QwtArray QwtSplineCurveFitter::fitSpline( - const QwtArray &points) const -#else -QPolygonF QwtSplineCurveFitter::fitSpline( - const QPolygonF &points) const -#endif +QPolygonF QwtSplineCurveFitter::fitSpline( const QPolygonF &points ) const { - d_data->spline.setPoints(points); + d_data->spline.setPoints( points ); if ( !d_data->spline.isValid() ) return points; -#if QT_VERSION < 0x040000 - QwtArray fittedPoints(d_data->splineSize); -#else - QPolygonF fittedPoints(d_data->splineSize); -#endif + QPolygonF fittedPoints( d_data->splineSize ); const double x1 = points[0].x(); - const double x2 = points[int(points.size() - 1)].x(); + const double x2 = points[int( points.size() - 1 )].x(); const double dx = x2 - x1; - const double delta = dx / (d_data->splineSize - 1); + const double delta = dx / ( d_data->splineSize - 1 ); - for (int i = 0; i < d_data->splineSize; i++) { - QwtDoublePoint &p = fittedPoints[i]; + for ( int i = 0; i < d_data->splineSize; i++ ) + { + QPointF &p = fittedPoints[i]; const double v = x1 + i * delta; - const double sv = d_data->spline.value(v); + const double sv = d_data->spline.value( v ); - p.setX(qRound(v)); - p.setY(qRound(sv)); + p.setX( v ); + p.setY( sv ); } d_data->spline.reset(); return fittedPoints; } -#if QT_VERSION < 0x040000 -QwtArray QwtSplineCurveFitter::fitParametric( - const QwtArray &points) const -#else -QPolygonF QwtSplineCurveFitter::fitParametric( - const QPolygonF &points) const -#endif +QPolygonF QwtSplineCurveFitter::fitParametric( const QPolygonF &points ) const { int i; const int size = points.size(); -#if QT_VERSION < 0x040000 - QwtArray fittedPoints(d_data->splineSize); - QwtArray splinePointsX(size); - QwtArray splinePointsY(size); -#else - QPolygonF fittedPoints(d_data->splineSize); - QPolygonF splinePointsX(size); - QPolygonF splinePointsY(size); -#endif + QPolygonF fittedPoints( d_data->splineSize ); + QPolygonF splinePointsX( size ); + QPolygonF splinePointsY( size ); - const QwtDoublePoint *p = points.data(); - QwtDoublePoint *spX = splinePointsX.data(); - QwtDoublePoint *spY = splinePointsY.data(); + const QPointF *p = points.data(); + QPointF *spX = splinePointsX.data(); + QPointF *spY = splinePointsY.data(); double param = 0.0; - for (i = 0; i < size; i++) { + for ( i = 0; i < size; i++ ) + { const double x = p[i].x(); const double y = p[i].y(); - if ( i > 0 ) { - const double delta = sqrt( qwtSqr(x - spX[i-1].y()) - + qwtSqr( y - spY[i-1].y() ) ); - param += qwtMax(delta, 1.0); + if ( i > 0 ) + { + const double delta = qSqrt( qwtSqr( x - spX[i-1].y() ) + + qwtSqr( y - spY[i-1].y() ) ); + param += qMax( delta, 1.0 ); } - spX[i].setX(param); - spX[i].setY(x); - spY[i].setX(param); - spY[i].setY(y); + spX[i].setX( param ); + spX[i].setY( x ); + spY[i].setX( param ); + spY[i].setY( y ); } - d_data->spline.setPoints(splinePointsX); + d_data->spline.setPoints( splinePointsX ); if ( !d_data->spline.isValid() ) return points; const double deltaX = - splinePointsX[size - 1].x() / (d_data->splineSize-1); - for (i = 0; i < d_data->splineSize; i++) { + splinePointsX[size - 1].x() / ( d_data->splineSize - 1 ); + for ( i = 0; i < d_data->splineSize; i++ ) + { const double dtmp = i * deltaX; - fittedPoints[i].setX(qRound(d_data->spline.value(dtmp))); + fittedPoints[i].setX( d_data->spline.value( dtmp ) ); } - d_data->spline.setPoints(splinePointsY); + d_data->spline.setPoints( splinePointsY ); if ( !d_data->spline.isValid() ) return points; const double deltaY = - splinePointsY[size - 1].x() / (d_data->splineSize-1); - for (i = 0; i < d_data->splineSize; i++) { + splinePointsY[size - 1].x() / ( d_data->splineSize - 1 ); + for ( i = 0; i < d_data->splineSize; i++ ) + { const double dtmp = i * deltaY; - fittedPoints[i].setY(qRound(d_data->spline.value(dtmp))); + fittedPoints[i].setY( d_data->spline.value( dtmp ) ); } return fittedPoints; } + +class QwtWeedingCurveFitter::PrivateData +{ +public: + PrivateData(): + tolerance( 1.0 ), + chunkSize( 0 ) + { + } + + double tolerance; + uint chunkSize; +}; + +class QwtWeedingCurveFitter::Line +{ +public: + Line( int i1 = 0, int i2 = 0 ): + from( i1 ), + to( i2 ) + { + } + + int from; + int to; +}; + +/*! + Constructor + + \param tolerance Tolerance + \sa setTolerance(), tolerance() +*/ +QwtWeedingCurveFitter::QwtWeedingCurveFitter( double tolerance ) +{ + d_data = new PrivateData; + setTolerance( tolerance ); +} + +//! Destructor +QwtWeedingCurveFitter::~QwtWeedingCurveFitter() +{ + delete d_data; +} + +/*! + Assign the tolerance + + The tolerance is the maximum distance, that is acceptable + between the original curve and the smoothed curve. + + Increasing the tolerance will reduce the number of the + resulting points. + + \param tolerance Tolerance + + \sa tolerance() +*/ +void QwtWeedingCurveFitter::setTolerance( double tolerance ) +{ + d_data->tolerance = qMax( tolerance, 0.0 ); +} + +/*! + \return Tolerance + \sa setTolerance() +*/ +double QwtWeedingCurveFitter::tolerance() const +{ + return d_data->tolerance; +} + +/*! + Limit the number of points passed to a run of the algorithm + + The runtime of the Douglas Peucker algorithm increases non linear + with the number of points. For a chunk size > 0 the polygon + is split into pieces passed to the algorithm one by one. + + \param numPoints Maximum for the number of points passed to the algorithm + + \sa chunkSize() +*/ +void QwtWeedingCurveFitter::setChunkSize( uint numPoints ) +{ + if ( numPoints > 0 ) + numPoints = qMax( numPoints, 3U ); + + d_data->chunkSize = numPoints; +} + +/*! + + \return Maximum for the number of points passed to a run + of the algorithm - or 0, when unlimited + \sa setChunkSize() +*/ +uint QwtWeedingCurveFitter::chunkSize() const +{ + return d_data->chunkSize; +} + +/*! + \param points Series of data points + \return Curve points +*/ +QPolygonF QwtWeedingCurveFitter::fitCurve( const QPolygonF &points ) const +{ + QPolygonF fittedPoints; + + if ( d_data->chunkSize == 0 ) + { + fittedPoints = simplify( points ); + } + else + { + for ( int i = 0; i < points.size(); i += d_data->chunkSize ) + { + const QPolygonF p = points.mid( i, d_data->chunkSize ); + fittedPoints += simplify( p ); + } + } + + return fittedPoints; +} + +QPolygonF QwtWeedingCurveFitter::simplify( const QPolygonF &points ) const +{ + const double toleranceSqr = d_data->tolerance * d_data->tolerance; + + QStack stack; + stack.reserve( 500 ); + + const QPointF *p = points.data(); + const int nPoints = points.size(); + + QVector usePoint( nPoints, false ); + + stack.push( Line( 0, nPoints - 1 ) ); + + while ( !stack.isEmpty() ) + { + const Line r = stack.pop(); + + // initialize line segment + const double vecX = p[r.to].x() - p[r.from].x(); + const double vecY = p[r.to].y() - p[r.from].y(); + + const double vecLength = qSqrt( vecX * vecX + vecY * vecY ); + + const double unitVecX = ( vecLength != 0.0 ) ? vecX / vecLength : 0.0; + const double unitVecY = ( vecLength != 0.0 ) ? vecY / vecLength : 0.0; + + double maxDistSqr = 0.0; + int nVertexIndexMaxDistance = r.from + 1; + for ( int i = r.from + 1; i < r.to; i++ ) + { + //compare to anchor + const double fromVecX = p[i].x() - p[r.from].x(); + const double fromVecY = p[i].y() - p[r.from].y(); + + double distToSegmentSqr; + if ( fromVecX * unitVecX + fromVecY * unitVecY < 0.0 ) + { + distToSegmentSqr = fromVecX * fromVecX + fromVecY * fromVecY; + } + else + { + const double toVecX = p[i].x() - p[r.to].x(); + const double toVecY = p[i].y() - p[r.to].y(); + const double toVecLength = toVecX * toVecX + toVecY * toVecY; + + const double s = toVecX * ( -unitVecX ) + toVecY * ( -unitVecY ); + if ( s < 0.0 ) + { + distToSegmentSqr = toVecLength; + } + else + { + distToSegmentSqr = qFabs( toVecLength - s * s ); + } + } + + if ( maxDistSqr < distToSegmentSqr ) + { + maxDistSqr = distToSegmentSqr; + nVertexIndexMaxDistance = i; + } + } + if ( maxDistSqr <= toleranceSqr ) + { + usePoint[r.from] = true; + usePoint[r.to] = true; + } + else + { + stack.push( Line( r.from, nVertexIndexMaxDistance ) ); + stack.push( Line( nVertexIndexMaxDistance, r.to ) ); + } + } + + QPolygonF stripped; + for ( int i = 0; i < nPoints; i++ ) + { + if ( usePoint[i] ) + stripped += p[i]; + } + + return stripped; +} diff --git a/libs/qwt/qwt_curve_fitter.h b/libs/qwt/qwt_curve_fitter.h index 1ed31e73e..eac376a34 100644 --- a/libs/qwt/qwt_curve_fitter.h +++ b/libs/qwt/qwt_curve_fitter.h @@ -11,31 +11,11 @@ #define QWT_CURVE_FITTER_H #include "qwt_global.h" -#include "qwt_double_rect.h" +#include +#include class QwtSpline; -#if QT_VERSION >= 0x040000 -#include -#else -#include "qwt_array.h" -#endif - -// MOC_SKIP_BEGIN - -#if defined(QWT_TEMPLATEDLL) - -#if QT_VERSION < 0x040000 -#ifndef QWTARRAY_TEMPLATE_QWTDOUBLEPOINT // by mjo3 -#define QWTARRAY_TEMPLATE_QWTDOUBLEPOINT -template class QWT_EXPORT QwtArray; -#endif //end of QWTARRAY_TEMPLATE_QWTDOUBLEPOINT -#endif - -#endif - -// MOC_SKIP_END - /*! \brief Abstract base class for a curve fitter */ @@ -44,12 +24,13 @@ class QWT_EXPORT QwtCurveFitter public: virtual ~QwtCurveFitter(); -#if QT_VERSION < 0x040000 - virtual QwtArray fitCurve( - const QwtArray&) const = 0; -#else - virtual QPolygonF fitCurve(const QPolygonF &) const = 0; -#endif + /*! + Find a curve which has the best fit to a series of data points + + \param polygon Series of data points + \return Curve points + */ + virtual QPolygonF fitCurve( const QPolygonF &polygon ) const = 0; protected: QwtCurveFitter(); @@ -65,42 +46,91 @@ private: class QWT_EXPORT QwtSplineCurveFitter: public QwtCurveFitter { public: - enum FitMode { + /*! + Spline type + The default setting is Auto + \sa setFitMode(), FitMode() + */ + enum FitMode + { + /*! + Use the default spline algorithm for polygons with + increasing x values ( p[i-1] < p[i] ), otherwise use + a parametric spline algorithm. + */ Auto, + + //! Use a default spline algorithm Spline, + + //! Use a parametric spline algorithm ParametricSpline }; QwtSplineCurveFitter(); virtual ~QwtSplineCurveFitter(); - void setFitMode(FitMode); + void setFitMode( FitMode ); FitMode fitMode() const; - void setSpline(const QwtSpline&); + void setSpline( const QwtSpline& ); const QwtSpline &spline() const; QwtSpline &spline(); - void setSplineSize(int size); + void setSplineSize( int size ); int splineSize() const; -#if QT_VERSION < 0x040000 - virtual QwtArray fitCurve( - const QwtArray &) const; -#else - virtual QPolygonF fitCurve(const QPolygonF &) const; -#endif + virtual QPolygonF fitCurve( const QPolygonF & ) const; private: -#if QT_VERSION < 0x040000 - QwtArray fitSpline( - const QwtArray &) const; - QwtArray fitParametric( - const QwtArray &) const; -#else - QPolygonF fitSpline(const QPolygonF &) const; - QPolygonF fitParametric(const QPolygonF &) const; -#endif + QPolygonF fitSpline( const QPolygonF & ) const; + QPolygonF fitParametric( const QPolygonF & ) const; + + class PrivateData; + PrivateData *d_data; +}; + +/*! + \brief A curve fitter implementing Douglas and Peucker algorithm + + The purpose of the Douglas and Peucker algorithm is that given a 'curve' + composed of line segments to find a curve not too dissimilar but that + has fewer points. The algorithm defines 'too dissimilar' based on the + maximum distance (tolerance) between the original curve and the + smoothed curve. + + The runtime of the algorithm increases non linear ( worst case O( n*n ) ) + and might be very slow for huge polygons. To avoid performance issues + it might be useful to split the polygon ( setChunkSize() ) and to run the algorithm + for these smaller parts. The disadvantage of having no interpolation + at the borders is for most use cases irrelevant. + + The smoothed curve consists of a subset of the points that defined the + original curve. + + In opposite to QwtSplineCurveFitter the Douglas and Peucker algorithm reduces + the number of points. By adjusting the tolerance parameter according to the + axis scales QwtSplineCurveFitter can be used to implement different + level of details to speed up painting of curves of many points. +*/ +class QWT_EXPORT QwtWeedingCurveFitter: public QwtCurveFitter +{ +public: + QwtWeedingCurveFitter( double tolerance = 1.0 ); + virtual ~QwtWeedingCurveFitter(); + + void setTolerance( double ); + double tolerance() const; + + void setChunkSize( uint ); + uint chunkSize() const; + + virtual QPolygonF fitCurve( const QPolygonF & ) const; + +private: + virtual QPolygonF simplify( const QPolygonF & ) const; + + class Line; class PrivateData; PrivateData *d_data; diff --git a/libs/qwt/qwt_date.cpp b/libs/qwt/qwt_date.cpp new file mode 100644 index 000000000..0cf5ca0d2 --- /dev/null +++ b/libs/qwt/qwt_date.cpp @@ -0,0 +1,654 @@ +#include "qwt_date.h" +#include +#include +#include +#include +#include + +#if QT_VERSION >= 0x050000 + +typedef qint64 QwtJulianDay; +static const QwtJulianDay minJulianDayD = Q_INT64_C( -784350574879 ); +static const QwtJulianDay maxJulianDayD = Q_INT64_C( 784354017364 ); + +#else + +// QDate stores the Julian day as unsigned int, but +// but it is QDate::fromJulianDay( int ). That's why +// we have the range [ 1, INT_MAX ] +typedef int QwtJulianDay; +static const QwtJulianDay minJulianDayD = 1; +static const QwtJulianDay maxJulianDayD = std::numeric_limits::max(); + +#endif + +static inline Qt::DayOfWeek qwtFirstDayOfWeek() +{ +#if QT_VERSION >= 0x040800 + return QLocale().firstDayOfWeek(); +#else + + switch( QLocale().country() ) + { + case QLocale::Maldives: + return Qt::Friday; + + case QLocale::Afghanistan: + case QLocale::Algeria: + case QLocale::Bahrain: + case QLocale::Djibouti: + case QLocale::Egypt: + case QLocale::Eritrea: + case QLocale::Ethiopia: + case QLocale::Iran: + case QLocale::Iraq: + case QLocale::Jordan: + case QLocale::Kenya: + case QLocale::Kuwait: + case QLocale::LibyanArabJamahiriya: + case QLocale::Morocco: + case QLocale::Oman: + case QLocale::Qatar: + case QLocale::SaudiArabia: + case QLocale::Somalia: + case QLocale::Sudan: + case QLocale::Tunisia: + case QLocale::Yemen: + return Qt::Saturday; + + case QLocale::AmericanSamoa: + case QLocale::Argentina: + case QLocale::Azerbaijan: + case QLocale::Botswana: + case QLocale::Canada: + case QLocale::China: + case QLocale::FaroeIslands: + case QLocale::Georgia: + case QLocale::Greenland: + case QLocale::Guam: + case QLocale::HongKong: + case QLocale::Iceland: + case QLocale::India: + case QLocale::Ireland: + case QLocale::Israel: + case QLocale::Jamaica: + case QLocale::Japan: + case QLocale::Kyrgyzstan: + case QLocale::Lao: + case QLocale::Malta: + case QLocale::MarshallIslands: + case QLocale::Macau: + case QLocale::Mongolia: + case QLocale::NewZealand: + case QLocale::NorthernMarianaIslands: + case QLocale::Pakistan: + case QLocale::Philippines: + case QLocale::RepublicOfKorea: + case QLocale::Singapore: + case QLocale::SyrianArabRepublic: + case QLocale::Taiwan: + case QLocale::Thailand: + case QLocale::TrinidadAndTobago: + case QLocale::UnitedStates: + case QLocale::UnitedStatesMinorOutlyingIslands: + case QLocale::USVirginIslands: + case QLocale::Uzbekistan: + case QLocale::Zimbabwe: + return Qt::Sunday; + + default: + return Qt::Monday; + } +#endif +} + +static inline void qwtFloorTime( + QwtDate::IntervalType intervalType, QDateTime &dt ) +{ + // when dt is inside the special hour where DST is ending + // an hour is no unique. Therefore we have to + // use UTC time. + + const Qt::TimeSpec timeSpec = dt.timeSpec(); + + if ( timeSpec == Qt::LocalTime ) + dt = dt.toTimeSpec( Qt::UTC ); + + const QTime t = dt.time(); + switch( intervalType ) + { + case QwtDate::Second: + { + dt.setTime( QTime( t.hour(), t.minute(), t.second() ) ); + break; + } + case QwtDate::Minute: + { + dt.setTime( QTime( t.hour(), t.minute(), 0 ) ); + break; + } + case QwtDate::Hour: + { + dt.setTime( QTime( t.hour(), 0, 0 ) ); + break; + } + default: + break; + } + + if ( timeSpec == Qt::LocalTime ) + dt = dt.toTimeSpec( Qt::LocalTime ); +} + +static inline QDateTime qwtToTimeSpec( + const QDateTime &dt, Qt::TimeSpec spec ) +{ + if ( dt.timeSpec() == spec ) + return dt; + + const qint64 jd = dt.date().toJulianDay(); + if ( jd < 0 || jd >= INT_MAX ) + { + // the conversion between local time and UTC + // is internally limited. To avoid + // overflows we simply ignore the difference + // for those dates + + QDateTime dt2 = dt; + dt2.setTimeSpec( spec ); + return dt2; + } + + return dt.toTimeSpec( spec ); +} + +static inline double qwtToJulianDay( int year, int month, int day ) +{ + // code from QDate but using doubles to avoid overflows + // for large values + + const int m1 = ( month - 14 ) / 12; + const int m2 = ( 367 * ( month - 2 - 12 * m1 ) ) / 12; + const double y1 = ::floor( ( 4900.0 + year + m1 ) / 100 ); + + return ::floor( ( 1461.0 * ( year + 4800 + m1 ) ) / 4 ) + m2 + - ::floor( ( 3 * y1 ) / 4 ) + day - 32075; +} + +static inline qint64 qwtFloorDiv64( qint64 a, int b ) +{ + if ( a < 0 ) + a -= b - 1; + + return a / b; +} + +static inline qint64 qwtFloorDiv( int a, int b ) +{ + if ( a < 0 ) + a -= b - 1; + + return a / b; +} + +static inline QDate qwtToDate( int year, int month = 1, int day = 1 ) +{ +#if QT_VERSION >= 0x050000 + return QDate( year, month, day ); +#else + if ( year > 100000 ) + { + // code from QDate but using doubles to avoid overflows + // for large values + + const int m1 = ( month - 14 ) / 12; + const int m2 = ( 367 * ( month - 2 - 12 * m1 ) ) / 12; + const double y1 = ::floor( ( 4900.0 + year + m1 ) / 100 ); + + const double jd = ::floor( ( 1461.0 * ( year + 4800 + m1 ) ) / 4 ) + m2 + - ::floor( ( 3 * y1 ) / 4 ) + day - 32075; + + if ( jd > maxJulianDayD ) + { + qWarning() << "qwtToDate: overflow"; + return QDate(); + } + + return QDate::fromJulianDay( static_cast( jd ) ); + } + else + { + return QDate( year, month, day ); + } +#endif +} + +/*! + Translate from double to QDateTime + + \param value Number of milliseconds since the epoch, + 1970-01-01T00:00:00 UTC + \param timeSpec Time specification + \return Datetime value + + \sa toDouble(), QDateTime::setMSecsSinceEpoch() + \note The return datetime for Qt::OffsetFromUTC will be Qt::UTC + */ +QDateTime QwtDate::toDateTime( double value, Qt::TimeSpec timeSpec ) +{ + const int msecsPerDay = 86400000; + + const double days = static_cast( ::floor( value / msecsPerDay ) ); + + const double jd = QwtDate::JulianDayForEpoch + days; + if ( ( jd > maxJulianDayD ) || ( jd < minJulianDayD ) ) + { + qWarning() << "QwtDate::toDateTime: overflow"; + return QDateTime(); + } + + const QDate d = QDate::fromJulianDay( static_cast( jd ) ); + + const int msecs = static_cast( value - days * msecsPerDay ); + + static const QTime timeNull( 0, 0, 0, 0 ); + + QDateTime dt( d, timeNull.addMSecs( msecs ), Qt::UTC ); + + if ( timeSpec == Qt::LocalTime ) + dt = qwtToTimeSpec( dt, timeSpec ); + + return dt; +} + +/*! + Translate from QDateTime to double + + \param dateTime Datetime value + \return Number of milliseconds since 1970-01-01T00:00:00 UTC has passed. + + \sa toDateTime(), QDateTime::toMSecsSinceEpoch() + \warning For values very far below or above 1970-01-01 UTC rounding errors + will happen due to the limited significance of a double. + */ +double QwtDate::toDouble( const QDateTime &dateTime ) +{ + const int msecsPerDay = 86400000; + + const QDateTime dt = qwtToTimeSpec( dateTime, Qt::UTC ); + + const double days = dt.date().toJulianDay() - QwtDate::JulianDayForEpoch; + + const QTime time = dt.time(); + const double secs = 3600.0 * time.hour() + + 60.0 * time.minute() + time.second(); + + return days * msecsPerDay + time.msec() + 1000.0 * secs; +} + +/*! + Ceil a datetime according the interval type + + \param dateTime Datetime value + \param intervalType Interval type, how to ceil. + F.e. when intervalType = QwtDate::Months, the result + will be ceiled to the next beginning of a month + \return Ceiled datetime + \sa floor() + */ +QDateTime QwtDate::ceil( const QDateTime &dateTime, IntervalType intervalType ) +{ + if ( dateTime.date() >= QwtDate::maxDate() ) + return dateTime; + + QDateTime dt = dateTime; + + switch ( intervalType ) + { + case QwtDate::Millisecond: + { + break; + } + case QwtDate::Second: + { + qwtFloorTime( QwtDate::Second, dt ); + if ( dt < dateTime ) + dt.addSecs( 1 ); + + break; + } + case QwtDate::Minute: + { + qwtFloorTime( QwtDate::Minute, dt ); + if ( dt < dateTime ) + dt.addSecs( 60 ); + + break; + } + case QwtDate::Hour: + { + qwtFloorTime( QwtDate::Hour, dt ); + if ( dt < dateTime ) + dt.addSecs( 3600 ); + + break; + } + case QwtDate::Day: + { + dt.setTime( QTime( 0, 0 ) ); + if ( dt < dateTime ) + dt = dt.addDays( 1 ); + + break; + } + case QwtDate::Week: + { + dt.setTime( QTime( 0, 0 ) ); + if ( dt < dateTime ) + dt = dt.addDays( 1 ); + + int days = qwtFirstDayOfWeek() - dt.date().dayOfWeek(); + if ( days < 0 ) + days += 7; + + dt = dt.addDays( days ); + + break; + } + case QwtDate::Month: + { + dt.setTime( QTime( 0, 0 ) ); + dt.setDate( qwtToDate( dateTime.date().year(), + dateTime.date().month() ) ); + + if ( dt < dateTime ) + dt.addMonths( 1 ); + + break; + } + case QwtDate::Year: + { + dt.setTime( QTime( 0, 0 ) ); + + const QDate d = dateTime.date(); + + int year = d.year(); + if ( d.month() > 1 || d.day() > 1 || !dateTime.time().isNull() ) + year++; + + if ( year == 0 ) + year++; // there is no year 0 + + dt.setDate( qwtToDate( year ) ); + break; + } + } + + return dt; +} + +/*! + Floor a datetime according the interval type + + \param dateTime Datetime value + \param intervalType Interval type, how to ceil. + F.e. when intervalType = QwtDate::Months, + the result will be ceiled to the next + beginning of a month + \return Floored datetime + \sa floor() + */ +QDateTime QwtDate::floor( const QDateTime &dateTime, + IntervalType intervalType ) +{ + if ( dateTime.date() <= QwtDate::minDate() ) + return dateTime; + + QDateTime dt = dateTime; + + switch ( intervalType ) + { + case QwtDate::Millisecond: + { + break; + } + case QwtDate::Second: + case QwtDate::Minute: + case QwtDate::Hour: + { + qwtFloorTime( intervalType, dt ); + break; + } + case QwtDate::Day: + { + dt.setTime( QTime( 0, 0 ) ); + break; + } + case QwtDate::Week: + { + dt.setTime( QTime( 0, 0 ) ); + + int days = dt.date().dayOfWeek() - qwtFirstDayOfWeek(); + if ( days < 0 ) + days += 7; + + dt = dt.addDays( -days ); + + break; + } + case QwtDate::Month: + { + dt.setTime( QTime( 0, 0 ) ); + + const QDate date = qwtToDate( dt.date().year(), + dt.date().month() ); + dt.setDate( date ); + + break; + } + case QwtDate::Year: + { + dt.setTime( QTime( 0, 0 ) ); + + const QDate date = qwtToDate( dt.date().year() ); + dt.setDate( date ); + + break; + } + } + + return dt; +} + +/*! + Minimum for the supported date range + + The range of valid dates depends on how QDate stores the + Julian day internally. + + - For Qt4 it is "Tue Jan 2 -4713" + - For Qt5 it is "Thu Jan 1 -2147483648" + + \return minimum of the date range + \sa maxDate() + */ +QDate QwtDate::minDate() +{ + static QDate date; + if ( !date.isValid() ) + date = QDate::fromJulianDay( minJulianDayD ); + + return date; +} + +/*! + Maximum for the supported date range + + The range of valid dates depends on how QDate stores the + Julian day internally. + + - For Qt4 it is "Tue Jun 3 5874898" + - For Qt5 it is "Tue Dec 31 2147483647" + + \return maximum of the date range + \sa minDate() + \note The maximum differs between Qt4 and Qt5 + */ +QDate QwtDate::maxDate() +{ + static QDate date; + if ( !date.isValid() ) + date = QDate::fromJulianDay( maxJulianDayD ); + + return date; +} + +/*! + \brief Date of the first day of the first week for a year + + The first day of a week depends on the current locale + ( QLocale::firstDayOfWeek() ). + + \param year Year + \param type Option how to identify the first week + \return First day of week 0 + + \sa QLocale::firstDayOfWeek(), weekNumber() + */ +QDate QwtDate::dateOfWeek0( int year, Week0Type type ) +{ + const Qt::DayOfWeek firstDayOfWeek = qwtFirstDayOfWeek(); + + QDate dt0( year, 1, 1 ); + + // floor to the first day of the week + int days = dt0.dayOfWeek() - firstDayOfWeek; + if ( days < 0 ) + days += 7; + + dt0 = dt0.addDays( -days ); + + if ( type == QwtDate::FirstThursday ) + { + // according to ISO 8601 the first week is defined + // by the first thursday. + + int d = Qt::Thursday - firstDayOfWeek; + if ( d < 0 ) + d += 7; + + if ( dt0.addDays( d ).year() < year ) + dt0 = dt0.addDays( 7 ); + } + + return dt0; +} + +/*! + Find the week number of a date + + - QwtDate::FirstThursday\n + Corresponding to ISO 8601 ( see QDate::weekNumber() ). + + - QwtDate::FirstDay\n + Number of weeks that have begun since dateOfWeek0(). + + \param date Date + \param type Option how to identify the first week + + \return Week number, starting with 1 + */ +int QwtDate::weekNumber( const QDate &date, Week0Type type ) +{ + int weekNo; + + if ( type == QwtDate::FirstDay ) + { + const QDate day0 = dateOfWeek0( date.year(), type ); + weekNo = day0.daysTo( date ) / 7 + 1; + } + else + { + weekNo = date.weekNumber(); + } + + return weekNo; +} + +/*! + Offset in seconds from Coordinated Universal Time + + The offset depends on the time specification of dateTime: + + - Qt::UTC + 0, dateTime has no offset + - Qt::OffsetFromUTC + returns dateTime.utcOffset() + - Qt::LocalTime: + number of seconds from the UTC + + For Qt::LocalTime the offset depends on the timezone and + daylight savings. + + \param dateTime Datetime value + \return Offset in seconds + */ +int QwtDate::utcOffset( const QDateTime &dateTime ) +{ + int seconds = 0; + + switch( dateTime.timeSpec() ) + { + case Qt::UTC: + { + break; + } + case Qt::OffsetFromUTC: + { + seconds = dateTime.utcOffset(); + } + default: + { + const QDateTime dt1( dateTime.date(), dateTime.time(), Qt::UTC ); + seconds = dateTime.secsTo( dt1 ); + } + } + + return seconds; +} + +/*! + Translate a datetime into a string + + Beside the format expressions documented in QDateTime::toString() + the following expressions are supported: + + - w\n + week number: ( 1 - 53 ) + - ww\n + week number with a leading zero ( 01 - 53 ) + + \param dateTime Datetime value + \param format Format string + \param week0Type Specification of week 0 + + \return Datetime string + \sa QDateTime::toString(), weekNumber(), QwtDateScaleDraw + */ +QString QwtDate::toString( const QDateTime &dateTime, + const QString & format, Week0Type week0Type ) +{ + QString weekNo; + weekNo.setNum( QwtDate::weekNumber( dateTime.date(), week0Type ) ); + + QString weekNoWW; + if ( weekNo.length() == 1 ) + weekNoWW += "0"; + weekNoWW += weekNo; + + QString fmt = format; + fmt.replace( "ww", weekNoWW ); + fmt.replace( "w", weekNo ); + + return dateTime.toString( fmt ); +} diff --git a/libs/qwt/qwt_date.h b/libs/qwt/qwt_date.h new file mode 100644 index 000000000..30422a1c1 --- /dev/null +++ b/libs/qwt/qwt_date.h @@ -0,0 +1,128 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef _QWT_DATE_H_ +#define _QWT_DATE_H_ + +#include "qwt_global.h" +#include + +/*! + \brief A collection of methods around date/time values + + Qt offers convenient classes for dealing with date/time values, + but Qwt uses coordinate systems that are based on doubles. + QwtDate offers methods to translate from QDateTime to double and v.v. + + A double is interpreted as the number of milliseconds since + 1970-01-01T00:00:00 Universal Coordinated Time - also known + as "The Epoch". + + While the range of the Julian day in Qt4 is limited to [0, MAX_INT], + Qt5 stores it as qint64 offering a huge range of valid dates. + As the significance of a double is below this ( assuming a + fraction of 52 bits ) the translation is not + bijective with rounding errors for dates very far from Epoch. + For a resolution of 1 ms those start to happen for dates above the + year 144683. + + An axis for a date/time interval is expected to be aligned + and divided in time/date units like seconds, minutes, ... + QwtDate offers several algorithms that are needed to + calculate these axes. + + \sa QwtDateScaleEngine, QwtDateScaleDraw, QDate, QTime +*/ +class QWT_EXPORT QwtDate +{ +public: + /*! + How to identify the first week of year differs between + countries. + */ + enum Week0Type + { + /*! + According to ISO 8601 the first week of a year is defined + as "the week with the year's first Thursday in it". + + FirstThursday corresponds to the numbering that is + implemented in QDate::weekNumber(). + */ + FirstThursday, + + /*! + "The week with January 1.1 in it." + + In the U.S. this definition is more common than + FirstThursday. + */ + FirstDay + }; + + /*! + Classification of an time interval + + Time intervals needs to be classified to decide how to + align and divide it. + */ + enum IntervalType + { + //! The interval is related to milliseconds + Millisecond, + + //! The interval is related to seconds + Second, + + //! The interval is related to minutes + Minute, + + //! The interval is related to hours + Hour, + + //! The interval is related to days + Day, + + //! The interval is related to weeks + Week, + + //! The interval is related to months + Month, + + //! The interval is related to years + Year + }; + + enum + { + //! The Julian day of "The Epoch" + JulianDayForEpoch = 2440588 + }; + + static QDate minDate(); + static QDate maxDate(); + + static QDateTime toDateTime( double value, + Qt::TimeSpec = Qt::UTC ); + + static double toDouble( const QDateTime & ); + + static QDateTime ceil( const QDateTime &, IntervalType ); + static QDateTime floor( const QDateTime &, IntervalType ); + + static QDate dateOfWeek0( int year, Week0Type ); + static int weekNumber( const QDate &, Week0Type ); + + static int utcOffset( const QDateTime & ); + + static QString toString( const QDateTime &, + const QString & format, Week0Type ); +}; + +#endif diff --git a/libs/qwt/qwt_date_scale_draw.cpp b/libs/qwt/qwt_date_scale_draw.cpp new file mode 100644 index 000000000..7cfc6dec0 --- /dev/null +++ b/libs/qwt/qwt_date_scale_draw.cpp @@ -0,0 +1,269 @@ +#include "qwt_date_scale_draw.h" + +class QwtDateScaleDraw::PrivateData +{ +public: + PrivateData( Qt::TimeSpec spec ): + timeSpec( spec ), + utcOffset( 0 ), + week0Type( QwtDate::FirstThursday ) + { + dateFormats[ QwtDate::Millisecond ] = "hh:mm:ss:zzz\nddd dd MMM yyyy"; + dateFormats[ QwtDate::Second ] = "hh:mm:ss\nddd dd MMM yyyy"; + dateFormats[ QwtDate::Minute ] = "hh:mm\nddd dd MMM yyyy"; + dateFormats[ QwtDate::Hour ] = "hh:mm\nddd dd MMM yyyy"; + dateFormats[ QwtDate::Day ] = "ddd dd MMM yyyy"; + dateFormats[ QwtDate::Week ] = "Www yyyy"; + dateFormats[ QwtDate::Month ] = "MMM yyyy"; + dateFormats[ QwtDate::Year ] = "yyyy"; + } + + Qt::TimeSpec timeSpec; + int utcOffset; + QwtDate::Week0Type week0Type; + QString dateFormats[ QwtDate::Year + 1 ]; +}; + +/*! + \brief Constructor + + The default setting is to display tick labels for the + given time specification. The first week of a year is defined like + for QwtDate::FirstThursday. + + \param timeSpec Time specification + + \sa setTimeSpec(), setWeek0Type() + */ +QwtDateScaleDraw::QwtDateScaleDraw( Qt::TimeSpec timeSpec ) +{ + d_data = new PrivateData( timeSpec ); +} + +//! Destructor +QwtDateScaleDraw::~QwtDateScaleDraw() +{ + delete d_data; +} + +/*! + Set the time specification used for the tick labels + + \param timeSpec Time specification + \sa timeSpec(), setUtcOffset(), toDateTime() + */ +void QwtDateScaleDraw::setTimeSpec( Qt::TimeSpec timeSpec ) +{ + d_data->timeSpec = timeSpec; +} + +/*! + \return Time specification used for the tick labels + \sa setTimeSpec(), utcOffset(), toDateTime() + */ +Qt::TimeSpec QwtDateScaleDraw::timeSpec() const +{ + return d_data->timeSpec; +} + +/*! + Set the offset in seconds from Coordinated Universal Time + + \param seconds Offset in seconds + + \note The offset has no effect beside for the time specification + Qt::OffsetFromUTC. + + \sa QDate::utcOffset(), setTimeSpec(), toDateTime() + */ +void QwtDateScaleDraw::setUtcOffset( int seconds ) +{ + d_data->utcOffset = seconds; +} + +/*! + \return Offset in seconds from Coordinated Universal Time + \note The offset has no effect beside for the time specification + Qt::OffsetFromUTC. + + \sa QDate::setUtcOffset(), setTimeSpec(), toDateTime() + */ +int QwtDateScaleDraw::utcOffset() const +{ + return d_data->utcOffset; +} + +/*! + Sets how to identify the first week of a year. + + \param week0Type Mode how to identify the first week of a year + + \sa week0Type(). + \note week0Type has no effect beside for intervals classified as + QwtDate::Week. + */ +void QwtDateScaleDraw::setWeek0Type( QwtDate::Week0Type week0Type ) +{ + d_data->week0Type = week0Type; +} + +/*! + \return Setting how to identify the first week of a year. + \sa setWeek0Type() + */ +QwtDate::Week0Type QwtDateScaleDraw::week0Type() const +{ + return d_data->week0Type; +} + +/*! + Set the default format string for an datetime interval type + + \param intervalType Interval type + \param format Default format string + + \sa dateFormat(), dateFormatOfDate(), QwtDate::toString() + */ +void QwtDateScaleDraw::setDateFormat( + QwtDate::IntervalType intervalType, const QString &format ) +{ + if ( intervalType >= QwtDate::Millisecond && + intervalType <= QwtDate::Year ) + { + d_data->dateFormats[ intervalType ] = format; + } +} + +/*! + \param intervalType Interval type + \return Default format string for an datetime interval type + \sa setDateFormat(), dateFormatOfDate() + */ +QString QwtDateScaleDraw::dateFormat( + QwtDate::IntervalType intervalType ) const +{ + if ( intervalType >= QwtDate::Millisecond && + intervalType <= QwtDate::Year ) + { + return d_data->dateFormats[ intervalType ]; + } + + return QString::null; +} + +/*! + Format string for the representation of a datetime + + dateFormatOfDate() is intended to be overloaded for + situations, where formats are individual for specific + datetime values. + + The default setting ignores dateTime and return + the default format for the interval type. + + \param dateTime Datetime value + \param intervalType Interval type + \return Format string + + \sa setDateFormat(), QwtDate::toString() + */ +QString QwtDateScaleDraw::dateFormatOfDate( const QDateTime &dateTime, + QwtDate::IntervalType intervalType ) const +{ + Q_UNUSED( dateTime ) + + if ( intervalType >= QwtDate::Millisecond && + intervalType <= QwtDate::Year ) + { + return d_data->dateFormats[ intervalType ]; + } + + return d_data->dateFormats[ QwtDate::Second ]; +} + +/*! + \brief Convert a value into its representing label + + The value is converted to a datetime value using toDateTime() + and converted to a plain text using QwtDate::toString(). + + \param value Value + \return Label string. + + \sa dateFormatOfDate() +*/ +QwtText QwtDateScaleDraw::label( double value ) const +{ + const QDateTime dt = toDateTime( value ); + const QString fmt = dateFormatOfDate( + dt, intervalType( scaleDiv() ) ); + + return QwtDate::toString( dt, fmt, d_data->week0Type ); +} + +/*! + Find the less detailed datetime unit, where no rounding + errors happen. + + \param scaleDiv Scale division + \return Interval type + + \sa dateFormatOfDate() + */ +QwtDate::IntervalType QwtDateScaleDraw::intervalType( + const QwtScaleDiv &scaleDiv ) const +{ + int intvType = QwtDate::Year; + + bool alignedToWeeks = true; + + const QList ticks = scaleDiv.ticks( QwtScaleDiv::MajorTick ); + for ( int i = 0; i < ticks.size(); i++ ) + { + const QDateTime dt = toDateTime( ticks[i] ); + for ( int j = QwtDate::Second; j <= intvType; j++ ) + { + const QDateTime dt0 = QwtDate::floor( dt, + static_cast( j ) ); + + if ( dt0 != dt ) + { + if ( j == QwtDate::Week ) + { + alignedToWeeks = false; + } + else + { + intvType = j - 1; + break; + } + } + } + + if ( intvType == QwtDate::Millisecond ) + break; + } + + if ( intvType == QwtDate::Week && !alignedToWeeks ) + intvType = QwtDate::Day; + + return static_cast( intvType ); +} + +/*! + Translate a double value into a QDateTime object. + + \return QDateTime object initialized with timeSpec() and utcOffset(). + \sa timeSpec(), utcOffset(), QwtDate::toDateTime() + */ +QDateTime QwtDateScaleDraw::toDateTime( double value ) const +{ + QDateTime dt = QwtDate::toDateTime( value, d_data->timeSpec ); + if ( d_data->timeSpec == Qt::OffsetFromUTC ) + { + dt = dt.addSecs( d_data->utcOffset ); + dt.setUtcOffset( d_data->utcOffset ); + } + + return dt; +} diff --git a/libs/qwt/qwt_date_scale_draw.h b/libs/qwt/qwt_date_scale_draw.h new file mode 100644 index 000000000..92589e890 --- /dev/null +++ b/libs/qwt/qwt_date_scale_draw.h @@ -0,0 +1,77 @@ +#ifndef _QWT_DATE_SCALE_DRAW_H_ +#define _QWT_DATE_SCALE_DRAW_H_ 1 + +#include "qwt_global.h" +#include "qwt_scale_draw.h" +#include "qwt_date.h" + +/*! + \brief A class for drawing datetime scales + + QwtDateScaleDraw displays values as datetime labels. + The format of the labels depends on the alignment of + the major tick labels. + + The default format strings are: + + - Millisecond\n + "hh:mm:ss:zzz\nddd dd MMM yyyy" + - Second\n + "hh:mm:ss\nddd dd MMM yyyy" + - Minute\n + "hh:mm\nddd dd MMM yyyy" + - Hour\n + "hh:mm\nddd dd MMM yyyy" + - Day\n + "ddd dd MMM yyyy" + - Week\n + "Www yyyy" + - Month\n + "MMM yyyy" + - Year\n + "yyyy" + + The format strings can be modified using setDateFormat() + or individually for each tick label by overloading dateFormatOfDate(), + + Usually QwtDateScaleDraw is used in combination with + QwtDateScaleEngine, that calculates scales for datetime + intervals. + + \sa QwtDateScaleEngine, QwtPlot::setAxisScaleDraw() +*/ +class QWT_EXPORT QwtDateScaleDraw: public QwtScaleDraw +{ +public: + QwtDateScaleDraw( Qt::TimeSpec = Qt::LocalTime ); + virtual ~QwtDateScaleDraw(); + + void setDateFormat( QwtDate::IntervalType, const QString & ); + QString dateFormat( QwtDate::IntervalType ) const; + + void setTimeSpec( Qt::TimeSpec ); + Qt::TimeSpec timeSpec() const; + + void setUtcOffset( int seconds ); + int utcOffset() const; + + void setWeek0Type( QwtDate::Week0Type ); + QwtDate::Week0Type week0Type() const; + + virtual QwtText label( double ) const; + + QDateTime toDateTime( double ) const; + +protected: + virtual QwtDate::IntervalType + intervalType( const QwtScaleDiv & ) const; + + virtual QString dateFormatOfDate( const QDateTime &, + QwtDate::IntervalType ) const; + +private: + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/libs/qwt/qwt_date_scale_engine.cpp b/libs/qwt/qwt_date_scale_engine.cpp new file mode 100644 index 000000000..85c1f175a --- /dev/null +++ b/libs/qwt/qwt_date_scale_engine.cpp @@ -0,0 +1,1300 @@ +#include "qwt_date_scale_engine.h" +#include "qwt_math.h" +#include "qwt_transform.h" +#include +#include + +static inline double qwtMsecsForType( QwtDate::IntervalType type ) +{ + static const double msecs[] = + { + 1.0, + 1000.0, + 60.0 * 1000.0, + 3600.0 * 1000.0, + 24.0 * 3600.0 * 1000.0, + 7.0 * 24.0 * 3600.0 * 1000.0, + 30.0 * 24.0 * 3600.0 * 1000.0, + 365.0 * 24.0 * 3600.0 * 1000.0, + }; + + if ( type < 0 || type >= static_cast( sizeof( msecs ) / sizeof( msecs[0] ) ) ) + return 1.0; + + return msecs[ type ]; +} + +static inline int qwtAlignValue( + double value, double stepSize, bool up ) +{ + double d = value / stepSize; + d = up ? ::ceil( d ) : ::floor( d ); + + return static_cast( d * stepSize ); +} + +static double qwtIntervalWidth( const QDateTime &minDate, + const QDateTime &maxDate, QwtDate::IntervalType intervalType ) +{ + switch( intervalType ) + { + case QwtDate::Millisecond: + { + const double secsTo = minDate.secsTo( maxDate ); + const double msecs = maxDate.time().msec() - + minDate.time().msec(); + + return secsTo * 1000 + msecs; + } + case QwtDate::Second: + { + return minDate.secsTo( maxDate ); + } + case QwtDate::Minute: + { + const double secsTo = minDate.secsTo( maxDate ); + return ::floor( secsTo / 60 ); + } + case QwtDate::Hour: + { + const double secsTo = minDate.secsTo( maxDate ); + return ::floor( secsTo / 3600 ); + } + case QwtDate::Day: + { + return minDate.daysTo( maxDate ); + } + case QwtDate::Week: + { + return ::floor( minDate.daysTo( maxDate ) / 7.0 ); + } + case QwtDate::Month: + { + const double years = + double( maxDate.date().year() ) - minDate.date().year(); + + int months = maxDate.date().month() - minDate.date().month(); + if ( maxDate.date().day() < minDate.date().day() ) + months--; + + return years * 12 + months; + } + case QwtDate::Year: + { + double years = + double( maxDate.date().year() ) - minDate.date().year(); + + if ( maxDate.date().month() < minDate.date().month() ) + years -= 1.0; + + return years; + } + } + + return 0.0; +} + +static double qwtRoundedIntervalWidth( + const QDateTime &minDate, const QDateTime &maxDate, + QwtDate::IntervalType intervalType ) +{ + const QDateTime minD = QwtDate::floor( minDate, intervalType ); + const QDateTime maxD = QwtDate::ceil( maxDate, intervalType ); + + return qwtIntervalWidth( minD, maxD, intervalType ); +} + +static inline int qwtStepCount( int intervalSize, int maxSteps, + const int limits[], size_t numLimits ) +{ + for ( uint i = 0; i < numLimits; i++ ) + { + const int numSteps = intervalSize / limits[ i ]; + + if ( numSteps > 1 && numSteps <= maxSteps && + numSteps * limits[ i ] == intervalSize ) + { + return numSteps; + } + } + + return 0; +} + +static int qwtStepSize( int intervalSize, int maxSteps, uint base ) +{ + if ( maxSteps <= 0 ) + return 0; + + if ( maxSteps > 2 ) + { + for ( int numSteps = maxSteps; numSteps > 1; numSteps-- ) + { + const double stepSize = double( intervalSize ) / numSteps; + + const double p = ::floor( ::log( stepSize ) / ::log( double( base ) ) ); + const double fraction = qPow( base, p ); + + for ( uint n = base; n >= 1; n /= 2 ) + { + if ( qFuzzyCompare( stepSize, n * fraction ) ) + return qRound( stepSize ); + + if ( n == 3 && ( base % 2 ) == 0 ) + { + if ( qFuzzyCompare( stepSize, 2 * fraction ) ) + return qRound( stepSize ); + } + } + } + } + + return 0; +} + +static int qwtDivideInterval( double intervalSize, int numSteps, + const int limits[], size_t numLimits ) +{ + const int v = qCeil( intervalSize / double( numSteps ) ); + + for ( uint i = 0; i < numLimits - 1; i++ ) + { + if ( v <= limits[i] ) + return limits[i]; + } + + return limits[ numLimits - 1 ]; +} + +static double qwtDivideScale( double intervalSize, int numSteps, + QwtDate::IntervalType intervalType ) +{ + if ( intervalType != QwtDate::Day ) + { + if ( ( intervalSize > numSteps ) && + ( intervalSize <= 2 * numSteps ) ) + { + return 2.0; + } + } + + double stepSize; + + switch( intervalType ) + { + case QwtDate::Second: + case QwtDate::Minute: + { + static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 }; + + stepSize = qwtDivideInterval( intervalSize, numSteps, + limits, sizeof( limits ) / sizeof( int ) ); + + break; + } + case QwtDate::Hour: + { + static int limits[] = { 1, 2, 3, 4, 6, 12, 24 }; + + stepSize = qwtDivideInterval( intervalSize, numSteps, + limits, sizeof( limits ) / sizeof( int ) ); + + break; + } + case QwtDate::Day: + { + const double v = intervalSize / double( numSteps ); + if ( v <= 5.0 ) + stepSize = qCeil( v ); + else + stepSize = qCeil( v / 7 ) * 7; + + break; + } + case QwtDate::Week: + { + static int limits[] = { 1, 2, 4, 8, 12, 26, 52 }; + + stepSize = qwtDivideInterval( intervalSize, numSteps, + limits, sizeof( limits ) / sizeof( int ) ); + + break; + } + case QwtDate::Month: + { + static int limits[] = { 1, 2, 3, 4, 6, 12 }; + + stepSize = qwtDivideInterval( intervalSize, numSteps, + limits, sizeof( limits ) / sizeof( int ) ); + + break; + } + case QwtDate::Year: + case QwtDate::Millisecond: + default: + { + stepSize = QwtScaleArithmetic::divideInterval( + intervalSize, numSteps, 10 ); + } + } + + return stepSize; +} + +static double qwtDivideMajorStep( double stepSize, int maxMinSteps, + QwtDate::IntervalType intervalType ) +{ + double minStepSize = 0.0; + + switch( intervalType ) + { + case QwtDate::Second: + { + minStepSize = qwtStepSize( stepSize, maxMinSteps, 10 ); + if ( minStepSize == 0.0 ) + minStepSize = 0.5 * stepSize; + + break; + } + case QwtDate::Minute: + { + static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 }; + + int numSteps; + + if ( stepSize > maxMinSteps ) + { + numSteps = qwtStepCount( stepSize, maxMinSteps, + limits, sizeof( limits ) / sizeof( int ) ); + + } + else + { + numSteps = qwtStepCount( stepSize * 60, maxMinSteps, + limits, sizeof( limits ) / sizeof( int ) ); + } + + if ( numSteps > 0 ) + minStepSize = double( stepSize ) / numSteps; + + break; + } + case QwtDate::Hour: + { + int numSteps = 0; + + if ( stepSize > maxMinSteps ) + { + static int limits[] = { 1, 2, 3, 4, 6, 12, 24, 48, 72 }; + + numSteps = qwtStepCount( stepSize, maxMinSteps, + limits, sizeof( limits ) / sizeof( int ) ); + } + else + { + static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 }; + + numSteps = qwtStepCount( stepSize * 60, maxMinSteps, + limits, sizeof( limits ) / sizeof( int ) ); + } + + if ( numSteps > 0 ) + minStepSize = double( stepSize ) / numSteps; + + break; + } + case QwtDate::Day: + { + int numSteps = 0; + + if ( stepSize > maxMinSteps ) + { + static int limits[] = { 1, 2, 3, 7, 14, 28 }; + + numSteps = qwtStepCount( stepSize, maxMinSteps, + limits, sizeof( limits ) / sizeof( int ) ); + } + else + { + static int limits[] = { 1, 2, 3, 4, 6, 12, 24, 48, 72 }; + + numSteps = qwtStepCount( stepSize * 24, maxMinSteps, + limits, sizeof( limits ) / sizeof( int ) ); + } + + if ( numSteps > 0 ) + minStepSize = double( stepSize ) / numSteps; + + break; + } + case QwtDate::Week: + { + const int daysInStep = stepSize * 7; + + if ( maxMinSteps >= daysInStep ) + { + // we want to have one tick per day + minStepSize = 1.0 / 7.0; + } + else + { + // when the stepSize is more than a week we want to + // have a tick for each week + + const int stepSizeInWeeks = stepSize; + + if ( stepSizeInWeeks <= maxMinSteps ) + { + minStepSize = 1; + } + else + { + minStepSize = QwtScaleArithmetic::divideInterval( + stepSizeInWeeks, maxMinSteps, 10 ); + } + } + break; + } + case QwtDate::Month: + { + // fractions of months doesn't make any sense + + if ( stepSize < maxMinSteps ) + maxMinSteps = static_cast( stepSize ); + + static int limits[] = { 1, 2, 3, 4, 6, 12 }; + + int numSteps = qwtStepCount( stepSize, maxMinSteps, + limits, sizeof( limits ) / sizeof( int ) ); + + if ( numSteps > 0 ) + minStepSize = double( stepSize ) / numSteps; + + break; + } + case QwtDate::Year: + { + if ( stepSize >= maxMinSteps ) + { + minStepSize = QwtScaleArithmetic::divideInterval( + stepSize, maxMinSteps, 10 ); + } + else + { + // something in months + + static int limits[] = { 1, 2, 3, 4, 6, 12 }; + + int numSteps = qwtStepCount( 12 * stepSize, maxMinSteps, + limits, sizeof( limits ) / sizeof( int ) ); + + if ( numSteps > 0 ) + minStepSize = double( stepSize ) / numSteps; + } + + break; + } + default: + break; + } + + if ( intervalType != QwtDate::Month + && minStepSize == 0.0 ) + { + minStepSize = 0.5 * stepSize; + } + + return minStepSize; +} + +static QList qwtDstTicks( const QDateTime &dateTime, + int secondsMajor, int secondsMinor ) +{ + if ( secondsMinor <= 0 ) + QList(); + + QDateTime minDate = dateTime.addSecs( -secondsMajor ); + minDate = QwtDate::floor( minDate, QwtDate::Hour ); + + const double utcOffset = QwtDate::utcOffset( dateTime ); + + // find the hours where daylight saving time happens + + double dstMin = QwtDate::toDouble( minDate ); + while ( minDate < dateTime && + QwtDate::utcOffset( minDate ) != utcOffset ) + { + minDate = minDate.addSecs( 3600 ); + dstMin += 3600 * 1000.0; + } + + QList ticks; + for ( int i = 0; i < 3600; i += secondsMinor ) + ticks += dstMin + i * 1000.0; + + return ticks; +} + +static QwtScaleDiv qwtDivideToSeconds( + const QDateTime &minDate, const QDateTime &maxDate, + double stepSize, int maxMinSteps, + QwtDate::IntervalType intervalType ) +{ + // calculate the min step size + double minStepSize = 0; + + if ( maxMinSteps > 1 ) + { + minStepSize = qwtDivideMajorStep( stepSize, + maxMinSteps, intervalType ); + } + + bool daylightSaving = false; + if ( minDate.timeSpec() == Qt::LocalTime ) + { + daylightSaving = intervalType > QwtDate::Hour; + if ( intervalType == QwtDate::Hour ) + { + daylightSaving = stepSize > 1; + } + } + + const double s = qwtMsecsForType( intervalType ) / 1000; + const int secondsMajor = static_cast( stepSize * s ); + const double secondsMinor = minStepSize * s; + + // UTC excludes daylight savings. So from the difference + // of a date and its UTC counterpart we can find out + // the daylight saving hours + + const double utcOffset = QwtDate::utcOffset( minDate ); + double dstOff = 0; + + QList majorTicks; + QList mediumTicks; + QList minorTicks; + + for ( QDateTime dt = minDate; dt <= maxDate; + dt = dt.addSecs( secondsMajor ) ) + { + if ( !dt.isValid() ) + break; + + double majorValue = QwtDate::toDouble( dt ); + + if ( daylightSaving ) + { + const double offset = utcOffset - QwtDate::utcOffset( dt ); + majorValue += offset * 1000.0; + + if ( offset > dstOff ) + { + // we add some minor ticks for the DST hour, + // otherwise the ticks will be unaligned: 0, 2, 3, 5 ... + minorTicks += qwtDstTicks( + dt, secondsMajor, qRound( secondsMinor ) ); + } + + dstOff = offset; + } + + if ( majorTicks.isEmpty() || majorTicks.last() != majorValue ) + majorTicks += majorValue; + + if ( secondsMinor > 0.0 ) + { + const int numMinorSteps = qFloor( secondsMajor / secondsMinor ); + + for ( int i = 1; i < numMinorSteps; i++ ) + { + const QDateTime mt = dt.addMSecs( + qRound64( i * secondsMinor * 1000 ) ); + + double minorValue = QwtDate::toDouble( mt ); + if ( daylightSaving ) + { + const double offset = utcOffset - QwtDate::utcOffset( mt ); + minorValue += offset * 1000.0; + } + + if ( minorTicks.isEmpty() || minorTicks.last() != minorValue ) + { + const bool isMedium = ( numMinorSteps % 2 == 0 ) + && ( i != 1 ) && ( i == numMinorSteps / 2 ); + + if ( isMedium ) + mediumTicks += minorValue; + else + minorTicks += minorValue; + } + } + } + } + + QwtScaleDiv scaleDiv; + + scaleDiv.setInterval( QwtDate::toDouble( minDate ), + QwtDate::toDouble( maxDate ) ); + + scaleDiv.setTicks( QwtScaleDiv::MajorTick, majorTicks ); + scaleDiv.setTicks( QwtScaleDiv::MediumTick, mediumTicks ); + scaleDiv.setTicks( QwtScaleDiv::MinorTick, minorTicks ); + + return scaleDiv; +} + +static QwtScaleDiv qwtDivideToMonths( + QDateTime &minDate, const QDateTime &maxDate, + double stepSize, int maxMinSteps ) +{ + // months are intervals with non + // equidistant ( in ms ) steps: we have to build the + // scale division manually + + int minStepDays = 0; + int minStepSize = 0.0; + + if ( maxMinSteps > 1 ) + { + if ( stepSize == 1 ) + { + if ( maxMinSteps >= 30 ) + minStepDays = 1; + else if ( maxMinSteps >= 6 ) + minStepDays = 5; + else if ( maxMinSteps >= 3 ) + minStepDays = 10; + + minStepDays = 15; + } + else + { + minStepSize = qwtDivideMajorStep( + stepSize, maxMinSteps, QwtDate::Month ); + } + } + + QList majorTicks; + QList mediumTicks; + QList minorTicks; + + for ( QDateTime dt = minDate; + dt <= maxDate; dt = dt.addMonths( stepSize ) ) + { + if ( !dt.isValid() ) + break; + + majorTicks += QwtDate::toDouble( dt ); + + if ( minStepDays > 0 ) + { + for ( int days = minStepDays; + days < 30; days += minStepDays ) + { + const double tick = QwtDate::toDouble( dt.addDays( days ) ); + + if ( days == 15 && minStepDays != 15 ) + mediumTicks += tick; + else + minorTicks += tick; + } + } + else if ( minStepSize > 0.0 ) + { + const int numMinorSteps = qRound( stepSize / (double) minStepSize ); + + for ( int i = 1; i < numMinorSteps; i++ ) + { + const double minorValue = + QwtDate::toDouble( dt.addMonths( i * minStepSize ) ); + + if ( ( numMinorSteps % 2 == 0 ) && ( i == numMinorSteps / 2 ) ) + mediumTicks += minorValue; + else + minorTicks += minorValue; + } + } + } + + QwtScaleDiv scaleDiv; + scaleDiv.setInterval( QwtDate::toDouble( minDate ), + QwtDate::toDouble( maxDate ) ); + + scaleDiv.setTicks( QwtScaleDiv::MajorTick, majorTicks ); + scaleDiv.setTicks( QwtScaleDiv::MediumTick, mediumTicks ); + scaleDiv.setTicks( QwtScaleDiv::MinorTick, minorTicks ); + + return scaleDiv; +} + +static QwtScaleDiv qwtDivideToYears( + const QDateTime &minDate, const QDateTime &maxDate, + double stepSize, int maxMinSteps ) +{ + QList majorTicks; + QList mediumTicks; + QList minorTicks; + + double minStepSize = 0.0; + + if ( maxMinSteps > 1 ) + { + minStepSize = qwtDivideMajorStep( + stepSize, maxMinSteps, QwtDate::Year ); + } + + int numMinorSteps = 0; + if ( minStepSize > 0.0 ) + numMinorSteps = qFloor( stepSize / minStepSize ); + + bool dateBC = minDate.date().year() < -1; + + for ( QDateTime dt = minDate; dt <= maxDate; + dt = dt.addYears( stepSize ) ) + { + if ( dateBC && dt.date().year() > 1 ) + { + // there is no year 0 in the Julian calendar + dt = dt.addYears( -1 ); + dateBC = false; + } + + if ( !dt.isValid() ) + break; + + majorTicks += QwtDate::toDouble( dt ); + + for ( int i = 1; i < numMinorSteps; i++ ) + { + QDateTime tickDate; + + const double years = qRound( i * minStepSize ); + if ( years >= INT_MAX / 12 ) + { + tickDate = dt.addYears( years ); + } + else + { + tickDate = dt.addMonths( qRound( years * 12 ) ); + } + + const bool isMedium = ( numMinorSteps > 2 ) && + ( numMinorSteps % 2 == 0 ) && ( i == numMinorSteps / 2 ); + + const double minorValue = QwtDate::toDouble( tickDate ); + if ( isMedium ) + mediumTicks += minorValue; + else + minorTicks += minorValue; + } + + if ( QwtDate::maxDate().addYears( -stepSize ) < dt.date() ) + { + break; + } + } + + QwtScaleDiv scaleDiv; + scaleDiv.setInterval( QwtDate::toDouble( minDate ), + QwtDate::toDouble( maxDate ) ); + + scaleDiv.setTicks( QwtScaleDiv::MajorTick, majorTicks ); + scaleDiv.setTicks( QwtScaleDiv::MediumTick, mediumTicks ); + scaleDiv.setTicks( QwtScaleDiv::MinorTick, minorTicks ); + + return scaleDiv; +} + +class QwtDateScaleEngine::PrivateData +{ +public: + PrivateData( Qt::TimeSpec spec ): + timeSpec( spec ), + utcOffset( 0 ), + week0Type( QwtDate::FirstThursday ), + maxWeeks( 4 ) + { + } + + Qt::TimeSpec timeSpec; + int utcOffset; + QwtDate::Week0Type week0Type; + int maxWeeks; +}; + + +/*! + \brief Constructor + + The engine is initialized to build scales for the + given time specification. It classifies intervals > 4 weeks + as >= Qt::Month. The first week of a year is defined like + for QwtDate::FirstThursday. + + \param timeSpec Time specification + + \sa setTimeSpec(), setMaxWeeks(), setWeek0Type() + */ +QwtDateScaleEngine::QwtDateScaleEngine( Qt::TimeSpec timeSpec ): + QwtLinearScaleEngine( 10 ) +{ + d_data = new PrivateData( timeSpec ); +} + +//! Destructor +QwtDateScaleEngine::~QwtDateScaleEngine() +{ + delete d_data; +} + +/*! + Set the time specification used by the engine + + \param timeSpec Time specification + \sa timeSpec(), setUtcOffset(), toDateTime() + */ +void QwtDateScaleEngine::setTimeSpec( Qt::TimeSpec timeSpec ) +{ + d_data->timeSpec = timeSpec; +} + +/*! + \return Time specification used by the engine + \sa setTimeSpec(), utcOffset(), toDateTime() + */ +Qt::TimeSpec QwtDateScaleEngine::timeSpec() const +{ + return d_data->timeSpec; +} + +/*! + Set the offset in seconds from Coordinated Universal Time + + \param seconds Offset in seconds + + \note The offset has no effect beside for the time specification + Qt::OffsetFromUTC. + + \sa QDate::utcOffset(), setTimeSpec(), toDateTime() + */ +void QwtDateScaleEngine::setUtcOffset( int seconds ) +{ + d_data->utcOffset = seconds; +} + +/*! + \return Offset in seconds from Coordinated Universal Time + \note The offset has no effect beside for the time specification + Qt::OffsetFromUTC. + + \sa QDate::setUtcOffset(), setTimeSpec(), toDateTime() + */ +int QwtDateScaleEngine::utcOffset() const +{ + return d_data->utcOffset; +} + +/*! + Sets how to identify the first week of a year. + + \param week0Type Mode how to identify the first week of a year + + \sa week0Type(), setMaxWeeks() + \note week0Type has no effect beside for intervals classified as + QwtDate::Week. + */ +void QwtDateScaleEngine::setWeek0Type( QwtDate::Week0Type week0Type ) +{ + d_data->week0Type = week0Type; +} + +/*! + \return Setting how to identify the first week of a year. + \sa setWeek0Type(), maxWeeks() + */ +QwtDate::Week0Type QwtDateScaleEngine::week0Type() const +{ + return d_data->week0Type; +} + +/*! + Set a upper limit for the number of weeks, when an interval + can be classified as Qt::Week. + + The default setting is 4 weeks. + + \param weeks Upper limit for the number of weeks + + \note In business charts a year is often devided + into weeks [1-52] + \sa maxWeeks(), setWeek0Type() + */ +void QwtDateScaleEngine::setMaxWeeks( int weeks ) +{ + d_data->maxWeeks = qMax( weeks, 0 ); +} + +/*! + \return Upper limit for the number of weeks, when an interval + can be classified as Qt::Week. + \sa setMaxWeeks(), week0Type() + */ +int QwtDateScaleEngine::maxWeeks() const +{ + return d_data->maxWeeks; +} + +/*! + Classification of a date/time interval division + + \param minDate Minimum ( = earlier ) of the interval + \param maxDate Maximum ( = later ) of the interval + \param maxSteps Maximum for the number of steps + + \return Interval classification + */ +QwtDate::IntervalType QwtDateScaleEngine::intervalType( + const QDateTime &minDate, const QDateTime &maxDate, + int maxSteps ) const +{ + const double jdMin = minDate.date().toJulianDay(); + const double jdMax = maxDate.date().toJulianDay(); + + if ( ( jdMax - jdMin ) / 365 > maxSteps ) + return QwtDate::Year; + + const int months = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Month ); + if ( months > maxSteps * 6 ) + return QwtDate::Year; + + const int days = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Day ); + const int weeks = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Week ); + + if ( weeks > d_data->maxWeeks ) + { + if ( days > 4 * maxSteps * 7 ) + return QwtDate::Month; + } + + if ( days > maxSteps * 7 ) + return QwtDate::Week; + + const int hours = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Hour ); + if ( hours > maxSteps * 24 ) + return QwtDate::Day; + + const int seconds = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Second ); + + if ( seconds >= maxSteps * 3600 ) + return QwtDate::Hour; + + if ( seconds >= maxSteps * 60 ) + return QwtDate::Minute; + + if ( seconds >= maxSteps ) + return QwtDate::Second; + + return QwtDate::Millisecond; +} + +/*! + Align and divide an interval + + The algorithm aligns and divides the interval into steps. + + Datetime interval divisions are usually not equidistant and the + calculated stepSize can only be used as an approximation + for the steps calculated by divideScale(). + + \param maxNumSteps Max. number of steps + \param x1 First limit of the interval (In/Out) + \param x2 Second limit of the interval (In/Out) + \param stepSize Step size (Out) + + \sa QwtScaleEngine::setAttribute() +*/ +void QwtDateScaleEngine::autoScale( int maxNumSteps, + double &x1, double &x2, double &stepSize ) const +{ + stepSize = 0.0; + + QwtInterval interval( x1, x2 ); + interval = interval.normalized(); + + interval.setMinValue( interval.minValue() - lowerMargin() ); + interval.setMaxValue( interval.maxValue() + upperMargin() ); + + if ( testAttribute( QwtScaleEngine::Symmetric ) ) + interval = interval.symmetrize( reference() ); + + if ( testAttribute( QwtScaleEngine::IncludeReference ) ) + interval = interval.extend( reference() ); + + if ( interval.width() == 0.0 ) + interval = buildInterval( interval.minValue() ); + + const QDateTime from = toDateTime( interval.minValue() ); + const QDateTime to = toDateTime( interval.maxValue() ); + + if ( from.isValid() && to.isValid() ) + { + if ( maxNumSteps < 1 ) + maxNumSteps = 1; + + const QwtDate::IntervalType intvType = + intervalType( from, to, maxNumSteps ); + + const double width = qwtIntervalWidth( from, to, intvType ); + + const double stepWidth = qwtDivideScale( width, maxNumSteps, intvType ); + if ( stepWidth != 0.0 && !testAttribute( QwtScaleEngine::Floating ) ) + { + const QDateTime d1 = alignDate( from, stepWidth, intvType, false ); + const QDateTime d2 = alignDate( to, stepWidth, intvType, true ); + + interval.setMinValue( QwtDate::toDouble( d1 ) ); + interval.setMaxValue( QwtDate::toDouble( d2 ) ); + } + + stepSize = stepWidth * qwtMsecsForType( intvType ); + } + + x1 = interval.minValue(); + x2 = interval.maxValue(); + + if ( testAttribute( QwtScaleEngine::Inverted ) ) + { + qSwap( x1, x2 ); + stepSize = -stepSize; + } +} + +/*! + \brief Calculate a scale division for a date/time interval + + \param x1 First interval limit + \param x2 Second interval limit + \param maxMajorSteps Maximum for the number of major steps + \param maxMinorSteps Maximum number of minor steps + \param stepSize Step size. If stepSize == 0, the scaleEngine + calculates one. + \return Calculated scale division +*/ +QwtScaleDiv QwtDateScaleEngine::divideScale( double x1, double x2, + int maxMajorSteps, int maxMinorSteps, double stepSize ) const +{ + if ( maxMajorSteps < 1 ) + maxMajorSteps = 1; + + const double min = qMin( x1, x2 ); + const double max = qMax( x1, x2 ); + + const QDateTime from = toDateTime( min ); + const QDateTime to = toDateTime( max ); + + if ( from == to ) + return QwtScaleDiv(); + + stepSize = qAbs( stepSize ); + if ( stepSize > 0.0 ) + { + // as interval types above hours are not equidistant + // ( even days might have 23/25 hours because of daylight saving ) + // the stepSize is used as a hint only + + maxMajorSteps = qCeil( ( max - min ) / stepSize ); + } + + const QwtDate::IntervalType intvType = + intervalType( from, to, maxMajorSteps ); + + QwtScaleDiv scaleDiv; + + if ( intvType == QwtDate::Millisecond ) + { + // for milliseconds and below we can use the decimal system + scaleDiv = QwtLinearScaleEngine::divideScale( min, max, + maxMajorSteps, maxMinorSteps, stepSize ); + } + else + { + const QDateTime minDate = QwtDate::floor( from, intvType ); + const QDateTime maxDate = QwtDate::ceil( to, intvType ); + + scaleDiv = buildScaleDiv( minDate, maxDate, + maxMajorSteps, maxMinorSteps, intvType ); + + // scaleDiv has been calculated from an extended interval + // adjusted to the step size. We have to shrink it again. + + scaleDiv = scaleDiv.bounded( min, max ); + } + + if ( x1 > x2 ) + scaleDiv.invert(); + + return scaleDiv; +} + +QwtScaleDiv QwtDateScaleEngine::buildScaleDiv( + const QDateTime &minDate, const QDateTime &maxDate, + int maxMajorSteps, int maxMinorSteps, + QwtDate::IntervalType intervalType ) const +{ + // calculate the step size + const double stepSize = qwtDivideScale( + qwtIntervalWidth( minDate, maxDate, intervalType ), + maxMajorSteps, intervalType ); + + // align minDate to the step size + QDateTime dt0 = alignDate( minDate, stepSize, intervalType, false ); + if ( !dt0.isValid() ) + { + // the floored date is out of the range of a + // QDateTime - we ceil instead. + dt0 = alignDate( minDate, stepSize, intervalType, true ); + } + + QwtScaleDiv scaleDiv; + + if ( intervalType <= QwtDate::Week ) + { + scaleDiv = qwtDivideToSeconds( dt0, maxDate, + stepSize, maxMinorSteps, intervalType ); + } + else + { + if( intervalType == QwtDate::Month ) + { + scaleDiv = qwtDivideToMonths( dt0, maxDate, + stepSize, maxMinorSteps ); + } + else if ( intervalType == QwtDate::Year ) + { + scaleDiv = qwtDivideToYears( dt0, maxDate, + stepSize, maxMinorSteps ); + } + } + + + return scaleDiv; +} + +/*! + Align a date/time value for a step size + + For Qt::Day alignments there is no "natural day 0" - + instead the first day of the year is used to avoid jumping + major ticks positions when panning a scale. For other alignments + ( f.e according to the first day of the month ) alignDate() + has to be overloaded. + + \param dateTime Date/time value + \param stepSize Step size + \param intervalType Interval type + \param up When true dateTime is ceiled - otherwise it is floored + + \return Aligned date/time value + */ +QDateTime QwtDateScaleEngine::alignDate( + const QDateTime &dateTime, double stepSize, + QwtDate::IntervalType intervalType, bool up ) const +{ + // what about: (year == 1582 && month == 10 && day > 4 && day < 15) ?? + + QDateTime dt = dateTime; + + if ( dateTime.timeSpec() == Qt::OffsetFromUTC ) + { + dt.setUtcOffset( 0 ); + } + + switch( intervalType ) + { + case QwtDate::Millisecond: + { + const int ms = qwtAlignValue( + dt.time().msec(), stepSize, up ) ; + + dt = QwtDate::floor( dateTime, QwtDate::Second ); + dt = dt.addMSecs( ms ); + + break; + } + case QwtDate::Second: + { + int second = dt.time().second(); + if ( up ) + { + if ( dt.time().msec() > 0 ) + second++; + } + + const int s = qwtAlignValue( second, stepSize, up ); + + dt = QwtDate::floor( dt, QwtDate::Minute ); + dt = dt.addSecs( s ); + + break; + } + case QwtDate::Minute: + { + int minute = dt.time().minute(); + if ( up ) + { + if ( dt.time().msec() > 0 || dt.time().second() > 0 ) + minute++; + } + + const int m = qwtAlignValue( minute, stepSize, up ); + + dt = QwtDate::floor( dt, QwtDate::Hour ); + dt = dt.addSecs( m * 60 ); + + break; + } + case QwtDate::Hour: + { + int hour = dt.time().hour(); + if ( up ) + { + if ( dt.time().msec() > 0 || dt.time().second() > 0 + || dt.time().minute() > 0 ) + { + hour++; + } + } + const int h = qwtAlignValue( hour, stepSize, up ); + + dt = QwtDate::floor( dt, QwtDate::Day ); + dt = dt.addSecs( h * 3600 ); + + break; + } + case QwtDate::Day: + { + // What date do we expect f.e. from an alignment of 5 days ?? + // Aligning them to the beginning of the year avoids at least + // jumping major ticks when panning + + int day = dt.date().dayOfYear(); + if ( up ) + { + if ( dt.time() > QTime( 0, 0 ) ) + day++; + } + + const int d = qwtAlignValue( day, stepSize, up ); + + dt = QwtDate::floor( dt, QwtDate::Year ); + dt = dt.addDays( d - 1 ); + + break; + } + case QwtDate::Week: + { + const QDate date = QwtDate::dateOfWeek0( + dt.date().year(), d_data->week0Type ); + + int numWeeks = date.daysTo( dt.date() ) / 7; + if ( up ) + { + if ( dt.time() > QTime( 0, 0 ) || + date.daysTo( dt.date() ) % 7 ) + { + numWeeks++; + } + } + + const int d = qwtAlignValue( numWeeks, stepSize, up ) * 7; + + dt = QwtDate::floor( dt, QwtDate::Day ); + dt.setDate( date ); + dt = dt.addDays( d ); + + break; + } + case QwtDate::Month: + { + int month = dt.date().month(); + if ( up ) + { + if ( dt.date().day() > 1 || + dt.time() > QTime( 0, 0 ) ) + { + month++; + } + } + + const int m = qwtAlignValue( month - 1, stepSize, up ); + + dt = QwtDate::floor( dt, QwtDate::Year ); + dt = dt.addMonths( m ); + + break; + } + case QwtDate::Year: + { + int year = dateTime.date().year(); + if ( up ) + { + if ( dateTime.date().dayOfYear() > 1 || + dt.time() > QTime( 0, 0 ) ) + { + year++; + } + } + + const int y = qwtAlignValue( year, stepSize, up ); + + dt = QwtDate::floor( dt, QwtDate::Day ); + if ( y == 0 ) + { + // there is no year 0 in the Julian calendar + dt.setDate( QDate( stepSize, 1, 1 ).addYears( -stepSize ) ); + } + else + { + dt.setDate( QDate( y, 1, 1 ) ); + } + + break; + } + } + + if ( dateTime.timeSpec() == Qt::OffsetFromUTC ) + { + dt.setUtcOffset( dateTime.utcOffset() ); + } + + return dt; +} + +/*! + Translate a double value into a QDateTime object. + + For QDateTime result is bounded by QwtDate::minDate() and QwtDate::maxDate() + + \return QDateTime object initialized with timeSpec() and utcOffset(). + \sa timeSpec(), utcOffset(), QwtDate::toDateTime() + */ +QDateTime QwtDateScaleEngine::toDateTime( double value ) const +{ + QDateTime dt = QwtDate::toDateTime( value, d_data->timeSpec ); + if ( !dt.isValid() ) + { + const QDate date = ( value <= 0.0 ) + ? QwtDate::minDate() : QwtDate::maxDate(); + + dt = QDateTime( date, QTime( 0, 0 ), d_data->timeSpec ); + } + + if ( d_data->timeSpec == Qt::OffsetFromUTC ) + { + dt = dt.addSecs( d_data->utcOffset ); + dt.setUtcOffset( d_data->utcOffset ); + } + + return dt; +} + diff --git a/libs/qwt/qwt_date_scale_engine.h b/libs/qwt/qwt_date_scale_engine.h new file mode 100644 index 000000000..db1d0f134 --- /dev/null +++ b/libs/qwt/qwt_date_scale_engine.h @@ -0,0 +1,77 @@ +#ifndef _QWT_DATE_SCALE_ENGINE_H_ +#define _QWT_DATE_SCALE_ENGINE_H_ 1 + +#include "qwt_date.h" +#include "qwt_scale_engine.h" + +/*! + \brief A scale engine for date/time values + + QwtDateScaleEngine builds scales from a time intervals. + Together with QwtDateScaleDraw it can be used for + axes according to date/time values. + + Years, months, weeks, days, hours and minutes are organized + in steps with non constant intervals. QwtDateScaleEngine + classifies intervals and aligns the boundaries and tick positions + according to this classification. + + QwtDateScaleEngine supports representations depending + on Qt::TimeSpec specifications. The valid range for scales + is limited by the range of QDateTime, that differs + between Qt4 and Qt5. + + Datetime values are expected as the number of milliseconds since + 1970-01-01T00:00:00 Universal Coordinated Time - also known + as "The Epoch", that can be converted to QDateTime using + QwtDate::toDateTime(). + + \sa QwtDate, QwtPlot::setAxisScaleEngine(), + QwtAbstractScale::setScaleEngine() +*/ +class QWT_EXPORT QwtDateScaleEngine: public QwtLinearScaleEngine +{ +public: + QwtDateScaleEngine( Qt::TimeSpec = Qt::LocalTime ); + virtual ~QwtDateScaleEngine(); + + void setTimeSpec( Qt::TimeSpec ); + Qt::TimeSpec timeSpec() const; + + void setUtcOffset( int seconds ); + int utcOffset() const; + + void setWeek0Type( QwtDate::Week0Type ); + QwtDate::Week0Type week0Type() const; + + void setMaxWeeks( int ); + int maxWeeks() const; + + virtual void autoScale( int maxNumSteps, + double &x1, double &x2, double &stepSize ) const; + + virtual QwtScaleDiv divideScale( + double x1, double x2, + int maxMajorSteps, int maxMinorSteps, + double stepSize = 0.0 ) const; + + virtual QwtDate::IntervalType intervalType( + const QDateTime &, const QDateTime &, int maxSteps ) const; + + QDateTime toDateTime( double ) const; + +protected: + virtual QDateTime alignDate( const QDateTime &, double stepSize, + QwtDate::IntervalType, bool up ) const; + +private: + QwtScaleDiv buildScaleDiv( const QDateTime &, const QDateTime &, + int maxMajorSteps, int maxMinorSteps, + QwtDate::IntervalType ) const; + +private: + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/libs/qwt/qwt_dial.cpp b/libs/qwt/qwt_dial.cpp index 35f77b022..1440eb14a 100644 --- a/libs/qwt/qwt_dial.cpp +++ b/libs/qwt/qwt_dial.cpp @@ -7,45 +7,93 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ -#include -#include -#if QT_VERSION >= 0x040000 -#include -#include -#endif -#include -#include +#include "qwt_dial.h" +#include "qwt_dial_needle.h" #include "qwt_math.h" #include "qwt_scale_engine.h" #include "qwt_scale_map.h" -#include "qwt_paint_buffer.h" +#include "qwt_round_scale_draw.h" #include "qwt_painter.h" -#include "qwt_dial_needle.h" -#include "qwt_dial.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static inline double qwtAngleDist( double a1, double a2 ) +{ + double dist = qAbs( a2 - a1 ); + if ( dist > 360.0 ) + dist -= 360.0; + + return dist; +} + +static inline bool qwtIsOnArc( double angle, double min, double max ) +{ + if ( min < max ) + { + return ( angle >= min ) && ( angle <= max ); + } + else + { + return ( angle >= min ) || ( angle <= max ); + } +} + +static inline double qwtBoundedAngle( double min, double angle, double max ) +{ + double from = qwtNormalizeDegrees( min ); + double to = qwtNormalizeDegrees( max ); + + double a; + + if ( qwtIsOnArc( angle, from, to ) ) + { + a = angle; + if ( a < min ) + a += 360.0; + } + else + { + if ( qwtAngleDist( angle, from ) < + qwtAngleDist( angle, to ) ) + { + a = min; + } + else + { + a = max; + } + } + + return a; +} class QwtDial::PrivateData { public: PrivateData(): - visibleBackground(true), - frameShadow(Sunken), - lineWidth(0), - mode(RotateNeedle), - origin(90.0), - minScaleArc(0.0), - maxScaleArc(0.0), - scaleDraw(0), - maxMajIntv(36), - maxMinIntv(10), - scaleStep(0.0), - needle(0) { + frameShadow( Sunken ), + lineWidth( 0 ), + mode( RotateNeedle ), + origin( 90.0 ), + minScaleArc( 0.0 ), + maxScaleArc( 0.0 ), + needle( NULL ), + arcOffset( 0.0 ), + mouseOffset( 0.0 ) + { } - ~PrivateData() { - delete scaleDraw; + ~PrivateData() + { delete needle; } - bool visibleBackground; Shadow frameShadow; int lineWidth; @@ -55,177 +103,79 @@ public: double minScaleArc; double maxScaleArc; - QwtDialScaleDraw *scaleDraw; - int maxMajIntv; - int maxMinIntv; - double scaleStep; - + double scalePenWidth; QwtDialNeedle *needle; - static double previousDir; -}; - -double QwtDial::PrivateData::previousDir = -1.0; - -/*! - Constructor - - \param parent Parent dial widget -*/ -QwtDialScaleDraw::QwtDialScaleDraw(QwtDial *parent): - d_parent(parent), - d_penWidth(1) -{ -} - -/*! - Set the pen width used for painting the scale - - \param penWidth Pen width - \sa penWidth(), QwtDial::drawScale() -*/ - -void QwtDialScaleDraw::setPenWidth(uint penWidth) -{ - d_penWidth = penWidth; -} + double arcOffset; + double mouseOffset; -/*! - \return Pen width used for painting the scale - \sa setPenWidth, QwtDial::drawScale() -*/ -uint QwtDialScaleDraw::penWidth() const -{ - return d_penWidth; -} - -/*! - Call QwtDial::scaleLabel of the parent dial widget. - - \param value Value to display - - \sa QwtDial::scaleLabel -*/ -QwtText QwtDialScaleDraw::label(double value) const -{ - if ( d_parent == NULL ) - return QwtRoundScaleDraw::label(value); - - return d_parent->scaleLabel(value); -} + QPixmap pixmapCache; +}; /*! \brief Constructor \param parent Parent widget - Create a dial widget with no scale and no needle. - The default origin is 90.0 with no valid value. It accepts - mouse and keyboard inputs and has no step size. The default mode - is QwtDial::RotateNeedle. -*/ + Create a dial widget with no needle. The scale is initialized + to [ 0.0, 360.0 ] and 360 steps ( QwtAbstractSlider::setTotalSteps() ). + The origin of the scale is at 90°, -QwtDial::QwtDial(QWidget* parent): - QwtAbstractSlider(Qt::Horizontal, parent) -{ - initDial(); -} + The value is set to 0.0. -#if QT_VERSION < 0x040000 -/*! - \brief Constructor - \param parent Parent widget - \param name Object name - - Create a dial widget with no scale and no needle. - The default origin is 90.0 with no valid value. It accepts - mouse and keyboard inputs and has no step size. The default mode - is QwtDial::RotateNeedle. + The default mode is QwtDial::RotateNeedle. */ -QwtDial::QwtDial(QWidget* parent, const char *name): - QwtAbstractSlider(Qt::Horizontal, parent) -{ - setName(name); - initDial(); -} -#endif - -void QwtDial::initDial() +QwtDial::QwtDial( QWidget* parent ): + QwtAbstractSlider( parent ) { d_data = new PrivateData; -#if QT_VERSION < 0x040000 - setWFlags(Qt::WNoAutoErase); -#endif - -#if QT_VERSION >= 0x040000 - using namespace Qt; -#endif - setFocusPolicy(TabFocus); + setFocusPolicy( Qt::TabFocus ); QPalette p = palette(); - for ( int i = 0; i < QPalette::NColorGroups; i++ ) { - const QPalette::ColorGroup cg = (QPalette::ColorGroup)i; + for ( int i = 0; i < QPalette::NColorGroups; i++ ) + { + const QPalette::ColorGroup colorGroup = + static_cast( i ); // Base: background color of the circle inside the frame. - // Foreground: background color of the circle inside the scale - -#if QT_VERSION < 0x040000 - p.setColor(cg, QColorGroup::Foreground, - p.color(cg, QColorGroup::Base)); -#else - p.setColor(cg, QPalette::Foreground, - p.color(cg, QPalette::Base)); -#endif + // WindowText: background color of the circle inside the scale + + p.setColor( colorGroup, QPalette::WindowText, + p.color( colorGroup, QPalette::Base ) ); } - setPalette(p); + setPalette( p ); - d_data->scaleDraw = new QwtDialScaleDraw(this); - d_data->scaleDraw->setRadius(0); + QwtRoundScaleDraw* scaleDraw = new QwtRoundScaleDraw(); + scaleDraw->setRadius( 0 ); - setScaleArc(0.0, 360.0); // scale as a full circle - setRange(0.0, 360.0, 1.0, 10); // degrees as deafult -} + setScaleDraw( scaleDraw ); -//! Destructor -QwtDial::~QwtDial() -{ - delete d_data; -} + setScaleArc( 0.0, 360.0 ); // scale as a full circle -/*! - Show/Hide the area outside of the frame - \param show Show if true, hide if false + setScaleMaxMajor( 10 ); + setScaleMaxMinor( 5 ); - \sa hasVisibleBackground(), setMask() - \warning When QwtDial is a toplevel widget the window - border might disappear too. -*/ -void QwtDial::showBackground(bool show) -{ - if ( d_data->visibleBackground != show ) { - d_data->visibleBackground = show; - updateMask(); - } + setValue( 0.0 ); } -/*! - true when the area outside of the frame is visible - - \sa showBackground(), setMask() -*/ -bool QwtDial::hasVisibleBackground() const +//! Destructor +QwtDial::~QwtDial() { - return d_data->visibleBackground; + delete d_data; } /*! Sets the frame shadow value from the frame style. + \param shadow Frame shadow \sa setLineWidth(), QFrame::setFrameShadow() */ -void QwtDial::setFrameShadow(Shadow shadow) +void QwtDial::setFrameShadow( Shadow shadow ) { - if ( shadow != d_data->frameShadow ) { + if ( shadow != d_data->frameShadow ) + { + invalidateCache(); + d_data->frameShadow = shadow; if ( lineWidth() > 0 ) update(); @@ -234,7 +184,7 @@ void QwtDial::setFrameShadow(Shadow shadow) /*! \return Frame shadow - /sa setFrameShadow(), lineWidth(), QFrame::frameShadow + /sa setFrameShadow(), lineWidth(), QFrame::frameShadow() */ QwtDial::Shadow QwtDial::frameShadow() const { @@ -242,17 +192,20 @@ QwtDial::Shadow QwtDial::frameShadow() const } /*! - Sets the line width + Sets the line width of the frame \param lineWidth Line width \sa setFrameShadow() */ -void QwtDial::setLineWidth(int lineWidth) +void QwtDial::setLineWidth( int lineWidth ) { if ( lineWidth < 0 ) lineWidth = 0; - if ( d_data->lineWidth != lineWidth ) { + if ( d_data->lineWidth != lineWidth ) + { + invalidateCache(); + d_data->lineWidth = lineWidth; update(); } @@ -268,65 +221,56 @@ int QwtDial::lineWidth() const } /*! - \return bounding rect of the circle inside the frame - \sa setLineWidth(), scaleContentsRect(), boundingRect() + \return bounding rectangle of the circle inside the frame + \sa setLineWidth(), scaleInnerRect(), boundingRect() */ -QRect QwtDial::contentsRect() const +QRect QwtDial::innerRect() const { const int lw = lineWidth(); - - QRect r = boundingRect(); - if ( lw > 0 ) { - r.setRect(r.x() + lw, r.y() + lw, - r.width() - 2 * lw, r.height() - 2 * lw); - } - return r; + return boundingRect().adjusted( lw, lw, -lw, -lw ); } /*! - \return bounding rect of the dial including the frame - \sa setLineWidth(), scaleContentsRect(), contentsRect() + \return bounding rectangle of the dial including the frame + \sa setLineWidth(), scaleInnerRect(), innerRect() */ QRect QwtDial::boundingRect() const { - const int radius = qwtMin(width(), height()) / 2; + const QRect cr = contentsRect(); + + const double dim = qMin( cr.width(), cr.height() ); + + QRect inner( 0, 0, dim, dim ); + inner.moveCenter( cr.center() ); - QRect r(0, 0, 2 * radius, 2 * radius); - r.moveCenter(rect().center()); - return r; + return inner; } /*! - \return rect inside the scale - \sa setLineWidth(), boundingRect(), contentsRect() + \return rectangle inside the scale + \sa setLineWidth(), boundingRect(), innerRect() */ -QRect QwtDial::scaleContentsRect() const +QRect QwtDial::scaleInnerRect() const { -#if QT_VERSION < 0x040000 - const QPen scalePen(colorGroup().text(), 0, Qt::NoPen); -#else - const QPen scalePen(palette().text(), 0, Qt::NoPen); -#endif - - int scaleDist = 0; - if ( d_data->scaleDraw ) { - scaleDist = d_data->scaleDraw->extent(scalePen, font()); + QRect rect = innerRect(); + + const QwtAbstractScaleDraw *sd = scaleDraw(); + if ( sd ) + { + int scaleDist = qCeil( sd->extent( font() ) ); scaleDist++; // margin + + rect.adjust( scaleDist, scaleDist, -scaleDist, -scaleDist ); } - const QRect rect = contentsRect(); - return QRect(rect.x() + scaleDist, rect.y() + scaleDist, - rect.width() - 2 * scaleDist, rect.height() - 2 * scaleDist); + return rect; } /*! - \brief Change the mode of the meter. + \brief Change the mode of the dial. \param mode New mode - The value of the meter is indicated by the difference - between north of the scale and the direction of the needle. - In case of QwtDial::RotateNeedle north is pointing - to the origin() and the needle is rotating, in case of + In case of QwtDial::RotateNeedle the needle is rotating, in case of QwtDial::RotateScale, the needle points to origin() and the scale is rotating. @@ -334,26 +278,19 @@ QRect QwtDial::scaleContentsRect() const \sa mode(), setValue(), setOrigin() */ -void QwtDial::setMode(Mode mode) +void QwtDial::setMode( Mode mode ) { - if ( mode != d_data->mode ) { + if ( mode != d_data->mode ) + { + invalidateCache(); + d_data->mode = mode; - update(); + sliderChange(); } } /*! - \return mode of the dial. - - The value of the dial is indicated by the difference - between the origin and the direction of the needle. - In case of QwtDial::RotateNeedle the scale arc is fixed - to the origin() and the needle is rotating, in case of - QwtDial::RotateScale, the needle points to origin() - and the scale is rotating. - - The default mode is QwtDial::RotateNeedle. - + \return Mode of the dial. \sa setMode(), origin(), setScaleArc(), value() */ QwtDial::Mode QwtDial::mode() const @@ -362,115 +299,72 @@ QwtDial::Mode QwtDial::mode() const } /*! - Sets whether it is possible to step the value from the highest value to - the lowest value and vice versa to on. - - \param wrapping en/disables wrapping - - \sa wrapping(), QwtDoubleRange::periodic() - \note The meaning of wrapping is like the wrapping property of QSpinBox, - but not like it is used in QDial. -*/ -void QwtDial::setWrapping(bool wrapping) + Invalidate the internal caches used to speed up repainting + */ +void QwtDial::invalidateCache() { - setPeriodic(wrapping); + d_data->pixmapCache = QPixmap(); } /*! - wrapping() holds whether it is possible to step the value from the - highest value to the lowest value and vice versa. - - \sa setWrapping(), QwtDoubleRange::setPeriodic() - \note The meaning of wrapping is like the wrapping property of QSpinBox, - but not like it is used in QDial. + Paint the dial + \param event Paint event */ -bool QwtDial::wrapping() const +void QwtDial::paintEvent( QPaintEvent *event ) { - return periodic(); -} + QPainter painter( this ); + painter.setClipRegion( event->region() ); -/*! - Resize the dial widget - \param e Resize event -*/ -void QwtDial::resizeEvent(QResizeEvent *e) -{ - QWidget::resizeEvent(e); + QStyleOption opt; + opt.init(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this); - if ( !hasVisibleBackground() ) - updateMask(); -} + if ( d_data->mode == QwtDial::RotateScale ) + { + painter.save(); + painter.setRenderHint( QPainter::Antialiasing, true ); -/*! - Paint the dial - \param e Paint event -*/ -void QwtDial::paintEvent(QPaintEvent *e) -{ - const QRect &ur = e->rect(); - if ( ur.isValid() ) { -#if QT_VERSION < 0x040000 - QwtPaintBuffer paintBuffer(this, ur); - QPainter &painter = *paintBuffer.painter(); -#else - QPainter painter(this); - painter.setRenderHint(QPainter::Antialiasing, true); -#endif + drawContents( &painter ); - painter.save(); - drawContents(&painter); painter.restore(); + } - painter.save(); - drawFrame(&painter); - painter.restore(); + const QRect r = contentsRect(); + if ( r.size() != d_data->pixmapCache.size() ) + { + d_data->pixmapCache = QwtPainter::backingStore( this, r.size() ); + d_data->pixmapCache.fill( Qt::transparent ); + + QPainter p( &d_data->pixmapCache ); + p.setRenderHint( QPainter::Antialiasing, true ); + p.translate( -r.topLeft() ); + + if ( d_data->mode != QwtDial::RotateScale ) + drawContents( &p ); - if ( hasFocus() ) - drawFocusIndicator(&painter); + if ( lineWidth() > 0 ) + drawFrame( &p ); + + if ( d_data->mode != QwtDial::RotateNeedle ) + drawNeedle( &p ); } + + painter.drawPixmap( r.topLeft(), d_data->pixmapCache ); + + if ( d_data->mode == QwtDial::RotateNeedle ) + drawNeedle( &painter ); + + if ( hasFocus() ) + drawFocusIndicator( &painter ); } /*! - Draw a dotted round circle, if !isReadOnly() - + Draw the focus indicator \param painter Painter */ -void QwtDial::drawFocusIndicator(QPainter *painter) const +void QwtDial::drawFocusIndicator( QPainter *painter ) const { - if ( !isReadOnly() ) { - QRect focusRect = contentsRect(); - - const int margin = 2; - focusRect.setRect( - focusRect.x() + margin, - focusRect.y() + margin, - focusRect.width() - 2 * margin, - focusRect.height() - 2 * margin); - -#if QT_VERSION < 0x040000 - QColor color = colorGroup().color(QColorGroup::Base); -#else - QColor color = palette().color(QPalette::Base); -#endif - if (color.isValid()) { - const QColor gray(Qt::gray); - - int h, s, v; -#if QT_VERSION < 0x040000 - color.hsv(&h, &s, &v); -#else - color.getHsv(&h, &s, &v); -#endif - color = (v > 128) ? gray.dark(120) : gray.light(120); - } else - color = Qt::darkGray; - - painter->save(); - painter->setBrush(Qt::NoBrush); - painter->setPen(QPen(color, 0, Qt::DotLine)); - painter->drawEllipse(focusRect); - painter->restore(); - } + QwtPainter::drawFocusRect( painter, this, boundingRect() ); } /*! @@ -479,160 +373,60 @@ void QwtDial::drawFocusIndicator(QPainter *painter) const \param painter Painter \sa lineWidth(), frameShadow() */ -void QwtDial::drawFrame(QPainter *painter) +void QwtDial::drawFrame( QPainter *painter ) { - const int lw = lineWidth(); - const int off = (lw + 1) % 2; - - QRect r = boundingRect(); - r.setRect(r.x() + lw / 2 - off, r.y() + lw / 2 - off, - r.width() - lw + off + 1, r.height() - lw + off + 1); -#if QT_VERSION >= 0x040000 -#ifdef __GNUC__ -#endif - r.setX(r.x() + 1); - r.setY(r.y() + 1); - r.setWidth(r.width() - 2); - r.setHeight(r.height() - 2); -#endif - - if ( lw > 0 ) { - switch(d_data->frameShadow) { - case QwtDial::Raised: -#if QT_VERSION < 0x040000 - QwtPainter::drawRoundFrame(painter, r, - lw, colorGroup(), false); -#else - QwtPainter::drawRoundFrame(painter, r, - lw, palette(), false); -#endif - break; - case QwtDial::Sunken: -#if QT_VERSION < 0x040000 - QwtPainter::drawRoundFrame(painter, r, - lw, colorGroup(), true); -#else - QwtPainter::drawRoundFrame(painter, r, - lw, palette(), true); -#endif - break; - default: { // Plain - painter->save(); - painter->setPen(QPen(Qt::black, lw)); - painter->setBrush(Qt::NoBrush); - painter->drawEllipse(r); - painter->restore(); - } - } - } + QwtPainter::drawRoundFrame( painter, boundingRect(), + palette(), lineWidth(), d_data->frameShadow ); } /*! \brief Draw the contents inside the frame - QColorGroup::Background is the background color outside of the frame. - QColorGroup::Base is the background color inside the frame. - QColorGroup::Foreground is the background color inside the scale. + QPalette::Window is the background color outside of the frame. + QPalette::Base is the background color inside the frame. + QPalette::WindowText is the background color inside the scale. \param painter Painter - \sa boundingRect(), contentsRect(), - scaleContentsRect(), QWidget::setPalette + + \sa boundingRect(), innerRect(), + scaleInnerRect(), QWidget::setPalette() */ -void QwtDial::drawContents(QPainter *painter) const +void QwtDial::drawContents( QPainter *painter ) const { -#if QT_VERSION < 0x040000 - if ( backgroundMode() == Qt::NoBackground || - colorGroup().brush(QColorGroup::Base) != - colorGroup().brush(QColorGroup::Background) ) -#else - if ( testAttribute(Qt::WA_NoSystemBackground) || - palette().brush(QPalette::Base) != - palette().brush(QPalette::Background) ) -#endif + if ( testAttribute( Qt::WA_NoSystemBackground ) || + palette().brush( QPalette::Base ) != + palette().brush( QPalette::Window ) ) { - - const QRect br = boundingRect(); + const QRectF br = boundingRect(); painter->save(); - painter->setPen(Qt::NoPen); - -#if QT_VERSION < 0x040000 - painter->setBrush(colorGroup().brush(QColorGroup::Base)); -#else - painter->setBrush(palette().brush(QPalette::Base)); -#endif - - painter->drawEllipse(br); + painter->setPen( Qt::NoPen ); + painter->setBrush( palette().brush( QPalette::Base ) ); + painter->drawEllipse( br ); painter->restore(); } - - const QRect insideScaleRect = scaleContentsRect(); -#if QT_VERSION < 0x040000 - if ( colorGroup().brush(QColorGroup::Foreground) != - colorGroup().brush(QColorGroup::Base) ) -#else - if ( palette().brush(QPalette::Foreground) != - palette().brush(QPalette::Base) ) -#endif + const QRectF insideScaleRect = scaleInnerRect(); + if ( palette().brush( QPalette::WindowText ) != + palette().brush( QPalette::Base ) ) { painter->save(); - painter->setPen(Qt::NoPen); - -#if QT_VERSION < 0x040000 - painter->setBrush(colorGroup().brush(QColorGroup::Foreground)); -#else - painter->setBrush(palette().brush(QPalette::Foreground)); -#endif - - painter->drawEllipse(insideScaleRect.x() - 1, insideScaleRect.y() - 1, - insideScaleRect.width(), insideScaleRect.height() ); - + painter->setPen( Qt::NoPen ); + painter->setBrush( palette().brush( QPalette::WindowText ) ); + painter->drawEllipse( insideScaleRect ); painter->restore(); } - const QPoint center = insideScaleRect.center(); - const int radius = insideScaleRect.width() / 2; + const QPointF center = insideScaleRect.center(); + const double radius = 0.5 * insideScaleRect.width(); painter->save(); - drawScaleContents(painter, center, radius); + drawScale( painter, center, radius ); painter->restore(); - double direction = d_data->origin; - - if (isValid()) { - direction = d_data->origin + d_data->minScaleArc; - if ( maxValue() > minValue() && d_data->maxScaleArc > d_data->minScaleArc ) { - const double ratio = - (value() - minValue()) / (maxValue() - minValue()); - direction += ratio * (d_data->maxScaleArc - d_data->minScaleArc); - } - - if ( direction >= 360.0 ) - direction -= 360.0; - } - - double origin = d_data->origin; - if ( mode() == RotateScale ) { - origin -= direction - d_data->origin; - direction = d_data->origin; - } - painter->save(); - drawScale(painter, center, radius, origin, d_data->minScaleArc, d_data->maxScaleArc); + drawScaleContents( painter, center, radius ); painter->restore(); - - if ( isValid() ) { - QPalette::ColorGroup cg; - if ( isEnabled() ) - cg = hasFocus() ? QPalette::Active : QPalette::Inactive; - else - cg = QPalette::Disabled; - - painter->save(); - drawNeedle(painter, center, radius, direction, cg); - painter->restore(); - } } /*! @@ -642,98 +436,96 @@ void QwtDial::drawContents(QPainter *painter) const \param center Center of the dial \param radius Length for the needle \param direction Direction of the needle in degrees, counter clockwise - \param cg ColorGroup + \param colorGroup ColorGroup */ -void QwtDial::drawNeedle(QPainter *painter, const QPoint ¢er, - int radius, double direction, QPalette::ColorGroup cg) const +void QwtDial::drawNeedle( QPainter *painter, const QPointF ¢er, + double radius, double direction, QPalette::ColorGroup colorGroup ) const { - if ( d_data->needle ) { + if ( d_data->needle ) + { direction = 360.0 - direction; // counter clockwise - d_data->needle->draw(painter, center, radius, direction, cg); + d_data->needle->draw( painter, center, radius, direction, colorGroup ); } } +void QwtDial::drawNeedle( QPainter *painter ) const +{ + if ( !isValid() ) + return; + + QPalette::ColorGroup colorGroup; + if ( isEnabled() ) + colorGroup = hasFocus() ? QPalette::Active : QPalette::Inactive; + else + colorGroup = QPalette::Disabled; + + const QRectF sr = scaleInnerRect(); + + painter->save(); + painter->setRenderHint( QPainter::Antialiasing, true ); + drawNeedle( painter, sr.center(), 0.5 * sr.width(), + transform( value() ) + 270.0, colorGroup ); + painter->restore(); +} + /*! Draw the scale \param painter Painter \param center Center of the dial \param radius Radius of the scale - \param origin Origin of the scale - \param minArc Minimum of the arc - \param maxArc Minimum of the arc - - \sa QwtAbstractScaleDraw::setAngleRange */ -void QwtDial::drawScale(QPainter *painter, const QPoint ¢er, - int radius, double origin, double minArc, double maxArc) const +void QwtDial::drawScale( QPainter *painter, + const QPointF ¢er, double radius ) const { - if ( d_data->scaleDraw == NULL ) + QwtRoundScaleDraw *sd = const_cast( scaleDraw() ); + if ( sd == NULL ) return; - origin -= 270.0; // hardcoded origin of QwtScaleDraw - - double angle = maxArc - minArc; - if ( angle > 360.0 ) - angle = fmod(angle, 360.0); - - minArc += origin; - if ( minArc < -360.0 ) - minArc = fmod(minArc, 360.0); + sd->setRadius( radius ); + sd->moveCenter( center ); - maxArc = minArc + angle; - if ( maxArc > 360.0 ) { - // QwtAbstractScaleDraw::setAngleRange accepts only values - // in the range [-360.0..360.0] - minArc -= 360.0; - maxArc -= 360.0; - } - - painter->setFont(font()); - - d_data->scaleDraw->setAngleRange(minArc, maxArc); - d_data->scaleDraw->setRadius(radius); - d_data->scaleDraw->moveCenter(center); - -#if QT_VERSION < 0x040000 - QColorGroup cg = colorGroup(); - - const QColor textColor = cg.color(QColorGroup::Text); - cg.setColor(QColorGroup::Foreground, textColor); - painter->setPen(QPen(textColor, d_data->scaleDraw->penWidth())); - - d_data->scaleDraw->draw(painter, cg); -#else QPalette pal = palette(); - const QColor textColor = pal.color(QPalette::Text); - pal.setColor(QPalette::Foreground, textColor); //ticks, backbone + const QColor textColor = pal.color( QPalette::Text ); + pal.setColor( QPalette::WindowText, textColor ); // ticks, backbone - painter->setPen(QPen(textColor, d_data->scaleDraw->penWidth())); + painter->setFont( font() ); + painter->setPen( QPen( textColor, sd->penWidth() ) ); - d_data->scaleDraw->draw(painter, pal); -#endif + painter->setBrush( Qt::red ); + sd->draw( painter, pal ); } -void QwtDial::drawScaleContents(QPainter *, - const QPoint &, int) const +/*! + Draw the contents inside the scale + + Paints nothing. + + \param painter Painter + \param center Center of the contents circle + \param radius Radius of the contents circle +*/ +void QwtDial::drawScaleContents( QPainter *painter, + const QPointF ¢er, double radius ) const { - // empty default implementation + Q_UNUSED(painter); + Q_UNUSED(center); + Q_UNUSED(radius); } /*! Set a needle for the dial - Qwt is missing a set of good looking needles. - Contributions are very welcome. - \param needle Needle + \warning The needle will be deleted, when a different needle is - set or in ~QwtDial() + set or in ~QwtDial() */ -void QwtDial::setNeedle(QwtDialNeedle *needle) +void QwtDial::setNeedle( QwtDialNeedle *needle ) { - if ( needle != d_data->needle ) { + if ( needle != d_data->needle ) + { if ( d_data->needle ) delete d_data->needle; @@ -760,142 +552,101 @@ QwtDialNeedle *QwtDial::needle() return d_data->needle; } -//! QwtDoubleRange update hook -void QwtDial::rangeChange() +//! \return the scale draw +QwtRoundScaleDraw *QwtDial::scaleDraw() { - updateScale(); + return static_cast( abstractScaleDraw() ); } -/*! - Update the scale with the current attributes - \sa setScale() -*/ -void QwtDial::updateScale() +//! \return the scale draw +const QwtRoundScaleDraw *QwtDial::scaleDraw() const { - if ( d_data->scaleDraw ) { - QwtLinearScaleEngine scaleEngine; - - const QwtScaleDiv scaleDiv = scaleEngine.divideScale( - minValue(), maxValue(), - d_data->maxMajIntv, d_data->maxMinIntv, d_data->scaleStep); - - d_data->scaleDraw->setTransformation(scaleEngine.transformation()); - d_data->scaleDraw->setScaleDiv(scaleDiv); - } -} - -//! Return the scale draw -QwtDialScaleDraw *QwtDial::scaleDraw() -{ - return d_data->scaleDraw; -} - -//! Return the scale draw -const QwtDialScaleDraw *QwtDial::scaleDraw() const -{ - return d_data->scaleDraw; + return static_cast( abstractScaleDraw() ); } /*! Set an individual scale draw + The motivation for setting a scale draw is often + to overload QwtRoundScaleDraw::label() to return + individual tick labels. + \param scaleDraw Scale draw \warning The previous scale draw is deleted */ -void QwtDial::setScaleDraw(QwtDialScaleDraw *scaleDraw) +void QwtDial::setScaleDraw( QwtRoundScaleDraw *scaleDraw ) { - if ( scaleDraw != d_data->scaleDraw ) { - if ( d_data->scaleDraw ) - delete d_data->scaleDraw; - - d_data->scaleDraw = scaleDraw; - updateScale(); - update(); - } + setAbstractScaleDraw( scaleDraw ); + sliderChange(); } /*! - Change the intervals of the scale - \sa QwtAbstractScaleDraw::setScale -*/ -void QwtDial::setScale(int maxMajIntv, int maxMinIntv, double step) -{ - d_data->maxMajIntv = maxMajIntv; - d_data->maxMinIntv = maxMinIntv; - d_data->scaleStep = step; + Change the arc of the scale - updateScale(); -} + \param minArc Lower limit + \param maxArc Upper limit -/*! - A wrapper method for accessing the scale draw. - - - options == 0\n - No visible scale: setScaleDraw(NULL) - - options & ScaleBackbone\n - En/disable the backbone of the scale. - - options & ScaleTicks\n - En/disable the ticks of the scale. - - options & ScaleLabel\n - En/disable scale labels - - \sa QwtAbstractScaleDraw::enableComponent + \sa minScaleArc(), maxScaleArc() */ -void QwtDial::setScaleOptions(int options) +void QwtDial::setScaleArc( double minArc, double maxArc ) { - if ( options == 0 ) - setScaleDraw(NULL); - - QwtDialScaleDraw *sd = d_data->scaleDraw; - if ( sd == NULL ) - return; + if ( minArc != 360.0 && minArc != -360.0 ) + minArc = ::fmod( minArc, 360.0 ); + if ( maxArc != 360.0 && maxArc != -360.0 ) + maxArc = ::fmod( maxArc, 360.0 ); - sd->enableComponent(QwtAbstractScaleDraw::Backbone, - options & ScaleBackbone); + double minScaleArc = qMin( minArc, maxArc ); + double maxScaleArc = qMax( minArc, maxArc ); - sd->enableComponent(QwtAbstractScaleDraw::Ticks, - options & ScaleTicks); + if ( maxScaleArc - minScaleArc > 360.0 ) + maxScaleArc = minScaleArc + 360.0; - sd->enableComponent(QwtAbstractScaleDraw::Labels, - options & ScaleLabel); -} + if ( ( minScaleArc != d_data->minScaleArc ) || + ( maxScaleArc != d_data->maxScaleArc ) ) + { + d_data->minScaleArc = minScaleArc; + d_data->maxScaleArc = maxScaleArc; -//! See: QwtAbstractScaleDraw::setTickLength, QwtDialScaleDraw::setPenWidth -void QwtDial::setScaleTicks(int minLen, int medLen, - int majLen, int penWidth) -{ - QwtDialScaleDraw *sd = d_data->scaleDraw; - if ( sd ) { - sd->setTickLength(QwtScaleDiv::MinorTick, minLen); - sd->setTickLength(QwtScaleDiv::MediumTick, medLen); - sd->setTickLength(QwtScaleDiv::MajorTick, majLen); - sd->setPenWidth(penWidth); + invalidateCache(); + sliderChange(); } } -/*! - Find the label for a value +/*! + Set the lower limit for the scale arc - \param value Value - \return label -*/ -QwtText QwtDial::scaleLabel(double value) const + \param min Lower limit of the scale arc + \sa setScaleArc(), setMaxScaleArc() + */ +void QwtDial::setMinScaleArc( double min ) { -#if 1 - if ( value == -0 ) - value = 0; -#endif - - return QString::number(value); + setScaleArc( min, d_data->maxScaleArc ); } -//! \return Lower limit of the scale arc +/*! + \return Lower limit of the scale arc + \sa setScaleArc() +*/ double QwtDial::minScaleArc() const { return d_data->minScaleArc; } -//! \return Upper limit of the scale arc +/*! + Set the upper limit for the scale arc + + \param max Upper limit of the scale arc + \sa setScaleArc(), setMinScaleArc() + */ +void QwtDial::setMaxScaleArc( double max ) +{ + setScaleArc( d_data->minScaleArc, max ); +} + +/*! + \return Upper limit of the scale arc + \sa setScaleArc() +*/ double QwtDial::maxScaleArc() const { return d_data->maxScaleArc; @@ -909,10 +660,12 @@ double QwtDial::maxScaleArc() const \param origin New origin \sa origin() */ -void QwtDial::setOrigin(double origin) +void QwtDial::setOrigin( double origin ) { + invalidateCache(); + d_data->origin = origin; - update(); + sliderChange(); } /*! @@ -926,266 +679,194 @@ double QwtDial::origin() const return d_data->origin; } -/*! - Change the arc of the scale - - \param minArc Lower limit - \param maxArc Upper limit -*/ -void QwtDial::setScaleArc(double minArc, double maxArc) -{ - if ( minArc != 360.0 && minArc != -360.0 ) - minArc = fmod(minArc, 360.0); - if ( maxArc != 360.0 && maxArc != -360.0 ) - maxArc = fmod(maxArc, 360.0); - - d_data->minScaleArc = qwtMin(minArc, maxArc); - d_data->maxScaleArc = qwtMax(minArc, maxArc); - if ( d_data->maxScaleArc - d_data->minScaleArc > 360.0 ) - d_data->maxScaleArc = d_data->minScaleArc + 360.0; - - update(); -} - -//! QwtDoubleRange update hook -void QwtDial::valueChange() -{ - update(); - QwtAbstractSlider::valueChange(); -} - /*! \return Size hint + \sa minimumSizeHint() */ QSize QwtDial::sizeHint() const { int sh = 0; - if ( d_data->scaleDraw ) - sh = d_data->scaleDraw->extent( QPen(), font() ); + if ( scaleDraw() ) + sh = qCeil( scaleDraw()->extent( font() ) ); const int d = 6 * sh + 2 * lineWidth(); - return QSize( d, d ); + QSize hint( d, d ); + if ( !isReadOnly() ) + hint = hint.expandedTo( QApplication::globalStrut() ); + + return hint; } /*! - \brief Return a minimum size hint - \warning The return value of QwtDial::minimumSizeHint() depends on the - font and the scale. + \return Minimum size hint + \sa sizeHint() */ QSize QwtDial::minimumSizeHint() const { int sh = 0; - if ( d_data->scaleDraw ) - sh = d_data->scaleDraw->extent(QPen(), font() ); + if ( scaleDraw() ) + sh = qCeil( scaleDraw()->extent( font() ) ); const int d = 3 * sh + 2 * lineWidth(); return QSize( d, d ); } -static double line2Radians(const QPoint &p1, const QPoint &p2) +/*! + \brief Determine what to do when the user presses a mouse button. + + \param pos Mouse position + + \retval True, when the inner circle contains pos + \sa scrolledTo() +*/ +bool QwtDial::isScrollPosition( const QPoint &pos ) const { - const QPoint p = p2 - p1; - - double angle; - if ( p.x() == 0 ) - angle = ( p.y() <= 0 ) ? M_PI_2 : 3 * M_PI_2; - else { - angle = atan(double(-p.y()) / double(p.x())); - if ( p.x() < 0 ) - angle += M_PI; - if ( angle < 0.0 ) - angle += 2 * M_PI; + const QRegion region( innerRect(), QRegion::Ellipse ); + if ( region.contains( pos ) && ( pos != innerRect().center() ) ) + { + double angle = QLineF( rect().center(), pos ).angle(); + if ( d_data->mode == QwtDial::RotateScale ) + angle = 360.0 - angle; + + double valueAngle = + qwtNormalizeDegrees( 90.0 - transform( value() ) ); + + d_data->mouseOffset = qwtNormalizeDegrees( angle - valueAngle ); + d_data->arcOffset = scaleMap().p1(); + + return true; } - return 360.0 - angle * 180.0 / M_PI; + + return false; } /*! - Find the value for a given position + \brief Determine the value for a new position of the + slider handle. + + \param pos Mouse position - \param pos Position - \return Value + \return Value for the mouse position + \sa isScrollPosition() */ -double QwtDial::getValue(const QPoint &pos) +double QwtDial::scrolledTo( const QPoint &pos ) const { - if ( d_data->maxScaleArc == d_data->minScaleArc || maxValue() == minValue() ) - return minValue(); + double angle = QLineF( rect().center(), pos ).angle(); + if ( d_data->mode == QwtDial::RotateScale ) + { + angle += scaleMap().p1() - d_data->arcOffset; + angle = 360.0 - angle; + } - double dir = line2Radians(rect().center(), pos) - d_data->origin; - if ( dir < 0.0 ) - dir += 360.0; + angle = qwtNormalizeDegrees( angle - d_data->mouseOffset ); + angle = qwtNormalizeDegrees( 90.0 - angle ); - if ( mode() == RotateScale ) - dir = 360.0 - dir; - - // The position might be in the area that is outside the scale arc. - // We need the range of the scale if it was a complete circle. - - const double completeCircle = 360.0 / (d_data->maxScaleArc - d_data->minScaleArc) - * (maxValue() - minValue()); - - double posValue = minValue() + completeCircle * dir / 360.0; - - if ( scrollMode() == ScrMouse ) { - if ( d_data->previousDir >= 0.0 ) { // valid direction - // We have to find out whether the mouse is moving - // clock or counter clockwise - - bool clockWise = false; - - const double angle = dir - d_data->previousDir; - if ( (angle >= 0.0 && angle <= 180.0) || angle < -180.0 ) - clockWise = true; - - if ( clockWise ) { - if ( dir < d_data->previousDir && mouseOffset() > 0.0 ) { - // We passed 360 -> 0 - setMouseOffset(mouseOffset() - completeCircle); - } - - if ( wrapping() ) { - if ( posValue - mouseOffset() > maxValue() ) { - // We passed maxValue and the value will be set - // to minValue. We have to adjust the mouseOffset. - - setMouseOffset(posValue - minValue()); - } - } else { - if ( posValue - mouseOffset() > maxValue() || - value() == maxValue() ) { - // We fix the value at maxValue by adjusting - // the mouse offset. - - setMouseOffset(posValue - maxValue()); - } - } - } else { - if ( dir > d_data->previousDir && mouseOffset() < 0.0 ) { - // We passed 0 -> 360 - setMouseOffset(mouseOffset() + completeCircle); - } - - if ( wrapping() ) { - if ( posValue - mouseOffset() < minValue() ) { - // We passed minValue and the value will be set - // to maxValue. We have to adjust the mouseOffset. - - setMouseOffset(posValue - maxValue()); - } - } else { - if ( posValue - mouseOffset() < minValue() || - value() == minValue() ) { - // We fix the value at minValue by adjusting - // the mouse offset. - - setMouseOffset(posValue - minValue()); - } - } + if ( scaleMap().pDist() >= 360.0 ) + { + if ( angle < scaleMap().p1() ) + angle += 360.0; + + if ( !wrapping() ) + { + double boundedAngle = angle; + + const double arc = angle - transform( value() ); + if ( qAbs( arc ) > 180.0 ) + { + boundedAngle = ( arc > 0 ) + ? scaleMap().p1() : scaleMap().p2(); } + + d_data->mouseOffset += ( boundedAngle - angle ); + + angle = boundedAngle; } - d_data->previousDir = dir; + } + else + { + const double boundedAngle = + qwtBoundedAngle( scaleMap().p1(), angle, scaleMap().p2() ); + + if ( !wrapping() ) + d_data->mouseOffset += ( boundedAngle - angle ); + + angle = boundedAngle; } - return posValue; + return invTransform( angle ); } /*! - \sa QwtAbstractSlider::getScrollMode + Change Event handler + \param event Change event + + Invalidates internal paint caches if necessary */ -void QwtDial::getScrollMode(const QPoint &p, int &scrollMode, int &direction) +void QwtDial::changeEvent( QEvent *event ) { - direction = 0; - scrollMode = ScrNone; - - const QRegion region(contentsRect(), QRegion::Ellipse); - if ( region.contains(p) && p != rect().center() ) { - scrollMode = ScrMouse; - d_data->previousDir = -1.0; + switch( event->type() ) + { + case QEvent::EnabledChange: + case QEvent::FontChange: + case QEvent::StyleChange: + case QEvent::PaletteChange: + case QEvent::LanguageChange: + case QEvent::LocaleChange: + { + invalidateCache(); + break; + } + default: + break; } + + QwtAbstractSlider::changeEvent( event ); } /*! - Handles key events - - - Key_Down, KeyLeft\n - Decrement by 1 - - Key_Prior\n - Decrement by pageSize() - - Key_Home\n - Set the value to minValue() - - - Key_Up, KeyRight\n - Increment by 1 - - Key_Next\n - Increment by pageSize() - - Key_End\n - Set the value to maxValue() - - \sa isReadOnly() + Wheel Event handler + \param event Wheel event */ -void QwtDial::keyPressEvent(QKeyEvent *e) +void QwtDial::wheelEvent( QWheelEvent *event ) { - if ( isReadOnly() ) { - e->ignore(); - return; - } - - if ( !isValid() ) - return; + const QRegion region( innerRect(), QRegion::Ellipse ); + if ( region.contains( event->pos() ) ) + QwtAbstractSlider::wheelEvent( event ); +} - double previous = prevValue(); - switch ( e->key() ) { - case Qt::Key_Down: - case Qt::Key_Left: - QwtDoubleRange::incValue(-1); - break; -#if QT_VERSION < 0x040000 - case Qt::Key_Prior: -#else - case Qt::Key_PageUp: -#endif - QwtDoubleRange::incValue(-pageSize()); - break; - case Qt::Key_Home: - setValue(minValue()); - break; - - case Qt::Key_Up: - case Qt::Key_Right: - QwtDoubleRange::incValue(1); - break; -#if QT_VERSION < 0x040000 - case Qt::Key_Next: -#else - case Qt::Key_PageDown: -#endif - QwtDoubleRange::incValue(pageSize()); - break; - case Qt::Key_End: - setValue(maxValue()); - break; - default: - ; - e->ignore(); +void QwtDial::setAngleRange( double angle, double span ) +{ + QwtRoundScaleDraw *sd = const_cast( scaleDraw() ); + if ( sd ) + { + angle = qwtNormalizeDegrees( angle - 270.0 ); + sd->setAngleRange( angle, angle + span ); } - - if (value() != previous) - emit sliderMoved(value()); } /*! - \brief Update the mask of the dial - - In case of "hasVisibleBackground() == false", the backgound is - transparent by a mask. + Invalidate the internal caches and call + QwtAbstractSlider::scaleChange() + */ +void QwtDial::scaleChange() +{ + invalidateCache(); + QwtAbstractSlider::scaleChange(); +} - \sa showBackground(), hasVisibleBackground() -*/ -void QwtDial::updateMask() +void QwtDial::sliderChange() { - if ( d_data->visibleBackground ) - clearMask(); - else - setMask(QRegion(boundingRect(), QRegion::Ellipse)); + setAngleRange( d_data->origin + d_data->minScaleArc, + d_data->maxScaleArc - d_data->minScaleArc ); + + if ( mode() == RotateScale ) + { + const double arc = transform( value() ) - scaleMap().p1(); + setAngleRange( d_data->origin - arc, + d_data->maxScaleArc - d_data->minScaleArc ); + } + + QwtAbstractSlider::sliderChange(); } diff --git a/libs/qwt/qwt_dial.h b/libs/qwt/qwt_dial.h index 37e5e783f..a409b4be6 100644 --- a/libs/qwt/qwt_dial.h +++ b/libs/qwt/qwt_dial.h @@ -7,38 +7,17 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ -// vim: expandtab - #ifndef QWT_DIAL_H #define QWT_DIAL_H 1 -#include -#include #include "qwt_global.h" #include "qwt_abstract_slider.h" -#include "qwt_round_scale_draw.h" +#include "qwt_abstract_scale_draw.h" +#include +#include class QwtDialNeedle; -class QwtDial; - -/*! - \brief A special scale draw made for QwtDial - - \sa QwtDial, QwtCompass -*/ -class QWT_EXPORT QwtDialScaleDraw: public QwtRoundScaleDraw -{ -public: - explicit QwtDialScaleDraw(QwtDial *); - virtual QwtText label(double value) const; - - void setPenWidth(uint); - uint penWidth() const; - -private: - QwtDial *d_parent; - int d_penWidth; -}; +class QwtRoundScaleDraw; /*! \brief QwtDial class provides a rounded range control. @@ -52,33 +31,34 @@ private: of the dial. Depending on Mode one of them is fixed and the other is rotating. If not isReadOnly() the dial can be rotated by dragging the mouse or using keyboard inputs - (see keyPressEvent()). A dial might be wrapping, what means + (see QwtAbstractSlider::keyPressEvent()). A dial might be wrapping, what means a rotation below/above one limit continues on the other limit (f.e compass). The scale might cover any arc of the dial, its values are related to the origin() of the dial. - Qwt is missing a set of good looking needles (QwtDialNeedle). - Contributions are very welcome. + Often dials have to be updated very often according to values from external + devices. For these high refresh rates QwtDial caches as much as possible. + For derived classes it might be necessary to clear these caches manually + according to attribute changes using invalidateCache(). \sa QwtCompass, QwtAnalogClock, QwtDialNeedle - \note The examples/dials example shows different types of dials. + \note The controls and dials examples shows different types of dials. + \note QDial is more similar to QwtKnob than to QwtDial */ class QWT_EXPORT QwtDial: public QwtAbstractSlider { Q_OBJECT - Q_ENUMS(Shadow) - Q_ENUMS(Mode) + Q_ENUMS( Shadow Mode Direction ) - Q_PROPERTY(bool visibleBackground READ hasVisibleBackground WRITE showBackground) - Q_PROPERTY(int lineWidth READ lineWidth WRITE setLineWidth) - Q_PROPERTY(Shadow frameShadow READ frameShadow WRITE setFrameShadow) - Q_PROPERTY(Mode mode READ mode WRITE setMode) - Q_PROPERTY(double origin READ origin WRITE setOrigin) - Q_PROPERTY(bool wrapping READ wrapping WRITE setWrapping) + Q_PROPERTY( int lineWidth READ lineWidth WRITE setLineWidth ) + Q_PROPERTY( Shadow frameShadow READ frameShadow WRITE setFrameShadow ) + Q_PROPERTY( Mode mode READ mode WRITE setMode ) + Q_PROPERTY( double origin READ origin WRITE setOrigin ) + Q_PROPERTY( double minScaleArc READ minScaleArc WRITE setMinScaleArc ) + Q_PROPERTY( double maxScaleArc READ maxScaleArc WRITE setMaxScaleArc ) - friend class QwtDialScaleDraw; public: /*! @@ -89,120 +69,97 @@ public: The following enum is made for the designer only. It is safe to use QFrame::Shadow instead. */ - enum Shadow { + enum Shadow + { + //! QFrame::Plain Plain = QFrame::Plain, + + //! QFrame::Raised Raised = QFrame::Raised, - Sunken = QFrame::Sunken - }; - //! see QwtDial::setScaleOptions - enum ScaleOptions { - ScaleBackbone = 1, - ScaleTicks = 2, - ScaleLabel = 4 + //! QFrame::Sunken + Sunken = QFrame::Sunken }; - /*! - In case of RotateNeedle the needle is rotating, in case of - RotateScale, the needle points to origin() - and the scale is rotating. - */ - enum Mode { + //! Mode controlling whether the needle or the scale is rotating + enum Mode + { + //! The needle is rotating RotateNeedle, + + //! The needle is fixed, the scales are rotating RotateScale }; - explicit QwtDial( QWidget *parent = NULL); -#if QT_VERSION < 0x040000 - explicit QwtDial( QWidget *parent, const char *name); -#endif - + explicit QwtDial( QWidget *parent = NULL ); virtual ~QwtDial(); - void setFrameShadow(Shadow); + void setFrameShadow( Shadow ); Shadow frameShadow() const; - bool hasVisibleBackground() const; - void showBackground(bool); - - void setLineWidth(int); + void setLineWidth( int ); int lineWidth() const; - void setMode(Mode); + void setMode( Mode ); Mode mode() const; - virtual void setWrapping(bool); - bool wrapping() const; - - virtual void setScale(int maxMajIntv, int maxMinIntv, double step = 0.0); - - void setScaleArc(double min, double max); - void setScaleOptions(int); - void setScaleTicks(int minLen, int medLen, int majLen, int penWidth = 1); + void setScaleArc( double min, double max ); + void setMinScaleArc( double min ); double minScaleArc() const; + + void setMaxScaleArc( double min ); double maxScaleArc() const; - virtual void setOrigin(double); + virtual void setOrigin( double ); double origin() const; - virtual void setNeedle(QwtDialNeedle *); + void setNeedle( QwtDialNeedle * ); const QwtDialNeedle *needle() const; QwtDialNeedle *needle(); QRect boundingRect() const; - QRect contentsRect() const; - virtual QRect scaleContentsRect() const; + QRect innerRect() const; + + virtual QRect scaleInnerRect() const; virtual QSize sizeHint() const; virtual QSize minimumSizeHint() const; - virtual void setScaleDraw(QwtDialScaleDraw *); + void setScaleDraw( QwtRoundScaleDraw * ); - QwtDialScaleDraw *scaleDraw(); - const QwtDialScaleDraw *scaleDraw() const; + QwtRoundScaleDraw *scaleDraw(); + const QwtRoundScaleDraw *scaleDraw() const; protected: - virtual void paintEvent(QPaintEvent *); - virtual void resizeEvent(QResizeEvent *); - virtual void keyPressEvent(QKeyEvent *); + virtual void wheelEvent( QWheelEvent * ); + virtual void paintEvent( QPaintEvent * ); + virtual void changeEvent( QEvent * ); - virtual void updateMask(); - - virtual void drawFrame(QPainter *p); - virtual void drawContents(QPainter *) const; - virtual void drawFocusIndicator(QPainter *) const; - - virtual void drawScale(QPainter *, const QPoint ¢er, - int radius, double origin, double arcMin, double arcMax) const; - - /*! - Draw the contents inside the scale + virtual void drawFrame( QPainter *p ); + virtual void drawContents( QPainter * ) const; + virtual void drawFocusIndicator( QPainter * ) const; - Paints nothing. + void invalidateCache(); - \param painter Painter - \param center Center of the contents circle - \param radius Radius of the contents circle - */ - virtual void drawScaleContents(QPainter *painter, const QPoint ¢er, - int radius) const; + virtual void drawScale( QPainter *, + const QPointF ¢er, double radius ) const; - virtual void drawNeedle(QPainter *, const QPoint &, - int radius, double direction, QPalette::ColorGroup) const; + virtual void drawScaleContents( QPainter *painter, + const QPointF ¢er, double radius ) const; - virtual QwtText scaleLabel(double) const; - void updateScale(); + virtual void drawNeedle( QPainter *, const QPointF &, + double radius, double direction, QPalette::ColorGroup ) const; - virtual void rangeChange(); - virtual void valueChange(); + virtual double scrolledTo( const QPoint & ) const; + virtual bool isScrollPosition( const QPoint & ) const; - virtual double getValue(const QPoint &); - virtual void getScrollMode(const QPoint &, - int &scrollMode, int &direction); + virtual void sliderChange(); + virtual void scaleChange(); private: - void initDial(); + void setAngleRange( double angle, double span ); + void drawNeedle( QPainter * ) const; class PrivateData; PrivateData *d_data; diff --git a/libs/qwt/qwt_dial_needle.cpp b/libs/qwt/qwt_dial_needle.cpp index d403b18dc..1b53a3d5b 100644 --- a/libs/qwt/qwt_dial_needle.cpp +++ b/libs/qwt/qwt_dial_needle.cpp @@ -7,559 +7,434 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ -#include -#include -#include +#include "qwt_dial_needle.h" +#include "qwt_global.h" #include "qwt_math.h" #include "qwt_painter.h" -#include "qwt_polygon.h" -#include "qwt_dial_needle.h" +#include +#include -#if QT_VERSION < 0x040000 -typedef QColorGroup QwtPalette; -#else -typedef QPalette QwtPalette; +#if QT_VERSION < 0x040601 +#define qFastSin(x) qSin(x) +#define qFastCos(x) qCos(x) #endif -//! Constructor -QwtDialNeedle::QwtDialNeedle(): - d_palette(QApplication::palette()) +static void qwtDrawStyle1Needle( QPainter *painter, + const QPalette &palette, QPalette::ColorGroup colorGroup, + double length ) { -} + const double r[] = { 0.4, 0.3, 1, 0.8, 1, 0.3, 0.4 }; + const double a[] = { -45, -20, -15, 0, 15, 20, 45 }; -//! Destructor -QwtDialNeedle::~QwtDialNeedle() -{ -} + QPainterPath path; + for ( int i = 0; i < 7; i++ ) + { + const double angle = a[i] / 180.0 * M_PI; + const double radius = r[i] * length; -/*! - Sets the palette for the needle. + const double x = radius * qFastCos( angle ); + const double y = radius * qFastSin( angle ); - \param palette New Palette -*/ -void QwtDialNeedle::setPalette(const QPalette &palette) -{ - d_palette = palette; -} + path.lineTo( x, -y ); + } -/*! - \return the palette of the needle. -*/ -const QPalette &QwtDialNeedle::palette() const -{ - return d_palette; + painter->setPen( Qt::NoPen ); + painter->setBrush( palette.brush( colorGroup, QPalette::Light ) ); + painter->drawPath( path ); } -//! Draw the knob -void QwtDialNeedle::drawKnob(QPainter *painter, - const QPoint &pos, int width, const QBrush &brush, bool sunken) +static void qwtDrawStyle2Needle( QPainter *painter, + const QPalette &palette, QPalette::ColorGroup colorGroup, double length ) { - painter->save(); + const double ratioX = 0.7; + const double ratioY = 0.3; - QRect rect(0, 0, width, width); - rect.moveCenter(pos); + QPainterPath path1; + path1.lineTo( ratioX * length, 0.0 ); + path1.lineTo( length, ratioY * length ); - painter->setPen(Qt::NoPen); - painter->setBrush(brush); - painter->drawEllipse(rect); + QPainterPath path2; + path2.lineTo( ratioX * length, 0.0 ); + path2.lineTo( length, -ratioY * length ); - painter->setBrush(Qt::NoBrush); + painter->setPen( Qt::NoPen ); - const int colorOffset = 20; + painter->setBrush( palette.brush( colorGroup, QPalette::Light ) ); + painter->drawPath( path1 ); - int startAngle = 45; - if ( sunken ) - startAngle += 180; + painter->setBrush( palette.brush( colorGroup, QPalette::Dark ) ); + painter->drawPath( path2 ); +} - QPen pen; - pen.setWidth(1); +static void qwtDrawShadedPointer( QPainter *painter, + const QColor &lightColor, const QColor &darkColor, + double length, double width ) +{ + const double peak = qMax( length / 10.0, 5.0 ); - pen.setColor(brush.color().dark(100 - colorOffset)); - painter->setPen(pen); - painter->drawArc(rect, startAngle * 16, 180 * 16); + const double knobWidth = width + 8; + QRectF knobRect( 0, 0, knobWidth, knobWidth ); + knobRect.moveCenter( QPointF(0, 0) ); - pen.setColor(brush.color().dark(100 + colorOffset)); - painter->setPen(pen); - painter->drawArc(rect, (startAngle + 180) * 16, 180 * 16); + QPainterPath path1; + path1.lineTo( 0.0, 0.5 * width ); + path1.lineTo( length - peak, 0.5 * width ); + path1.lineTo( length, 0.0 ); + path1.lineTo( 0.0, 0.0 ); - painter->restore(); -} + QPainterPath arcPath1; + arcPath1.arcTo( knobRect, 0.0, -90.0 ); -/*! - Constructor -*/ -QwtDialSimpleNeedle::QwtDialSimpleNeedle(Style style, bool hasKnob, - const QColor &mid, const QColor &base): - d_style(style), - d_hasKnob(hasKnob), - d_width(-1) -{ - QPalette palette; - for ( int i = 0; i < QPalette::NColorGroups; i++ ) { - palette.setColor((QPalette::ColorGroup)i, - QwtPalette::Mid, mid); - palette.setColor((QPalette::ColorGroup)i, - QwtPalette::Base, base); - } + path1 = path1.united( arcPath1 ); - setPalette(palette); -} + QPainterPath path2; + path2.lineTo( 0.0, -0.5 * width ); + path2.lineTo( length - peak, -0.5 * width ); + path2.lineTo( length, 0.0 ); + path2.lineTo( 0.0, 0.0 ); -//! Set the width of the needle -void QwtDialSimpleNeedle::setWidth(int width) -{ - d_width = width; -} + QPainterPath arcPath2; + arcPath2.arcTo( knobRect, 0.0, 90.0 ); -/*! - \return the width of the needle -*/ -int QwtDialSimpleNeedle::width() const -{ - return d_width; -} + path2 = path2.united( arcPath2 ); -/*! - Draw the needle + painter->setPen( Qt::NoPen ); - \param painter Painter - \param center Center of the dial, start position for the needle - \param length Length of the needle - \param direction Direction of the needle, in degrees counter clockwise - \param colorGroup Color group, used for painting -*/ -void QwtDialSimpleNeedle::draw(QPainter *painter, const QPoint ¢er, - int length, double direction, QPalette::ColorGroup colorGroup) const -{ - if ( d_style == Arrow ) { - drawArrowNeedle(painter, palette(), colorGroup, - center, length, d_width, direction, d_hasKnob); - } else { - drawRayNeedle(painter, palette(), colorGroup, - center, length, d_width, direction, d_hasKnob); - } + painter->setBrush( lightColor ); + painter->drawPath( path1 ); + + painter->setBrush( darkColor ); + painter->drawPath( path2 ); } -/*! - Draw a needle looking like a ray -*/ -void QwtDialSimpleNeedle::drawRayNeedle(QPainter *painter, - const QPalette &palette, QPalette::ColorGroup colorGroup, - const QPoint ¢er, int length, int width, double direction, - bool hasKnob) +static void qwtDrawArrowNeedle( QPainter *painter, + const QPalette &palette, QPalette::ColorGroup colorGroup, + double length, double width ) { if ( width <= 0 ) - width = 5; + width = qMax( length * 0.06, 9.0 ); - direction *= M_PI / 180.0; + const double peak = qMax( 2.0, 0.4 * width ); - painter->save(); + QPainterPath path; + path.moveTo( 0.0, 0.5 * width ); + path.lineTo( length - peak, 0.3 * width ); + path.lineTo( length, 0.0 ); + path.lineTo( length - peak, -0.3 * width ); + path.lineTo( 0.0, -0.5 * width ); - const QPoint p1(center.x() + 1, center.y() + 2); - const QPoint p2 = qwtPolar2Pos(p1, length, direction); - - if ( width == 1 ) { - const QColor midColor = - palette.color(colorGroup, QwtPalette::Mid); - - painter->setPen(QPen(midColor, 1)); - painter->drawLine(p1, p2); - } else { - QwtPolygon pa(4); - pa.setPoint(0, qwtPolar2Pos(p1, width / 2, direction + M_PI_2)); - pa.setPoint(1, qwtPolar2Pos(p2, width / 2, direction + M_PI_2)); - pa.setPoint(2, qwtPolar2Pos(p2, width / 2, direction - M_PI_2)); - pa.setPoint(3, qwtPolar2Pos(p1, width / 2, direction - M_PI_2)); - - painter->setPen(Qt::NoPen); - painter->setBrush(palette.brush(colorGroup, QwtPalette::Mid)); - painter->drawPolygon(pa); - } - if ( hasKnob ) { - int knobWidth = qwtMax(qRound(width * 0.7), 5); - if ( knobWidth % 2 == 0 ) - knobWidth++; - - drawKnob(painter, center, knobWidth, - palette.brush(colorGroup, QwtPalette::Base), - false); - } + QRectF br = path.boundingRect(); - painter->restore(); -} + QPalette pal( palette.color( QPalette::Mid ) ); + QColor c1 = pal.color( QPalette::Light ); + QColor c2 = pal.color( QPalette::Dark ); -/*! - Draw a needle looking like an arrow -*/ -void QwtDialSimpleNeedle::drawArrowNeedle(QPainter *painter, - const QPalette &palette, QPalette::ColorGroup colorGroup, - const QPoint ¢er, int length, int width, - double direction, bool hasKnob) -{ - direction *= M_PI / 180.0; + QLinearGradient gradient( br.topLeft(), br.bottomLeft() ); + gradient.setColorAt( 0.0, c1 ); + gradient.setColorAt( 0.5, c1 ); + gradient.setColorAt( 0.5001, c2 ); + gradient.setColorAt( 1.0, c2 ); - painter->save(); + QPen pen( gradient, 1 ); + pen.setJoinStyle( Qt::MiterJoin ); - if ( width <= 0 ) { - width = (int)qwtMax(length * 0.06, 9.0); - if ( width % 2 == 0 ) - width++; - } + painter->setPen( pen ); + painter->setBrush( palette.brush( colorGroup, QPalette::Mid ) ); + + painter->drawPath( path ); +} - const int peak = 3; - const QPoint p1(center.x() + 1, center.y() + 1); - const QPoint p2 = qwtPolar2Pos(p1, length - peak, direction); - const QPoint p3 = qwtPolar2Pos(p1, length, direction); +static void qwtDrawTriangleNeedle( QPainter *painter, + const QPalette &palette, QPalette::ColorGroup colorGroup, + double length ) +{ + const double width = qRound( length / 3.0 ); - QwtPolygon pa(5); - pa.setPoint(0, qwtPolar2Pos(p1, width / 2, direction - M_PI_2)); - pa.setPoint(1, qwtPolar2Pos(p2, 1, direction - M_PI_2)); - pa.setPoint(2, p3); - pa.setPoint(3, qwtPolar2Pos(p2, 1, direction + M_PI_2)); - pa.setPoint(4, qwtPolar2Pos(p1, width / 2, direction + M_PI_2)); + QPainterPath path[4]; - painter->setPen(Qt::NoPen); - painter->setBrush(palette.brush(colorGroup, QwtPalette::Mid)); - painter->drawPolygon(pa); + path[0].lineTo( length, 0.0 ); + path[0].lineTo( 0.0, width / 2 ); - QwtPolygon shadowPa(3); + path[1].lineTo( length, 0.0 ); + path[1].lineTo( 0.0, -width / 2 ); - const int colorOffset = 10; + path[2].lineTo( -length, 0.0 ); + path[2].lineTo( 0.0, width / 2 ); - int i; - for ( i = 0; i < 3; i++ ) - shadowPa.setPoint(i, pa[i]); + path[3].lineTo( -length, 0.0 ); + path[3].lineTo( 0.0, -width / 2 ); - const QColor midColor = palette.color(colorGroup, QwtPalette::Mid); - painter->setPen(midColor.dark(100 + colorOffset)); - painter->drawPolyline(shadowPa); + const int colorOffset = 10; + const QColor darkColor = palette.color( colorGroup, QPalette::Dark ); + const QColor lightColor = palette.color( colorGroup, QPalette::Light ); - for ( i = 0; i < 3; i++ ) - shadowPa.setPoint(i, pa[i + 2]); + QColor color[4]; + color[0] = darkColor.light( 100 + colorOffset ); + color[1] = darkColor.dark( 100 + colorOffset ); + color[2] = lightColor.light( 100 + colorOffset ); + color[3] = lightColor.dark( 100 + colorOffset ); - painter->setPen(midColor.dark(100 - colorOffset)); - painter->drawPolyline(shadowPa); + painter->setPen( Qt::NoPen ); - if ( hasKnob ) { - drawKnob(painter, center, qRound(width * 1.3), - palette.brush(colorGroup, QwtPalette::Base), - false); + for ( int i = 0; i < 4; i++ ) + { + painter->setBrush( color[i] ); + painter->drawPath( path[i] ); } - - painter->restore(); } //! Constructor - -QwtCompassMagnetNeedle::QwtCompassMagnetNeedle(Style style, - const QColor &light, const QColor &dark): - d_style(style) +QwtDialNeedle::QwtDialNeedle(): + d_palette( QApplication::palette() ) { - QPalette palette; - for ( int i = 0; i < QPalette::NColorGroups; i++ ) { - palette.setColor((QPalette::ColorGroup)i, - QwtPalette::Light, light); - palette.setColor((QPalette::ColorGroup)i, - QwtPalette::Dark, dark); - palette.setColor((QPalette::ColorGroup)i, - QwtPalette::Base, Qt::darkGray); - } +} - setPalette(palette); +//! Destructor +QwtDialNeedle::~QwtDialNeedle() +{ } /*! - Draw the needle + Sets the palette for the needle. - \param painter Painter - \param center Center of the dial, start position for the needle - \param length Length of the needle - \param direction Direction of the needle, in degrees counter clockwise - \param colorGroup Color group, used for painting + \param palette New Palette */ -void QwtCompassMagnetNeedle::draw(QPainter *painter, const QPoint ¢er, - int length, double direction, QPalette::ColorGroup colorGroup) const +void QwtDialNeedle::setPalette( const QPalette &palette ) { - if ( d_style == ThinStyle ) { - drawThinNeedle(painter, palette(), colorGroup, - center, length, direction); - } else { - drawTriangleNeedle(painter, palette(), colorGroup, - center, length, direction); - } + d_palette = palette; } /*! - Draw a compass needle - - \param painter Painter - \param palette Palette - \param colorGroup Color group - \param center Center, where the needle starts - \param length Length of the needle - \param direction Direction + \return the palette of the needle. */ -void QwtCompassMagnetNeedle::drawTriangleNeedle(QPainter *painter, - const QPalette &palette, QPalette::ColorGroup colorGroup, - const QPoint ¢er, int length, double direction) +const QPalette &QwtDialNeedle::palette() const { - const QBrush darkBrush = palette.brush(colorGroup, QwtPalette::Dark); - const QBrush lightBrush = palette.brush(colorGroup, QwtPalette::Light); - - QBrush brush; - - const int width = qRound(length / 3.0); - const int colorOffset = 10; - - painter->save(); - painter->setPen(Qt::NoPen); - - const QPoint arrowCenter(center.x() + 1, center.y() + 1); - - QwtPolygon pa(3); - pa.setPoint(0, arrowCenter); - pa.setPoint(1, qwtDegree2Pos(arrowCenter, length, direction)); - - pa.setPoint(2, qwtDegree2Pos(arrowCenter, width / 2, direction + 90.0)); - - brush = darkBrush; - brush.setColor(brush.color().dark(100 + colorOffset)); - painter->setBrush(brush); - painter->drawPolygon(pa); - - pa.setPoint(2, qwtDegree2Pos(arrowCenter, width / 2, direction - 90.0)); - - brush = darkBrush; - brush.setColor(brush.color().dark(100 - colorOffset)); - painter->setBrush(brush); - painter->drawPolygon(pa); - - // -- - - pa.setPoint(1, qwtDegree2Pos(arrowCenter, length, direction + 180.0)); - - pa.setPoint(2, qwtDegree2Pos(arrowCenter, width / 2, direction + 90.0)); - - brush = lightBrush; - brush.setColor(brush.color().dark(100 + colorOffset)); - painter->setBrush(brush); - painter->drawPolygon(pa); - - pa.setPoint(2, qwtDegree2Pos(arrowCenter, width / 2, direction - 90.0)); - - brush = lightBrush; - brush.setColor(brush.color().dark(100 - colorOffset)); - painter->setBrush(brush); - painter->drawPolygon(pa); - - painter->restore(); + return d_palette; } /*! - Draw a compass needle + Draw the needle \param painter Painter - \param palette Palette - \param colorGroup Color group - \param center Center, where the needle starts + \param center Center of the dial, start position for the needle \param length Length of the needle - \param direction Direction + \param direction Direction of the needle, in degrees counter clockwise + \param colorGroup Color group, used for painting */ -void QwtCompassMagnetNeedle::drawThinNeedle(QPainter *painter, - const QPalette &palette, QPalette::ColorGroup colorGroup, - const QPoint ¢er, int length, double direction) +void QwtDialNeedle::draw( QPainter *painter, + const QPointF ¢er, double length, double direction, + QPalette::ColorGroup colorGroup ) const { - const QBrush darkBrush = palette.brush(colorGroup, QwtPalette::Dark); - const QBrush lightBrush = palette.brush(colorGroup, QwtPalette::Light); - const QBrush baseBrush = palette.brush(colorGroup, QwtPalette::Base); - - const int colorOffset = 10; - const int width = qwtMax(qRound(length / 6.0), 3); - painter->save(); - const QPoint arrowCenter(center.x() + 1, center.y() + 1); + painter->translate( center ); + painter->rotate( -direction ); - drawPointer(painter, darkBrush, colorOffset, - arrowCenter, length, width, direction); - drawPointer(painter, lightBrush, -colorOffset, - arrowCenter, length, width, direction + 180.0); - - drawKnob(painter, arrowCenter, width, baseBrush, true); + drawNeedle( painter, length, colorGroup ); painter->restore(); } -/*! - Draw a compass needle - - \param painter Painter - \param brush Brush - \param colorOffset Color offset - \param center Center, where the needle starts - \param length Length of the needle - \param width Width of the needle - \param direction Direction -*/ -void QwtCompassMagnetNeedle::drawPointer( - QPainter *painter, const QBrush &brush, - int colorOffset, const QPoint ¢er, int length, - int width, double direction) +//! Draw the knob +void QwtDialNeedle::drawKnob( QPainter *painter, + double width, const QBrush &brush, bool sunken ) const { - painter->save(); - - const int peak = qwtMax(qRound(length / 10.0), 5); + QPalette palette( brush.color() ); - const int knobWidth = width + 8; - QRect knobRect(0, 0, knobWidth, knobWidth); - knobRect.moveCenter(center); + QColor c1 = palette.color( QPalette::Light ); + QColor c2 = palette.color( QPalette::Dark ); - QwtPolygon pa(5); + if ( sunken ) + qSwap( c1, c2 ); - pa.setPoint(0, qwtDegree2Pos(center, width / 2, direction + 90.0)); - pa.setPoint(1, center); - pa.setPoint(2, qwtDegree2Pos(pa.point(1), length - peak, direction)); - pa.setPoint(3, qwtDegree2Pos(center, length, direction)); - pa.setPoint(4, qwtDegree2Pos(pa.point(0), length - peak, direction)); + QRectF rect( 0.0, 0.0, width, width ); + rect.moveCenter( painter->combinedTransform().map( QPointF() ) ); - painter->setPen(Qt::NoPen); + QLinearGradient gradient( rect.topLeft(), rect.bottomRight() ); + gradient.setColorAt( 0.0, c1 ); + gradient.setColorAt( 0.3, c1 ); + gradient.setColorAt( 0.7, c2 ); + gradient.setColorAt( 1.0, c2 ); - QBrush darkBrush = brush; - darkBrush.setColor(darkBrush.color().dark(100 + colorOffset)); - painter->setBrush(darkBrush); - painter->drawPolygon(pa); - painter->drawPie(knobRect, qRound(direction * 16), 90 * 16); + painter->save(); - pa.setPoint(0, qwtDegree2Pos(center, width / 2, direction - 90.0)); - pa.setPoint(4, qwtDegree2Pos(pa.point(0), length - peak, direction)); + painter->resetTransform(); - QBrush lightBrush = brush; - lightBrush.setColor(lightBrush.color().dark(100 - colorOffset)); - painter->setBrush(lightBrush); - painter->drawPolygon(pa); - painter->drawPie(knobRect, qRound(direction * 16), -90 * 16); + painter->setPen( QPen( gradient, 1 ) ); + painter->setBrush( brush ); + painter->drawEllipse( rect ); painter->restore(); } /*! - Constructor + Constructor - \param style Arrow style - \param light Light color - \param dark Dark color + \param style Style + \param hasKnob With/Without knob + \param mid Middle color + \param base Base color */ -QwtCompassWindArrow::QwtCompassWindArrow(Style style, - const QColor &light, const QColor &dark): - d_style(style) +QwtDialSimpleNeedle::QwtDialSimpleNeedle( Style style, bool hasKnob, + const QColor &mid, const QColor &base ): + d_style( style ), + d_hasKnob( hasKnob ), + d_width( -1 ) { QPalette palette; - for ( int i = 0; i < QPalette::NColorGroups; i++ ) { - palette.setColor((QPalette::ColorGroup)i, - QwtPalette::Light, light); - palette.setColor((QPalette::ColorGroup)i, - QwtPalette::Dark, dark); - } + palette.setColor( QPalette::Mid, mid ); + palette.setColor( QPalette::Base, base ); - setPalette(palette); + setPalette( palette ); } /*! - Draw the needle + Set the width of the needle + \param width Width + \sa width() +*/ +void QwtDialSimpleNeedle::setWidth( double width ) +{ + d_width = width; +} - \param painter Painter - \param center Center of the dial, start position for the needle - \param length Length of the needle - \param direction Direction of the needle, in degrees counter clockwise - \param colorGroup Color group, used for painting +/*! + \return the width of the needle + \sa setWidth() */ -void QwtCompassWindArrow::draw(QPainter *painter, const QPoint ¢er, - int length, double direction, QPalette::ColorGroup colorGroup) const +double QwtDialSimpleNeedle::width() const { - if ( d_style == Style1 ) { - drawStyle1Needle(painter, palette(), colorGroup, - center, length, direction); - } else { - drawStyle2Needle(painter, palette(), colorGroup, - center, length, direction); - } + return d_width; } /*! - Draw a compass needle + Draw the needle \param painter Painter - \param palette Palette - \param colorGroup colorGroup - \param center Center of the dial, start position for the needle \param length Length of the needle - \param direction Direction of the needle, in degrees counter clockwise + \param colorGroup Color group, used for painting */ -void QwtCompassWindArrow::drawStyle1Needle(QPainter *painter, - const QPalette &palette, QPalette::ColorGroup colorGroup, - const QPoint ¢er, int length, double direction) +void QwtDialSimpleNeedle::drawNeedle( QPainter *painter, + double length, QPalette::ColorGroup colorGroup ) const { - const QBrush lightBrush = palette.brush(colorGroup, QwtPalette::Light); + double knobWidth = 0.0; + double width = d_width; - const double AR1[] = {0, 0.4, 0.3, 1, 0.8, 1, 0.3, 0.4}; - const double AW1[] = {0, -45, -20, -15, 0, 15, 20, 45}; + if ( d_style == Arrow ) + { + if ( width <= 0.0 ) + width = qMax(length * 0.06, 6.0); - const QPoint arrowCenter(center.x() + 1, center.y() + 1); + qwtDrawArrowNeedle( painter, + palette(), colorGroup, length, width ); - QwtPolygon pa(8); - pa.setPoint(0, arrowCenter); - for (int i=1; i<8; i++) { - const QPoint p = qwtDegree2Pos(center, - AR1[i] * length, direction + AW1[i]); - pa.setPoint(i, p); + knobWidth = qMin( width * 2.0, 0.2 * length ); + } + else + { + if ( width <= 0.0 ) + width = 5.0; + + QPen pen ( palette().brush( colorGroup, QPalette::Mid ), width ); + pen.setCapStyle( Qt::FlatCap ); + + painter->setPen( pen ); + painter->drawLine( QPointF( 0.0, 0.0 ), QPointF( length, 0.0 ) ); + + knobWidth = qMax( width * 3.0, 5.0 ); } - painter->save(); - painter->setPen(Qt::NoPen); - painter->setBrush(lightBrush); - painter->drawPolygon(pa); - painter->restore(); + if ( d_hasKnob && knobWidth > 0.0 ) + { + drawKnob( painter, knobWidth, + palette().brush( colorGroup, QPalette::Base ), false ); + } } -/*! - Draw a compass needle - - \param painter Painter - \param palette Palette - \param colorGroup colorGroup - \param center Center of the dial, start position for the needle - \param length Length of the needle - \param direction Direction of the needle, in degrees counter clockwise -*/ -void QwtCompassWindArrow::drawStyle2Needle(QPainter *painter, - const QPalette &palette, QPalette::ColorGroup colorGroup, - const QPoint ¢er, int length, double direction) +//! Constructor +QwtCompassMagnetNeedle::QwtCompassMagnetNeedle( Style style, + const QColor &light, const QColor &dark ): + d_style( style ) { - const QBrush lightBrush = palette.brush(colorGroup, QwtPalette::Light); - const QBrush darkBrush = palette.brush(colorGroup, QwtPalette::Dark); + QPalette palette; + palette.setColor( QPalette::Light, light ); + palette.setColor( QPalette::Dark, dark ); + palette.setColor( QPalette::Base, Qt::gray ); - painter->save(); - painter->setPen(Qt::NoPen); + setPalette( palette ); +} - const double angle = 12.0; - const double ratio = 0.7; +/*! + Draw the needle - const QPoint arrowCenter(center.x() + 1, center.y() + 1); + \param painter Painter + \param length Length of the needle + \param colorGroup Color group, used for painting +*/ +void QwtCompassMagnetNeedle::drawNeedle( QPainter *painter, + double length, QPalette::ColorGroup colorGroup ) const +{ + if ( d_style == ThinStyle ) + { + const double width = qMax( length / 6.0, 3.0 ); + + const int colorOffset = 10; + + const QColor light = palette().color( colorGroup, QPalette::Light ); + const QColor dark = palette().color( colorGroup, QPalette::Dark ); + + qwtDrawShadedPointer( painter, + dark.light( 100 + colorOffset ), + dark.dark( 100 + colorOffset ), + length, width ); + + painter->rotate( 180.0 ); + + qwtDrawShadedPointer( painter, + light.light( 100 + colorOffset ), + light.dark( 100 + colorOffset ), + length, width ); + + const QBrush baseBrush = palette().brush( colorGroup, QPalette::Base ); + drawKnob( painter, width, baseBrush, true ); + } + else + { + qwtDrawTriangleNeedle( painter, palette(), colorGroup, length ); + } +} - QwtPolygon pa(3); +/*! + Constructor - pa.setPoint(0, center); - pa.setPoint(2, qwtDegree2Pos(arrowCenter, ratio * length, direction)); + \param style Arrow style + \param light Light color + \param dark Dark color +*/ +QwtCompassWindArrow::QwtCompassWindArrow( Style style, + const QColor &light, const QColor &dark ): + d_style( style ) +{ + QPalette palette; + palette.setColor( QPalette::Light, light ); + palette.setColor( QPalette::Dark, dark ); - pa.setPoint(1, qwtDegree2Pos(arrowCenter, length, direction + angle)); - painter->setBrush(darkBrush); - painter->drawPolygon(pa); + setPalette( palette ); +} - pa.setPoint(1, qwtDegree2Pos(arrowCenter, length, direction - angle)); - painter->setBrush(lightBrush); - painter->drawPolygon(pa); +/*! + Draw the needle - painter->restore(); + \param painter Painter + \param length Length of the needle + \param colorGroup Color group, used for painting +*/ +void QwtCompassWindArrow::drawNeedle( QPainter *painter, + double length, QPalette::ColorGroup colorGroup ) const +{ + if ( d_style == Style1 ) + qwtDrawStyle1Needle( painter, palette(), colorGroup, length ); + else + qwtDrawStyle2Needle( painter, palette(), colorGroup, length ); } - diff --git a/libs/qwt/qwt_dial_needle.h b/libs/qwt/qwt_dial_needle.h index e17d01bfd..d84384a0f 100644 --- a/libs/qwt/qwt_dial_needle.h +++ b/libs/qwt/qwt_dial_needle.h @@ -10,8 +10,8 @@ #ifndef QWT_DIAL_NEEDLE_H #define QWT_DIAL_NEEDLE_H 1 -#include #include "qwt_global.h" +#include class QPainter; class QPoint; @@ -22,9 +22,6 @@ class QPoint; QwtDialNeedle is a pointer that indicates a value by pointing to a specific direction. - Qwt is missing a set of good looking needles. - Contributions are very welcome. - \sa QwtDial, QwtCompass */ @@ -34,25 +31,34 @@ public: QwtDialNeedle(); virtual ~QwtDialNeedle(); + virtual void setPalette( const QPalette & ); + const QPalette &palette() const; + + virtual void draw( QPainter *painter, const QPointF ¢er, + double length, double direction, + QPalette::ColorGroup = QPalette::Active ) const; + +protected: /*! - Draw the needle + \brief Draw the needle - \param painter Painter - \param center Center of the dial, start position for the needle - \param length Length of the needle - \param direction Direction of the needle, in degrees counter clockwise - \param cg Color group, used for painting - */ - virtual void draw(QPainter *painter, const QPoint ¢er, - int length, double direction, - QPalette::ColorGroup cg = QPalette::Active) const = 0; + The origin of the needle is at position (0.0, 0.0 ) + pointing in direction 0.0 ( = east ). - virtual void setPalette(const QPalette &); - const QPalette &palette() const; + The painter is already initialized with translation and + rotation. -protected: - static void drawKnob(QPainter *, const QPoint &pos, - int width, const QBrush &, bool sunken); + \param painter Painter + \param length Length of the needle + \param colorGroup Color group, used for painting + + \sa setPalette(), palette() + */ + virtual void drawNeedle( QPainter *painter, + double length, QPalette::ColorGroup colorGroup ) const = 0; + + virtual void drawKnob( QPainter *, double width, + const QBrush &, bool sunken ) const; private: QPalette d_palette; @@ -62,9 +68,10 @@ private: \brief A needle for dial widgets The following colors are used: - - QColorGroup::Mid\n + + - QPalette::Mid\n Pointer - - QColorGroup::base\n + - QPalette::Base\n Knob \sa QwtDial, QwtCompass @@ -74,34 +81,29 @@ class QWT_EXPORT QwtDialSimpleNeedle: public QwtDialNeedle { public: //! Style of the needle - enum Style { + enum Style + { + //! Arrow Arrow, + + //! A straight line from the center Ray }; - QwtDialSimpleNeedle(Style, bool hasKnob = true, - const QColor &mid = Qt::gray, const QColor &base = Qt::darkGray); - - virtual void draw(QPainter *, const QPoint &, int length, - double direction, QPalette::ColorGroup = QPalette::Active) const; + QwtDialSimpleNeedle( Style, bool hasKnob = true, + const QColor &mid = Qt::gray, const QColor &base = Qt::darkGray ); - static void drawArrowNeedle(QPainter *, - const QPalette&, QPalette::ColorGroup, - const QPoint &, int length, int width, double direction, - bool hasKnob); + void setWidth( double width ); + double width() const; - static void drawRayNeedle(QPainter *, - const QPalette&, QPalette::ColorGroup, - const QPoint &, int length, int width, double direction, - bool hasKnob); - - void setWidth(int width); - int width() const; +protected: + virtual void drawNeedle( QPainter *, double length, + QPalette::ColorGroup ) const; private: Style d_style; bool d_hasKnob; - int d_width; + double d_width; }; /*! @@ -111,11 +113,11 @@ private: north and south. The following colors are used: - - QColorGroup::Light\n + - QPalette::Light\n Used for pointing south - - QColorGroup::Dark\n + - QPalette::Dark\n Used for pointing north - - QColorGroup::Base\n + - QPalette::Base\n Knob (ThinStyle only) \sa QwtDial, QwtCompass @@ -125,28 +127,21 @@ class QWT_EXPORT QwtCompassMagnetNeedle: public QwtDialNeedle { public: //! Style of the needle - enum Style { + enum Style + { + //! A needle with a triangular shape TriangleStyle, + + //! A thin needle ThinStyle }; - QwtCompassMagnetNeedle(Style = TriangleStyle, - const QColor &light = Qt::white, const QColor &dark = Qt::red); - virtual void draw(QPainter *, const QPoint &, int length, - double direction, QPalette::ColorGroup = QPalette::Active) const; - - static void drawTriangleNeedle(QPainter *, - const QPalette &, QPalette::ColorGroup, - const QPoint &, int length, double direction); - - static void drawThinNeedle(QPainter *, - const QPalette &, QPalette::ColorGroup, - const QPoint &, int length, double direction); + QwtCompassMagnetNeedle( Style = TriangleStyle, + const QColor &light = Qt::white, const QColor &dark = Qt::red ); protected: - static void drawPointer(QPainter *painter, const QBrush &brush, - int colorOffset, const QPoint ¢er, - int length, int width, double direction); + virtual void drawNeedle( QPainter *, + double length, QPalette::ColorGroup ) const; private: Style d_style; @@ -157,9 +152,9 @@ private: QwtCompassWindArrow shows the direction where the wind comes from. - - QColorGroup::Light\n + - QPalette::Light\n Used for Style1, or the light half of Style2 - - QColorGroup::Dark\n + - QPalette::Dark\n Used for the dark half of Style2 \sa QwtDial, QwtCompass @@ -169,27 +164,24 @@ class QWT_EXPORT QwtCompassWindArrow: public QwtDialNeedle { public: //! Style of the arrow - enum Style { + enum Style + { + //! A needle pointing to the center Style1, + + //! A needle pointing to the center Style2 }; - QwtCompassWindArrow(Style, const QColor &light = Qt::white, - const QColor &dark = Qt::gray); + QwtCompassWindArrow( Style, const QColor &light = Qt::white, + const QColor &dark = Qt::gray ); - virtual void draw(QPainter *, const QPoint &, int length, - double direction, QPalette::ColorGroup = QPalette::Active) const; - - static void drawStyle1Needle(QPainter *, - const QPalette &, QPalette::ColorGroup, - const QPoint &, int length, double direction); - - static void drawStyle2Needle(QPainter *, - const QPalette &, QPalette::ColorGroup, - const QPoint &, int length, double direction); +protected: + virtual void drawNeedle( QPainter *, + double length, QPalette::ColorGroup ) const; private: Style d_style; }; -#endif // QWT_DIAL_NEEDLE_H +#endif diff --git a/libs/qwt/qwt_dyngrid_layout.cpp b/libs/qwt/qwt_dyngrid_layout.cpp index ac98ad1c0..9e0fa2851 100644 --- a/libs/qwt/qwt_dyngrid_layout.cpp +++ b/libs/qwt/qwt_dyngrid_layout.cpp @@ -7,128 +7,72 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ -#include #include "qwt_dyngrid_layout.h" #include "qwt_math.h" - -#if QT_VERSION < 0x040000 -#include -#else +#include #include -#endif class QwtDynGridLayout::PrivateData { public: - -#if QT_VERSION < 0x040000 - class LayoutIterator: public QGLayoutIterator - { - public: - LayoutIterator(PrivateData *data): - d_data(data) { - d_iterator = d_data->itemList.begin(); - } - - virtual QLayoutItem *current() { - if (d_iterator == d_data->itemList.end()) - return NULL; - - return *d_iterator; - } - - virtual QLayoutItem *next() { - if (d_iterator == d_data->itemList.end()) - return NULL; - - d_iterator++; - if (d_iterator == d_data->itemList.end()) - return NULL; - - return *d_iterator; - } - - virtual QLayoutItem *takeCurrent() { - if ( d_iterator == d_data->itemList.end() ) - return NULL; - - QLayoutItem *item = *d_iterator; - - d_data->isDirty = true; - d_iterator = d_data->itemList.remove(d_iterator); - return item; - } - - private: - - QValueListIterator d_iterator; - QwtDynGridLayout::PrivateData *d_data; - }; -#endif - PrivateData(): - isDirty(true) { + isDirty( true ) + { } -#if QT_VERSION < 0x040000 - typedef QValueList LayoutItemList; -#else - typedef QList LayoutItemList; -#endif + void updateLayoutCache(); - mutable LayoutItemList itemList; + mutable QList itemList; - uint maxCols; + uint maxColumns; uint numRows; - uint numCols; + uint numColumns; -#if QT_VERSION < 0x040000 - QSizePolicy::ExpandData expanding; -#else Qt::Orientations expanding; -#endif bool isDirty; - QwtArray itemSizeHints; + QVector itemSizeHints; }; +void QwtDynGridLayout::PrivateData::updateLayoutCache() +{ + itemSizeHints.resize( itemList.count() ); -/*! - \param parent Parent widget - \param margin Margin - \param spacing Spacing -*/ + int index = 0; -QwtDynGridLayout::QwtDynGridLayout(QWidget *parent, - int margin, int spacing): - QLayout(parent) -{ - init(); + for ( QList::iterator it = itemList.begin(); + it != itemList.end(); ++it, index++ ) + { + itemSizeHints[ index ] = ( *it )->sizeHint(); + } - setSpacing(spacing); - setMargin(margin); + isDirty = false; } -#if QT_VERSION < 0x040000 /*! \param parent Parent widget + \param margin Margin \param spacing Spacing */ -QwtDynGridLayout::QwtDynGridLayout(QLayout *parent, int spacing): - QLayout(parent, spacing) + +QwtDynGridLayout::QwtDynGridLayout( QWidget *parent, + int margin, int spacing ): + QLayout( parent ) { init(); + + setSpacing( spacing ); + setMargin( margin ); } -#endif /*! \param spacing Spacing */ -QwtDynGridLayout::QwtDynGridLayout(int spacing) +QwtDynGridLayout::QwtDynGridLayout( int spacing ) { init(); - setSpacing(spacing); + setSpacing( spacing ); } /*! @@ -137,82 +81,63 @@ QwtDynGridLayout::QwtDynGridLayout(int spacing) void QwtDynGridLayout::init() { d_data = new QwtDynGridLayout::PrivateData; - d_data->maxCols = d_data->numRows - = d_data->numCols = 0; - -#if QT_VERSION < 0x040000 - d_data->expanding = QSizePolicy::NoDirection; - setSupportsMargin(true); -#else + d_data->maxColumns = d_data->numRows = d_data->numColumns = 0; d_data->expanding = 0; -#endif } //! Destructor QwtDynGridLayout::~QwtDynGridLayout() { -#if QT_VERSION < 0x040000 - deleteAllItems(); -#endif + for ( int i = 0; i < d_data->itemList.size(); i++ ) + delete d_data->itemList[i]; delete d_data; } +//! Invalidate all internal caches void QwtDynGridLayout::invalidate() { d_data->isDirty = true; QLayout::invalidate(); } -void QwtDynGridLayout::updateLayoutCache() -{ - d_data->itemSizeHints.resize(itemCount()); - - int index = 0; - - for (PrivateData::LayoutItemList::iterator it = d_data->itemList.begin(); - it != d_data->itemList.end(); ++it, index++) { - d_data->itemSizeHints[int(index)] = (*it)->sizeHint(); - } - - d_data->isDirty = false; -} - /*! Limit the number of columns. - \param maxCols upper limit, 0 means unlimited - \sa QwtDynGridLayout::maxCols() + \param maxColumns upper limit, 0 means unlimited + \sa maxColumns() */ - -void QwtDynGridLayout::setMaxCols(uint maxCols) +void QwtDynGridLayout::setMaxColumns( uint maxColumns ) { - d_data->maxCols = maxCols; + d_data->maxColumns = maxColumns; } /*! - Return the upper limit for the number of columns. + \brief Return the upper limit for the number of columns. + 0 means unlimited, what is the default. - \sa QwtDynGridLayout::setMaxCols() -*/ -uint QwtDynGridLayout::maxCols() const + \return Upper limit for the number of columns + \sa setMaxColumns() +*/ +uint QwtDynGridLayout::maxColumns() const { - return d_data->maxCols; + return d_data->maxColumns; } -//! Adds item to the next free position. - -void QwtDynGridLayout::addItem(QLayoutItem *item) +/*! + \brief Add an item to the next free position. + \param item Layout item + */ +void QwtDynGridLayout::addItem( QLayoutItem *item ) { - d_data->itemList.append(item); + d_data->itemList.append( item ); invalidate(); } /*! \return true if this layout is empty. */ - bool QwtDynGridLayout::isEmpty() const { return d_data->itemList.isEmpty(); @@ -221,140 +146,134 @@ bool QwtDynGridLayout::isEmpty() const /*! \return number of layout items */ - uint QwtDynGridLayout::itemCount() const { return d_data->itemList.count(); } -#if QT_VERSION < 0x040000 /*! - \return An iterator over the children of this layout. -*/ - -QLayoutIterator QwtDynGridLayout::iterator() -{ - return QLayoutIterator( - new QwtDynGridLayout::PrivateData::LayoutIterator(d_data) ); -} + Find the item at a specific index -/*! - Set whether this layout can make use of more space than sizeHint(). - A value of Vertical or Horizontal means that it wants to grow in only - one dimension, while BothDirections means that it wants to grow in - both dimensions. The default value is NoDirection. - \sa QwtDynGridLayout::expanding() + \param index Index + \return Item at a specific index + \sa takeAt() */ - -void QwtDynGridLayout::setExpanding(QSizePolicy::ExpandData expanding) -{ - d_data->expanding = expanding; -} - -/*! - Returns whether this layout can make use of more space than sizeHint(). - A value of Vertical or Horizontal means that it wants to grow in only - one dimension, while BothDirections means that it wants to grow in - both dimensions. - \sa QwtDynGridLayout::setExpanding() -*/ - -QSizePolicy::ExpandData QwtDynGridLayout::expanding() const -{ - return d_data->expanding; -} - -#else // QT_VERSION >= 0x040000 - QLayoutItem *QwtDynGridLayout::itemAt( int index ) const { if ( index < 0 || index >= d_data->itemList.count() ) return NULL; - return d_data->itemList.at(index); + return d_data->itemList.at( index ); } +/*! + Find the item at a specific index and remove it from the layout + + \param index Index + \return Layout item, removed from the layout + \sa itemAt() +*/ QLayoutItem *QwtDynGridLayout::takeAt( int index ) { if ( index < 0 || index >= d_data->itemList.count() ) return NULL; d_data->isDirty = true; - return d_data->itemList.takeAt(index); + return d_data->itemList.takeAt( index ); } +//! \return Number of items in the layout int QwtDynGridLayout::count() const { return d_data->itemList.count(); } -void QwtDynGridLayout::setExpandingDirections(Qt::Orientations expanding) +/*! + Set whether this layout can make use of more space than sizeHint(). + A value of Qt::Vertical or Qt::Horizontal means that it wants to grow in only + one dimension, while Qt::Vertical | Qt::Horizontal means that it wants + to grow in both dimensions. The default value is 0. + + \param expanding Or'd orientations + \sa expandingDirections() +*/ +void QwtDynGridLayout::setExpandingDirections( Qt::Orientations expanding ) { d_data->expanding = expanding; } +/*! + \brief Returns whether this layout can make use of more space than sizeHint(). + + A value of Qt::Vertical or Qt::Horizontal means that it wants to grow in only + one dimension, while Qt::Vertical | Qt::Horizontal means that it wants + to grow in both dimensions. + + \return Orientations, where the layout expands + \sa setExpandingDirections() +*/ Qt::Orientations QwtDynGridLayout::expandingDirections() const { return d_data->expanding; } -#endif - /*! - Reorganizes columns and rows and resizes managed widgets within - the rectangle rect. -*/ + Reorganizes columns and rows and resizes managed items within + a rectangle. -void QwtDynGridLayout::setGeometry(const QRect &rect) + \param rect Layout geometry +*/ +void QwtDynGridLayout::setGeometry( const QRect &rect ) { - QLayout::setGeometry(rect); + QLayout::setGeometry( rect ); if ( isEmpty() ) return; - d_data->numCols = columnsForWidth(rect.width()); - d_data->numRows = itemCount() / d_data->numCols; - if ( itemCount() % d_data->numCols ) + d_data->numColumns = columnsForWidth( rect.width() ); + d_data->numRows = itemCount() / d_data->numColumns; + if ( itemCount() % d_data->numColumns ) d_data->numRows++; -#if QT_VERSION < 0x040000 - QValueList itemGeometries = layoutItems(rect, d_data->numCols); -#else - QList itemGeometries = layoutItems(rect, d_data->numCols); -#endif + QList itemGeometries = layoutItems( rect, d_data->numColumns ); int index = 0; - for (PrivateData::LayoutItemList::iterator it = d_data->itemList.begin(); - it != d_data->itemList.end(); ++it) { - QWidget *w = (*it)->widget(); - if ( w ) { - w->setGeometry(itemGeometries[index]); - index++; - } + for ( QList::iterator it = d_data->itemList.begin(); + it != d_data->itemList.end(); ++it ) + { + ( *it )->setGeometry( itemGeometries[index] ); + index++; } } /*! - Calculate the number of columns for a given width. It tries to - use as many columns as possible (limited by maxCols()) + \brief Calculate the number of columns for a given width. + + The calculation tries to use as many columns as possible + ( limited by maxColumns() ) \param width Available width for all columns - \sa QwtDynGridLayout::maxCols(), QwtDynGridLayout::setMaxCols() -*/ + \return Number of columns for a given width -uint QwtDynGridLayout::columnsForWidth(int width) const + \sa maxColumns(), setMaxColumns() +*/ +uint QwtDynGridLayout::columnsForWidth( int width ) const { if ( isEmpty() ) return 0; - const int maxCols = (d_data->maxCols > 0) ? d_data->maxCols : itemCount(); - if ( maxRowWidth(maxCols) <= width ) - return maxCols; + uint maxColumns = itemCount(); + if ( d_data->maxColumns > 0 ) + maxColumns = qMin( d_data->maxColumns, maxColumns ); + + if ( maxRowWidth( maxColumns ) <= width ) + return maxColumns; - for (int numCols = 2; numCols <= maxCols; numCols++ ) { - const int rowWidth = maxRowWidth(numCols); + for ( uint numColumns = 2; numColumns <= maxColumns; numColumns++ ) + { + const int rowWidth = maxRowWidth( numColumns ); if ( rowWidth > width ) - return numCols - 1; + return numColumns - 1; } return 1; // At least 1 column @@ -364,29 +283,30 @@ uint QwtDynGridLayout::columnsForWidth(int width) const Calculate the width of a layout for a given number of columns. - \param numCols Given number of columns + \param numColumns Given number of columns \param itemWidth Array of the width hints for all items */ -int QwtDynGridLayout::maxRowWidth(int numCols) const +int QwtDynGridLayout::maxRowWidth( int numColumns ) const { int col; - QwtArray colWidth(numCols); - for ( col = 0; col < (int)numCols; col++ ) + QVector colWidth( numColumns ); + for ( col = 0; col < numColumns; col++ ) colWidth[col] = 0; if ( d_data->isDirty ) - ((QwtDynGridLayout*)this)->updateLayoutCache(); + d_data->updateLayoutCache(); - for ( uint index = 0; - index < (uint)d_data->itemSizeHints.count(); index++ ) { - col = index % numCols; - colWidth[col] = qwtMax(colWidth[col], - d_data->itemSizeHints[int(index)].width()); + for ( int index = 0; + index < d_data->itemSizeHints.count(); index++ ) + { + col = index % numColumns; + colWidth[col] = qMax( colWidth[col], + d_data->itemSizeHints[int( index )].width() ); } - int rowWidth = 2 * margin() + (numCols - 1) * spacing(); - for ( col = 0; col < (int)numCols; col++ ) + int rowWidth = 2 * margin() + ( numColumns - 1 ) * spacing(); + for ( col = 0; col < numColumns; col++ ) rowWidth += colWidth[col]; return rowWidth; @@ -395,18 +315,18 @@ int QwtDynGridLayout::maxRowWidth(int numCols) const /*! \return the maximum width of all layout items */ - int QwtDynGridLayout::maxItemWidth() const { if ( isEmpty() ) return 0; if ( d_data->isDirty ) - ((QwtDynGridLayout*)this)->updateLayoutCache(); + d_data->updateLayoutCache(); int w = 0; - for ( uint i = 0; i < (uint)d_data->itemSizeHints.count(); i++ ) { - const int itemW = d_data->itemSizeHints[int(i)].width(); + for ( int i = 0; i < d_data->itemSizeHints.count(); i++ ) + { + const int itemW = d_data->itemSizeHints[i].width(); if ( itemW > w ) w = itemW; } @@ -416,79 +336,69 @@ int QwtDynGridLayout::maxItemWidth() const /*! Calculate the geometries of the layout items for a layout - with numCols columns and a given rect. + with numColumns columns and a given rectangle. + \param rect Rect where to place the items - \param numCols Number of columns + \param numColumns Number of columns \return item geometries */ -#if QT_VERSION < 0x040000 -QValueList QwtDynGridLayout::layoutItems(const QRect &rect, - uint numCols) const -#else -QList QwtDynGridLayout::layoutItems(const QRect &rect, - uint numCols) const -#endif +QList QwtDynGridLayout::layoutItems( const QRect &rect, + uint numColumns ) const { -#if QT_VERSION < 0x040000 - QValueList itemGeometries; -#else QList itemGeometries; -#endif - if ( numCols == 0 || isEmpty() ) + if ( numColumns == 0 || isEmpty() ) return itemGeometries; - uint numRows = itemCount() / numCols; - if ( numRows % itemCount() ) + uint numRows = itemCount() / numColumns; + if ( numColumns % itemCount() ) numRows++; - QwtArray rowHeight(numRows); - QwtArray colWidth(numCols); + if ( numRows == 0 ) + return itemGeometries; + + QVector rowHeight( numRows ); + QVector colWidth( numColumns ); - layoutGrid(numCols, rowHeight, colWidth); + layoutGrid( numColumns, rowHeight, colWidth ); bool expandH, expandV; -#if QT_VERSION >= 0x040000 expandH = expandingDirections() & Qt::Horizontal; expandV = expandingDirections() & Qt::Vertical; -#else - expandH = expanding() & QSizePolicy::Horizontally; - expandV = expanding() & QSizePolicy::Vertically; -#endif if ( expandH || expandV ) - stretchGrid(rect, numCols, rowHeight, colWidth); + stretchGrid( rect, numColumns, rowHeight, colWidth ); - QwtDynGridLayout *that = (QwtDynGridLayout *)this; - const int maxCols = d_data->maxCols; - that->d_data->maxCols = numCols; - const QRect alignedRect = alignmentRect(rect); - that->d_data->maxCols = maxCols; + const int maxColumns = d_data->maxColumns; + d_data->maxColumns = numColumns; + const QRect alignedRect = alignmentRect( rect ); + d_data->maxColumns = maxColumns; const int xOffset = expandH ? 0 : alignedRect.x(); const int yOffset = expandV ? 0 : alignedRect.y(); - QwtArray colX(numCols); - QwtArray rowY(numRows); + QVector colX( numColumns ); + QVector rowY( numRows ); const int xySpace = spacing(); rowY[0] = yOffset + margin(); - for ( int r = 1; r < (int)numRows; r++ ) + for ( uint r = 1; r < numRows; r++ ) rowY[r] = rowY[r-1] + rowHeight[r-1] + xySpace; colX[0] = xOffset + margin(); - for ( int c = 1; c < (int)numCols; c++ ) + for ( uint c = 1; c < numColumns; c++ ) colX[c] = colX[c-1] + colWidth[c-1] + xySpace; const int itemCount = d_data->itemList.size(); - for ( int i = 0; i < itemCount; i++ ) { - const int row = i / numCols; - const int col = i % numCols; + for ( int i = 0; i < itemCount; i++ ) + { + const int row = i / numColumns; + const int col = i % numColumns; - QRect itemGeometry(colX[col], rowY[row], - colWidth[col], rowHeight[row]); - itemGeometries.append(itemGeometry); + QRect itemGeometry( colX[col], rowY[row], + colWidth[col], rowHeight[row] ); + itemGeometries.append( itemGeometry ); } return itemGeometries; @@ -497,67 +407,66 @@ QList QwtDynGridLayout::layoutItems(const QRect &rect, /*! Calculate the dimensions for the columns and rows for a grid - of numCols columns. - \param numCols Number of columns. + of numColumns columns. + + \param numColumns Number of columns. \param rowHeight Array where to fill in the calculated row heights. \param colWidth Array where to fill in the calculated column widths. */ -void QwtDynGridLayout::layoutGrid(uint numCols, - QwtArray& rowHeight, QwtArray& colWidth) const +void QwtDynGridLayout::layoutGrid( uint numColumns, + QVector& rowHeight, QVector& colWidth ) const { - if ( numCols <= 0 ) + if ( numColumns <= 0 ) return; if ( d_data->isDirty ) - ((QwtDynGridLayout*)this)->updateLayoutCache(); + d_data->updateLayoutCache(); - for ( uint index = 0; - index < (uint)d_data->itemSizeHints.count(); index++ ) { - const int row = index / numCols; - const int col = index % numCols; + for ( int index = 0; index < d_data->itemSizeHints.count(); index++ ) + { + const int row = index / numColumns; + const int col = index % numColumns; - const QSize &size = d_data->itemSizeHints[int(index)]; + const QSize &size = d_data->itemSizeHints[int( index )]; - rowHeight[row] = (col == 0) - ? size.height() : qwtMax(rowHeight[row], size.height()); - colWidth[col] = (row == 0) - ? size.width() : qwtMax(colWidth[col], size.width()); + rowHeight[row] = ( col == 0 ) + ? size.height() : qMax( rowHeight[row], size.height() ); + colWidth[col] = ( row == 0 ) + ? size.width() : qMax( colWidth[col], size.width() ); } } /*! - \return true: QwtDynGridLayout implements heightForWidth. - \sa QwtDynGridLayout::heightForWidth() + \return true: QwtDynGridLayout implements heightForWidth(). + \sa heightForWidth() */ - bool QwtDynGridLayout::hasHeightForWidth() const { return true; } /*! - \return The preferred height for this layout, given the width w. - \sa QwtDynGridLayout::hasHeightForWidth() + \return The preferred height for this layout, given a width. + \sa hasHeightForWidth() */ - -int QwtDynGridLayout::heightForWidth(int width) const +int QwtDynGridLayout::heightForWidth( int width ) const { if ( isEmpty() ) return 0; - const uint numCols = columnsForWidth(width); - uint numRows = itemCount() / numCols; - if ( itemCount() % numCols ) + const uint numColumns = columnsForWidth( width ); + uint numRows = itemCount() / numColumns; + if ( itemCount() % numColumns ) numRows++; - QwtArray rowHeight(numRows); - QwtArray colWidth(numCols); + QVector rowHeight( numRows ); + QVector colWidth( numColumns ); - layoutGrid(numCols, rowHeight, colWidth); + layoutGrid( numColumns, rowHeight, colWidth ); - int h = 2 * margin() + (numRows - 1) * spacing(); - for ( int row = 0; row < (int)numRows; row++ ) + int h = 2 * margin() + ( numRows - 1 ) * spacing(); + for ( uint row = 0; row < numRows; row++ ) h += rowHeight[row]; return h; @@ -567,50 +476,56 @@ int QwtDynGridLayout::heightForWidth(int width) const Stretch columns in case of expanding() & QSizePolicy::Horizontal and rows in case of expanding() & QSizePolicy::Vertical to fill the entire rect. Rows and columns are stretched with the same factor. - \sa QwtDynGridLayout::setExpanding(), QwtDynGridLayout::expanding() -*/ -void QwtDynGridLayout::stretchGrid(const QRect &rect, - uint numCols, QwtArray& rowHeight, QwtArray& colWidth) const + \param rect Bounding rectangle + \param numColumns Number of columns + \param rowHeight Array to be filled with the calculated row heights + \param colWidth Array to be filled with the calculated column widths + + \sa setExpanding(), expanding() +*/ +void QwtDynGridLayout::stretchGrid( const QRect &rect, + uint numColumns, QVector& rowHeight, QVector& colWidth ) const { - if ( numCols == 0 || isEmpty() ) + if ( numColumns == 0 || isEmpty() ) return; bool expandH, expandV; -#if QT_VERSION >= 0x040000 expandH = expandingDirections() & Qt::Horizontal; expandV = expandingDirections() & Qt::Vertical; -#else - expandH = expanding() & QSizePolicy::Horizontally; - expandV = expanding() & QSizePolicy::Vertically; -#endif - - if ( expandH ) { - int xDelta = rect.width() - 2 * margin() - (numCols - 1) * spacing(); - for ( int col = 0; col < (int)numCols; col++ ) + + if ( expandH ) + { + int xDelta = rect.width() - 2 * margin() - ( numColumns - 1 ) * spacing(); + for ( uint col = 0; col < numColumns; col++ ) xDelta -= colWidth[col]; - if ( xDelta > 0 ) { - for ( int col = 0; col < (int)numCols; col++ ) { - const int space = xDelta / (numCols - col); + if ( xDelta > 0 ) + { + for ( uint col = 0; col < numColumns; col++ ) + { + const int space = xDelta / ( numColumns - col ); colWidth[col] += space; xDelta -= space; } } } - if ( expandV ) { - uint numRows = itemCount() / numCols; - if ( itemCount() % numCols ) + if ( expandV ) + { + uint numRows = itemCount() / numColumns; + if ( itemCount() % numColumns ) numRows++; - int yDelta = rect.height() - 2 * margin() - (numRows - 1) * spacing(); - for ( int row = 0; row < (int)numRows; row++ ) + int yDelta = rect.height() - 2 * margin() - ( numRows - 1 ) * spacing(); + for ( uint row = 0; row < numRows; row++ ) yDelta -= rowHeight[row]; - if ( yDelta > 0 ) { - for ( int row = 0; row < (int)numRows; row++ ) { - const int space = yDelta / (numRows - row); + if ( yDelta > 0 ) + { + for ( uint row = 0; row < numRows; row++ ) + { + const int space = yDelta / ( numRows - row ); rowHeight[row] += space; yDelta -= space; } @@ -619,41 +534,45 @@ void QwtDynGridLayout::stretchGrid(const QRect &rect, } /*! - Return the size hint. If maxCols() > 0 it is the size for - a grid with maxCols() columns, otherwise it is the size for + Return the size hint. If maxColumns() > 0 it is the size for + a grid with maxColumns() columns, otherwise it is the size for a grid with only one row. - \sa QwtDynGridLayout::maxCols(), QwtDynGridLayout::setMaxCols() -*/ + \return Size hint + \sa maxColumns(), setMaxColumns() +*/ QSize QwtDynGridLayout::sizeHint() const { if ( isEmpty() ) return QSize(); - const uint numCols = (d_data->maxCols > 0 ) ? d_data->maxCols : itemCount(); - uint numRows = itemCount() / numCols; - if ( itemCount() % numCols ) + uint numColumns = itemCount(); + if ( d_data->maxColumns > 0 ) + numColumns = qMin( d_data->maxColumns, numColumns ); + + uint numRows = itemCount() / numColumns; + if ( itemCount() % numColumns ) numRows++; - QwtArray rowHeight(numRows); - QwtArray colWidth(numCols); + QVector rowHeight( numRows ); + QVector colWidth( numColumns ); - layoutGrid(numCols, rowHeight, colWidth); + layoutGrid( numColumns, rowHeight, colWidth ); - int h = 2 * margin() + (numRows - 1) * spacing(); - for ( int row = 0; row < (int)numRows; row++ ) + int h = 2 * margin() + ( numRows - 1 ) * spacing(); + for ( uint row = 0; row < numRows; row++ ) h += rowHeight[row]; - int w = 2 * margin() + (numCols - 1) * spacing(); - for ( int col = 0; col < (int)numCols; col++ ) + int w = 2 * margin() + ( numColumns - 1 ) * spacing(); + for ( uint col = 0; col < numColumns; col++ ) w += colWidth[col]; - return QSize(w, h); + return QSize( w, h ); } /*! \return Number of rows of the current layout. - \sa QwtDynGridLayout::numCols + \sa numColumns() \warning The number of rows might change whenever the geometry changes */ uint QwtDynGridLayout::numRows() const @@ -663,10 +582,10 @@ uint QwtDynGridLayout::numRows() const /*! \return Number of columns of the current layout. - \sa QwtDynGridLayout::numRows + \sa numRows() \warning The number of columns might change whenever the geometry changes */ -uint QwtDynGridLayout::numCols() const +uint QwtDynGridLayout::numColumns() const { - return d_data->numCols; + return d_data->numColumns; } diff --git a/libs/qwt/qwt_dyngrid_layout.h b/libs/qwt/qwt_dyngrid_layout.h index a59e4e719..dd2026612 100644 --- a/libs/qwt/qwt_dyngrid_layout.h +++ b/libs/qwt/qwt_dyngrid_layout.h @@ -10,15 +10,10 @@ #ifndef QWT_DYNGRID_LAYOUT_H #define QWT_DYNGRID_LAYOUT_H +#include "qwt_global.h" #include #include -#if QT_VERSION >= 0x040000 #include -#else -#include -#endif -#include "qwt_global.h" -#include "qwt_array.h" /*! \brief The QwtDynGridLayout class lays out widgets in a grid, @@ -26,81 +21,62 @@ QwtDynGridLayout takes the space it gets, divides it up into rows and columns, and puts each of the widgets it manages into the correct cell(s). - It lays out as many number of columns as possible (limited by maxCols()). + It lays out as many number of columns as possible (limited by maxColumns()). */ class QWT_EXPORT QwtDynGridLayout : public QLayout { Q_OBJECT public: - explicit QwtDynGridLayout(QWidget *, int margin = 0, int space = -1); -#if QT_VERSION < 0x040000 - explicit QwtDynGridLayout(QLayout *, int space = -1); -#endif - explicit QwtDynGridLayout(int space = -1); + explicit QwtDynGridLayout( QWidget *, int margin = 0, int space = -1 ); + explicit QwtDynGridLayout( int space = -1 ); virtual ~QwtDynGridLayout(); virtual void invalidate(); - void setMaxCols(uint maxCols); - uint maxCols() const; + void setMaxColumns( uint maxCols ); + uint maxColumns() const; uint numRows () const; - uint numCols () const; + uint numColumns () const; - virtual void addItem(QLayoutItem *); + virtual void addItem( QLayoutItem * ); -#if QT_VERSION >= 0x040000 virtual QLayoutItem *itemAt( int index ) const; virtual QLayoutItem *takeAt( int index ); virtual int count() const; - void setExpandingDirections(Qt::Orientations); + void setExpandingDirections( Qt::Orientations ); virtual Qt::Orientations expandingDirections() const; - QList layoutItems(const QRect &, uint numCols) const; -#else - virtual QLayoutIterator iterator(); - - void setExpanding(QSizePolicy::ExpandData); - virtual QSizePolicy::ExpandData expanding() const; - QValueList layoutItems(const QRect &, uint numCols) const; -#endif + QList layoutItems( const QRect &, uint numCols ) const; virtual int maxItemWidth() const; - virtual void setGeometry(const QRect &rect); + virtual void setGeometry( const QRect &rect ); virtual bool hasHeightForWidth() const; - virtual int heightForWidth(int) const; + virtual int heightForWidth( int ) const; virtual QSize sizeHint() const; virtual bool isEmpty() const; uint itemCount() const; - virtual uint columnsForWidth(int width) const; + virtual uint columnsForWidth( int width ) const; protected: - void layoutGrid(uint numCols, - QwtArray& rowHeight, QwtArray& colWidth) const; - void stretchGrid(const QRect &rect, uint numCols, - QwtArray& rowHeight, QwtArray& colWidth) const; - + void layoutGrid( uint numCols, + QVector& rowHeight, QVector& colWidth ) const; + void stretchGrid( const QRect &rect, uint numCols, + QVector& rowHeight, QVector& colWidth ) const; private: void init(); - int maxRowWidth(int numCols) const; - void updateLayoutCache(); + int maxRowWidth( int numCols ) const; -#if QT_VERSION < 0x040000 -// xlC 5.1, the IBM/AIX C++ compiler, needs it to be public -public: -#endif class PrivateData; - -private: PrivateData *d_data; }; diff --git a/libs/qwt/qwt_event_pattern.cpp b/libs/qwt/qwt_event_pattern.cpp index 56ece45b4..463774362 100644 --- a/libs/qwt/qwt_event_pattern.cpp +++ b/libs/qwt/qwt_event_pattern.cpp @@ -7,8 +7,8 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ -#include #include "qwt_event_pattern.h" +#include /*! Constructor @@ -17,11 +17,11 @@ */ QwtEventPattern::QwtEventPattern(): - d_mousePattern(MousePatternCount), - d_keyPattern(KeyPatternCount) + d_mousePattern( MousePatternCount ), + d_keyPattern( KeyPatternCount ) { initKeyPattern(); - initMousePattern(3); + initMousePattern( 3 ); } //! Destructor @@ -35,44 +35,42 @@ QwtEventPattern::~QwtEventPattern() \param numButtons Number of mouse buttons ( <= 3 ) \sa MousePatternCode */ -void QwtEventPattern::initMousePattern(int numButtons) +void QwtEventPattern::initMousePattern( int numButtons ) { -#if QT_VERSION < 0x040000 - const int altButton = Qt::AltButton; - const int controlButton = Qt::ControlButton; - const int shiftButton = Qt::ShiftButton; -#else - const int altButton = Qt::AltModifier; - const int controlButton = Qt::ControlModifier; - const int shiftButton = Qt::ShiftModifier; -#endif - - d_mousePattern.resize(MousePatternCount); - - switch(numButtons) { - case 1: { - setMousePattern(MouseSelect1, Qt::LeftButton); - setMousePattern(MouseSelect2, Qt::LeftButton, controlButton); - setMousePattern(MouseSelect3, Qt::LeftButton, altButton); - break; - } - case 2: { - setMousePattern(MouseSelect1, Qt::LeftButton); - setMousePattern(MouseSelect2, Qt::RightButton); - setMousePattern(MouseSelect3, Qt::LeftButton, altButton); - break; - } - default: { - setMousePattern(MouseSelect1, Qt::LeftButton); - setMousePattern(MouseSelect2, Qt::RightButton); - setMousePattern(MouseSelect3, Qt::MidButton); - } - } - for ( int i = 0; i < 3; i++ ) { - setMousePattern(MouseSelect4 + i, - d_mousePattern[MouseSelect1 + i].button, - d_mousePattern[MouseSelect1 + i].state | shiftButton); + d_mousePattern.resize( MousePatternCount ); + + switch ( numButtons ) + { + case 1: + { + setMousePattern( MouseSelect1, Qt::LeftButton ); + setMousePattern( MouseSelect2, Qt::LeftButton, Qt::ControlModifier ); + setMousePattern( MouseSelect3, Qt::LeftButton, Qt::AltModifier ); + break; + } + case 2: + { + setMousePattern( MouseSelect1, Qt::LeftButton ); + setMousePattern( MouseSelect2, Qt::RightButton ); + setMousePattern( MouseSelect3, Qt::LeftButton, Qt::AltModifier ); + break; + } + default: + { + setMousePattern( MouseSelect1, Qt::LeftButton ); + setMousePattern( MouseSelect2, Qt::RightButton ); + setMousePattern( MouseSelect3, Qt::MidButton ); + } } + + setMousePattern( MouseSelect4, d_mousePattern[MouseSelect1].button, + d_mousePattern[MouseSelect1].modifiers | Qt::ShiftModifier ); + + setMousePattern( MouseSelect5, d_mousePattern[MouseSelect2].button, + d_mousePattern[MouseSelect2].modifiers | Qt::ShiftModifier ); + + setMousePattern( MouseSelect6, d_mousePattern[MouseSelect3].button, + d_mousePattern[MouseSelect3].modifiers | Qt::ShiftModifier ); } /*! @@ -82,20 +80,20 @@ void QwtEventPattern::initMousePattern(int numButtons) */ void QwtEventPattern::initKeyPattern() { - d_keyPattern.resize(KeyPatternCount); + d_keyPattern.resize( KeyPatternCount ); - setKeyPattern(KeySelect1, Qt::Key_Return); - setKeyPattern(KeySelect2, Qt::Key_Space); - setKeyPattern(KeyAbort, Qt::Key_Escape); + setKeyPattern( KeySelect1, Qt::Key_Return ); + setKeyPattern( KeySelect2, Qt::Key_Space ); + setKeyPattern( KeyAbort, Qt::Key_Escape ); - setKeyPattern(KeyLeft, Qt::Key_Left); - setKeyPattern(KeyRight, Qt::Key_Right); - setKeyPattern(KeyUp, Qt::Key_Up); - setKeyPattern(KeyDown, Qt::Key_Down); + setKeyPattern( KeyLeft, Qt::Key_Left ); + setKeyPattern( KeyRight, Qt::Key_Right ); + setKeyPattern( KeyUp, Qt::Key_Up ); + setKeyPattern( KeyDown, Qt::Key_Down ); - setKeyPattern(KeyRedo, Qt::Key_Plus); - setKeyPattern(KeyUndo, Qt::Key_Minus); - setKeyPattern(KeyHome, Qt::Key_Escape); + setKeyPattern( KeyRedo, Qt::Key_Plus ); + setKeyPattern( KeyUndo, Qt::Key_Minus ); + setKeyPattern( KeyHome, Qt::Key_Escape ); } /*! @@ -103,15 +101,17 @@ void QwtEventPattern::initKeyPattern() \param pattern Index of the pattern \param button Button - \param state State + \param modifiers Keyboard modifiers \sa QMouseEvent */ -void QwtEventPattern::setMousePattern(uint pattern, int button, int state) +void QwtEventPattern::setMousePattern( MousePatternCode pattern, + Qt::MouseButton button, Qt::KeyboardModifiers modifiers ) { - if ( pattern < (uint)d_mousePattern.count() ) { - d_mousePattern[int(pattern)].button = button; - d_mousePattern[int(pattern)].state = state; + if ( pattern >= 0 && pattern < MousePatternCount ) + { + d_mousePattern[ pattern ].button = button; + d_mousePattern[ pattern ].modifiers = modifiers; } } @@ -120,52 +120,54 @@ void QwtEventPattern::setMousePattern(uint pattern, int button, int state) \param pattern Index of the pattern \param key Key - \param state State + \param modifiers Keyboard modifiers \sa QKeyEvent */ -void QwtEventPattern::setKeyPattern(uint pattern, int key, int state) +void QwtEventPattern::setKeyPattern( KeyPatternCode pattern, + int key, Qt::KeyboardModifiers modifiers ) { - if ( pattern < (uint)d_keyPattern.count() ) { - d_keyPattern[int(pattern)].key = key; - d_keyPattern[int(pattern)].state = state; + if ( pattern >= 0 && pattern < KeyPatternCount ) + { + d_keyPattern[ pattern ].key = key; + d_keyPattern[ pattern ].modifiers = modifiers; } } //! Change the mouse event patterns -void QwtEventPattern::setMousePattern(const QwtArray &pattern) +void QwtEventPattern::setMousePattern( const QVector &pattern ) { d_mousePattern = pattern; } //! Change the key event patterns -void QwtEventPattern::setKeyPattern(const QwtArray &pattern) +void QwtEventPattern::setKeyPattern( const QVector &pattern ) { d_keyPattern = pattern; } -//! Return mouse patterns -const QwtArray & +//! \return Mouse pattern +const QVector & QwtEventPattern::mousePattern() const { return d_mousePattern; } -//! Return key patterns -const QwtArray & +//! \return Key pattern +const QVector & QwtEventPattern::keyPattern() const { return d_keyPattern; } -//! Return ,ouse patterns -QwtArray &QwtEventPattern::mousePattern() +//! \return Mouse pattern +QVector &QwtEventPattern::mousePattern() { return d_mousePattern; } -//! Return Key patterns -QwtArray &QwtEventPattern::keyPattern() +//! \return Key pattern +QVector &QwtEventPattern::keyPattern() { return d_keyPattern; } @@ -177,20 +179,19 @@ QwtArray &QwtEventPattern::keyPattern() value and in the state value the same key flags(Qt::KeyButtonMask) are set. - \param pattern Index of the event pattern - \param e Mouse event + \param code Index of the event pattern + \param event Mouse event \return true if matches \sa keyMatch() */ -bool QwtEventPattern::mouseMatch(uint pattern, const QMouseEvent *e) const +bool QwtEventPattern::mouseMatch( MousePatternCode code, + const QMouseEvent *event ) const { - bool ok = false; - - if ( e && pattern < (uint)d_mousePattern.count() ) - ok = mouseMatch(d_mousePattern[int(pattern)], e); + if ( code >= 0 && code < MousePatternCount ) + return mouseMatch( d_mousePattern[ code ], event ); - return ok; + return false; } /*! @@ -201,28 +202,20 @@ bool QwtEventPattern::mouseMatch(uint pattern, const QMouseEvent *e) const are set. \param pattern Mouse event pattern - \param e Mouse event + \param event Mouse event \return true if matches \sa keyMatch() */ -bool QwtEventPattern::mouseMatch(const MousePattern &pattern, - const QMouseEvent *e) const +bool QwtEventPattern::mouseMatch( const MousePattern &pattern, + const QMouseEvent *event ) const { - if ( e->button() != pattern.button ) + if ( event == NULL ) return false; - const bool matched = -#if QT_VERSION < 0x040000 - (e->state() & Qt::KeyButtonMask) == - (pattern.state & Qt::KeyButtonMask); -#else - (e->modifiers() & Qt::KeyboardModifierMask) == - (int)(pattern.state & Qt::KeyboardModifierMask); -#endif - - return matched; + const MousePattern mousePattern( event->button(), event->modifiers() ); + return mousePattern == pattern; } /*! @@ -232,20 +225,19 @@ bool QwtEventPattern::mouseMatch(const MousePattern &pattern, value and in the state value the same key flags (Qt::KeyButtonMask) are set. - \param pattern Index of the event pattern - \param e Key event + \param code Index of the event pattern + \param event Key event \return true if matches \sa mouseMatch() */ -bool QwtEventPattern::keyMatch(uint pattern, const QKeyEvent *e) const +bool QwtEventPattern::keyMatch( KeyPatternCode code, + const QKeyEvent *event ) const { - bool ok = false; - - if ( e && pattern < (uint)d_keyPattern.count() ) - ok = keyMatch(d_keyPattern[int(pattern)], e); + if ( code >= 0 && code < KeyPatternCount ) + return keyMatch( d_keyPattern[ code ], event ); - return ok; + return false; } /*! @@ -256,26 +248,18 @@ bool QwtEventPattern::keyMatch(uint pattern, const QKeyEvent *e) const are set. \param pattern Key event pattern - \param e Key event + \param event Key event \return true if matches \sa mouseMatch() */ bool QwtEventPattern::keyMatch( - const KeyPattern &pattern, const QKeyEvent *e) const + const KeyPattern &pattern, const QKeyEvent *event ) const { - if ( e->key() != pattern.key) + if ( event == NULL ) return false; - const bool matched = -#if QT_VERSION < 0x040000 - (e->state() & Qt::KeyButtonMask) == - (pattern.state & Qt::KeyButtonMask); -#else - (e->modifiers() & Qt::KeyboardModifierMask) == - (int)(pattern.state & Qt::KeyboardModifierMask); -#endif - - return matched; + const KeyPattern keyPattern( event->key(), event->modifiers() ); + return keyPattern == pattern; } diff --git a/libs/qwt/qwt_event_pattern.h b/libs/qwt/qwt_event_pattern.h index e4455b12a..7c5d1a37f 100644 --- a/libs/qwt/qwt_event_pattern.h +++ b/libs/qwt/qwt_event_pattern.h @@ -10,8 +10,9 @@ #ifndef QWT_EVENT_PATTERN #define QWT_EVENT_PATTERN 1 +#include "qwt_global.h" #include -#include "qwt_array.h" +#include class QMouseEvent; class QKeyEvent; @@ -31,103 +32,115 @@ public: /*! \brief Symbolic mouse input codes - The default initialization for 3 button mice is: - - MouseSelect1\n - Qt::LeftButton - - MouseSelect2\n - Qt::RightButton - - MouseSelect3\n - Qt::MidButton - - MouseSelect4\n - Qt::LeftButton + Qt::ShiftButton - - MouseSelect5\n - Qt::RightButton + Qt::ShiftButton - - MouseSelect6\n - Qt::MidButton + Qt::ShiftButton - - The default initialization for 2 button mice is: - - MouseSelect1\n - Qt::LeftButton - - MouseSelect2\n - Qt::RightButton - - MouseSelect3\n - Qt::LeftButton + Qt::AltButton - - MouseSelect4\n - Qt::LeftButton + Qt::ShiftButton - - MouseSelect5\n - Qt::RightButton + Qt::ShiftButton - - MouseSelect6\n - Qt::LeftButton + Qt::AltButton + Qt::ShiftButton - - The default initialization for 1 button mice is: - - MouseSelect1\n - Qt::LeftButton - - MouseSelect2\n - Qt::LeftButton + Qt::ControlButton - - MouseSelect3\n - Qt::LeftButton + Qt::AltButton - - MouseSelect4\n - Qt::LeftButton + Qt::ShiftButton - - MouseSelect5\n - Qt::LeftButton + Qt::ControlButton + Qt::ShiftButton - - MouseSelect6\n - Qt::LeftButton + Qt::AltButton + Qt::ShiftButton - - \sa initMousePattern() + QwtEventPattern implements 3 different settings for + mice with 1, 2, or 3 buttons that can be activated + using initMousePattern(). The default setting is for + 3 button mice. + + Individual settings can be configured using setMousePattern(). + + \sa initMousePattern(), setMousePattern(), setKeyPattern() */ + enum MousePatternCode + { + /*! + The default setting for 1, 2 and 3 button mice is: - enum MousePatternCode { + - Qt::LeftButton + - Qt::LeftButton + - Qt::LeftButton + */ MouseSelect1, + + /*! + The default setting for 1, 2 and 3 button mice is: + + - Qt::LeftButton + Qt::ControlModifier + - Qt::RightButton + - Qt::RightButton + */ MouseSelect2, + + /*! + The default setting for 1, 2 and 3 button mice is: + + - Qt::LeftButton + Qt::AltModifier + - Qt::LeftButton + Qt::AltModifier + - Qt::MidButton + */ MouseSelect3, + + /*! + The default setting for 1, 2 and 3 button mice is: + + - Qt::LeftButton + Qt::ShiftModifier + - Qt::LeftButton + Qt::ShiftModifier + - Qt::LeftButton + Qt::ShiftModifier + */ MouseSelect4, + + /*! + The default setting for 1, 2 and 3 button mice is: + + - Qt::LeftButton + Qt::ControlButton | Qt::ShiftModifier + - Qt::RightButton + Qt::ShiftModifier + - Qt::RightButton + Qt::ShiftModifier + */ MouseSelect5, + + /*! + The default setting for 1, 2 and 3 button mice is: + + - Qt::LeftButton + Qt::AltModifier + Qt::ShiftModifier + - Qt::LeftButton + Qt::AltModifier | Qt::ShiftModifier + - Qt::MidButton + Qt::ShiftModifier + */ MouseSelect6, + //! Number of mouse patterns MousePatternCount }; /*! \brief Symbolic keyboard input codes - Default initialization: - - KeySelect1\n - Qt::Key_Return - - KeySelect2\n - Qt::Key_Space - - KeyAbort\n - Qt::Key_Escape - - - KeyLeft\n - Qt::Key_Left - - KeyRight\n - Qt::Key_Right - - KeyUp\n - Qt::Key_Up - - KeyDown\n - Qt::Key_Down - - - KeyUndo\n - Qt::Key_Minus - - KeyRedo\n - Qt::Key_Plus - - KeyHome\n - Qt::Key_Escape + Individual settings can be configured using setKeyPattern() + + \sa setKeyPattern(), setMousePattern() */ - enum KeyPatternCode { + enum KeyPatternCode + { + //! Qt::Key_Return KeySelect1, + + //! Qt::Key_Space KeySelect2, + + //! Qt::Key_Escape KeyAbort, + //! Qt::Key_Left KeyLeft, + + //! Qt::Key_Right KeyRight, + + //! Qt::Key_Up KeyUp, + + //! Qt::Key_Down KeyDown, + //! Qt::Key_Plus KeyRedo, + + //! Qt::Key_Minus KeyUndo, + + //! Qt::Key_Escape KeyHome, + //! Number of key patterns KeyPatternCount }; @@ -135,52 +148,67 @@ public: class MousePattern { public: - MousePattern(int btn = Qt::NoButton, int st = Qt::NoButton) { - button = btn; - state = st; + //! Constructor + MousePattern( Qt::MouseButton btn = Qt::NoButton, + Qt::KeyboardModifiers modifierCodes = Qt::NoModifier ): + button( btn ), + modifiers( modifierCodes ) + { } - int button; - int state; + //! Button + Qt::MouseButton button; + + //! Keyboard modifier + Qt::KeyboardModifiers modifiers; }; //! A pattern for key events class KeyPattern { public: - KeyPattern(int k = 0, int st = Qt::NoButton) { - key = k; - state = st; + //! Constructor + KeyPattern( int keyCode = Qt::Key_unknown, + Qt::KeyboardModifiers modifierCodes = Qt::NoModifier ): + key( keyCode ), + modifiers( modifierCodes ) + { } + //! Key code int key; - int state; + + //! Modifiers + Qt::KeyboardModifiers modifiers; }; QwtEventPattern(); virtual ~QwtEventPattern(); - void initMousePattern(int numButtons); + void initMousePattern( int numButtons ); void initKeyPattern(); - void setMousePattern(uint pattern, int button, int state = Qt::NoButton); - void setKeyPattern(uint pattern, int key, int state = Qt::NoButton); + void setMousePattern( MousePatternCode, Qt::MouseButton button, + Qt::KeyboardModifiers = Qt::NoModifier ); - void setMousePattern(const QwtArray &); - void setKeyPattern(const QwtArray &); + void setKeyPattern( KeyPatternCode, int keyCode, + Qt::KeyboardModifiers modifierCodes = Qt::NoModifier ); - const QwtArray &mousePattern() const; - const QwtArray &keyPattern() const; + void setMousePattern( const QVector & ); + void setKeyPattern( const QVector & ); - QwtArray &mousePattern(); - QwtArray &keyPattern(); + const QVector &mousePattern() const; + const QVector &keyPattern() const; - bool mouseMatch(uint pattern, const QMouseEvent *) const; - bool keyMatch(uint pattern, const QKeyEvent *) const; + QVector &mousePattern(); + QVector &keyPattern(); + + bool mouseMatch( MousePatternCode, const QMouseEvent * ) const; + bool keyMatch( KeyPatternCode, const QKeyEvent * ) const; protected: - virtual bool mouseMatch(const MousePattern &, const QMouseEvent *) const; - virtual bool keyMatch(const KeyPattern &, const QKeyEvent *) const; + virtual bool mouseMatch( const MousePattern &, const QMouseEvent * ) const; + virtual bool keyMatch( const KeyPattern &, const QKeyEvent * ) const; private: @@ -188,30 +216,25 @@ private: #pragma warning(push) #pragma warning(disable: 4251) #endif - QwtArray d_mousePattern; - QwtArray d_keyPattern; + QVector d_mousePattern; + QVector d_keyPattern; #if defined(_MSC_VER) #pragma warning(pop) #endif }; -inline bool operator==(QwtEventPattern::MousePattern b1, - QwtEventPattern::MousePattern b2) +//! Compare operator +inline bool operator==( QwtEventPattern::MousePattern b1, + QwtEventPattern::MousePattern b2 ) { - return b1.button == b2.button && b1.state == b2.state; + return b1.button == b2.button && b1.modifiers == b2.modifiers; } -inline bool operator==(QwtEventPattern::KeyPattern b1, - QwtEventPattern::KeyPattern b2) +//! Compare operator +inline bool operator==( QwtEventPattern::KeyPattern b1, + QwtEventPattern::KeyPattern b2 ) { - return b1.key == b2.key && b1.state == b2.state; + return b1.key == b2.key && b1.modifiers == b2.modifiers; } -#if defined(QWT_TEMPLATEDLL) -// MOC_SKIP_BEGIN -template class QWT_EXPORT QwtArray; -template class QWT_EXPORT QwtArray; -// MOC_SKIP_END -#endif - #endif diff --git a/libs/qwt/qwt_global.h b/libs/qwt/qwt_global.h index d8c96e589..3833077c5 100644 --- a/libs/qwt/qwt_global.h +++ b/libs/qwt/qwt_global.h @@ -7,45 +7,35 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ -// vim: expandtab - #ifndef QWT_GLOBAL_H #define QWT_GLOBAL_H #include -#if QT_VERSION < 0x040000 -#include -#endif // QWT_VERSION is (major << 16) + (minor << 8) + patch. -#define QWT_VERSION 0x050102 -#define QWT_VERSION_STR "5.1.2" - -#if defined(Q_WS_WIN) +#define QWT_VERSION 0x060101 +#define QWT_VERSION_STR "6.1.1" #if defined(_MSC_VER) /* MSVC Compiler */ /* template-class specialization 'identifier' is already instantiated */ #pragma warning(disable: 4660) +/* inherits via dominance */ +#pragma warning(disable: 4250) #endif // _MSC_VER #ifdef QWT_DLL #if defined(QWT_MAKEDLL) // create a Qwt DLL library -#define QWT_EXPORT __declspec(dllexport) -#define QWT_TEMPLATEDLL +#define QWT_EXPORT Q_DECL_EXPORT #else // use a Qwt DLL library -#define QWT_EXPORT __declspec(dllimport) +#define QWT_EXPORT Q_DECL_IMPORT #endif #endif // QWT_DLL -#endif // Q_WS_WIN - #ifndef QWT_EXPORT #define QWT_EXPORT #endif -// #define QWT_NO_COMPAT 1 // disable withdrawn functionality - -#endif // QWT_GLOBAL_H +#endif diff --git a/libs/qwt/qwt_graphic.cpp b/libs/qwt/qwt_graphic.cpp new file mode 100644 index 000000000..d67bcea0e --- /dev/null +++ b/libs/qwt/qwt_graphic.cpp @@ -0,0 +1,986 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_graphic.h" +#include "qwt_painter_command.h" +#include +#include +#include +#include +#include +#include +#include + +static bool qwtHasScalablePen( const QPainter *painter ) +{ + const QPen pen = painter->pen(); + + bool scalablePen = false; + + if ( pen.style() != Qt::NoPen && pen.brush().style() != Qt::NoBrush ) + { + scalablePen = !pen.isCosmetic(); + if ( !scalablePen && pen.widthF() == 0.0 ) + { + const QPainter::RenderHints hints = painter->renderHints(); + if ( hints.testFlag( QPainter::NonCosmeticDefaultPen ) ) + scalablePen = true; + } + } + + return scalablePen; +} + +static QRectF qwtStrokedPathRect( + const QPainter *painter, const QPainterPath &path ) +{ + QPainterPathStroker stroker; + stroker.setWidth( painter->pen().widthF() ); + stroker.setCapStyle( painter->pen().capStyle() ); + stroker.setJoinStyle( painter->pen().joinStyle() ); + stroker.setMiterLimit( painter->pen().miterLimit() ); + + QRectF rect; + if ( qwtHasScalablePen( painter ) ) + { + QPainterPath stroke = stroker.createStroke(path); + rect = painter->transform().map(stroke).boundingRect(); + } + else + { + QPainterPath mappedPath = painter->transform().map(path); + mappedPath = stroker.createStroke( mappedPath ); + + rect = mappedPath.boundingRect(); + } + + return rect; +} + +static inline void qwtExecCommand( + QPainter *painter, const QwtPainterCommand &cmd, + QwtGraphic::RenderHints renderHints, + const QTransform &transform ) +{ + switch( cmd.type() ) + { + case QwtPainterCommand::Path: + { + bool doMap = false; + + if ( renderHints.testFlag( QwtGraphic::RenderPensUnscaled ) + && painter->transform().isScaling() ) + { + bool isCosmetic = painter->pen().isCosmetic(); + if ( isCosmetic && painter->pen().widthF() == 0.0 ) + { + QPainter::RenderHints hints = painter->renderHints(); + if ( hints.testFlag( QPainter::NonCosmeticDefaultPen ) ) + isCosmetic = false; + } + + doMap = !isCosmetic; + } + + if ( doMap ) + { + const QTransform transform = painter->transform(); + + painter->resetTransform(); + painter->drawPath( transform.map( *cmd.path() ) ); + + painter->setTransform( transform ); + } + else + { + painter->drawPath( *cmd.path() ); + } + break; + } + case QwtPainterCommand::Pixmap: + { + const QwtPainterCommand::PixmapData *data = cmd.pixmapData(); + painter->drawPixmap( data->rect, data->pixmap, data->subRect ); + break; + } + case QwtPainterCommand::Image: + { + const QwtPainterCommand::ImageData *data = cmd.imageData(); + painter->drawImage( data->rect, data->image, + data->subRect, data->flags ); + break; + } + case QwtPainterCommand::State: + { + const QwtPainterCommand::StateData *data = cmd.stateData(); + + if ( data->flags & QPaintEngine::DirtyPen ) + painter->setPen( data->pen ); + + if ( data->flags & QPaintEngine::DirtyBrush ) + painter->setBrush( data->brush ); + + if ( data->flags & QPaintEngine::DirtyBrushOrigin ) + painter->setBrushOrigin( data->brushOrigin ); + + if ( data->flags & QPaintEngine::DirtyFont ) + painter->setFont( data->font ); + + if ( data->flags & QPaintEngine::DirtyBackground ) + { + painter->setBackgroundMode( data->backgroundMode ); + painter->setBackground( data->backgroundBrush ); + } + + if ( data->flags & QPaintEngine::DirtyTransform ) + { + painter->setTransform( data->transform * transform ); + } + + if ( data->flags & QPaintEngine::DirtyClipEnabled ) + painter->setClipping( data->isClipEnabled ); + + if ( data->flags & QPaintEngine::DirtyClipRegion) + { + painter->setClipRegion( data->clipRegion, + data->clipOperation ); + } + + if ( data->flags & QPaintEngine::DirtyClipPath ) + { + painter->setClipPath( data->clipPath, data->clipOperation ); + } + + if ( data->flags & QPaintEngine::DirtyHints) + { + const QPainter::RenderHints hints = data->renderHints; + + painter->setRenderHint( QPainter::Antialiasing, + hints.testFlag( QPainter::Antialiasing ) ); + + painter->setRenderHint( QPainter::TextAntialiasing, + hints.testFlag( QPainter::TextAntialiasing ) ); + + painter->setRenderHint( QPainter::SmoothPixmapTransform, + hints.testFlag( QPainter::SmoothPixmapTransform ) ); + + painter->setRenderHint( QPainter::HighQualityAntialiasing, + hints.testFlag( QPainter::HighQualityAntialiasing ) ); + + painter->setRenderHint( QPainter::NonCosmeticDefaultPen, + hints.testFlag( QPainter::NonCosmeticDefaultPen ) ); + } + + if ( data->flags & QPaintEngine::DirtyCompositionMode) + painter->setCompositionMode( data->compositionMode ); + + if ( data->flags & QPaintEngine::DirtyOpacity) + painter->setOpacity( data->opacity ); + + break; + } + default: + break; + } + +} + +class QwtGraphic::PathInfo +{ +public: + PathInfo(): + d_scalablePen( false ) + { + // QVector needs a default constructor + } + + PathInfo( const QRectF &pointRect, + const QRectF &boundingRect, bool scalablePen ): + d_pointRect( pointRect ), + d_boundingRect( boundingRect ), + d_scalablePen( scalablePen ) + { + } + + inline QRectF scaledBoundingRect( double sx, double sy, + bool scalePens ) const + { + if ( sx == 1.0 && sy == 1.0 ) + return d_boundingRect; + + QTransform transform; + transform.scale( sx, sy ); + + QRectF rect; + if ( scalePens && d_scalablePen ) + { + rect = transform.mapRect( d_boundingRect ); + } + else + { + rect = transform.mapRect( d_pointRect ); + + const double l = qAbs( d_pointRect.left() - d_boundingRect.left() ); + const double r = qAbs( d_pointRect.right() - d_boundingRect.right() ); + const double t = qAbs( d_pointRect.top() - d_boundingRect.top() ); + const double b = qAbs( d_pointRect.bottom() - d_boundingRect.bottom() ); + + rect.adjust( -l, -t, r, b ); + } + + return rect; + } + + inline double scaleFactorX( const QRectF& pathRect, + const QRectF &targetRect, bool scalePens ) const + { + if ( pathRect.width() <= 0.0 ) + return 0.0; + + const QPointF p0 = d_pointRect.center(); + + const double l = qAbs( pathRect.left() - p0.x() ); + const double r = qAbs( pathRect.right() - p0.x() ); + + const double w = 2.0 * qMin( l, r ) + * targetRect.width() / pathRect.width(); + + double sx; + if ( scalePens && d_scalablePen ) + { + sx = w / d_boundingRect.width(); + } + else + { + const double pw = qMax( + qAbs( d_boundingRect.left() - d_pointRect.left() ), + qAbs( d_boundingRect.right() - d_pointRect.right() ) ); + + sx = ( w - 2 * pw ) / d_pointRect.width(); + } + + return sx; + } + + inline double scaleFactorY( const QRectF& pathRect, + const QRectF &targetRect, bool scalePens ) const + { + if ( pathRect.height() <= 0.0 ) + return 0.0; + + const QPointF p0 = d_pointRect.center(); + + const double t = qAbs( pathRect.top() - p0.y() ); + const double b = qAbs( pathRect.bottom() - p0.y() ); + + const double h = 2.0 * qMin( t, b ) + * targetRect.height() / pathRect.height(); + + double sy; + if ( scalePens && d_scalablePen ) + { + sy = h / d_boundingRect.height(); + } + else + { + const double pw = + qMax( qAbs( d_boundingRect.top() - d_pointRect.top() ), + qAbs( d_boundingRect.bottom() - d_pointRect.bottom() ) ); + + sy = ( h - 2 * pw ) / d_pointRect.height(); + } + + return sy; + } + +private: + QRectF d_pointRect; + QRectF d_boundingRect; + bool d_scalablePen; +}; + +class QwtGraphic::PrivateData +{ +public: + PrivateData(): + boundingRect( 0.0, 0.0, -1.0, -1.0 ), + pointRect( 0.0, 0.0, -1.0, -1.0 ) + { + } + + QSizeF defaultSize; + QVector commands; + QVector pathInfos; + + QRectF boundingRect; + QRectF pointRect; + + QwtGraphic::RenderHints renderHints; +}; + +/*! + \brief Constructor + + Initializes a null graphic + \sa isNull() + */ +QwtGraphic::QwtGraphic(): + QwtNullPaintDevice() +{ + setMode( QwtNullPaintDevice::PathMode ); + d_data = new PrivateData; +} + +/*! + \brief Copy constructor + + \param other Source + \sa operator=() + */ +QwtGraphic::QwtGraphic( const QwtGraphic &other ): + QwtNullPaintDevice() +{ + setMode( other.mode() ); + d_data = new PrivateData( *other.d_data ); +} + +//! Destructor +QwtGraphic::~QwtGraphic() +{ + delete d_data; +} + +/*! + \brief Assignment operator + + \param other Source + \return A reference of this object + */ +QwtGraphic& QwtGraphic::operator=(const QwtGraphic &other) +{ + setMode( other.mode() ); + *d_data = *other.d_data; + + return *this; +} + +/*! + \brief Clear all stored commands + \sa isNull() + */ +void QwtGraphic::reset() +{ + d_data->commands.clear(); + d_data->pathInfos.clear(); + + d_data->boundingRect = QRectF( 0.0, 0.0, -1.0, -1.0 ); + d_data->pointRect = QRectF( 0.0, 0.0, -1.0, -1.0 ); + d_data->defaultSize = QSizeF(); + +} + +/*! + \return True, when no painter commands have been stored + \sa isEmpty(), commands() +*/ +bool QwtGraphic::isNull() const +{ + return d_data->commands.isEmpty(); +} + +/*! + \return True, when the bounding rectangle is empty + \sa boundingRect(), isNull() +*/ +bool QwtGraphic::isEmpty() const +{ + return d_data->boundingRect.isEmpty(); +} + +/*! + Toggle an render hint + + \param hint Render hint + \param on true/false + + \sa testRenderHint(), RenderHint +*/ +void QwtGraphic::setRenderHint( RenderHint hint, bool on ) +{ + if ( on ) + d_data->renderHints |= hint; + else + d_data->renderHints &= ~hint; +} + +/*! + Test a render hint + + \param hint Render hint + \return true/false + \sa setRenderHint(), RenderHint +*/ +bool QwtGraphic::testRenderHint( RenderHint hint ) const +{ + return d_data->renderHints.testFlag( hint ); +} + +/*! + The bounding rectangle is the controlPointRect() + extended by the areas needed for rendering the outlines + with unscaled pens. + + \return Bounding rectangle of the graphic + \sa controlPointRect(), scaledBoundingRect() + */ +QRectF QwtGraphic::boundingRect() const +{ + if ( d_data->boundingRect.width() < 0 ) + return QRectF(); + + return d_data->boundingRect; +} + +/*! + The control point rectangle is the bounding rectangle + of all control points of the paths and the target + rectangles of the images/pixmaps. + + \return Control point rectangle + \sa boundingRect(), scaledBoundingRect() + */ +QRectF QwtGraphic::controlPointRect() const +{ + if ( d_data->pointRect.width() < 0 ) + return QRectF(); + + return d_data->pointRect; +} + +/*! + \brief Calculate the target rectangle for scaling the graphic + + \param sx Horizontal scaling factor + \param sy Vertical scaling factor + + \note In case of paths that are painted with a cosmetic pen + ( see QPen::isCosmetic() ) the target rectangle is different to + multiplying the bounding rectangle. + + \return Scaled bounding rectangle + \sa boundingRect(), controlPointRect() + */ +QRectF QwtGraphic::scaledBoundingRect( double sx, double sy ) const +{ + if ( sx == 1.0 && sy == 1.0 ) + return d_data->boundingRect; + + QTransform transform; + transform.scale( sx, sy ); + + QRectF rect = transform.mapRect( d_data->pointRect ); + + for ( int i = 0; i < d_data->pathInfos.size(); i++ ) + { + rect |= d_data->pathInfos[i].scaledBoundingRect( sx, sy, + !d_data->renderHints.testFlag( RenderPensUnscaled ) ); + } + + return rect; +} + +//! \return Ceiled defaultSize() +QSize QwtGraphic::sizeMetrics() const +{ + const QSizeF sz = defaultSize(); + return QSize( qCeil( sz.width() ), qCeil( sz.height() ) ); +} + +/*! + \brief Set a default size + + The default size is used in all methods rendering the graphic, + where no size is explicitly specified. Assigning an empty size + means, that the default size will be calculated from the bounding + rectangle. + + The default setting is an empty size. + + \param size Default size + + \sa defaultSize(), boundingRect() + */ +void QwtGraphic::setDefaultSize( const QSizeF &size ) +{ + const double w = qMax( qreal( 0.0 ), size.width() ); + const double h = qMax( qreal( 0.0 ), size.height() ); + + d_data->defaultSize = QSizeF( w, h ); +} + +/*! + \brief Default size + + When a non empty size has been assigned by setDefaultSize() this + size will be returned. Otherwise the default size is the size + of the bounding rectangle. + + The default size is used in all methods rendering the graphic, + where no size is explicitly specified. + + \return Default size + \sa setDefaultSize(), boundingRect() + */ +QSizeF QwtGraphic::defaultSize() const +{ + if ( !d_data->defaultSize.isEmpty() ) + return d_data->defaultSize; + + return boundingRect().size(); +} + +/*! + \brief Replay all recorded painter commands + \param painter Qt painter + */ +void QwtGraphic::render( QPainter *painter ) const +{ + if ( isNull() ) + return; + + const int numCommands = d_data->commands.size(); + const QwtPainterCommand *commands = d_data->commands.constData(); + + const QTransform transform = painter->transform(); + + painter->save(); + + for ( int i = 0; i < numCommands; i++ ) + { + qwtExecCommand( painter, commands[i], + d_data->renderHints, transform ); + } + + painter->restore(); +} + +/*! + \brief Replay all recorded painter commands + + The graphic is scaled to fit into the rectangle + of the given size starting at ( 0, 0 ). + + \param painter Qt painter + \param size Size for the scaled graphic + \param aspectRatioMode Mode how to scale - See Qt::AspectRatioMode + */ +void QwtGraphic::render( QPainter *painter, const QSizeF &size, + Qt::AspectRatioMode aspectRatioMode ) const +{ + const QRectF r( 0.0, 0.0, size.width(), size.height() ); + render( painter, r, aspectRatioMode ); +} + +/*! + \brief Replay all recorded painter commands + + The graphic is scaled to fit into the given rectangle + + \param painter Qt painter + \param rect Rectangle for the scaled graphic + \param aspectRatioMode Mode how to scale - See Qt::AspectRatioMode + */ +void QwtGraphic::render( QPainter *painter, const QRectF &rect, + Qt::AspectRatioMode aspectRatioMode ) const +{ + if ( isEmpty() || rect.isEmpty() ) + return; + + double sx = 1.0; + double sy = 1.0; + + if ( d_data->pointRect.width() > 0.0 ) + sx = rect.width() / d_data->pointRect.width(); + + if ( d_data->pointRect.height() > 0.0 ) + sy = rect.height() / d_data->pointRect.height(); + + const bool scalePens = + !d_data->renderHints.testFlag( RenderPensUnscaled ); + + for ( int i = 0; i < d_data->pathInfos.size(); i++ ) + { + const PathInfo info = d_data->pathInfos[i]; + + const double ssx = info.scaleFactorX( + d_data->pointRect, rect, scalePens ); + + if ( ssx > 0.0 ) + sx = qMin( sx, ssx ); + + const double ssy = info.scaleFactorY( + d_data->pointRect, rect, scalePens ); + + if ( ssy > 0.0 ) + sy = qMin( sy, ssy ); + } + + if ( aspectRatioMode == Qt::KeepAspectRatio ) + { + const double s = qMin( sx, sy ); + sx = s; + sy = s; + } + else if ( aspectRatioMode == Qt::KeepAspectRatioByExpanding ) + { + const double s = qMax( sx, sy ); + sx = s; + sy = s; + } + + QTransform tr; + tr.translate( rect.center().x() - 0.5 * sx * d_data->pointRect.width(), + rect.center().y() - 0.5 * sy * d_data->pointRect.height() ); + tr.scale( sx, sy ); + tr.translate( -d_data->pointRect.x(), -d_data->pointRect.y() ); + + const QTransform transform = painter->transform(); + + painter->setTransform( tr, true ); + render( painter ); + + painter->setTransform( transform ); +} + +/*! + \brief Replay all recorded painter commands + + The graphic is scaled to the defaultSize() and aligned + to a position. + + \param painter Qt painter + \param pos Reference point, where to render + \param alignment Flags how to align the target rectangle + to pos. + */ +void QwtGraphic::render( QPainter *painter, + const QPointF &pos, Qt::Alignment alignment ) const +{ + QRectF r( pos, defaultSize() ); + + if ( alignment & Qt::AlignLeft ) + { + r.moveLeft( pos.x() ); + } + else if ( alignment & Qt::AlignHCenter ) + { + r.moveCenter( QPointF( pos.x(), r.center().y() ) ); + } + else if ( alignment & Qt::AlignRight ) + { + r.moveRight( pos.x() ); + } + + if ( alignment & Qt::AlignTop ) + { + r.moveTop( pos.y() ); + } + else if ( alignment & Qt::AlignVCenter ) + { + r.moveCenter( QPointF( r.center().x(), pos.y() ) ); + } + else if ( alignment & Qt::AlignBottom ) + { + r.moveBottom( pos.y() ); + } + + render( painter, r ); +} + +/*! + \brief Convert the graphic to a QPixmap + + All pixels of the pixmap get initialized by Qt::transparent + before the graphic is scaled and rendered on it. + + The size of the pixmap is the default size ( ceiled to integers ) + of the graphic. + + \return The graphic as pixmap in default size + \sa defaultSize(), toImage(), render() + */ +QPixmap QwtGraphic::toPixmap() const +{ + if ( isNull() ) + return QPixmap(); + + const QSizeF sz = defaultSize(); + + const int w = qCeil( sz.width() ); + const int h = qCeil( sz.height() ); + + QPixmap pixmap( w, h ); + pixmap.fill( Qt::transparent ); + + const QRectF r( 0.0, 0.0, sz.width(), sz.height() ); + + QPainter painter( &pixmap ); + render( &painter, r, Qt::KeepAspectRatio ); + painter.end(); + + return pixmap; +} + +/*! + \brief Convert the graphic to a QPixmap + + All pixels of the pixmap get initialized by Qt::transparent + before the graphic is scaled and rendered on it. + + \param size Size of the image + \param aspectRatioMode Aspect ratio how to scale the graphic + + \return The graphic as pixmap + \sa toImage(), render() + */ +QPixmap QwtGraphic::toPixmap( const QSize &size, + Qt::AspectRatioMode aspectRatioMode ) const +{ + QPixmap pixmap( size ); + pixmap.fill( Qt::transparent ); + + const QRect r( 0, 0, size.width(), size.height() ); + + QPainter painter( &pixmap ); + render( &painter, r, aspectRatioMode ); + painter.end(); + + return pixmap; +} + +/*! + \brief Convert the graphic to a QImage + + All pixels of the image get initialized by 0 ( transparent ) + before the graphic is scaled and rendered on it. + + The format of the image is QImage::Format_ARGB32_Premultiplied. + + \param size Size of the image + \param aspectRatioMode Aspect ratio how to scale the graphic + + \return The graphic as image + \sa toPixmap(), render() + */ +QImage QwtGraphic::toImage( const QSize &size, + Qt::AspectRatioMode aspectRatioMode ) const +{ + QImage image( size, QImage::Format_ARGB32_Premultiplied ); + image.fill( 0 ); + + const QRect r( 0, 0, size.width(), size.height() ); + + QPainter painter( &image ); + render( &painter, r, aspectRatioMode ); + painter.end(); + + return image; +} + +/*! + \brief Convert the graphic to a QImage + + All pixels of the image get initialized by 0 ( transparent ) + before the graphic is scaled and rendered on it. + + The format of the image is QImage::Format_ARGB32_Premultiplied. + + The size of the image is the default size ( ceiled to integers ) + of the graphic. + + \return The graphic as image in default size + \sa defaultSize(), toPixmap(), render() + */ +QImage QwtGraphic::toImage() const +{ + if ( isNull() ) + return QImage(); + + const QSizeF sz = defaultSize(); + + const int w = qCeil( sz.width() ); + const int h = qCeil( sz.height() ); + + QImage image( w, h, QImage::Format_ARGB32 ); + image.fill( 0 ); + + const QRect r( 0, 0, sz.width(), sz.height() ); + + QPainter painter( &image ); + render( &painter, r, Qt::KeepAspectRatio ); + painter.end(); + + return image; +} + +/*! + Store a path command in the command list + + \param path Painter path + \sa QPaintEngine::drawPath() +*/ +void QwtGraphic::drawPath( const QPainterPath &path ) +{ + const QPainter *painter = paintEngine()->painter(); + if ( painter == NULL ) + return; + + d_data->commands += QwtPainterCommand( path ); + + if ( !path.isEmpty() ) + { + const QPainterPath scaledPath = painter->transform().map( path ); + + QRectF pointRect = scaledPath.boundingRect(); + QRectF boundingRect = pointRect; + + if ( painter->pen().style() != Qt::NoPen + && painter->pen().brush().style() != Qt::NoBrush ) + { + boundingRect = qwtStrokedPathRect( painter, path ); + } + + updateControlPointRect( pointRect ); + updateBoundingRect( boundingRect ); + + d_data->pathInfos += PathInfo( pointRect, + boundingRect, qwtHasScalablePen( painter ) ); + } +} + +/*! + \brief Store a pixmap command in the command list + + \param rect target rectangle + \param pixmap Pixmap to be painted + \param subRect Reactangle of the pixmap to be painted + + \sa QPaintEngine::drawPixmap() +*/ +void QwtGraphic::drawPixmap( const QRectF &rect, + const QPixmap &pixmap, const QRectF &subRect ) +{ + const QPainter *painter = paintEngine()->painter(); + if ( painter == NULL ) + return; + + d_data->commands += QwtPainterCommand( rect, pixmap, subRect ); + + const QRectF r = painter->transform().mapRect( rect ); + updateControlPointRect( r ); + updateBoundingRect( r ); +} + +/*! + \brief Store a image command in the command list + + \param rect traget rectangle + \param image Image to be painted + \param subRect Reactangle of the pixmap to be painted + \param flags Image conversion flags + + \sa QPaintEngine::drawImage() + */ +void QwtGraphic::drawImage( const QRectF &rect, const QImage &image, + const QRectF &subRect, Qt::ImageConversionFlags flags) +{ + const QPainter *painter = paintEngine()->painter(); + if ( painter == NULL ) + return; + + d_data->commands += QwtPainterCommand( rect, image, subRect, flags ); + + const QRectF r = painter->transform().mapRect( rect ); + + updateControlPointRect( r ); + updateBoundingRect( r ); +} + +/*! + \brief Store a state command in the command list + + \param state State to be stored + \sa QPaintEngine::updateState() + */ +void QwtGraphic::updateState( const QPaintEngineState &state) +{ + d_data->commands += QwtPainterCommand( state ); +} + +void QwtGraphic::updateBoundingRect( const QRectF &rect ) +{ + QRectF br = rect; + + const QPainter *painter = paintEngine()->painter(); + if ( painter && painter->hasClipping() ) + { + QRectF cr = painter->clipRegion().boundingRect(); + cr = painter->transform().mapRect( br ); + + br &= cr; + } + + if ( d_data->boundingRect.width() < 0 ) + d_data->boundingRect = br; + else + d_data->boundingRect |= br; +} + +void QwtGraphic::updateControlPointRect( const QRectF &rect ) +{ + if ( d_data->pointRect.width() < 0.0 ) + d_data->pointRect = rect; + else + d_data->pointRect |= rect; +} + +/*! + \return List of recorded paint commands + \sa setCommands() + */ +const QVector< QwtPainterCommand > &QwtGraphic::commands() const +{ + return d_data->commands; +} + +/*! + \brief Append paint commands + + \param commands Paint commands + \sa commands() + */ +void QwtGraphic::setCommands( QVector< QwtPainterCommand > &commands ) +{ + reset(); + + const int numCommands = commands.size(); + if ( numCommands <= 0 ) + return; + + // to calculate a proper bounding rectangle we don't simply copy + // the commands. + + const QwtPainterCommand *cmds = commands.constData(); + + QPainter painter( this ); + for ( int i = 0; i < numCommands; i++ ) + qwtExecCommand( &painter, cmds[i], RenderHints(), QTransform() ); + + painter.end(); +} diff --git a/libs/qwt/qwt_graphic.h b/libs/qwt/qwt_graphic.h new file mode 100644 index 000000000..a1ce1e8a2 --- /dev/null +++ b/libs/qwt/qwt_graphic.h @@ -0,0 +1,172 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_GRAPHIC_H +#define QWT_GRAPHIC_H + +#include "qwt_global.h" +#include "qwt_null_paintdevice.h" +#include +#include +#include + +class QwtPainterCommand; + +/*! + \brief A paint device for scalable graphics + + QwtGraphic is the representation of a graphic that is tailored for + scalability. Like QPicture it will be initialized by QPainter + operations and can be replayed later to any target paint device. + + While the usual image representations QImage and QPixmap are not + scalable Qt offers two paint devices, that might be candidates + for representing a vector graphic: + + - QPicture\n + Unfortunately QPicture had been forgotten, when Qt4 + introduced floating point based render engines. Its API + is still on integers, what make it unusable for proper scaling. + + - QSvgRenderer/QSvgGenerator\n + Unfortunately QSvgRenderer hides to much information about + its nodes in internal APIs, that are necessary for proper + layout calculations. Also it is derived from QObject and + can't be copied like QImage/QPixmap. + + QwtGraphic maps all scalable drawing primitives to a QPainterPath + and stores them together with the painter state changes + ( pen, brush, transformation ... ) in a list of QwtPaintCommands. + For being a complete QPaintDevice it also stores pixmaps or images, + what is somehow against the idea of the class, because these objects + can't be scaled without a loss in quality. + + The main issue about scaling a QwtGraphic object are the pens used for + drawing the outlines of the painter paths. While non cosmetic pens + ( QPen::isCosmetic() ) are scaled with the same ratio as the path, + cosmetic pens have a fixed width. A graphic might have paths with + different pens - cosmetic and non-cosmetic. + + QwtGraphic caches 2 different rectangles: + + - control point rectangle\n + The control point rectangle is the bounding rectangle of all + control point rectangles of the painter paths, or the target + rectangle of the pixmaps/images. + + - bounding rectangle\n + The bounding rectangle extends the control point rectangle by + what is needed for rendering the outline with an unscaled pen. + + Because the offset for drawing the outline depends on the shape + of the painter path ( the peak of a triangle is different than the flat side ) + scaling with a fixed aspect ratio always needs to be calculated from the + control point rectangle. + + \sa QwtPainterCommand + */ +class QWT_EXPORT QwtGraphic: public QwtNullPaintDevice +{ +public: + /*! + Hint how to render a graphic + \sa setRenderHint(), testRenderHint() + */ + enum RenderHint + { + /*! + When RenderPensUnscaled is set non cosmetic pens are + painted unscaled - like cosmetic pens. The difference to + using cosmetic pens is, when the graphic is rendered + to a document in a scalable vector format ( PDF, SVG ): + the width of non cosmetic pens will be scaled by the + document viewer. + */ + RenderPensUnscaled = 0x1 + }; + + /*! + \brief Render hints + + The default setting is to disable all hints + */ + typedef QFlags RenderHints; + + QwtGraphic(); + QwtGraphic( const QwtGraphic & ); + + virtual ~QwtGraphic(); + + QwtGraphic& operator=( const QwtGraphic & ); + + void reset(); + + bool isNull() const; + bool isEmpty() const; + + void render( QPainter * ) const; + + void render( QPainter *, const QSizeF &, + Qt::AspectRatioMode = Qt::IgnoreAspectRatio ) const; + + void render( QPainter *, const QRectF &, + Qt::AspectRatioMode = Qt::IgnoreAspectRatio ) const; + + void render( QPainter *, const QPointF &, + Qt::Alignment = Qt::AlignTop | Qt::AlignLeft ) const; + + QPixmap toPixmap() const; + QPixmap toPixmap( const QSize &, + Qt::AspectRatioMode = Qt::IgnoreAspectRatio ) const; + + QImage toImage() const; + QImage toImage( const QSize &, + Qt::AspectRatioMode = Qt::IgnoreAspectRatio ) const; + + QRectF scaledBoundingRect( double sx, double sy ) const; + + QRectF boundingRect() const; + QRectF controlPointRect() const; + + const QVector< QwtPainterCommand > &commands() const; + void setCommands( QVector< QwtPainterCommand > & ); + + void setDefaultSize( const QSizeF & ); + QSizeF defaultSize() const; + + void setRenderHint( RenderHint, bool on = true ); + bool testRenderHint( RenderHint ) const; + +protected: + virtual QSize sizeMetrics() const; + + virtual void drawPath( const QPainterPath & ); + + virtual void drawPixmap( const QRectF &, + const QPixmap &, const QRectF & ); + + virtual void drawImage( const QRectF &, + const QImage &, const QRectF &, Qt::ImageConversionFlags ); + + virtual void updateState( const QPaintEngineState &state ); + +private: + void updateBoundingRect( const QRectF & ); + void updateControlPointRect( const QRectF & ); + + class PathInfo; + + class PrivateData; + PrivateData *d_data; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtGraphic::RenderHints ) +Q_DECLARE_METATYPE( QwtGraphic ) + +#endif diff --git a/libs/qwt/qwt_interval.cpp b/libs/qwt/qwt_interval.cpp new file mode 100644 index 000000000..b7c6ee992 --- /dev/null +++ b/libs/qwt/qwt_interval.cpp @@ -0,0 +1,354 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_interval.h" +#include "qwt_math.h" +#include + +/*! + \brief Normalize the limits of the interval + + If maxValue() < minValue() the limits will be inverted. + \return Normalized interval + + \sa isValid(), inverted() +*/ +QwtInterval QwtInterval::normalized() const +{ + if ( d_minValue > d_maxValue ) + { + return inverted(); + } + if ( d_minValue == d_maxValue && d_borderFlags == ExcludeMinimum ) + { + return inverted(); + } + + return *this; +} + +/*! + Invert the limits of the interval + \return Inverted interval + \sa normalized() +*/ +QwtInterval QwtInterval::inverted() const +{ + BorderFlags borderFlags = IncludeBorders; + if ( d_borderFlags & ExcludeMinimum ) + borderFlags |= ExcludeMaximum; + if ( d_borderFlags & ExcludeMaximum ) + borderFlags |= ExcludeMinimum; + + return QwtInterval( d_maxValue, d_minValue, borderFlags ); +} + +/*! + Test if a value is inside an interval + + \param value Value + \return true, if value >= minValue() && value <= maxValue() +*/ +bool QwtInterval::contains( double value ) const +{ + if ( !isValid() ) + return false; + + if ( value < d_minValue || value > d_maxValue ) + return false; + + if ( value == d_minValue && d_borderFlags & ExcludeMinimum ) + return false; + + if ( value == d_maxValue && d_borderFlags & ExcludeMaximum ) + return false; + + return true; +} + +//! Unite 2 intervals +QwtInterval QwtInterval::unite( const QwtInterval &other ) const +{ + /* + If one of the intervals is invalid return the other one. + If both are invalid return an invalid default interval + */ + if ( !isValid() ) + { + if ( !other.isValid() ) + return QwtInterval(); + else + return other; + } + if ( !other.isValid() ) + return *this; + + QwtInterval united; + BorderFlags flags = IncludeBorders; + + // minimum + if ( d_minValue < other.minValue() ) + { + united.setMinValue( d_minValue ); + flags &= d_borderFlags & ExcludeMinimum; + } + else if ( other.minValue() < d_minValue ) + { + united.setMinValue( other.minValue() ); + flags &= other.borderFlags() & ExcludeMinimum; + } + else // d_minValue == other.minValue() + { + united.setMinValue( d_minValue ); + flags &= ( d_borderFlags & other.borderFlags() ) & ExcludeMinimum; + } + + // maximum + if ( d_maxValue > other.maxValue() ) + { + united.setMaxValue( d_maxValue ); + flags &= d_borderFlags & ExcludeMaximum; + } + else if ( other.maxValue() > d_maxValue ) + { + united.setMaxValue( other.maxValue() ); + flags &= other.borderFlags() & ExcludeMaximum; + } + else // d_maxValue == other.maxValue() ) + { + united.setMaxValue( d_maxValue ); + flags &= d_borderFlags & other.borderFlags() & ExcludeMaximum; + } + + united.setBorderFlags( flags ); + return united; +} + +/*! + \brief Intersect 2 intervals + + \param other Interval to be intersect with + \return Intersection + */ +QwtInterval QwtInterval::intersect( const QwtInterval &other ) const +{ + if ( !other.isValid() || !isValid() ) + return QwtInterval(); + + QwtInterval i1 = *this; + QwtInterval i2 = other; + + // swap i1/i2, so that the minimum of i1 + // is smaller then the minimum of i2 + + if ( i1.minValue() > i2.minValue() ) + { + qSwap( i1, i2 ); + } + else if ( i1.minValue() == i2.minValue() ) + { + if ( i1.borderFlags() & ExcludeMinimum ) + qSwap( i1, i2 ); + } + + if ( i1.maxValue() < i2.minValue() ) + { + return QwtInterval(); + } + + if ( i1.maxValue() == i2.minValue() ) + { + if ( i1.borderFlags() & ExcludeMaximum || + i2.borderFlags() & ExcludeMinimum ) + { + return QwtInterval(); + } + } + + QwtInterval intersected; + BorderFlags flags = IncludeBorders; + + intersected.setMinValue( i2.minValue() ); + flags |= i2.borderFlags() & ExcludeMinimum; + + if ( i1.maxValue() < i2.maxValue() ) + { + intersected.setMaxValue( i1.maxValue() ); + flags |= i1.borderFlags() & ExcludeMaximum; + } + else if ( i2.maxValue() < i1.maxValue() ) + { + intersected.setMaxValue( i2.maxValue() ); + flags |= i2.borderFlags() & ExcludeMaximum; + } + else // i1.maxValue() == i2.maxValue() + { + intersected.setMaxValue( i1.maxValue() ); + flags |= i1.borderFlags() & i2.borderFlags() & ExcludeMaximum; + } + + intersected.setBorderFlags( flags ); + return intersected; +} + +/*! + \brief Unite this interval with the given interval. + + \param other Interval to be united with + \return This interval + */ +QwtInterval& QwtInterval::operator|=( const QwtInterval &other ) +{ + *this = *this | other; + return *this; +} + +/*! + \brief Intersect this interval with the given interval. + + \param other Interval to be intersected with + \return This interval + */ +QwtInterval& QwtInterval::operator&=( const QwtInterval &other ) +{ + *this = *this & other; + return *this; +} + +/*! + \brief Test if two intervals overlap + + \param other Interval + \return True, when the intervals are intersecting +*/ +bool QwtInterval::intersects( const QwtInterval &other ) const +{ + if ( !isValid() || !other.isValid() ) + return false; + + QwtInterval i1 = *this; + QwtInterval i2 = other; + + // swap i1/i2, so that the minimum of i1 + // is smaller then the minimum of i2 + + if ( i1.minValue() > i2.minValue() ) + { + qSwap( i1, i2 ); + } + else if ( i1.minValue() == i2.minValue() && + i1.borderFlags() & ExcludeMinimum ) + { + qSwap( i1, i2 ); + } + + if ( i1.maxValue() > i2.minValue() ) + { + return true; + } + if ( i1.maxValue() == i2.minValue() ) + { + return !( ( i1.borderFlags() & ExcludeMaximum ) || + ( i2.borderFlags() & ExcludeMinimum ) ); + } + return false; +} + +/*! + Adjust the limit that is closer to value, so that value becomes + the center of the interval. + + \param value Center + \return Interval with value as center +*/ +QwtInterval QwtInterval::symmetrize( double value ) const +{ + if ( !isValid() ) + return *this; + + const double delta = + qMax( qAbs( value - d_maxValue ), qAbs( value - d_minValue ) ); + + return QwtInterval( value - delta, value + delta ); +} + +/*! + Limit the interval, keeping the border modes + + \param lowerBound Lower limit + \param upperBound Upper limit + + \return Limited interval +*/ +QwtInterval QwtInterval::limited( double lowerBound, double upperBound ) const +{ + if ( !isValid() || lowerBound > upperBound ) + return QwtInterval(); + + double minValue = qMax( d_minValue, lowerBound ); + minValue = qMin( minValue, upperBound ); + + double maxValue = qMax( d_maxValue, lowerBound ); + maxValue = qMin( maxValue, upperBound ); + + return QwtInterval( minValue, maxValue, d_borderFlags ); +} + +/*! + \brief Extend the interval + + If value is below minValue(), value becomes the lower limit. + If value is above maxValue(), value becomes the upper limit. + + extend() has no effect for invalid intervals + + \param value Value + \return extended interval + + \sa isValid() +*/ +QwtInterval QwtInterval::extend( double value ) const +{ + if ( !isValid() ) + return *this; + + return QwtInterval( qMin( value, d_minValue ), + qMax( value, d_maxValue ), d_borderFlags ); +} + +/*! + Extend an interval + + \param value Value + \return Reference of the extended interval + + \sa extend() +*/ +QwtInterval& QwtInterval::operator|=( double value ) +{ + *this = *this | value; + return *this; +} + +#ifndef QT_NO_DEBUG_STREAM + +QDebug operator<<( QDebug debug, const QwtInterval &interval ) +{ + const int flags = interval.borderFlags(); + + debug.nospace() << "QwtInterval(" + << ( ( flags & QwtInterval::ExcludeMinimum ) ? "]" : "[" ) + << interval.minValue() << "," << interval.maxValue() + << ( ( flags & QwtInterval::ExcludeMaximum ) ? "[" : "]" ) + << ")"; + + return debug.space(); +} + +#endif diff --git a/libs/qwt/qwt_interval.h b/libs/qwt/qwt_interval.h new file mode 100644 index 000000000..68841e00b --- /dev/null +++ b/libs/qwt/qwt_interval.h @@ -0,0 +1,320 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_INTERVAL_H +#define QWT_INTERVAL_H + +#include "qwt_global.h" +#include + +#ifndef QT_NO_DEBUG_STREAM +#include +#endif + +/*! + \brief A class representing an interval + + The interval is represented by 2 doubles, the lower and the upper limit. +*/ + +class QWT_EXPORT QwtInterval +{ +public: + /*! + Flag indicating if a border is included or excluded + \sa setBorderFlags(), borderFlags() + */ + enum BorderFlag + { + //! Min/Max values are inside the interval + IncludeBorders = 0x00, + + //! Min value is not included in the interval + ExcludeMinimum = 0x01, + + //! Max value is not included in the interval + ExcludeMaximum = 0x02, + + //! Min/Max values are not included in the interval + ExcludeBorders = ExcludeMinimum | ExcludeMaximum + }; + + //! Border flags + typedef QFlags BorderFlags; + + QwtInterval(); + QwtInterval( double minValue, double maxValue, + BorderFlags = IncludeBorders ); + + void setInterval( double minValue, double maxValue, + BorderFlags = IncludeBorders ); + + QwtInterval normalized() const; + QwtInterval inverted() const; + QwtInterval limited( double minValue, double maxValue ) const; + + bool operator==( const QwtInterval & ) const; + bool operator!=( const QwtInterval & ) const; + + void setBorderFlags( BorderFlags ); + BorderFlags borderFlags() const; + + double minValue() const; + double maxValue() const; + + double width() const; + + void setMinValue( double ); + void setMaxValue( double ); + + bool contains( double value ) const; + + bool intersects( const QwtInterval & ) const; + QwtInterval intersect( const QwtInterval & ) const; + QwtInterval unite( const QwtInterval & ) const; + + QwtInterval operator|( const QwtInterval & ) const; + QwtInterval operator&( const QwtInterval & ) const; + + QwtInterval &operator|=( const QwtInterval & ); + QwtInterval &operator&=( const QwtInterval & ); + + QwtInterval extend( double value ) const; + QwtInterval operator|( double ) const; + QwtInterval &operator|=( double ); + + bool isValid() const; + bool isNull() const; + void invalidate(); + + QwtInterval symmetrize( double value ) const; + +private: + double d_minValue; + double d_maxValue; + BorderFlags d_borderFlags; +}; + +Q_DECLARE_TYPEINFO(QwtInterval, Q_MOVABLE_TYPE); + +/*! + \brief Default Constructor + + Creates an invalid interval [0.0, -1.0] + \sa setInterval(), isValid() +*/ +inline QwtInterval::QwtInterval(): + d_minValue( 0.0 ), + d_maxValue( -1.0 ), + d_borderFlags( IncludeBorders ) +{ +} + +/*! + Constructor + + Build an interval with from min/max values + + \param minValue Minimum value + \param maxValue Maximum value + \param borderFlags Include/Exclude borders +*/ +inline QwtInterval::QwtInterval( + double minValue, double maxValue, BorderFlags borderFlags ): + d_minValue( minValue ), + d_maxValue( maxValue ), + d_borderFlags( borderFlags ) +{ +} + +/*! + Assign the limits of the interval + + \param minValue Minimum value + \param maxValue Maximum value + \param borderFlags Include/Exclude borders +*/ +inline void QwtInterval::setInterval( + double minValue, double maxValue, BorderFlags borderFlags ) +{ + d_minValue = minValue; + d_maxValue = maxValue; + d_borderFlags = borderFlags; +} + +/*! + Change the border flags + + \param borderFlags Or'd BorderMode flags + \sa borderFlags() +*/ +inline void QwtInterval::setBorderFlags( BorderFlags borderFlags ) +{ + d_borderFlags = borderFlags; +} + +/*! + \return Border flags + \sa setBorderFlags() +*/ +inline QwtInterval::BorderFlags QwtInterval::borderFlags() const +{ + return d_borderFlags; +} + +/*! + Assign the lower limit of the interval + + \param minValue Minimum value +*/ +inline void QwtInterval::setMinValue( double minValue ) +{ + d_minValue = minValue; +} + +/*! + Assign the upper limit of the interval + + \param maxValue Maximum value +*/ +inline void QwtInterval::setMaxValue( double maxValue ) +{ + d_maxValue = maxValue; +} + +//! \return Lower limit of the interval +inline double QwtInterval::minValue() const +{ + return d_minValue; +} + +//! \return Upper limit of the interval +inline double QwtInterval::maxValue() const +{ + return d_maxValue; +} + +/*! + A interval is valid when minValue() <= maxValue(). + In case of QwtInterval::ExcludeBorders it is true + when minValue() < maxValue() + + \return True, when the interval is valid +*/ +inline bool QwtInterval::isValid() const +{ + if ( ( d_borderFlags & ExcludeBorders ) == 0 ) + return d_minValue <= d_maxValue; + else + return d_minValue < d_maxValue; +} + +/*! + \brief Return the width of an interval + + The width of invalid intervals is 0.0, otherwise the result is + maxValue() - minValue(). + + \return Interval width + \sa isValid() +*/ +inline double QwtInterval::width() const +{ + return isValid() ? ( d_maxValue - d_minValue ) : 0.0; +} + +/*! + \brief Intersection of two intervals + + \param other Interval to intersect with + \return Intersection of this and other + + \sa intersect() +*/ +inline QwtInterval QwtInterval::operator&( + const QwtInterval &other ) const +{ + return intersect( other ); +} + +/*! + Union of two intervals + + \param other Interval to unite with + \return Union of this and other + + \sa unite() +*/ +inline QwtInterval QwtInterval::operator|( + const QwtInterval &other ) const +{ + return unite( other ); +} + +/*! + \brief Compare two intervals + + \param other Interval to compare with + \return True, when this and other are equal +*/ +inline bool QwtInterval::operator==( const QwtInterval &other ) const +{ + return ( d_minValue == other.d_minValue ) && + ( d_maxValue == other.d_maxValue ) && + ( d_borderFlags == other.d_borderFlags ); +} +/*! + \brief Compare two intervals + + \param other Interval to compare with + \return True, when this and other are not equal +*/ +inline bool QwtInterval::operator!=( const QwtInterval &other ) const +{ + return ( !( *this == other ) ); +} + +/*! + Extend an interval + + \param value Value + \return Extended interval + \sa extend() +*/ +inline QwtInterval QwtInterval::operator|( double value ) const +{ + return extend( value ); +} + +//! \return true, if isValid() && (minValue() >= maxValue()) +inline bool QwtInterval::isNull() const +{ + return isValid() && d_minValue >= d_maxValue; +} + +/*! + Invalidate the interval + + The limits are set to interval [0.0, -1.0] + \sa isValid() +*/ +inline void QwtInterval::invalidate() +{ + d_minValue = 0.0; + d_maxValue = -1.0; +} + +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtInterval::BorderFlags ) +Q_DECLARE_METATYPE( QwtInterval ) + +#ifndef QT_NO_DEBUG_STREAM +QWT_EXPORT QDebug operator<<( QDebug, const QwtInterval & ); +#endif + +#endif diff --git a/libs/qwt/qwt_interval_symbol.cpp b/libs/qwt/qwt_interval_symbol.cpp new file mode 100644 index 000000000..83c842d98 --- /dev/null +++ b/libs/qwt/qwt_interval_symbol.cpp @@ -0,0 +1,319 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_interval_symbol.h" +#include "qwt_painter.h" +#include "qwt_math.h" +#include + +#if QT_VERSION < 0x040601 +#define qAtan2(y, x) ::atan2(y, x) +#define qFastSin(x) qSin(x) +#define qFastCos(x) qCos(x) +#endif + +class QwtIntervalSymbol::PrivateData +{ +public: + PrivateData(): + style( QwtIntervalSymbol::NoSymbol ), + width( 6 ) + { + } + + bool operator==( const PrivateData &other ) const + { + return ( style == other.style ) + && ( width == other.width ) + && ( brush == other.brush ) + && ( pen == other.pen ); + } + + QwtIntervalSymbol::Style style; + int width; + + QPen pen; + QBrush brush; +}; + +/*! + Constructor + + \param style Style of the symbol + \sa setStyle(), style(), Style +*/ +QwtIntervalSymbol::QwtIntervalSymbol( Style style ) +{ + d_data = new PrivateData(); + d_data->style = style; +} + +//! Copy constructor +QwtIntervalSymbol::QwtIntervalSymbol( const QwtIntervalSymbol &other ) +{ + d_data = new PrivateData(); + *d_data = *other.d_data; +} + +//! Destructor +QwtIntervalSymbol::~QwtIntervalSymbol() +{ + delete d_data; +} + +//! \brief Assignment operator +QwtIntervalSymbol &QwtIntervalSymbol::operator=( + const QwtIntervalSymbol &other ) +{ + *d_data = *other.d_data; + return *this; +} + +//! \brief Compare two symbols +bool QwtIntervalSymbol::operator==( + const QwtIntervalSymbol &other ) const +{ + return *d_data == *other.d_data; +} + +//! \brief Compare two symbols +bool QwtIntervalSymbol::operator!=( + const QwtIntervalSymbol &other ) const +{ + return !( *d_data == *other.d_data ); +} + +/*! + Specify the symbol style + + \param style Style + \sa style(), Style +*/ +void QwtIntervalSymbol::setStyle( Style style ) +{ + d_data->style = style; +} + +/*! + \return Current symbol style + \sa setStyle() +*/ +QwtIntervalSymbol::Style QwtIntervalSymbol::style() const +{ + return d_data->style; +} + +/*! + Specify the width of the symbol + It is used depending on the style. + + \param width Width + \sa width(), setStyle() +*/ +void QwtIntervalSymbol::setWidth( int width ) +{ + d_data->width = width; +} + +/*! + \return Width of the symbol. + \sa setWidth(), setStyle() +*/ +int QwtIntervalSymbol::width() const +{ + return d_data->width; +} + +/*! + \brief Assign a brush + + The brush is used for the Box style. + + \param brush Brush + \sa brush() +*/ +void QwtIntervalSymbol::setBrush( const QBrush &brush ) +{ + d_data->brush = brush; +} + +/*! + \return Brush + \sa setBrush() +*/ +const QBrush& QwtIntervalSymbol::brush() const +{ + return d_data->brush; +} + +/*! + Build and assign a pen + + In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it + non cosmetic ( see QPen::isCosmetic() ). This method has been introduced + to hide this incompatibility. + + \param color Pen color + \param width Pen width + \param style Pen style + + \sa pen(), brush() + */ +void QwtIntervalSymbol::setPen( const QColor &color, + qreal width, Qt::PenStyle style ) +{ + setPen( QPen( color, width, style ) ); +} + +/*! + Assign a pen + + \param pen Pen + \sa pen(), setBrush() +*/ +void QwtIntervalSymbol::setPen( const QPen &pen ) +{ + d_data->pen = pen; +} + +/*! + \return Pen + \sa setPen(), brush() +*/ +const QPen& QwtIntervalSymbol::pen() const +{ + return d_data->pen; +} + +/*! + Draw a symbol depending on its style + + \param painter Painter + \param orientation Orientation + \param from Start point of the interval in target device coordinates + \param to End point of the interval in target device coordinates + + \sa setStyle() +*/ +void QwtIntervalSymbol::draw( QPainter *painter, Qt::Orientation orientation, + const QPointF &from, const QPointF &to ) const +{ + const qreal pw = qMax( painter->pen().widthF(), qreal( 1.0 ) ); + + QPointF p1 = from; + QPointF p2 = to; + if ( QwtPainter::roundingAlignment( painter ) ) + { + p1 = p1.toPoint(); + p2 = p2.toPoint(); + } + + switch ( d_data->style ) + { + case QwtIntervalSymbol::Bar: + { + QwtPainter::drawLine( painter, p1, p2 ); + if ( d_data->width > pw ) + { + if ( ( orientation == Qt::Horizontal ) + && ( p1.y() == p2.y() ) ) + { + const double sw = d_data->width; + + const double y = p1.y() - sw / 2; + QwtPainter::drawLine( painter, + p1.x(), y, p1.x(), y + sw ); + QwtPainter::drawLine( painter, + p2.x(), y, p2.x(), y + sw ); + } + else if ( ( orientation == Qt::Vertical ) + && ( p1.x() == p2.x() ) ) + { + const double sw = d_data->width; + + const double x = p1.x() - sw / 2; + QwtPainter::drawLine( painter, + x, p1.y(), x + sw, p1.y() ); + QwtPainter::drawLine( painter, + x, p2.y(), x + sw, p2.y() ); + } + else + { + const double sw = d_data->width; + + const double dx = p2.x() - p1.x(); + const double dy = p2.y() - p1.y(); + const double angle = qAtan2( dy, dx ) + M_PI_2; + double dw2 = sw / 2.0; + + const double cx = qFastCos( angle ) * dw2; + const double sy = qFastSin( angle ) * dw2; + + QwtPainter::drawLine( painter, + p1.x() - cx, p1.y() - sy, + p1.x() + cx, p1.y() + sy ); + QwtPainter::drawLine( painter, + p2.x() - cx, p2.y() - sy, + p2.x() + cx, p2.y() + sy ); + } + } + break; + } + case QwtIntervalSymbol::Box: + { + if ( d_data->width <= pw ) + { + QwtPainter::drawLine( painter, p1, p2 ); + } + else + { + if ( ( orientation == Qt::Horizontal ) + && ( p1.y() == p2.y() ) ) + { + const double sw = d_data->width; + + const double y = p1.y() - d_data->width / 2; + QwtPainter::drawRect( painter, + p1.x(), y, p2.x() - p1.x(), sw ); + } + else if ( ( orientation == Qt::Vertical ) + && ( p1.x() == p2.x() ) ) + { + const double sw = d_data->width; + + const double x = p1.x() - d_data->width / 2; + QwtPainter::drawRect( painter, + x, p1.y(), sw, p2.y() - p1.y() ); + } + else + { + const double sw = d_data->width; + + const double dx = p2.x() - p1.x(); + const double dy = p2.y() - p1.y(); + const double angle = qAtan2( dy, dx ) + M_PI_2; + double dw2 = sw / 2.0; + + const double cx = qFastCos( angle ) * dw2; + const double sy = qFastSin( angle ) * dw2; + + QPolygonF polygon; + polygon += QPointF( p1.x() - cx, p1.y() - sy ); + polygon += QPointF( p1.x() + cx, p1.y() + sy ); + polygon += QPointF( p2.x() + cx, p2.y() + sy ); + polygon += QPointF( p2.x() - cx, p2.y() - sy ); + + QwtPainter::drawPolygon( painter, polygon ); + } + } + break; + } + default:; + } +} diff --git a/libs/qwt/qwt_interval_symbol.h b/libs/qwt/qwt_interval_symbol.h new file mode 100644 index 000000000..f32e1c41d --- /dev/null +++ b/libs/qwt/qwt_interval_symbol.h @@ -0,0 +1,87 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_INTERVAL_SYMBOL_H +#define QWT_INTERVAL_SYMBOL_H + +#include "qwt_global.h" +#include +#include + +class QPainter; +class QRect; +class QPointF; + +/*! + \brief A drawing primitive for displaying an interval like an error bar + + \sa QwtPlotIntervalCurve +*/ +class QWT_EXPORT QwtIntervalSymbol +{ +public: + //! Symbol style + enum Style + { + //! No Style. The symbol cannot be drawn. + NoSymbol = -1, + + /*! + The symbol displays a line with caps at the beginning/end. + The size of the caps depends on the symbol width(). + */ + Bar, + + /*! + The symbol displays a plain rectangle using pen() and brush(). + The size of the rectangle depends on the translated interval and + the width(), + */ + Box, + + /*! + Styles >= UserSymbol are reserved for derived + classes of QwtIntervalSymbol that overload draw() with + additional application specific symbol types. + */ + UserSymbol = 1000 + }; + +public: + QwtIntervalSymbol( Style = NoSymbol ); + QwtIntervalSymbol( const QwtIntervalSymbol & ); + virtual ~QwtIntervalSymbol(); + + QwtIntervalSymbol &operator=( const QwtIntervalSymbol & ); + bool operator==( const QwtIntervalSymbol & ) const; + bool operator!=( const QwtIntervalSymbol & ) const; + + void setWidth( int ); + int width() const; + + void setBrush( const QBrush& b ); + const QBrush& brush() const; + + void setPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine ); + void setPen( const QPen & ); + const QPen& pen() const; + + void setStyle( Style ); + Style style() const; + + virtual void draw( QPainter *, Qt::Orientation, + const QPointF& from, const QPointF& to ) const; + +private: + + class PrivateData; + PrivateData* d_data; +}; + +#endif diff --git a/libs/qwt/qwt_knob.cpp b/libs/qwt/qwt_knob.cpp index 9ce3b84b2..5860e42c4 100644 --- a/libs/qwt/qwt_knob.cpp +++ b/libs/qwt/qwt_knob.cpp @@ -7,88 +7,120 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ +#include "qwt_knob.h" +#include "qwt_round_scale_draw.h" +#include "qwt_math.h" +#include "qwt_painter.h" +#include "qwt_scale_map.h" #include #include #include +#include #include -#include "qwt_round_scale_draw.h" -#include "qwt_knob.h" -#include "qwt_math.h" -#include "qwt_painter.h" -#include "qwt_paint_buffer.h" +#include +#include + +#if QT_VERSION < 0x040601 +#define qAtan2(y, x) ::atan2(y, x) +#define qFabs(x) ::fabs(x) +#define qFastCos(x) qCos(x) +#define qFastSin(x) qSin(x) +#endif + +static QSize qwtKnobSizeHint( const QwtKnob *knob, int min ) +{ + int knobWidth = knob->knobWidth(); + if ( knobWidth <= 0 ) + knobWidth = qMax( 3 * knob->markerSize(), min ); + + // Add the scale radial thickness to the knobWidth + const int extent = qCeil( knob->scaleDraw()->extent( knob->font() ) ); + const int d = 2 * ( extent + 4 ) + knobWidth; + + int left, right, top, bottom; + knob->getContentsMargins( &left, &top, &right, &bottom ); + + return QSize( d + left + right, d + top + bottom ); +} + +static inline double qwtToScaleAngle( double angle ) +{ + // the map is counter clockwise with the origin + // at 90° using angles from -180° -> 180° + + double a = 90.0 - angle; + if ( a <= -180.0 ) + a += 360.0; + else if ( a >= 180.0 ) + a -= 360.0; + + return a; +} + +static double qwtToDegrees( double value ) +{ + return qwtNormalizeDegrees( 90.0 - value ); +} class QwtKnob::PrivateData { public: - PrivateData() { - angle = 0.0; - nTurns = 0.0; - borderWidth = 2; - borderDist = 4; - totalAngle = 270.0; - scaleDist = 4; - symbol = Line; - maxScaleTicks = 11; - knobWidth = 50; - dotWidth = 8; + PrivateData(): + knobStyle( QwtKnob::Raised ), + markerStyle( QwtKnob::Notch ), + borderWidth( 2 ), + borderDist( 4 ), + scaleDist( 4 ), + maxScaleTicks( 11 ), + knobWidth( 0 ), + alignment( Qt::AlignCenter ), + markerSize( 8 ), + totalAngle( 270.0 ), + mouseOffset( 0.0 ) + { } + QwtKnob::KnobStyle knobStyle; + QwtKnob::MarkerStyle markerStyle; + int borderWidth; int borderDist; int scaleDist; int maxScaleTicks; int knobWidth; - int dotWidth; + Qt::Alignment alignment; + int markerSize; - Symbol symbol; - double angle; double totalAngle; - double nTurns; - QRect knobRect; // bounding rect of the knob without scale + double mouseOffset; }; /*! - Constructor - \param parent Parent widget -*/ -QwtKnob::QwtKnob(QWidget* parent): - QwtAbstractSlider(Qt::Horizontal, parent) -{ - initKnob(); -} + \brief Constructor + + Construct a knob with an angle of 270°. The style is + QwtKnob::Raised and the marker style is QwtKnob::Notch. + The width of the knob is set to 50 pixels. -#if QT_VERSION < 0x040000 -/*! - Constructor \param parent Parent widget - \param name Object name -*/ -QwtKnob::QwtKnob(QWidget* parent, const char *name): - QwtAbstractSlider(Qt::Horizontal, parent) -{ - setName(name); - initKnob(); -} -#endif -void QwtKnob::initKnob() + \sa setTotalAngle() +*/ +QwtKnob::QwtKnob( QWidget* parent ): + QwtAbstractSlider( parent ) { -#if QT_VERSION < 0x040000 - setWFlags(Qt::WNoAutoErase); -#endif - d_data = new PrivateData; - setScaleDraw(new QwtRoundScaleDraw()); + setScaleDraw( new QwtRoundScaleDraw() ); - setUpdateTime(50); setTotalAngle( 270.0 ); - recalcAngle(); - setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum)); - setRange(0.0, 10.0, 1.0); - setValue(0.0); + setScale( 0.0, 10.0 ); + setValue( 0.0 ); + + setSizePolicy( QSizePolicy::MinimumExpanding, + QSizePolicy::MinimumExpanding ); } //! Destructor @@ -98,52 +130,130 @@ QwtKnob::~QwtKnob() } /*! - \brief Set the symbol of the knob - \sa symbol() + \brief Set the knob type + + \param knobStyle Knob type + \sa knobStyle(), setBorderWidth() +*/ +void QwtKnob::setKnobStyle( KnobStyle knobStyle ) +{ + if ( d_data->knobStyle != knobStyle ) + { + d_data->knobStyle = knobStyle; + update(); + } +} + +/*! + \return Marker type of the knob + \sa setKnobStyle(), setBorderWidth() +*/ +QwtKnob::KnobStyle QwtKnob::knobStyle() const +{ + return d_data->knobStyle; +} + +/*! + \brief Set the marker type of the knob + + \param markerStyle Marker type + \sa markerStyle(), setMarkerSize() */ -void QwtKnob::setSymbol(QwtKnob::Symbol s) +void QwtKnob::setMarkerStyle( MarkerStyle markerStyle ) { - if ( d_data->symbol != s ) { - d_data->symbol = s; + if ( d_data->markerStyle != markerStyle ) + { + d_data->markerStyle = markerStyle; update(); } } /*! - \return symbol of the knob - \sa setSymbol() + \return Marker type of the knob + \sa setMarkerStyle(), setMarkerSize() */ -QwtKnob::Symbol QwtKnob::symbol() const +QwtKnob::MarkerStyle QwtKnob::markerStyle() const { - return d_data->symbol; + return d_data->markerStyle; } /*! \brief Set the total angle by which the knob can be turned \param angle Angle in degrees. - The default angle is 270 degrees. It is possible to specify - an angle of more than 360 degrees so that the knob can be - turned several times around its axis. + The angle has to be between [10, 360] degrees. Angles above + 360 ( so that the knob can be turned several times around its axis ) + have to be set using setNumTurns(). + + The default angle is 270 degrees. + + \sa totalAngle(), setNumTurns() */ -void QwtKnob::setTotalAngle (double angle) +void QwtKnob::setTotalAngle ( double angle ) { - if (angle < 10.0) - d_data->totalAngle = 10.0; - else + angle = qBound( 10.0, angle, 360.0 ); + + if ( angle != d_data->totalAngle ) + { d_data->totalAngle = angle; - scaleDraw()->setAngleRange( -0.5 * d_data->totalAngle, - 0.5 * d_data->totalAngle); - layoutKnob(); + scaleDraw()->setAngleRange( -0.5 * d_data->totalAngle, + 0.5 * d_data->totalAngle ); + + updateGeometry(); + update(); + } } -//! Return the total angle +/*! + \return the total angle + \sa setTotalAngle(), setNumTurns(), numTurns() + */ double QwtKnob::totalAngle() const { return d_data->totalAngle; } +/*! + \brief Set the number of turns + + When numTurns > 1 the knob can be turned several times around its axis + - otherwise the total angle is floored to 360°. + + \sa numTurns(), totalAngle(), setTotalAngle() +*/ + +void QwtKnob::setNumTurns( int numTurns ) +{ + numTurns = qMax( numTurns, 1 ); + + if ( numTurns == 1 && d_data->totalAngle <= 360.0 ) + return; + + const double angle = numTurns * 360.0; + if ( angle != d_data->totalAngle ) + { + d_data->totalAngle = angle; + + scaleDraw()->setAngleRange( -0.5 * d_data->totalAngle, + 0.5 * d_data->totalAngle ); + + updateGeometry(); + update(); + } +} + +/*! + \return Number of turns. + + When the total angle is below 360° numTurns() is ceiled to 1. + \sa setNumTurns(), setTotalAngle(), totalAngle() + */ +int QwtKnob::numTurns() const +{ + return qCeil( d_data->totalAngle / 360.0 ); +} + /*! Change the scale draw of the knob @@ -153,10 +263,10 @@ double QwtKnob::totalAngle() const \sa scaleDraw() */ -void QwtKnob::setScaleDraw(QwtRoundScaleDraw *scaleDraw) +void QwtKnob::setScaleDraw( QwtRoundScaleDraw *scaleDraw ) { - setAbstractScaleDraw(scaleDraw); - setTotalAngle(d_data->totalAngle); + setAbstractScaleDraw( scaleDraw ); + setTotalAngle( d_data->totalAngle ); } /*! @@ -165,7 +275,7 @@ void QwtKnob::setScaleDraw(QwtRoundScaleDraw *scaleDraw) */ const QwtRoundScaleDraw *QwtKnob::scaleDraw() const { - return (QwtRoundScaleDraw *)abstractScaleDraw(); + return static_cast( abstractScaleDraw() ); } /*! @@ -174,270 +284,495 @@ const QwtRoundScaleDraw *QwtKnob::scaleDraw() const */ QwtRoundScaleDraw *QwtKnob::scaleDraw() { - return (QwtRoundScaleDraw *)abstractScaleDraw(); + return static_cast( abstractScaleDraw() ); } /*! - \brief Draw the knob - \param painter painter - \param r Bounding rectangle of the knob (without scale) -*/ -void QwtKnob::drawKnob(QPainter *painter, const QRect &r) -{ -#if QT_VERSION < 0x040000 - const QBrush buttonBrush = colorGroup().brush(QColorGroup::Button); - const QColor buttonTextColor = colorGroup().buttonText(); - const QColor lightColor = colorGroup().light(); - const QColor darkColor = colorGroup().dark(); -#else - const QBrush buttonBrush = palette().brush(QPalette::Button); - const QColor buttonTextColor = palette().color(QPalette::ButtonText); - const QColor lightColor = palette().color(QPalette::Light); - const QColor darkColor = palette().color(QPalette::Dark); -#endif + Calculate the bounding rectangle of the knob without the scale - const int bw2 = d_data->borderWidth / 2; + \return Bounding rectangle of the knob + \sa knobWidth(), alignment(), QWidget::contentsRect() + */ +QRect QwtKnob::knobRect() const +{ + const QRect cr = contentsRect(); - const int radius = (qwtMin(r.width(), r.height()) - bw2) / 2; + const int extent = qCeil( scaleDraw()->extent( font() ) ); + const int d = extent + d_data->scaleDist; - const QRect aRect( - r.center().x() - radius, r.center().y() - radius, - 2 * radius, 2 * radius); + int w = d_data->knobWidth; + if ( w <= 0 ) + { + const int dim = qMin( cr.width(), cr.height() ); - // - // draw button face - // - painter->setBrush(buttonBrush); - painter->drawEllipse(aRect); + w = dim - 2 * ( d ); + w = qMax( 0, w ); + } - // - // draw button shades - // - QPen pn; - pn.setWidth(d_data->borderWidth); + QRect r( 0, 0, w, w ); - pn.setColor(lightColor); - painter->setPen(pn); - painter->drawArc(aRect, 45*16, 180*16); + if ( d_data->alignment & Qt::AlignLeft ) + { + r.moveLeft( cr.left() + d ); + } + else if ( d_data->alignment & Qt::AlignRight ) + { + r.moveRight( cr.right() - d ); + } + else + { + r.moveCenter( QPoint( cr.center().x(), r.center().y() ) ); + } - pn.setColor(darkColor); - painter->setPen(pn); - painter->drawArc(aRect, 225*16, 180*16); + if ( d_data->alignment & Qt::AlignTop ) + { + r.moveTop( cr.top() + d ); + } + else if ( d_data->alignment & Qt::AlignBottom ) + { + r.moveBottom( cr.bottom() - d ); + } + else + { + r.moveCenter( QPoint( r.center().x(), cr.center().y() ) ); + } - // - // draw marker - // - if ( isValid() ) - drawMarker(painter, d_data->angle, buttonTextColor); + return r; } /*! - \brief Notify change of value + \brief Determine what to do when the user presses a mouse button. + + \param pos Mouse position - Sets the knob's value to the nearest multiple - of the step size. + \retval True, when pos is inside the circle of the knob. + \sa scrolledTo() */ -void QwtKnob::valueChange() +bool QwtKnob::isScrollPosition( const QPoint &pos ) const { - recalcAngle(); - update(); - QwtAbstractSlider::valueChange(); + const QRect kr = knobRect(); + + const QRegion region( kr, QRegion::Ellipse ); + if ( region.contains( pos ) && ( pos != kr.center() ) ) + { + const double angle = QLineF( kr.center(), pos ).angle(); + const double valueAngle = qwtToDegrees( transform( value() ) ); + + d_data->mouseOffset = qwtNormalizeDegrees( angle - valueAngle ); + + return true; + } + + return false; } /*! - \brief Determine the value corresponding to a specified position + \brief Determine the value for a new position of the mouse - Called by QwtAbstractSlider - \param p point + \param pos Mouse position + + \return Value for the mouse position + \sa isScrollPosition() */ -double QwtKnob::getValue(const QPoint &p) +double QwtKnob::scrolledTo( const QPoint &pos ) const { - const double dx = double((rect().x() + rect().width() / 2) - p.x() ); - const double dy = double((rect().y() + rect().height() / 2) - p.y() ); + double angle = QLineF( rect().center(), pos ).angle(); + angle = qwtNormalizeDegrees( angle - d_data->mouseOffset ); - const double arc = atan2(-dx,dy) * 180.0 / M_PI; + if ( scaleMap().pDist() > 360.0 ) + { + angle = qwtToDegrees( angle ); - double newValue = 0.5 * (minValue() + maxValue()) - + (arc + d_data->nTurns * 360.0) * (maxValue() - minValue()) - / d_data->totalAngle; + const double v = transform( value() ); - const double oneTurn = fabs(maxValue() - minValue()) * 360.0 / d_data->totalAngle; - const double eqValue = value() + mouseOffset(); + int numTurns = qFloor( ( v - scaleMap().p1() ) / 360.0 ); - if (fabs(newValue - eqValue) > 0.5 * oneTurn) { - if (newValue < eqValue) - newValue += oneTurn; - else - newValue -= oneTurn; - } + double valueAngle = qwtNormalizeDegrees( v ); + if ( qAbs( valueAngle - angle ) > 180.0 ) + { + numTurns += ( angle > valueAngle ) ? -1 : 1; + } - return newValue; -} + angle += scaleMap().p1() + numTurns * 360.0; -/*! - \brief Set the scrolling mode and direction + if ( !wrapping() ) + { + const double boundedAngle = + qBound( scaleMap().p1(), angle, scaleMap().p2() ); - Called by QwtAbstractSlider - \param p Point in question -*/ -void QwtKnob::getScrollMode(const QPoint &p, int &scrollMode, int &direction) -{ - const int r = d_data->knobRect.width() / 2; - - const int dx = d_data->knobRect.x() + r - p.x(); - const int dy = d_data->knobRect.y() + r - p.y(); - - if ( (dx * dx) + (dy * dy) <= (r * r)) { // point is inside the knob - scrollMode = ScrMouse; - direction = 0; - } else { // point lies outside - scrollMode = ScrTimer; - double arc = atan2(double(-dx),double(dy)) * 180.0 / M_PI; - if ( arc < d_data->angle) - direction = -1; - else if (arc > d_data->angle) - direction = 1; - else - direction = 0; + d_data->mouseOffset += ( boundedAngle - angle ); + angle = boundedAngle; + } } -} + else + { + angle = qwtToScaleAngle( angle ); + const double boundedAngle = + qBound( scaleMap().p1(), angle, scaleMap().p2() ); -/*! - \brief Notify a change of the range + if ( !wrapping() ) + d_data->mouseOffset += ( boundedAngle - angle ); - Called by QwtAbstractSlider -*/ -void QwtKnob::rangeChange() -{ - if (autoScale()) - rescale(minValue(), maxValue()); + angle = boundedAngle; + } - layoutKnob(); - recalcAngle(); + return invTransform( angle ); } -/*! - \brief Qt Resize Event +/*! + Handle QEvent::StyleChange and QEvent::FontChange; + \param event Change event */ -void QwtKnob::resizeEvent(QResizeEvent *) +void QwtKnob::changeEvent( QEvent *event ) { - layoutKnob( false ); + switch( event->type() ) + { + case QEvent::StyleChange: + case QEvent::FontChange: + { + updateGeometry(); + update(); + break; + } + default: + break; + } } -//! Recalculate the knob's geometry and layout based on -// the current rect and fonts. -// \param update_geometry notify the layout system and call update -// to redraw the scale -void QwtKnob::layoutKnob( bool update_geometry ) +/*! + Repaint the knob + \param event Paint event +*/ +void QwtKnob::paintEvent( QPaintEvent *event ) { - const QRect r = rect(); - const int radius = d_data->knobWidth / 2; + const QRectF knobRect = this->knobRect(); - d_data->knobRect.setWidth(2 * radius); - d_data->knobRect.setHeight(2 * radius); - d_data->knobRect.moveCenter(r.center()); + QPainter painter( this ); + painter.setClipRegion( event->region() ); - scaleDraw()->setRadius(radius + d_data->scaleDist); - scaleDraw()->moveCenter(r.center()); + QStyleOption opt; + opt.init(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this); - if ( update_geometry ) { - updateGeometry(); - update(); - } -} + painter.setRenderHint( QPainter::Antialiasing, true ); -/*! - \brief Repaint the knob -*/ -void QwtKnob::paintEvent(QPaintEvent *e) -{ - const QRect &ur = e->rect(); - if ( ur.isValid() ) { -#if QT_VERSION < 0x040000 - QwtPaintBuffer paintBuffer(this, ur); - draw(paintBuffer.painter(), ur); -#else - QPainter painter(this); - painter.setRenderHint(QPainter::Antialiasing); - draw(&painter, ur); -#endif + if ( !knobRect.contains( event->region().boundingRect() ) ) + { + scaleDraw()->setRadius( 0.5 * knobRect.width() + d_data->scaleDist ); + scaleDraw()->moveCenter( knobRect.center() ); + + scaleDraw()->draw( &painter, palette() ); } + + drawKnob( &painter, knobRect ); + + drawMarker( &painter, knobRect, + qwtNormalizeDegrees( transform( value() ) ) ); + + painter.setRenderHint( QPainter::Antialiasing, false ); + + if ( hasFocus() ) + drawFocusIndicator( &painter ); } /*! - \brief Repaint the knob + \brief Draw the knob + + \param painter painter + \param knobRect Bounding rectangle of the knob (without scale) */ -void QwtKnob::draw(QPainter *painter, const QRect& ur) +void QwtKnob::drawKnob( QPainter *painter, const QRectF &knobRect ) const { - if ( !d_data->knobRect.contains( ur ) ) { // event from valueChange() -#if QT_VERSION < 0x040000 - scaleDraw()->draw( painter, colorGroup() ); -#else - scaleDraw()->draw( painter, palette() ); -#endif + double dim = qMin( knobRect.width(), knobRect.height() ); + dim -= d_data->borderWidth * 0.5; + + QRectF aRect( 0, 0, dim, dim ); + aRect.moveCenter( knobRect.center() ); + + QPen pen( Qt::NoPen ); + if ( d_data->borderWidth > 0 ) + { + QColor c1 = palette().color( QPalette::Light ); + QColor c2 = palette().color( QPalette::Dark ); + + QLinearGradient gradient( aRect.topLeft(), aRect.bottomRight() ); + gradient.setColorAt( 0.0, c1 ); + gradient.setColorAt( 0.3, c1 ); + gradient.setColorAt( 0.7, c2 ); + gradient.setColorAt( 1.0, c2 ); + + pen = QPen( gradient, d_data->borderWidth ); } - drawKnob( painter, d_data->knobRect ); + QBrush brush; + switch( d_data->knobStyle ) + { + case QwtKnob::Raised: + { + double off = 0.3 * knobRect.width(); + QRadialGradient gradient( knobRect.center(), + knobRect.width(), knobRect.topLeft() + QPointF( off, off ) ); + + gradient.setColorAt( 0.0, palette().color( QPalette::Midlight ) ); + gradient.setColorAt( 1.0, palette().color( QPalette::Button ) ); + + brush = QBrush( gradient ); + + break; + } + case QwtKnob::Styled: + { + QRadialGradient gradient(knobRect.center().x() - knobRect.width() / 3, + knobRect.center().y() - knobRect.height() / 2, + knobRect.width() * 1.3, + knobRect.center().x(), + knobRect.center().y() - knobRect.height() / 2); + + const QColor c = palette().color( QPalette::Button ); + gradient.setColorAt(0, c.lighter(110)); + gradient.setColorAt(qreal(0.5), c); + gradient.setColorAt(qreal(0.501), c.darker(102)); + gradient.setColorAt(1, c.darker(115)); + + brush = QBrush( gradient ); + + break; + } + case QwtKnob::Sunken: + { + QLinearGradient gradient( + knobRect.topLeft(), knobRect.bottomRight() ); + gradient.setColorAt( 0.0, palette().color( QPalette::Mid ) ); + gradient.setColorAt( 0.5, palette().color( QPalette::Button ) ); + gradient.setColorAt( 1.0, palette().color( QPalette::Midlight ) ); + brush = QBrush( gradient ); + + break; + } + case QwtKnob::Flat: + default: + brush = palette().brush( QPalette::Button ); + } - if ( hasFocus() ) - QwtPainter::drawFocusRect(painter, this); + painter->setPen( pen ); + painter->setBrush( brush ); + painter->drawEllipse( aRect ); } + /*! \brief Draw the marker at the knob's front - \param p Painter - \param arc Angle of the marker - \param c Marker color + + \param painter Painter + \param rect Bounding rectangle of the knob without scale + \param angle Angle of the marker in degrees + ( clockwise, 0 at the 12 o'clock position ) */ -void QwtKnob::drawMarker(QPainter *p, double arc, const QColor &c) +void QwtKnob::drawMarker( QPainter *painter, + const QRectF &rect, double angle ) const { - const double rarc = arc * M_PI / 180.0; - const double ca = cos(rarc); - const double sa = - sin(rarc); + if ( d_data->markerStyle == NoMarker || !isValid() ) + return; + + const double radians = qwtRadians( angle ); + const double sinA = -qFastSin( radians ); + const double cosA = qFastCos( radians ); + + const double xm = rect.center().x(); + const double ym = rect.center().y(); + const double margin = 4.0; + + double radius = 0.5 * ( rect.width() - d_data->borderWidth ) - margin; + if ( radius < 1.0 ) + radius = 1.0; + + int markerSize = d_data->markerSize; + if ( markerSize <= 0 ) + markerSize = qRound( 0.4 * radius ); + + switch ( d_data->markerStyle ) + { + case Notch: + case Nub: + { + const double dotWidth = + qMin( double( markerSize ), radius); + + const double dotCenterDist = radius - 0.5 * dotWidth; + if ( dotCenterDist > 0.0 ) + { + const QPointF center( xm - sinA * dotCenterDist, + ym - cosA * dotCenterDist ); + + QRectF ellipse( 0.0, 0.0, dotWidth, dotWidth ); + ellipse.moveCenter( center ); + + QColor c1 = palette().color( QPalette::Light ); + QColor c2 = palette().color( QPalette::Mid ); + + if ( d_data->markerStyle == Notch ) + qSwap( c1, c2 ); + + QLinearGradient gradient( + ellipse.topLeft(), ellipse.bottomRight() ); + gradient.setColorAt( 0.0, c1 ); + gradient.setColorAt( 1.0, c2 ); + + painter->setPen( Qt::NoPen ); + painter->setBrush( gradient ); + + painter->drawEllipse( ellipse ); + } + break; + } + case Dot: + { + const double dotWidth = + qMin( double( markerSize ), radius); + + const double dotCenterDist = radius - 0.5 * dotWidth; + if ( dotCenterDist > 0.0 ) + { + const QPointF center( xm - sinA * dotCenterDist, + ym - cosA * dotCenterDist ); + + QRectF ellipse( 0.0, 0.0, dotWidth, dotWidth ); + ellipse.moveCenter( center ); + + painter->setPen( Qt::NoPen ); + painter->setBrush( palette().color( QPalette::ButtonText ) ); + painter->drawEllipse( ellipse ); + } + + break; + } + case Tick: + { + const double rb = qMax( radius - markerSize, 1.0 ); + const double re = radius; + + const QLineF line( xm - sinA * rb, ym - cosA * rb, + xm - sinA * re, ym - cosA * re ); + + QPen pen( palette().color( QPalette::ButtonText ), 0 ); + pen.setCapStyle( Qt::FlatCap ); + painter->setPen( pen ); + painter->drawLine ( line ); + + break; + } + case Triangle: + { + const double rb = qMax( radius - markerSize, 1.0 ); + const double re = radius; + + painter->translate( rect.center() ); + painter->rotate( angle - 90.0 ); + + QPolygonF polygon; + polygon += QPointF( re, 0.0 ); + polygon += QPointF( rb, 0.5 * ( re - rb ) ); + polygon += QPointF( rb, -0.5 * ( re - rb ) ); + + painter->setPen( Qt::NoPen ); + painter->setBrush( palette().color( QPalette::ButtonText ) ); + painter->drawPolygon( polygon ); + + painter->resetTransform(); + + break; + } + default: + break; + } +} - int radius = d_data->knobRect.width() / 2 - d_data->borderWidth; - if (radius < 3) - radius = 3; +/*! + Draw the focus indicator + \param painter Painter +*/ +void QwtKnob::drawFocusIndicator( QPainter *painter ) const +{ + const QRect cr = contentsRect(); + + int w = d_data->knobWidth; + if ( w <= 0 ) + { + w = qMin( cr.width(), cr.height() ); + } + else + { + const int extent = qCeil( scaleDraw()->extent( font() ) ); + w += 2 * ( extent + d_data->scaleDist ); + } - const int ym = d_data->knobRect.y() + radius + d_data->borderWidth; - const int xm = d_data->knobRect.x() + radius + d_data->borderWidth; + QRect focusRect( 0, 0, w, w ); + focusRect.moveCenter( cr.center() ); - switch (d_data->symbol) { - case Dot: { - p->setBrush(c); - p->setPen(Qt::NoPen); + QwtPainter::drawFocusRect( painter, this, focusRect ); +} - const double rb = double(qwtMax(radius - 4 - d_data->dotWidth / 2, 0)); - p->drawEllipse(xm - qRound(sa * rb) - d_data->dotWidth / 2, - ym - qRound(ca * rb) - d_data->dotWidth / 2, - d_data->dotWidth, d_data->dotWidth); - break; - } - case Line: { - p->setPen(QPen(c, 2)); +/*! + \brief Set the alignment of the knob - const double rb = qwtMax(double((radius - 4) / 3.0), 0.0); - const double re = qwtMax(double(radius - 4), 0.0); + Similar to a QLabel::alignment() the flags decide how + to align the knob inside of contentsRect(). - p->drawLine ( xm - qRound(sa * rb), ym - qRound(ca * rb), - xm - qRound(sa * re), ym - qRound(ca * re)); + The default setting is Qt::AlignCenter - break; - } + \param alignment Or'd alignment flags + + \sa alignment(), setKnobWidth(), knobRect() + */ +void QwtKnob::setAlignment( Qt::Alignment alignment ) +{ + if ( d_data->alignment != alignment ) + { + d_data->alignment = alignment; + update(); } } +/*! + \return Alignment of the knob inside of contentsRect() + \sa setAlignment(), knobWidth(), knobRect() + */ +Qt::Alignment QwtKnob::alignment() const +{ + return d_data->alignment; +} + /*! \brief Change the knob's width. - The specified width must be >= 5, or it will be clipped. - \param w New width + Setting a fixed value for the diameter of the knob + is helpful for aligning several knobs in a row. + + \param width New width + + \sa knobWidth(), setAlignment() + \note Modifies the sizePolicy() */ -void QwtKnob::setKnobWidth(int w) +void QwtKnob::setKnobWidth( int width ) { - d_data->knobWidth = qwtMax(w,5); - layoutKnob(); + width = qMax( width, 0 ); + + if ( width != d_data->knobWidth ) + { + QSizePolicy::Policy policy; + if ( width > 0 ) + policy = QSizePolicy::Minimum; + else + policy = QSizePolicy::MinimumExpanding; + + setSizePolicy( policy, policy ); + + d_data->knobWidth = width; + + updateGeometry(); + update(); + } } //! Return the width of the knob @@ -448,12 +783,15 @@ int QwtKnob::knobWidth() const /*! \brief Set the knob's border width - \param bw new border width + \param borderWidth new border width */ -void QwtKnob::setBorderWidth(int bw) +void QwtKnob::setBorderWidth( int borderWidth ) { - d_data->borderWidth = qwtMax(bw, 0); - layoutKnob(); + d_data->borderWidth = qMax( borderWidth, 0 ); + + updateGeometry(); + update(); + } //! Return the border width @@ -463,63 +801,45 @@ int QwtKnob::borderWidth() const } /*! - \brief Recalculate the marker angle corresponding to the - current value -*/ -void QwtKnob::recalcAngle() -{ - // - // calculate the angle corresponding to the value - // - if (maxValue() == minValue()) { - d_data->angle = 0; - d_data->nTurns = 0; - } else { - d_data->angle = (value() - 0.5 * (minValue() + maxValue())) - / (maxValue() - minValue()) * d_data->totalAngle; - d_data->nTurns = floor((d_data->angle + 180.0) / 360.0); - d_data->angle = d_data->angle - d_data->nTurns * 360.0; - } -} + \brief Set the size of the marker + When setting a size <= 0 the marker will + automatically scaled to 40% of the radius of the knob. -/*! - Recalculates the layout - \sa layoutKnob() + \sa markerSize(), markerStyle() */ -void QwtKnob::scaleChange() +void QwtKnob::setMarkerSize( int size ) { - layoutKnob(); + if ( d_data->markerSize != size ) + { + d_data->markerSize = size; + update(); + } } -/*! - Recalculates the layout - \sa layoutKnob() -*/ -void QwtKnob::fontChange(const QFont &f) +/*! + \return Marker size + \sa setMarkerSize() + */ +int QwtKnob::markerSize() const { - QwtAbstractSlider::fontChange( f ); - layoutKnob(); + return d_data->markerSize; } /*! - \return minimumSizeHint() + \return sizeHint() */ QSize QwtKnob::sizeHint() const { - return minimumSizeHint(); + const QSize hint = qwtKnobSizeHint( this, 50 ); + return hint.expandedTo( QApplication::globalStrut() ); } /*! - \brief Return a minimum size hint - \warning The return value of QwtKnob::minimumSizeHint() depends on the - font and the scale. + \return Minimum size hint + \sa sizeHint() */ QSize QwtKnob::minimumSizeHint() const { - // Add the scale radial thickness to the knobWidth - const int sh = scaleDraw()->extent( QPen(), font() ); - const int d = 2 * sh + 2 * d_data->scaleDist + d_data->knobWidth; - - return QSize( d, d ); + return qwtKnobSizeHint( this, 20 ); } diff --git a/libs/qwt/qwt_knob.h b/libs/qwt/qwt_knob.h index ee43aa71b..852374c02 100644 --- a/libs/qwt/qwt_knob.h +++ b/libs/qwt/qwt_knob.h @@ -12,87 +12,165 @@ #include "qwt_global.h" #include "qwt_abstract_slider.h" -#include "qwt_abstract_scale.h" class QwtRoundScaleDraw; /*! \brief The Knob Widget - The QwtKnob widget imitates look and behaviour of a volume knob on a radio. - It contains a scale around the knob which is set up automatically or can - be configured manually (see QwtAbstractScale). - Automatic scrolling is enabled when the user presses a mouse - button on the scale. For a description of signals, slots and other - members, see QwtAbstractSlider. + The QwtKnob widget imitates look and behavior of a volume knob on a radio. + It looks similar to QDial - not to QwtDial. + The value range of a knob might be divided into several turns. + + The layout of the knob depends on the knobWidth(). + + - width > 0 + The diameter of the knob is fixed and the knob is aligned + according to the alignment() flags inside of the contentsRect(). + + - width <= 0 + The knob is extended to the minimum of width/height of the contentsRect() + and aligned in the other direction according to alignment(). + + Setting a fixed knobWidth() is helpful to align several knobs with different + scale labels. + \image html knob.png - \sa QwtAbstractSlider and QwtAbstractScale for the descriptions - of the inherited members. */ -class QWT_EXPORT QwtKnob : public QwtAbstractSlider, public QwtAbstractScale +class QWT_EXPORT QwtKnob: public QwtAbstractSlider { Q_OBJECT - Q_ENUMS (Symbol) + + Q_ENUMS ( KnobStyle MarkerStyle ) + + Q_PROPERTY( KnobStyle knobStyle READ knobStyle WRITE setKnobStyle ) Q_PROPERTY( int knobWidth READ knobWidth WRITE setKnobWidth ) - Q_PROPERTY( int borderWidth READ borderWidth WRITE setBorderWidth ) + Q_PROPERTY( Qt::Alignment alignment READ alignment WRITE setAlignment ) Q_PROPERTY( double totalAngle READ totalAngle WRITE setTotalAngle ) - Q_PROPERTY( Symbol symbol READ symbol WRITE setSymbol ) + Q_PROPERTY( int numTurns READ numTurns WRITE setNumTurns ) + Q_PROPERTY( MarkerStyle markerStyle READ markerStyle WRITE setMarkerStyle ) + Q_PROPERTY( int markerSize READ markerSize WRITE setMarkerSize ) + Q_PROPERTY( int borderWidth READ borderWidth WRITE setBorderWidth ) public: - /*! - Symbol - \sa QwtKnob::QwtKnob() - */ + /*! + \brief Style of the knob surface + + Depending on the KnobStyle the surface of the knob is + filled from the brushes of the widget palette(). + + \sa setKnobStyle(), knobStyle() + */ + enum KnobStyle + { + //! Fill the knob with a brush from QPalette::Button. + Flat, + + //! Build a gradient from QPalette::Midlight and QPalette::Button + Raised, + + /*! + Build a gradient from QPalette::Midlight, QPalette::Button + and QPalette::Midlight + */ + Sunken, + + /*! + Build a radial gradient from QPalette::Button + like it is used for QDial in various Qt styles. + */ + Styled + }; - enum Symbol { Line, Dot }; + /*! + \brief Marker type + + The marker indicates the current value on the knob + The default setting is a Notch marker. - explicit QwtKnob(QWidget* parent = NULL); -#if QT_VERSION < 0x040000 - explicit QwtKnob(QWidget* parent, const char *name); -#endif + \sa setMarkerStyle(), setMarkerSize() + */ + enum MarkerStyle + { + //! Don't paint any marker + NoMarker = -1, + + //! Paint a single tick in QPalette::ButtonText color + Tick, + + //! Paint a triangle in QPalette::ButtonText color + Triangle, + + //! Paint a circle in QPalette::ButtonText color + Dot, + + /*! + Draw a raised ellipse with a gradient build from + QPalette::Light and QPalette::Mid + */ + Nub, + + /*! + Draw a sunken ellipse with a gradient build from + QPalette::Light and QPalette::Mid + */ + Notch + }; + + explicit QwtKnob( QWidget* parent = NULL ); virtual ~QwtKnob(); - void setKnobWidth(int w); + void setAlignment( Qt::Alignment ); + Qt::Alignment alignment() const; + + void setKnobWidth( int ); int knobWidth() const; - void setTotalAngle (double angle); + void setNumTurns( int ); + int numTurns() const; + + void setTotalAngle ( double angle ); double totalAngle() const; - void setBorderWidth(int bw); + void setKnobStyle( KnobStyle ); + KnobStyle knobStyle() const; + + void setBorderWidth( int bw ); int borderWidth() const; - void setSymbol(Symbol); - Symbol symbol() const; + void setMarkerStyle( MarkerStyle ); + MarkerStyle markerStyle() const; + + void setMarkerSize( int ); + int markerSize() const; virtual QSize sizeHint() const; virtual QSize minimumSizeHint() const; - void setScaleDraw(QwtRoundScaleDraw *); + void setScaleDraw( QwtRoundScaleDraw * ); + const QwtRoundScaleDraw *scaleDraw() const; QwtRoundScaleDraw *scaleDraw(); + QRect knobRect() const; + protected: - virtual void paintEvent(QPaintEvent *e); - virtual void resizeEvent(QResizeEvent *e); + virtual void paintEvent( QPaintEvent * ); + virtual void changeEvent( QEvent * ); - void draw(QPainter *p, const QRect& ur); - void drawKnob(QPainter *p, const QRect &r); - void drawMarker(QPainter *p, double arc, const QColor &c); + virtual void drawKnob( QPainter *, const QRectF & ) const; -private: - void initKnob(); - void layoutKnob( bool update = true ); - double getValue(const QPoint &p); - void getScrollMode( const QPoint &p, int &scrollMode, int &direction ); - void recalcAngle(); + virtual void drawFocusIndicator( QPainter * ) const; - virtual void valueChange(); - virtual void rangeChange(); - virtual void scaleChange(); - virtual void fontChange(const QFont &oldFont); + virtual void drawMarker( QPainter *, + const QRectF &, double arc ) const; + virtual double scrolledTo( const QPoint & ) const; + virtual bool isScrollPosition( const QPoint & ) const; + +private: class PrivateData; PrivateData *d_data; }; diff --git a/libs/qwt/qwt_legend.cpp b/libs/qwt/qwt_legend.cpp index c06236471..25916ad4c 100644 --- a/libs/qwt/qwt_legend.cpp +++ b/libs/qwt/qwt_legend.cpp @@ -7,116 +7,198 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ -// vim: expandtab - +#include "qwt_legend.h" +#include "qwt_legend_label.h" +#include "qwt_dyngrid_layout.h" +#include "qwt_math.h" +#include "qwt_plot_item.h" +#include "qwt_painter.h" #include -#include -#if QT_VERSION >= 0x040000 #include -#endif -#include "qwt_math.h" -#include "qwt_dyngrid_layout.h" -#include "qwt_legend_itemmanager.h" -#include "qwt_legend_item.h" -#include "qwt_legend.h" +#include +#include +#include +#include -class QwtLegend::PrivateData +class QwtLegendMap { public: - class LegendMap + inline bool isEmpty() const { return d_entries.isEmpty(); } + + void insert( const QVariant &, const QList & ); + void remove( const QVariant & ); + + void removeWidget( const QWidget * ); + + QList legendWidgets( const QVariant & ) const; + QVariant itemInfo( const QWidget * ) const; + +private: + // we don't know anything about itemInfo and therefore don't have + // any key that can be used for a map or hashtab. + // But a simple linear list is o.k. here, as we will never have + // more than a few entries. + + class Entry { public: - void insert(const QwtLegendItemManager *, QWidget *); + QVariant itemInfo; + QList widgets; + }; - void remove(const QwtLegendItemManager *); - void remove(QWidget *); + QList< Entry > d_entries; +}; - void clear(); +void QwtLegendMap::insert( const QVariant &itemInfo, + const QList &widgets ) +{ + for ( int i = 0; i < d_entries.size(); i++ ) + { + Entry &entry = d_entries[i]; + if ( entry.itemInfo == itemInfo ) + { + entry.widgets = widgets; + return; + } + } - uint count() const; + Entry newEntry; + newEntry.itemInfo = itemInfo; + newEntry.widgets = widgets; - inline const QWidget *find(const QwtLegendItemManager *) const; - inline QWidget *find(const QwtLegendItemManager *); + d_entries += newEntry; +} - inline const QwtLegendItemManager *find(const QWidget *) const; - inline QwtLegendItemManager *find(const QWidget *); +void QwtLegendMap::remove( const QVariant &itemInfo ) +{ + for ( int i = 0; i < d_entries.size(); i++ ) + { + Entry &entry = d_entries[i]; + if ( entry.itemInfo == itemInfo ) + { + d_entries.removeAt( i ); + return; + } + } +} - const QMap &widgetMap() const; - QMap &widgetMap(); +void QwtLegendMap::removeWidget( const QWidget *widget ) +{ + QWidget *w = const_cast( widget ); - private: - QMap d_widgetMap; - QMap d_itemMap; - }; + for ( int i = 0; i < d_entries.size(); i++ ) + d_entries[ i ].widgets.removeAll( w ); +} - QwtLegend::LegendItemMode itemMode; - QwtLegend::LegendDisplayPolicy displayPolicy; - int identifierMode; +QVariant QwtLegendMap::itemInfo( const QWidget *widget ) const +{ + if ( widget != NULL ) + { + QWidget *w = const_cast( widget ); - LegendMap map; + for ( int i = 0; i < d_entries.size(); i++ ) + { + const Entry &entry = d_entries[i]; + if ( entry.widgets.indexOf( w ) >= 0 ) + return entry.itemInfo; + } + } - class LegendView; - LegendView *view; -}; + return QVariant(); +} + +QList QwtLegendMap::legendWidgets( const QVariant &itemInfo ) const +{ + if ( itemInfo.isValid() ) + { + for ( int i = 0; i < d_entries.size(); i++ ) + { + const Entry &entry = d_entries[i]; + if ( entry.itemInfo == itemInfo ) + return entry.widgets; + } + } -#if QT_VERSION < 0x040000 -#include + return QList(); +} -class QwtLegend::PrivateData::LegendView: public QScrollView +class QwtLegend::PrivateData { public: - LegendView(QWidget *parent): - QScrollView(parent) { - setResizePolicy(Manual); + PrivateData(): + itemMode( QwtLegendData::ReadOnly ), + view( NULL ) + { + } - viewport()->setBackgroundMode(Qt::NoBackground); // Avoid flicker + QwtLegendData::Mode itemMode; + QwtLegendMap itemMap; - contentsWidget = new QWidget(viewport()); + class LegendView; + LegendView *view; +}; - addChild(contentsWidget); - } +class QwtLegend::PrivateData::LegendView: public QScrollArea +{ +public: + LegendView( QWidget *parent ): + QScrollArea( parent ) + { + contentsWidget = new QWidget( this ); + contentsWidget->setObjectName( "QwtLegendViewContents" ); - void viewportResizeEvent(QResizeEvent *e) { - QScrollView::viewportResizeEvent(e); + setWidget( contentsWidget ); + setWidgetResizable( false ); - // It's not safe to update the layout now, because - // we are in an internal update of the scrollview framework. - // So we delay the update by posting a LayoutHint. + viewport()->setObjectName( "QwtLegendViewport" ); - QApplication::postEvent(contentsWidget, - new QEvent(QEvent::LayoutHint)); + // QScrollArea::setWidget internally sets autoFillBackground to true + // But we don't want a background. + contentsWidget->setAutoFillBackground( false ); + viewport()->setAutoFillBackground( false ); } - QWidget *contentsWidget; -}; + virtual bool event( QEvent *event ) + { + if ( event->type() == QEvent::PolishRequest ) + { + setFocusPolicy( Qt::NoFocus ); + } -#else // QT_VERSION >= 0x040000 + if ( event->type() == QEvent::Resize ) + { + // adjust the size to en/disable the scrollbars + // before QScrollArea adjusts the viewport size -#include + const QRect cr = contentsRect(); -class QwtLegend::PrivateData::LegendView: public QScrollArea -{ -public: - LegendView(QWidget *parent): - QScrollArea(parent) { - contentsWidget = new QWidget(this); + int w = cr.width(); + int h = contentsWidget->heightForWidth( cr.width() ); + if ( h > w ) + { + w -= verticalScrollBar()->sizeHint().width(); + h = contentsWidget->heightForWidth( w ); + } + + contentsWidget->resize( w, h ); + } - setWidget(contentsWidget); - setWidgetResizable(false); - setFocusPolicy(Qt::NoFocus); + return QScrollArea::event( event ); } - virtual bool viewportEvent(QEvent *e) { - bool ok = QScrollArea::viewportEvent(e); + virtual bool viewportEvent( QEvent *event ) + { + bool ok = QScrollArea::viewportEvent( event ); - if ( e->type() == QEvent::Resize ) { - QEvent event(QEvent::LayoutRequest); - QApplication::sendEvent(contentsWidget, &event); + if ( event->type() == QEvent::Resize ) + { + layoutContents(); } return ok; } - QSize viewportSize(int w, int h) const { + QSize viewportSize( int w, int h ) const + { const int sbHeight = horizontalScrollBar()->sizeHint().height(); const int sbWidth = verticalScrollBar()->sizeHint().width(); @@ -129,509 +211,591 @@ public: if ( w > vw ) vh -= sbHeight; - if ( h > vh ) { + if ( h > vh ) + { vw -= sbWidth; if ( w > vw && vh == ch ) vh -= sbHeight; } - return QSize(vw, vh); + return QSize( vw, vh ); } - QWidget *contentsWidget; -}; - -#endif - - -void QwtLegend::PrivateData::LegendMap::insert( - const QwtLegendItemManager *item, QWidget *widget) -{ - d_itemMap.insert(item, widget); - d_widgetMap.insert(widget, item); -} - -void QwtLegend::PrivateData::LegendMap::remove(const QwtLegendItemManager *item) -{ - QWidget *widget = d_itemMap[item]; - d_itemMap.remove(item); - d_widgetMap.remove(widget); -} - -void QwtLegend::PrivateData::LegendMap::remove(QWidget *widget) -{ - const QwtLegendItemManager *item = d_widgetMap[widget]; - d_itemMap.remove(item); - d_widgetMap.remove(widget); -} - -void QwtLegend::PrivateData::LegendMap::clear() -{ + void layoutContents() + { + const QwtDynGridLayout *tl = qobject_cast( + contentsWidget->layout() ); + if ( tl == NULL ) + return; - /* - We can't delete the widgets in the following loop, because - we would get ChildRemoved events, changing d_itemMap, while - we are iterating. - */ + const QSize visibleSize = viewport()->contentsRect().size(); -#if QT_VERSION < 0x040000 - QValueList widgets; + const int minW = int( tl->maxItemWidth() ) + 2 * tl->margin(); - QMap::const_iterator it; - for ( it = d_itemMap.begin(); it != d_itemMap.end(); ++it ) - widgets.append(it.data()); -#else - QList widgets; + int w = qMax( visibleSize.width(), minW ); + int h = qMax( tl->heightForWidth( w ), visibleSize.height() ); - QMap::const_iterator it; - for ( it = d_itemMap.begin(); it != d_itemMap.end(); ++it ) - widgets.append(it.value()); -#endif + const int vpWidth = viewportSize( w, h ).width(); + if ( w > vpWidth ) + { + w = qMax( vpWidth, minW ); + h = qMax( tl->heightForWidth( w ), visibleSize.height() ); + } - d_itemMap.clear(); - d_widgetMap.clear(); + contentsWidget->resize( w, h ); + } - for ( int i = 0; i < (int)widgets.size(); i++ ) - delete widgets[i]; -} + QWidget *contentsWidget; +}; -uint QwtLegend::PrivateData::LegendMap::count() const +/*! + Constructor + \param parent Parent widget +*/ +QwtLegend::QwtLegend( QWidget *parent ): + QwtAbstractLegend( parent ) { - return d_itemMap.count(); -} + setFrameStyle( NoFrame ); -inline const QWidget *QwtLegend::PrivateData::LegendMap::find(const QwtLegendItemManager *item) const -{ - if ( !d_itemMap.contains((QwtLegendItemManager *)item) ) - return NULL; + d_data = new QwtLegend::PrivateData; - return d_itemMap[(QwtLegendItemManager *)item]; -} + d_data->view = new QwtLegend::PrivateData::LegendView( this ); + d_data->view->setObjectName( "QwtLegendView" ); + d_data->view->setFrameStyle( NoFrame ); -inline QWidget *QwtLegend::PrivateData::LegendMap::find(const QwtLegendItemManager *item) -{ - if ( !d_itemMap.contains((QwtLegendItemManager *)item) ) - return NULL; + QwtDynGridLayout *gridLayout = new QwtDynGridLayout( + d_data->view->contentsWidget ); + gridLayout->setAlignment( Qt::AlignHCenter | Qt::AlignTop ); + + d_data->view->contentsWidget->installEventFilter( this ); - return d_itemMap[(QwtLegendItemManager *)item]; + QVBoxLayout *layout = new QVBoxLayout( this ); + layout->setContentsMargins( 0, 0, 0, 0 ); + layout->addWidget( d_data->view ); } -inline const QwtLegendItemManager *QwtLegend::PrivateData::LegendMap::find( - const QWidget *widget) const +//! Destructor +QwtLegend::~QwtLegend() { - if ( !d_widgetMap.contains((QWidget *)widget) ) - return NULL; - - return d_widgetMap[(QWidget *)widget]; + delete d_data; } -inline QwtLegendItemManager *QwtLegend::PrivateData::LegendMap::find( - const QWidget *widget) -{ - if ( !d_widgetMap.contains((QWidget *)widget) ) - return NULL; +/*! + \brief Set the maximum number of entries in a row - return (QwtLegendItemManager *)d_widgetMap[(QWidget *)widget]; -} + F.e when the maximum is set to 1 all items are aligned + vertically. 0 means unlimited -inline const QMap & -QwtLegend::PrivateData::LegendMap::widgetMap() const -{ - return d_widgetMap; -} + \param numColums Maximum number of entries in a row -inline QMap & -QwtLegend::PrivateData::LegendMap::widgetMap() + \sa maxColumns(), QwtDynGridLayout::setMaxColumns() + */ +void QwtLegend::setMaxColumns( uint numColums ) { - return d_widgetMap; + QwtDynGridLayout *tl = qobject_cast( + d_data->view->contentsWidget->layout() ); + if ( tl ) + tl->setMaxColumns( numColums ); } /*! - \param parent Parent widget -*/ -QwtLegend::QwtLegend(QWidget *parent): - QFrame(parent) + \return Maximum number of entries in a row + \sa setMaxColumns(), QwtDynGridLayout::maxColumns() + */ +uint QwtLegend::maxColumns() const { - setFrameStyle(NoFrame); + uint maxCols = 0; - d_data = new QwtLegend::PrivateData; - d_data->itemMode = QwtLegend::ReadOnlyItem; - d_data->displayPolicy = QwtLegend::AutoIdentifier; - d_data->identifierMode = QwtLegendItem::ShowLine | - QwtLegendItem::ShowSymbol | QwtLegendItem::ShowText; - - d_data->view = new QwtLegend::PrivateData::LegendView(this); - d_data->view->setFrameStyle(NoFrame); - - QwtDynGridLayout *layout = new QwtDynGridLayout( - d_data->view->contentsWidget); -#if QT_VERSION < 0x040000 - layout->setAutoAdd(true); -#endif - layout->setAlignment(Qt::AlignHCenter | Qt::AlignTop); - - d_data->view->contentsWidget->installEventFilter(this); -} + const QwtDynGridLayout *tl = qobject_cast( + d_data->view->contentsWidget->layout() ); + if ( tl ) + maxCols = tl->maxColumns(); -//! Destructor -QwtLegend::~QwtLegend() -{ - delete d_data; + return maxCols; } /*! - Set the legend display policy to: - - \param policy Legend display policy - \param mode Identifier mode (or'd ShowLine, ShowSymbol, ShowText) - - \sa displayPolicy, LegendDisplayPolicy -*/ -void QwtLegend::setDisplayPolicy(LegendDisplayPolicy policy, int mode) -{ - d_data->displayPolicy = policy; - if (-1 != mode) - d_data->identifierMode = mode; - - QMap &map = - d_data->map.widgetMap(); - - QMap::iterator it; - for ( it = map.begin(); it != map.end(); ++it ) { -#if QT_VERSION < 0x040000 - QwtLegendItemManager *item = (QwtLegendItemManager *)it.data(); -#else - QwtLegendItemManager *item = (QwtLegendItemManager *)it.value(); -#endif - if ( item ) - item->updateLegend(this); - } -} + \brief Set the default mode for legend labels -/*! - \return the legend display policy. - Default is LegendDisplayPolicy::Auto. - \sa setDisplayPolicy, LegendDisplayPolicy -*/ + Legend labels will be constructed according to the + attributes in a QwtLegendData object. When it doesn't + contain a value for the QwtLegendData::ModeRole the + label will be initialized with the default mode of the legend. -QwtLegend::LegendDisplayPolicy QwtLegend::displayPolicy() const -{ - return d_data->displayPolicy; -} + \param mode Default item mode -void QwtLegend::setItemMode(LegendItemMode mode) + \sa itemMode(), QwtLegendData::value(), QwtPlotItem::legendData() + \note Changing the mode doesn't have any effect on existing labels. + */ +void QwtLegend::setDefaultItemMode( QwtLegendData::Mode mode ) { d_data->itemMode = mode; } -QwtLegend::LegendItemMode QwtLegend::itemMode() const -{ - return d_data->itemMode; -} - /*! - \return the IdentifierMode to be used in combination with - LegendDisplayPolicy::Fixed. - - Default is ShowLine | ShowSymbol | ShowText. + \return Default item mode + \sa setDefaultItemMode() */ - -int QwtLegend::identifierMode() const +QwtLegendData::Mode QwtLegend::defaultItemMode() const { - return d_data->identifierMode; + return d_data->itemMode; } /*! - The contents widget is the only child of the viewport() and - the parent widget of all legend items. + The contents widget is the only child of the viewport of + the internal QScrollArea and the parent widget of all legend items. + + \return Container widget of the legend items */ QWidget *QwtLegend::contentsWidget() { return d_data->view->contentsWidget; } +/*! + \return Horizontal scrollbar + \sa verticalScrollBar() +*/ QScrollBar *QwtLegend::horizontalScrollBar() const { return d_data->view->horizontalScrollBar(); } +/*! + \return Vertical scrollbar + \sa horizontalScrollBar() +*/ QScrollBar *QwtLegend::verticalScrollBar() const { return d_data->view->verticalScrollBar(); } /*! - The contents widget is the only child of the viewport() and - the parent widget of all legend items. -*/ + The contents widget is the only child of the viewport of + the internal QScrollArea and the parent widget of all legend items. + \return Container widget of the legend items + +*/ const QWidget *QwtLegend::contentsWidget() const { return d_data->view->contentsWidget; } /*! - Insert a new item for a plot item - \param plotItem Plot item - \param legendItem New legend item - \note The parent of item will be changed to QwtLegend::contentsWidget() -*/ -void QwtLegend::insert(const QwtLegendItemManager *plotItem, QWidget *legendItem) + \brief Update the entries for an item + + \param itemInfo Info for an item + \param data List of legend entry attributes for the item + */ +void QwtLegend::updateLegend( const QVariant &itemInfo, + const QList &data ) { - if ( legendItem == NULL || plotItem == NULL ) - return; + QList widgetList = legendWidgets( itemInfo ); - QWidget *contentsWidget = d_data->view->contentsWidget; + if ( widgetList.size() != data.size() ) + { + QLayout *contentsLayout = d_data->view->contentsWidget->layout(); - if ( legendItem->parent() != contentsWidget ) { -#if QT_VERSION >= 0x040000 - legendItem->setParent(contentsWidget); -#else - legendItem->reparent(contentsWidget, QPoint(0, 0)); -#endif - } + while ( widgetList.size() > data.size() ) + { + QWidget *w = widgetList.takeLast(); - legendItem->show(); + contentsLayout->removeWidget( w ); - d_data->map.insert(plotItem, legendItem); + // updates might be triggered by signals from the legend widget + // itself. So we better don't delete it here. - layoutContents(); + w->hide(); + w->deleteLater(); + } - if ( contentsWidget->layout() ) { -#if QT_VERSION >= 0x040000 - contentsWidget->layout()->addWidget(legendItem); -#endif + for ( int i = widgetList.size(); i < data.size(); i++ ) + { + QWidget *widget = createWidget( data[i] ); - // set tab focus chain + if ( contentsLayout ) + contentsLayout->addWidget( widget ); - QWidget *w = NULL; + widgetList += widget; + } -#if QT_VERSION < 0x040000 - QLayoutIterator layoutIterator = - contentsWidget->layout()->iterator(); - for ( QLayoutItem *item = layoutIterator.current(); - item != 0; item = ++layoutIterator) { -#else - for (int i = 0; i < contentsWidget->layout()->count(); i++) { - QLayoutItem *item = contentsWidget->layout()->itemAt(i); -#endif - if ( w && item->widget() ) { - QWidget::setTabOrder(w, item->widget()); - w = item->widget(); - } + if ( widgetList.isEmpty() ) + { + d_data->itemMap.remove( itemInfo ); } + else + { + d_data->itemMap.insert( itemInfo, widgetList ); + } + + updateTabOrder(); } - if ( parentWidget() && parentWidget()->layout() == NULL ) { - /* - updateGeometry() doesn't post LayoutRequest in certain - situations, like when we are hidden. But we want the - parent widget notified, so it can show/hide the legend - depending on its items. - */ -#if QT_VERSION < 0x040000 - QApplication::postEvent(parentWidget(), - new QEvent(QEvent::LayoutHint)); -#else - QApplication::postEvent(parentWidget(), - new QEvent(QEvent::LayoutRequest)); -#endif - } + + for ( int i = 0; i < data.size(); i++ ) + updateWidget( widgetList[i], data[i] ); } /*! - Find the widget that represents a plot item + \brief Create a widget to be inserted into the legend - \param plotItem Plot item - \return Widget on the legend, or NULL -*/ -QWidget *QwtLegend::find(const QwtLegendItemManager *plotItem) const + The default implementation returns a QwtLegendLabel. + + \param data Attributes of the legend entry + \return Widget representing data on the legend + + \note updateWidget() will called soon after createWidget() + with the same attributes. + */ +QWidget *QwtLegend::createWidget( const QwtLegendData &data ) const { - return d_data->map.find(plotItem); -} + Q_UNUSED( data ); -/*! - Find the widget that represents a plot item + QwtLegendLabel *label = new QwtLegendLabel(); + label->setItemMode( defaultItemMode() ); - \param plotItem Plot item - \return Widget on the legend, or NULL -*/ -QwtLegendItemManager *QwtLegend::find(const QWidget *legendItem) const -{ - return d_data->map.find(legendItem); + connect( label, SIGNAL( clicked() ), SLOT( itemClicked() ) ); + connect( label, SIGNAL( checked( bool ) ), SLOT( itemChecked( bool ) ) ); + + return label; } /*! - Find the corresponding item for a plotItem and remove it - from the item list. + \brief Update the widget - \param plotItem Plot item -*/ -void QwtLegend::remove(const QwtLegendItemManager *plotItem) + \param widget Usually a QwtLegendLabel + \param data Attributes to be displayed + + \sa createWidget() + \note When widget is no QwtLegendLabel updateWidget() does nothing. + */ +void QwtLegend::updateWidget( QWidget *widget, const QwtLegendData &data ) { - QWidget *legendItem = d_data->map.find(plotItem); - d_data->map.remove(legendItem); - delete legendItem; + QwtLegendLabel *label = qobject_cast( widget ); + if ( label ) + { + label->setData( data ); + if ( !data.value( QwtLegendData::ModeRole ).isValid() ) + { + // use the default mode, when there is no specific + // hint from the legend data + + label->setItemMode( defaultItemMode() ); + } + } } -//! Remove all items. -void QwtLegend::clear() +void QwtLegend::updateTabOrder() { -#if QT_VERSION < 0x040000 - bool doUpdate = isUpdatesEnabled(); -#else - bool doUpdate = updatesEnabled(); -#endif - setUpdatesEnabled(false); + QLayout *contentsLayout = d_data->view->contentsWidget->layout(); + if ( contentsLayout ) + { + // set tab focus chain - d_data->map.clear(); + QWidget *w = NULL; + + for ( int i = 0; i < contentsLayout->count(); i++ ) + { + QLayoutItem *item = contentsLayout->itemAt( i ); + if ( w && item->widget() ) + QWidget::setTabOrder( w, item->widget() ); - setUpdatesEnabled(doUpdate); - update(); + w = item->widget(); + } + } } //! Return a size hint. QSize QwtLegend::sizeHint() const { QSize hint = d_data->view->contentsWidget->sizeHint(); - hint += QSize(2 * frameWidth(), 2 * frameWidth()); + hint += QSize( 2 * frameWidth(), 2 * frameWidth() ); return hint; } /*! - \return The preferred height, for the width w. + \return The preferred height, for a width. \param width Width */ -int QwtLegend::heightForWidth(int width) const +int QwtLegend::heightForWidth( int width ) const { width -= 2 * frameWidth(); - int h = d_data->view->contentsWidget->heightForWidth(width); -#if QT_VERSION < 0x040000 - - // Asking the layout is the default implementation in Qt4 - - if ( h <= 0 ) { - QLayout *l = d_data->view->contentsWidget->layout(); - if ( l && l->hasHeightForWidth() ) - h = l->heightForWidth(width); - } -#endif + int h = d_data->view->contentsWidget->heightForWidth( width ); if ( h >= 0 ) h += 2 * frameWidth(); return h; } + /*! - Adjust contents widget and item layout to the size of the viewport(). -*/ -void QwtLegend::layoutContents() -{ - const QSize visibleSize = d_data->view->viewport()->size(); + Handle QEvent::ChildRemoved andQEvent::LayoutRequest events + for the contentsWidget(). - const QLayout *l = d_data->view->contentsWidget->layout(); - if ( l && l->inherits("QwtDynGridLayout") ) { - const QwtDynGridLayout *tl = (const QwtDynGridLayout *)l; + \param object Object to be filtered + \param event Event - const int minW = int(tl->maxItemWidth()) + 2 * tl->margin(); + \return Forwarded to QwtAbstractLegend::eventFilter() +*/ +bool QwtLegend::eventFilter( QObject *object, QEvent *event ) +{ + if ( object == d_data->view->contentsWidget ) + { + switch ( event->type() ) + { + case QEvent::ChildRemoved: + { + const QChildEvent *ce = + static_cast(event); + if ( ce->child()->isWidgetType() ) + { + QWidget *w = static_cast< QWidget * >( ce->child() ); + d_data->itemMap.removeWidget( w ); + } + break; + } + case QEvent::LayoutRequest: + { + d_data->view->layoutContents(); + + if ( parentWidget() && parentWidget()->layout() == NULL ) + { + /* + We want the parent widget ( usually QwtPlot ) to recalculate + its layout, when the contentsWidget has changed. But + because of the scroll view we have to forward the LayoutRequest + event manually. + + We don't use updateGeometry() because it doesn't post LayoutRequest + events when the legend is hidden. But we want the + parent widget notified, so it can show/hide the legend + depending on its items. + */ + QApplication::postEvent( parentWidget(), + new QEvent( QEvent::LayoutRequest ) ); + } + break; + } + default: + break; + } + } - int w = qwtMax(visibleSize.width(), minW); - int h = qwtMax(tl->heightForWidth(w), visibleSize.height()); + return QwtAbstractLegend::eventFilter( object, event ); +} - const int vpWidth = d_data->view->viewportSize(w, h).width(); - if ( w > vpWidth ) { - w = qwtMax(vpWidth, minW); - h = qwtMax(tl->heightForWidth(w), visibleSize.height()); +/*! + Called internally when the legend has been clicked on. + Emits a clicked() signal. +*/ +void QwtLegend::itemClicked() +{ + QWidget *w = qobject_cast( sender() ); + if ( w ) + { + const QVariant itemInfo = d_data->itemMap.itemInfo( w ); + if ( itemInfo.isValid() ) + { + const QList widgetList = + d_data->itemMap.legendWidgets( itemInfo ); + + const int index = widgetList.indexOf( w ); + if ( index >= 0 ) + Q_EMIT clicked( itemInfo, index ); } + } +} - d_data->view->contentsWidget->resize(w, h); -#if QT_VERSION < 0x040000 - d_data->view->resizeContents(w, h); -#endif +/*! + Called internally when the legend has been checked + Emits a checked() signal. +*/ +void QwtLegend::itemChecked( bool on ) +{ + QWidget *w = qobject_cast( sender() ); + if ( w ) + { + const QVariant itemInfo = d_data->itemMap.itemInfo( w ); + if ( itemInfo.isValid() ) + { + const QList widgetList = + d_data->itemMap.legendWidgets( itemInfo ); + + const int index = widgetList.indexOf( w ); + if ( index >= 0 ) + Q_EMIT checked( itemInfo, on, index ); + } } } -/* - Filter layout related events of QwtLegend::contentsWidget(). +/*! + Render the legend into a given rectangle. - \param o Object to be filtered - \param e Event -*/ + \param painter Painter + \param rect Bounding rectangle + \param fillBackground When true, fill rect with the widget background -bool QwtLegend::eventFilter(QObject *o, QEvent *e) + \sa renderLegend() is used by QwtPlotRenderer - not by QwtLegend itself +*/ +void QwtLegend::renderLegend( QPainter *painter, + const QRectF &rect, bool fillBackground ) const { - if ( o == d_data->view->contentsWidget ) { - switch(e->type()) { - case QEvent::ChildRemoved: { - const QChildEvent *ce = (const QChildEvent *)e; - if ( ce->child()->isWidgetType() ) - d_data->map.remove((QWidget *)ce->child()); - break; - } -#if QT_VERSION < 0x040000 - case QEvent::LayoutHint: -#else - case QEvent::LayoutRequest: -#endif - { - layoutContents(); - break; - } -#if QT_VERSION < 0x040000 - case QEvent::Resize: { - updateGeometry(); - break; - } -#endif - default: - break; + if ( d_data->itemMap.isEmpty() ) + return; + + if ( fillBackground ) + { + if ( autoFillBackground() || + testAttribute( Qt::WA_StyledBackground ) ) + { + QwtPainter::drawBackgound( painter, rect, this ); } } - return QFrame::eventFilter(o, e); + const QwtDynGridLayout *legendLayout = + qobject_cast( contentsWidget()->layout() ); + if ( legendLayout == NULL ) + return; + + int left, right, top, bottom; + getContentsMargins( &left, &top, &right, &bottom ); + + QRect layoutRect; + layoutRect.setLeft( qCeil( rect.left() ) + left ); + layoutRect.setTop( qCeil( rect.top() ) + top ); + layoutRect.setRight( qFloor( rect.right() ) - right ); + layoutRect.setBottom( qFloor( rect.bottom() ) - bottom ); + + uint numCols = legendLayout->columnsForWidth( layoutRect.width() ); + QList itemRects = + legendLayout->layoutItems( layoutRect, numCols ); + + int index = 0; + + for ( int i = 0; i < legendLayout->count(); i++ ) + { + QLayoutItem *item = legendLayout->itemAt( i ); + QWidget *w = item->widget(); + if ( w ) + { + painter->save(); + + painter->setClipRect( itemRects[index], Qt::IntersectClip ); + renderItem( painter, w, itemRects[index], fillBackground ); + + index++; + painter->restore(); + } + } } +/*! + Render a legend entry into a given rectangle. -//! Return true, if there are no legend items. -bool QwtLegend::isEmpty() const + \param painter Painter + \param widget Widget representing a legend entry + \param rect Bounding rectangle + \param fillBackground When true, fill rect with the widget background + + \note When widget is not derived from QwtLegendLabel renderItem + does nothing beside the background +*/ +void QwtLegend::renderItem( QPainter *painter, + const QWidget *widget, const QRectF &rect, bool fillBackground ) const { - return d_data->map.count() == 0; + if ( fillBackground ) + { + if ( widget->autoFillBackground() || + widget->testAttribute( Qt::WA_StyledBackground ) ) + { + QwtPainter::drawBackgound( painter, rect, widget ); + } + } + + const QwtLegendLabel *label = qobject_cast( widget ); + if ( label ) + { + // icon + + const QwtGraphic &icon = label->data().icon(); + const QSizeF sz = icon.defaultSize(); + + const QRectF iconRect( rect.x() + label->margin(), + rect.center().y() - 0.5 * sz.height(), + sz.width(), sz.height() ); + + icon.render( painter, iconRect, Qt::KeepAspectRatio ); + + // title + + QRectF titleRect = rect; + titleRect.setX( iconRect.right() + 2 * label->spacing() ); + + painter->setFont( label->font() ); + painter->setPen( label->palette().color( QPalette::Text ) ); + const_cast< QwtLegendLabel *>( label )->drawText( painter, titleRect ); + } } -//! Return the number of legend items. -uint QwtLegend::itemCount() const +/*! + \return List of widgets associated to a item + \param itemInfo Info about an item + \sa legendWidget(), itemInfo(), QwtPlot::itemToInfo() + */ +QList QwtLegend::legendWidgets( const QVariant &itemInfo ) const { - return d_data->map.count(); + return d_data->itemMap.legendWidgets( itemInfo ); } -#if QT_VERSION < 0x040000 -QValueList QwtLegend::legendItems() const -#else -QList QwtLegend::legendItems() const -#endif +/*! + \return First widget in the list of widgets associated to an item + \param itemInfo Info about an item + \sa itemInfo(), QwtPlot::itemToInfo() + \note Almost all types of items have only one widget +*/ +QWidget *QwtLegend::legendWidget( const QVariant &itemInfo ) const { - const QMap &map = - d_data->map.widgetMap(); + const QList list = d_data->itemMap.legendWidgets( itemInfo ); + if ( list.isEmpty() ) + return NULL; -#if QT_VERSION < 0x040000 - QValueList list; -#else - QList list; -#endif + return list[0]; +} + +/*! + Find the item that is associated to a widget - QMap::const_iterator it; - for ( it = map.begin(); it != map.end(); ++it ) - list += it.key(); + \param widget Widget on the legend + \return Associated item info + \sa legendWidget() + */ +QVariant QwtLegend::itemInfo( const QWidget *widget ) const +{ + return d_data->itemMap.itemInfo( widget ); +} - return list; +//! \return True, when no item is inserted +bool QwtLegend::isEmpty() const +{ + return d_data->itemMap.isEmpty(); } /*! - Resize event - \param e Event -*/ -void QwtLegend::resizeEvent(QResizeEvent *e) + Return the extent, that is needed for the scrollbars + + \param orientation Orientation ( + \return The width of the vertical scrollbar for Qt::Horizontal and v.v. + */ +int QwtLegend::scrollExtent( Qt::Orientation orientation ) const { - QFrame::resizeEvent(e); - d_data->view->setGeometry(contentsRect()); + int extent = 0; + + if ( orientation == Qt::Horizontal ) + extent = verticalScrollBar()->sizeHint().width(); + else + extent = horizontalScrollBar()->sizeHint().height(); + + return extent; } + diff --git a/libs/qwt/qwt_legend.h b/libs/qwt/qwt_legend.h index 4d7306992..3d8fca67a 100644 --- a/libs/qwt/qwt_legend.h +++ b/libs/qwt/qwt_legend.h @@ -7,117 +7,111 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ -// vim: expandtab - #ifndef QWT_LEGEND_H #define QWT_LEGEND_H -#include #include "qwt_global.h" -#if QT_VERSION < 0x040000 -#include -#else -#include -#endif +#include "qwt_abstract_legend.h" +#include class QScrollBar; -class QwtLegendItemManager; /*! \brief The legend widget The QwtLegend widget is a tabular arrangement of legend items. Legend items might be any type of widget, but in general they will be - a QwtLegendItem. + a QwtLegendLabel. - \sa QwtLegendItem, QwtLegendItemManager QwtPlot + \sa QwtLegendLabel, QwtPlotItem, QwtPlot */ -class QWT_EXPORT QwtLegend : public QFrame +class QWT_EXPORT QwtLegend : public QwtAbstractLegend { Q_OBJECT public: - /*! - \brief Display policy + explicit QwtLegend( QWidget *parent = NULL ); + virtual ~QwtLegend(); - - NoIdentifier\n - The client code is responsible how to display of each legend item. - The Qwt library will not interfere. + void setMaxColumns( uint numColums ); + uint maxColumns() const; - - FixedIdentifier\n - All legend items are displayed with the QwtLegendItem::IdentifierMode - to be passed in 'mode'. + void setDefaultItemMode( QwtLegendData::Mode ); + QwtLegendData::Mode defaultItemMode() const; - - AutoIdentifier\n - Each legend item is displayed with a mode that is a bitwise or of - - QwtLegendItem::ShowLine (if its curve is drawn with a line) and - - QwtLegendItem::ShowSymbol (if its curve is drawn with symbols) and - - QwtLegendItem::ShowText (if the has a title). + QWidget *contentsWidget(); + const QWidget *contentsWidget() const; - Default is AutoIdentifier. - \sa setDisplayPolicy(), displayPolicy(), QwtLegendItem::IdentifierMode - */ + QWidget *legendWidget( const QVariant & ) const; + QList legendWidgets( const QVariant & ) const; - enum LegendDisplayPolicy { - NoIdentifier = 0, - FixedIdentifier = 1, - AutoIdentifier = 2 - }; + QVariant itemInfo( const QWidget * ) const; - //! Interaction mode for the legend items - enum LegendItemMode { - ReadOnlyItem, - ClickableItem, - CheckableItem - }; + virtual bool eventFilter( QObject *, QEvent * ); - explicit QwtLegend(QWidget *parent = NULL); - virtual ~QwtLegend(); + virtual QSize sizeHint() const; + virtual int heightForWidth( int w ) const; - void setDisplayPolicy(LegendDisplayPolicy policy, int mode); - LegendDisplayPolicy displayPolicy() const; + QScrollBar *horizontalScrollBar() const; + QScrollBar *verticalScrollBar() const; - void setItemMode(LegendItemMode); - LegendItemMode itemMode() const; + virtual void renderLegend( QPainter *, + const QRectF &, bool fillBackground ) const; - int identifierMode() const; + virtual void renderItem( QPainter *, + const QWidget *, const QRectF &, bool fillBackground ) const; - QWidget *contentsWidget(); - const QWidget *contentsWidget() const; + virtual bool isEmpty() const; + virtual int scrollExtent( Qt::Orientation ) const; - void insert(const QwtLegendItemManager *, QWidget *); - void remove(const QwtLegendItemManager *); +Q_SIGNALS: + /*! + A signal which is emitted when the user has clicked on + a legend label, which is in QwtLegendData::Clickable mode. - QWidget *find(const QwtLegendItemManager *) const; - QwtLegendItemManager *find(const QWidget *) const; + \param itemInfo Info for the item item of the + selected legend item + \param index Index of the legend label in the list of widgets + that are associated with the plot item -#if QT_VERSION < 0x040000 - virtual QValueList legendItems() const; -#else - virtual QList legendItems() const; -#endif + \note clicks are disabled as default + \sa setDefaultItemMode(), defaultItemMode(), QwtPlot::itemToInfo() + */ + void clicked( const QVariant &itemInfo, int index ); - void clear(); + /*! + A signal which is emitted when the user has clicked on + a legend label, which is in QwtLegendData::Checkable mode - bool isEmpty() const; - uint itemCount() const; + \param itemInfo Info for the item of the + selected legend label + \param index Index of the legend label in the list of widgets + that are associated with the plot item + \param on True when the legend label is checked - virtual bool eventFilter(QObject *, QEvent *); + \note clicks are disabled as default + \sa setDefaultItemMode(), defaultItemMode(), QwtPlot::itemToInfo() + */ + void checked( const QVariant &itemInfo, bool on, int index ); - virtual QSize sizeHint() const; - virtual int heightForWidth(int w) const; +public Q_SLOTS: + virtual void updateLegend( const QVariant &, + const QList & ); - QScrollBar *horizontalScrollBar() const; - QScrollBar *verticalScrollBar() const; +protected Q_SLOTS: + void itemClicked(); + void itemChecked( bool ); protected: - virtual void resizeEvent(QResizeEvent *); - virtual void layoutContents(); + virtual QWidget *createWidget( const QwtLegendData & ) const; + virtual void updateWidget( QWidget *widget, const QwtLegendData &data ); private: + void updateTabOrder(); + class PrivateData; PrivateData *d_data; }; -#endif // QWT_LEGEND_H +#endif diff --git a/libs/qwt/qwt_legend_data.cpp b/libs/qwt/qwt_legend_data.cpp new file mode 100644 index 000000000..cf0cb2ce3 --- /dev/null +++ b/libs/qwt/qwt_legend_data.cpp @@ -0,0 +1,129 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_legend_data.h" + +//! Constructor +QwtLegendData::QwtLegendData() +{ +} + +//! Destructor +QwtLegendData::~QwtLegendData() +{ +} + +/*! + Set the legend attributes + + QwtLegendData actually is a QMap with some + convenience interfaces + + \param map Values + \sa values() + */ +void QwtLegendData::setValues( const QMap &map ) +{ + d_map = map; +} + +/*! + \return Legend attributes + \sa setValues() + */ +const QMap &QwtLegendData::values() const +{ + return d_map; +} + +/*! + \param role Attribute role + \return True, when the internal map has an entry for role + */ +bool QwtLegendData::hasRole( int role ) const +{ + return d_map.contains( role ); +} + +/*! + Set an attribute value + + \param role Attribute role + \param data Attribute value + + \sa value() + */ +void QwtLegendData::setValue( int role, const QVariant &data ) +{ + d_map[role] = data; +} + +/*! + \param role Attribute role + \return Attribute value for a specific role + */ +QVariant QwtLegendData::value( int role ) const +{ + if ( !d_map.contains( role ) ) + return QVariant(); + + return d_map[role]; +} + +//! \return True, when the internal map is empty +bool QwtLegendData::isValid() const +{ + return !d_map.isEmpty(); +} + +//! \return Value of the TitleRole attribute +QwtText QwtLegendData::title() const +{ + QwtText text; + + const QVariant titleValue = value( QwtLegendData::TitleRole ); + if ( titleValue.canConvert() ) + { + text = qvariant_cast( titleValue ); + } + else if ( titleValue.canConvert() ) + { + text.setText( qvariant_cast( titleValue ) ); + } + + return text; +} + +//! \return Value of the IconRole attribute +QwtGraphic QwtLegendData::icon() const +{ + const QVariant iconValue = value( QwtLegendData::IconRole ); + + QwtGraphic graphic; + if ( iconValue.canConvert() ) + { + graphic = qvariant_cast( iconValue ); + } + + return graphic; +} + +//! \return Value of the ModeRole attribute +QwtLegendData::Mode QwtLegendData::mode() const +{ + const QVariant modeValue = value( QwtLegendData::ModeRole ); + if ( modeValue.canConvert() ) + { + const int mode = qvariant_cast( modeValue ); + return static_cast( mode ); + } + + return QwtLegendData::ReadOnly; +} + diff --git a/libs/qwt/qwt_legend_data.h b/libs/qwt/qwt_legend_data.h new file mode 100644 index 000000000..d83e13264 --- /dev/null +++ b/libs/qwt/qwt_legend_data.h @@ -0,0 +1,87 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_LEGEND_DATA_H +#define QWT_LEGEND_DATA_H + +#include "qwt_global.h" +#include "qwt_text.h" +#include "qwt_graphic.h" +#include +#include +#include + +/*! + \brief Attributes of an entry on a legend + + QwtLegendData is an abstract container ( like QAbstractModel ) + to exchange attributes, that are only known between to + the plot item and the legend. + + By overloading QwtPlotItem::legendData() any other set of attributes + could be used, that can be handled by a modified ( or completely + different ) implementation of a legend. + + \sa QwtLegend, QwtPlotLegendItem + \note The stockchart example implements a legend as a tree + with checkable items + */ +class QWT_EXPORT QwtLegendData +{ +public: + //! Mode defining how a legend entry interacts + enum Mode + { + //! The legend item is not interactive, like a label + ReadOnly, + + //! The legend item is clickable, like a push button + Clickable, + + //! The legend item is checkable, like a checkable button + Checkable + }; + + //! Identifier how to interprete a QVariant + enum Role + { + // The value is a Mode + ModeRole, + + // The value is a title + TitleRole, + + // The value is an icon + IconRole, + + // Values < UserRole are reserved for internal use + UserRole = 32 + }; + + QwtLegendData(); + ~QwtLegendData(); + + void setValues( const QMap & ); + const QMap &values() const; + + void setValue( int role, const QVariant & ); + QVariant value( int role ) const; + + bool hasRole( int role ) const; + bool isValid() const; + + QwtGraphic icon() const; + QwtText title() const; + Mode mode() const; + +private: + QMap d_map; +}; + +#endif diff --git a/libs/qwt/qwt_legend_label.cpp b/libs/qwt/qwt_legend_label.cpp new file mode 100644 index 000000000..19a7eb957 --- /dev/null +++ b/libs/qwt/qwt_legend_label.cpp @@ -0,0 +1,421 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_legend_label.h" +#include "qwt_legend_data.h" +#include "qwt_math.h" +#include "qwt_painter.h" +#include "qwt_symbol.h" +#include "qwt_graphic.h" +#include +#include +#include +#include +#include +#include +#include + +static const int ButtonFrame = 2; +static const int Margin = 2; + +static QSize buttonShift( const QwtLegendLabel *w ) +{ + QStyleOption option; + option.init( w ); + + const int ph = w->style()->pixelMetric( + QStyle::PM_ButtonShiftHorizontal, &option, w ); + const int pv = w->style()->pixelMetric( + QStyle::PM_ButtonShiftVertical, &option, w ); + return QSize( ph, pv ); +} + +class QwtLegendLabel::PrivateData +{ +public: + PrivateData(): + itemMode( QwtLegendData::ReadOnly ), + isDown( false ), + spacing( Margin ) + { + } + + QwtLegendData::Mode itemMode; + QwtLegendData legendData; + bool isDown; + + QPixmap icon; + + int spacing; +}; + +/*! + Set the attributes of the legend label + + \param legendData Attributes of the label + \sa data() + */ +void QwtLegendLabel::setData( const QwtLegendData &legendData ) +{ + d_data->legendData = legendData; + + const bool doUpdate = updatesEnabled(); + setUpdatesEnabled( false ); + + setText( legendData.title() ); + setIcon( legendData.icon().toPixmap() ); + + if ( legendData.hasRole( QwtLegendData::ModeRole ) ) + setItemMode( legendData.mode() ); + + if ( doUpdate ) + { + setUpdatesEnabled( true ); + update(); + } +} + +/*! + \return Attributes of the label + \sa setData(), QwtPlotItem::legendData() + */ +const QwtLegendData &QwtLegendLabel::data() const +{ + return d_data->legendData; +} + +/*! + \param parent Parent widget +*/ +QwtLegendLabel::QwtLegendLabel( QWidget *parent ): + QwtTextLabel( parent ) +{ + d_data = new PrivateData; + setMargin( Margin ); + setIndent( Margin ); +} + +//! Destructor +QwtLegendLabel::~QwtLegendLabel() +{ + delete d_data; + d_data = NULL; +} + +/*! + Set the text to the legend item + + \param text Text label + \sa QwtTextLabel::text() +*/ +void QwtLegendLabel::setText( const QwtText &text ) +{ + const int flags = Qt::AlignLeft | Qt::AlignVCenter + | Qt::TextExpandTabs | Qt::TextWordWrap; + + QwtText txt = text; + txt.setRenderFlags( flags ); + + QwtTextLabel::setText( txt ); +} + +/*! + Set the item mode + The default is QwtLegendData::ReadOnly + + \param mode Item mode + \sa itemMode() +*/ +void QwtLegendLabel::setItemMode( QwtLegendData::Mode mode ) +{ + if ( mode != d_data->itemMode ) + { + d_data->itemMode = mode; + d_data->isDown = false; + + setFocusPolicy( ( mode != QwtLegendData::ReadOnly ) + ? Qt::TabFocus : Qt::NoFocus ); + setMargin( ButtonFrame + Margin ); + + updateGeometry(); + } +} + +/*! + \return Item mode + \sa setItemMode() +*/ +QwtLegendData::Mode QwtLegendLabel::itemMode() const +{ + return d_data->itemMode; +} + +/*! + Assign the icon + + \param icon Pixmap representing a plot item + + \sa icon(), QwtPlotItem::legendIcon() +*/ +void QwtLegendLabel::setIcon( const QPixmap &icon ) +{ + d_data->icon = icon; + + int indent = margin() + d_data->spacing; + if ( icon.width() > 0 ) + indent += icon.width() + d_data->spacing; + + setIndent( indent ); +} + +/*! + \return Pixmap representing a plot item + \sa setIcon() +*/ +QPixmap QwtLegendLabel::icon() const +{ + return d_data->icon; +} + +/*! + \brief Change the spacing between icon and text + + \param spacing Spacing + \sa spacing(), QwtTextLabel::margin() +*/ +void QwtLegendLabel::setSpacing( int spacing ) +{ + spacing = qMax( spacing, 0 ); + if ( spacing != d_data->spacing ) + { + d_data->spacing = spacing; + + int indent = margin() + d_data->spacing; + if ( d_data->icon.width() > 0 ) + indent += d_data->icon.width() + d_data->spacing; + + setIndent( indent ); + } +} + +/*! + \return Spacing between icon and text + \sa setSpacing(), QwtTextLabel::margin() +*/ +int QwtLegendLabel::spacing() const +{ + return d_data->spacing; +} + +/*! + Check/Uncheck a the item + + \param on check/uncheck + \sa setItemMode() +*/ +void QwtLegendLabel::setChecked( bool on ) +{ + if ( d_data->itemMode == QwtLegendData::Checkable ) + { + const bool isBlocked = signalsBlocked(); + blockSignals( true ); + + setDown( on ); + + blockSignals( isBlocked ); + } +} + +//! Return true, if the item is checked +bool QwtLegendLabel::isChecked() const +{ + return d_data->itemMode == QwtLegendData::Checkable && isDown(); +} + +//! Set the item being down +void QwtLegendLabel::setDown( bool down ) +{ + if ( down == d_data->isDown ) + return; + + d_data->isDown = down; + update(); + + if ( d_data->itemMode == QwtLegendData::Clickable ) + { + if ( d_data->isDown ) + Q_EMIT pressed(); + else + { + Q_EMIT released(); + Q_EMIT clicked(); + } + } + + if ( d_data->itemMode == QwtLegendData::Checkable ) + Q_EMIT checked( d_data->isDown ); +} + +//! Return true, if the item is down +bool QwtLegendLabel::isDown() const +{ + return d_data->isDown; +} + +//! Return a size hint +QSize QwtLegendLabel::sizeHint() const +{ + QSize sz = QwtTextLabel::sizeHint(); + sz.setHeight( qMax( sz.height(), d_data->icon.height() + 4 ) ); + + if ( d_data->itemMode != QwtLegendData::ReadOnly ) + { + sz += buttonShift( this ); + sz = sz.expandedTo( QApplication::globalStrut() ); + } + + return sz; +} + +//! Paint event +void QwtLegendLabel::paintEvent( QPaintEvent *e ) +{ + const QRect cr = contentsRect(); + + QPainter painter( this ); + painter.setClipRegion( e->region() ); + + if ( d_data->isDown ) + { + qDrawWinButton( &painter, 0, 0, width(), height(), + palette(), true ); + } + + painter.save(); + + if ( d_data->isDown ) + { + const QSize shiftSize = buttonShift( this ); + painter.translate( shiftSize.width(), shiftSize.height() ); + } + + painter.setClipRect( cr ); + + drawContents( &painter ); + + if ( !d_data->icon.isNull() ) + { + QRect iconRect = cr; + iconRect.setX( iconRect.x() + margin() ); + if ( d_data->itemMode != QwtLegendData::ReadOnly ) + iconRect.setX( iconRect.x() + ButtonFrame ); + + iconRect.setSize( d_data->icon.size() ); + iconRect.moveCenter( QPoint( iconRect.center().x(), cr.center().y() ) ); + + painter.drawPixmap( iconRect, d_data->icon ); + } + + painter.restore(); +} + +//! Handle mouse press events +void QwtLegendLabel::mousePressEvent( QMouseEvent *e ) +{ + if ( e->button() == Qt::LeftButton ) + { + switch ( d_data->itemMode ) + { + case QwtLegendData::Clickable: + { + setDown( true ); + return; + } + case QwtLegendData::Checkable: + { + setDown( !isDown() ); + return; + } + default:; + } + } + QwtTextLabel::mousePressEvent( e ); +} + +//! Handle mouse release events +void QwtLegendLabel::mouseReleaseEvent( QMouseEvent *e ) +{ + if ( e->button() == Qt::LeftButton ) + { + switch ( d_data->itemMode ) + { + case QwtLegendData::Clickable: + { + setDown( false ); + return; + } + case QwtLegendData::Checkable: + { + return; // do nothing, but accept + } + default:; + } + } + QwtTextLabel::mouseReleaseEvent( e ); +} + +//! Handle key press events +void QwtLegendLabel::keyPressEvent( QKeyEvent *e ) +{ + if ( e->key() == Qt::Key_Space ) + { + switch ( d_data->itemMode ) + { + case QwtLegendData::Clickable: + { + if ( !e->isAutoRepeat() ) + setDown( true ); + return; + } + case QwtLegendData::Checkable: + { + if ( !e->isAutoRepeat() ) + setDown( !isDown() ); + return; + } + default:; + } + } + + QwtTextLabel::keyPressEvent( e ); +} + +//! Handle key release events +void QwtLegendLabel::keyReleaseEvent( QKeyEvent *e ) +{ + if ( e->key() == Qt::Key_Space ) + { + switch ( d_data->itemMode ) + { + case QwtLegendData::Clickable: + { + if ( !e->isAutoRepeat() ) + setDown( false ); + return; + } + case QwtLegendData::Checkable: + { + return; // do nothing, but accept + } + default:; + } + } + + QwtTextLabel::keyReleaseEvent( e ); +} diff --git a/libs/qwt/qwt_legend_label.h b/libs/qwt/qwt_legend_label.h new file mode 100644 index 000000000..f0a1584af --- /dev/null +++ b/libs/qwt/qwt_legend_label.h @@ -0,0 +1,80 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_LEGEND_LABEL_H +#define QWT_LEGEND_LABEL_H + +#include "qwt_global.h" +#include "qwt_legend_data.h" +#include "qwt_text.h" +#include "qwt_text_label.h" +#include + +class QwtLegendData; + +/*! + \brief A widget representing something on a QwtLegend. +*/ +class QWT_EXPORT QwtLegendLabel: public QwtTextLabel +{ + Q_OBJECT +public: + explicit QwtLegendLabel( QWidget *parent = 0 ); + virtual ~QwtLegendLabel(); + + void setData( const QwtLegendData & ); + const QwtLegendData &data() const; + + void setItemMode( QwtLegendData::Mode ); + QwtLegendData::Mode itemMode() const; + + void setSpacing( int spacing ); + int spacing() const; + + virtual void setText( const QwtText & ); + + void setIcon( const QPixmap & ); + QPixmap icon() const; + + virtual QSize sizeHint() const; + + bool isChecked() const; + +public Q_SLOTS: + void setChecked( bool on ); + +Q_SIGNALS: + //! Signal, when the legend item has been clicked + void clicked(); + + //! Signal, when the legend item has been pressed + void pressed(); + + //! Signal, when the legend item has been released + void released(); + + //! Signal, when the legend item has been toggled + void checked( bool ); + +protected: + void setDown( bool ); + bool isDown() const; + + virtual void paintEvent( QPaintEvent * ); + virtual void mousePressEvent( QMouseEvent * ); + virtual void mouseReleaseEvent( QMouseEvent * ); + virtual void keyPressEvent( QKeyEvent * ); + virtual void keyReleaseEvent( QKeyEvent * ); + +private: + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/libs/qwt/qwt_magnifier.cpp b/libs/qwt/qwt_magnifier.cpp index 61b0d8ee1..55e7bb5eb 100644 --- a/libs/qwt/qwt_magnifier.cpp +++ b/libs/qwt/qwt_magnifier.cpp @@ -7,51 +7,47 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ -// vim: expandtab - -#include +#include "qwt_magnifier.h" +#include "qwt_math.h" #include #include -#include "qwt_math.h" -#include "qwt_magnifier.h" class QwtMagnifier::PrivateData { public: PrivateData(): - isEnabled(false), - wheelFactor(0.9), - wheelButtonState(Qt::NoButton), - mouseFactor(0.95), - mouseButton(Qt::RightButton), - mouseButtonState(Qt::NoButton), - keyFactor(0.9), - zoomInKey(Qt::Key_Plus), - zoomOutKey(Qt::Key_Minus), -#if QT_VERSION < 0x040000 - zoomInKeyModifiers(Qt::NoButton), - zoomOutKeyModifiers(Qt::NoButton), -#else - zoomInKeyModifiers(Qt::NoModifier), - zoomOutKeyModifiers(Qt::NoModifier), -#endif - mousePressed(false) { + isEnabled( false ), + wheelFactor( 0.9 ), + wheelModifiers( Qt::NoModifier ), + mouseFactor( 0.95 ), + mouseButton( Qt::RightButton ), + mouseButtonModifiers( Qt::NoModifier ), + keyFactor( 0.9 ), + zoomInKey( Qt::Key_Plus ), + zoomInKeyModifiers( Qt::NoModifier ), + zoomOutKey( Qt::Key_Minus ), + zoomOutKeyModifiers( Qt::NoModifier ), + mousePressed( false ) + { } bool isEnabled; double wheelFactor; - int wheelButtonState; + Qt::KeyboardModifiers wheelModifiers; double mouseFactor; - int mouseButton; - int mouseButtonState; + + Qt::MouseButton mouseButton; + Qt::KeyboardModifiers mouseButtonModifiers; double keyFactor; + int zoomInKey; + Qt::KeyboardModifiers zoomInKeyModifiers; + int zoomOutKey; - int zoomInKeyModifiers; - int zoomOutKeyModifiers; + Qt::KeyboardModifiers zoomOutKeyModifiers; bool mousePressed; bool hasMouseTracking; @@ -62,11 +58,11 @@ public: Constructor \param parent Widget to be magnified */ -QwtMagnifier::QwtMagnifier(QWidget *parent): - QObject(parent) +QwtMagnifier::QwtMagnifier( QWidget *parent ): + QObject( parent ) { d_data = new PrivateData(); - setEnabled(true); + setEnabled( true ); } //! Destructor @@ -84,24 +80,26 @@ QwtMagnifier::~QwtMagnifier() \param on true or false \sa isEnabled(), eventFilter() */ -void QwtMagnifier::setEnabled(bool on) +void QwtMagnifier::setEnabled( bool on ) { - if ( d_data->isEnabled != on ) { + if ( d_data->isEnabled != on ) + { d_data->isEnabled = on; QObject *o = parent(); - if ( o ) { + if ( o ) + { if ( d_data->isEnabled ) - o->installEventFilter(this); + o->installEventFilter( this ); else - o->removeEventFilter(this); + o->removeEventFilter( this ); } } } /*! \return true when enabled, false otherwise - \sa setEnabled, eventFilter() + \sa setEnabled(), eventFilter() */ bool QwtMagnifier::isEnabled() const { @@ -113,13 +111,18 @@ bool QwtMagnifier::isEnabled() const The wheel factor defines the ratio between the current range on the parent widget and the zoomed range for each step of the wheel. + + Use values > 1 for magnification (i.e. 2.0) and values < 1 for + scaling down (i.e. 1/2.0 = 0.5). You can use this feature for + inverting the direction of the wheel. + The default value is 0.9. \param factor Wheel factor \sa wheelFactor(), setWheelButtonState(), setMouseFactor(), setKeyFactor() */ -void QwtMagnifier::setWheelFactor(double factor) +void QwtMagnifier::setWheelFactor( double factor ) { d_data->wheelFactor = factor; } @@ -134,24 +137,24 @@ double QwtMagnifier::wheelFactor() const } /*! - Assign a mandatory button state for zooming in/out using the wheel. - The default button state is Qt::NoButton. + Assign keyboard modifiers for zooming in/out using the wheel. + The default modifiers are Qt::NoModifiers. - \param buttonState Button state - \sa wheelButtonState + \param modifiers Keyboard modifiers + \sa wheelModifiers() */ -void QwtMagnifier::setWheelButtonState(int buttonState) +void QwtMagnifier::setWheelModifiers( Qt::KeyboardModifiers modifiers ) { - d_data->wheelButtonState = buttonState; + d_data->wheelModifiers = modifiers; } /*! - \return Wheel button state - \sa setWheelButtonState + \return Wheel modifiers + \sa setWheelModifiers() */ -int QwtMagnifier::wheelButtonState() const +Qt::KeyboardModifiers QwtMagnifier::wheelModifiers() const { - return d_data->wheelButtonState; + return d_data->wheelModifiers; } /*! @@ -164,7 +167,7 @@ int QwtMagnifier::wheelButtonState() const \param factor Wheel factor \sa mouseFactor(), setMouseButton(), setWheelFactor(), setKeyFactor() */ -void QwtMagnifier::setMouseFactor(double factor) +void QwtMagnifier::setMouseFactor( double factor ) { d_data->mouseFactor = factor; } @@ -183,21 +186,23 @@ double QwtMagnifier::mouseFactor() const The default value is Qt::RightButton. \param button Button - \param buttonState Button state - \sa getMouseButton + \param modifiers Keyboard modifiers + + \sa getMouseButton() */ -void QwtMagnifier::setMouseButton(int button, int buttonState) +void QwtMagnifier::setMouseButton( + Qt::MouseButton button, Qt::KeyboardModifiers modifiers ) { d_data->mouseButton = button; - d_data->mouseButtonState = buttonState; + d_data->mouseButtonModifiers = modifiers; } -//! \sa setMouseButton +//! \sa setMouseButton() void QwtMagnifier::getMouseButton( - int &button, int &buttonState) const + Qt::MouseButton &button, Qt::KeyboardModifiers &modifiers ) const { button = d_data->mouseButton; - buttonState = d_data->mouseButtonState; + modifiers = d_data->mouseButtonModifiers; } /*! @@ -211,7 +216,7 @@ void QwtMagnifier::getMouseButton( \sa keyFactor(), setZoomInKey(), setZoomOutKey(), setWheelFactor, setMouseFactor() */ -void QwtMagnifier::setKeyFactor(double factor) +void QwtMagnifier::setKeyFactor( double factor ) { d_data->keyFactor = factor; } @@ -233,14 +238,23 @@ double QwtMagnifier::keyFactor() const \param modifiers \sa getZoomInKey(), setZoomOutKey() */ -void QwtMagnifier::setZoomInKey(int key, int modifiers) +void QwtMagnifier::setZoomInKey( int key, + Qt::KeyboardModifiers modifiers ) { d_data->zoomInKey = key; d_data->zoomInKeyModifiers = modifiers; } -//! \sa setZoomInKey -void QwtMagnifier::getZoomInKey(int &key, int &modifiers) const +/*! + \brief Retrieve the settings of the zoom in key + + \param key Key code, see Qt::Key + \param modifiers Keyboard modifiers + + \sa setZoomInKey() +*/ +void QwtMagnifier::getZoomInKey( int &key, + Qt::KeyboardModifiers &modifiers ) const { key = d_data->zoomInKey; modifiers = d_data->zoomInKeyModifiers; @@ -254,14 +268,23 @@ void QwtMagnifier::getZoomInKey(int &key, int &modifiers) const \param modifiers \sa getZoomOutKey(), setZoomOutKey() */ -void QwtMagnifier::setZoomOutKey(int key, int modifiers) +void QwtMagnifier::setZoomOutKey( int key, + Qt::KeyboardModifiers modifiers ) { d_data->zoomOutKey = key; d_data->zoomOutKeyModifiers = modifiers; } -//! \sa setZoomOutKey -void QwtMagnifier::getZoomOutKey(int &key, int &modifiers) const +/*! + \brief Retrieve the settings of the zoom out key + + \param key Key code, see Qt::Key + \param modifiers Keyboard modifiers + + \sa setZoomOutKey() +*/ +void QwtMagnifier::getZoomOutKey( int &key, + Qt::KeyboardModifiers &modifiers ) const { key = d_data->zoomOutKey; modifiers = d_data->zoomOutKeyModifiers; @@ -270,130 +293,141 @@ void QwtMagnifier::getZoomOutKey(int &key, int &modifiers) const /*! \brief Event filter - When isEnabled() the mouse events of the observed widget are filtered. + When isEnabled() is true, the mouse events of the + observed widget are filtered. + + \param object Object to be filtered + \param event Event + + \return Forwarded to QObject::eventFilter() \sa widgetMousePressEvent(), widgetMouseReleaseEvent(), widgetMouseMoveEvent(), widgetWheelEvent(), widgetKeyPressEvent() widgetKeyReleaseEvent() */ -bool QwtMagnifier::eventFilter(QObject *o, QEvent *e) +bool QwtMagnifier::eventFilter( QObject *object, QEvent *event ) { - if ( o && o == parent() ) { - switch(e->type() ) { - case QEvent::MouseButtonPress: { - widgetMousePressEvent((QMouseEvent *)e); - break; - } - case QEvent::MouseMove: { - widgetMouseMoveEvent((QMouseEvent *)e); - break; - } - case QEvent::MouseButtonRelease: { - widgetMouseReleaseEvent((QMouseEvent *)e); - break; - } - case QEvent::Wheel: { - widgetWheelEvent((QWheelEvent *)e); - break; - } - case QEvent::KeyPress: { - widgetKeyPressEvent((QKeyEvent *)e); - break; - } - case QEvent::KeyRelease: { - widgetKeyReleaseEvent((QKeyEvent *)e); - break; - } - default: - ; + if ( object && object == parent() ) + { + switch ( event->type() ) + { + case QEvent::MouseButtonPress: + { + widgetMousePressEvent( static_cast( event ) ); + break; + } + case QEvent::MouseMove: + { + widgetMouseMoveEvent( static_cast( event ) ); + break; + } + case QEvent::MouseButtonRelease: + { + widgetMouseReleaseEvent( static_cast( event ) ); + break; + } + case QEvent::Wheel: + { + widgetWheelEvent( static_cast( event ) ); + break; + } + case QEvent::KeyPress: + { + widgetKeyPressEvent( static_cast( event ) ); + break; + } + case QEvent::KeyRelease: + { + widgetKeyReleaseEvent( static_cast( event ) ); + break; + } + default:; } } - return QObject::eventFilter(o, e); + return QObject::eventFilter( object, event ); } /*! Handle a mouse press event for the observed widget. - \param me Mouse event + \param mouseEvent Mouse event \sa eventFilter(), widgetMouseReleaseEvent(), widgetMouseMoveEvent() */ -void QwtMagnifier::widgetMousePressEvent(QMouseEvent *me) +void QwtMagnifier::widgetMousePressEvent( QMouseEvent *mouseEvent ) { - if ( me->button() != d_data->mouseButton || parentWidget() == NULL ) + if ( parentWidget() == NULL ) return; -#if QT_VERSION < 0x040000 - if ( (me->state() & Qt::KeyButtonMask) != - (d_data->mouseButtonState & Qt::KeyButtonMask) ) -#else - if ( (me->modifiers() & Qt::KeyboardModifierMask) != - (int)(d_data->mouseButtonState & Qt::KeyboardModifierMask) ) -#endif + if ( ( mouseEvent->button() != d_data->mouseButton ) || + ( mouseEvent->modifiers() != d_data->mouseButtonModifiers ) ) { return; } d_data->hasMouseTracking = parentWidget()->hasMouseTracking(); - parentWidget()->setMouseTracking(true); - d_data->mousePos = me->pos(); + + parentWidget()->setMouseTracking( true ); + d_data->mousePos = mouseEvent->pos(); d_data->mousePressed = true; } /*! Handle a mouse release event for the observed widget. + + \param mouseEvent Mouse event + \sa eventFilter(), widgetMousePressEvent(), widgetMouseMoveEvent(), */ -void QwtMagnifier::widgetMouseReleaseEvent(QMouseEvent *) +void QwtMagnifier::widgetMouseReleaseEvent( QMouseEvent *mouseEvent ) { - if ( d_data->mousePressed && parentWidget() ) { + Q_UNUSED( mouseEvent ); + + if ( d_data->mousePressed && parentWidget() ) + { d_data->mousePressed = false; - parentWidget()->setMouseTracking(d_data->hasMouseTracking); + parentWidget()->setMouseTracking( d_data->hasMouseTracking ); } } /*! Handle a mouse move event for the observed widget. - \param me Mouse event + \param mouseEvent Mouse event \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(), */ -void QwtMagnifier::widgetMouseMoveEvent(QMouseEvent *me) +void QwtMagnifier::widgetMouseMoveEvent( QMouseEvent *mouseEvent ) { if ( !d_data->mousePressed ) return; - const int dy = me->pos().y() - d_data->mousePos.y(); - if ( dy != 0 ) { + const int dy = mouseEvent->pos().y() - d_data->mousePos.y(); + if ( dy != 0 ) + { double f = d_data->mouseFactor; if ( dy < 0 ) f = 1 / f; - rescale(f); + rescale( f ); } - d_data->mousePos = me->pos(); + d_data->mousePos = mouseEvent->pos(); } /*! Handle a wheel event for the observed widget. - \param we Wheel event + \param wheelEvent Wheel event \sa eventFilter() */ -void QwtMagnifier::widgetWheelEvent(QWheelEvent *we) +void QwtMagnifier::widgetWheelEvent( QWheelEvent *wheelEvent ) { -#if QT_VERSION < 0x040000 - if ( (we->state() & Qt::KeyButtonMask) != - (d_data->wheelButtonState & Qt::KeyButtonMask) ) -#else - if ( (we->modifiers() & Qt::KeyboardModifierMask) != - (int)(d_data->wheelButtonState & Qt::KeyboardModifierMask) ) -#endif + if ( wheelEvent->modifiers() != d_data->wheelModifiers ) { return; } - if ( d_data->wheelFactor != 0.0 ) { + if ( d_data->wheelFactor != 0.0 ) + { /* A positive delta indicates that the wheel was rotated forwards away from the user; a negative @@ -403,62 +437,56 @@ void QwtMagnifier::widgetWheelEvent(QWheelEvent *we) in which case the delta value is a multiple of 120 (== 15 * 8). */ - double f = ::pow(d_data->wheelFactor, - qwtAbs(we->delta() / 120)); - if ( we->delta() > 0 ) + double f = qPow( d_data->wheelFactor, + qAbs( wheelEvent->delta() / 120.0 ) ); + + if ( wheelEvent->delta() > 0 ) f = 1 / f; - rescale(f); + rescale( f ); } } /*! Handle a key press event for the observed widget. - \param ke Key event + \param keyEvent Key event \sa eventFilter(), widgetKeyReleaseEvent() */ -void QwtMagnifier::widgetKeyPressEvent(QKeyEvent *ke) +void QwtMagnifier::widgetKeyPressEvent( QKeyEvent *keyEvent ) { - const int key = ke->key(); -#if QT_VERSION < 0x040000 - const int state = ke->state(); -#else - const int state = ke->modifiers(); -#endif - - if ( key == d_data->zoomInKey && - state == d_data->zoomInKeyModifiers ) { - rescale(d_data->keyFactor); - } else if ( key == d_data->zoomOutKey && - state == d_data->zoomOutKeyModifiers ) { - rescale(1.0 / d_data->keyFactor); + if ( keyEvent->key() == d_data->zoomInKey && + keyEvent->modifiers() == d_data->zoomInKeyModifiers ) + { + rescale( d_data->keyFactor ); + } + else if ( keyEvent->key() == d_data->zoomOutKey && + keyEvent->modifiers() == d_data->zoomOutKeyModifiers ) + { + rescale( 1.0 / d_data->keyFactor ); } } /*! Handle a key release event for the observed widget. - \param ke Key event + \param keyEvent Key event \sa eventFilter(), widgetKeyReleaseEvent() */ -void QwtMagnifier::widgetKeyReleaseEvent(QKeyEvent *) +void QwtMagnifier::widgetKeyReleaseEvent( QKeyEvent *keyEvent ) { + Q_UNUSED( keyEvent ); } +//! \return Parent widget, where the rescaling happens QWidget *QwtMagnifier::parentWidget() { - if ( parent()->inherits("QWidget") ) - return (QWidget *)parent(); - - return NULL; + return qobject_cast( parent() ); } +//! \return Parent widget, where the rescaling happens const QWidget *QwtMagnifier::parentWidget() const { - if ( parent()->inherits("QWidget") ) - return (const QWidget *)parent(); - - return NULL; + return qobject_cast( parent() ); } diff --git a/libs/qwt/qwt_magnifier.h b/libs/qwt/qwt_magnifier.h index 973a20810..48e8ed8c4 100644 --- a/libs/qwt/qwt_magnifier.h +++ b/libs/qwt/qwt_magnifier.h @@ -29,50 +29,54 @@ class QWT_EXPORT QwtMagnifier: public QObject Q_OBJECT public: - explicit QwtMagnifier(QWidget *); + explicit QwtMagnifier( QWidget * ); virtual ~QwtMagnifier(); QWidget *parentWidget(); const QWidget *parentWidget() const; - void setEnabled(bool); + void setEnabled( bool ); bool isEnabled() const; // mouse - void setMouseFactor(double); + void setMouseFactor( double ); double mouseFactor() const; - void setMouseButton(int button, int buttonState = Qt::NoButton); - void getMouseButton(int &button, int &buttonState) const; + void setMouseButton( Qt::MouseButton, Qt::KeyboardModifiers = Qt::NoModifier ); + void getMouseButton( Qt::MouseButton &, Qt::KeyboardModifiers & ) const; // mouse wheel - void setWheelFactor(double); + void setWheelFactor( double ); double wheelFactor() const; - void setWheelButtonState(int buttonState); - int wheelButtonState() const; + void setWheelModifiers( Qt::KeyboardModifiers ); + Qt::KeyboardModifiers wheelModifiers() const; // keyboard - void setKeyFactor(double); + void setKeyFactor( double ); double keyFactor() const; - void setZoomInKey(int key, int modifiers); - void getZoomInKey(int &key, int &modifiers) const; + void setZoomInKey( int key, Qt::KeyboardModifiers = Qt::NoModifier ); + void getZoomInKey( int &key, Qt::KeyboardModifiers & ) const; - void setZoomOutKey(int key, int modifiers); - void getZoomOutKey(int &key, int &modifiers) const; + void setZoomOutKey( int key, Qt::KeyboardModifiers = Qt::NoModifier ); + void getZoomOutKey( int &key, Qt::KeyboardModifiers & ) const; - virtual bool eventFilter(QObject *, QEvent *); + virtual bool eventFilter( QObject *, QEvent * ); protected: - virtual void rescale(double factor) = 0; - - virtual void widgetMousePressEvent(QMouseEvent *); - virtual void widgetMouseReleaseEvent(QMouseEvent *); - virtual void widgetMouseMoveEvent(QMouseEvent *); - virtual void widgetWheelEvent(QWheelEvent *); - virtual void widgetKeyPressEvent(QKeyEvent *); - virtual void widgetKeyReleaseEvent(QKeyEvent *); + /*! + Rescale the parent widget + \param factor Scale factor + */ + virtual void rescale( double factor ) = 0; + + virtual void widgetMousePressEvent( QMouseEvent * ); + virtual void widgetMouseReleaseEvent( QMouseEvent * ); + virtual void widgetMouseMoveEvent( QMouseEvent * ); + virtual void widgetWheelEvent( QWheelEvent * ); + virtual void widgetKeyPressEvent( QKeyEvent * ); + virtual void widgetKeyReleaseEvent( QKeyEvent * ); private: class PrivateData; diff --git a/libs/qwt/qwt_math.cpp b/libs/qwt/qwt_math.cpp index e99f67eed..9e898c105 100644 --- a/libs/qwt/qwt_math.cpp +++ b/libs/qwt/qwt_math.cpp @@ -7,8 +7,6 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ -// vim: expandtab - #include "qwt_math.h" /*! @@ -16,14 +14,14 @@ \param array Pointer to an array \param size Array size */ -double qwtGetMin(const double *array, int size) +double qwtGetMin( const double *array, int size ) { - if (size <= 0) + if ( size <= 0 ) return 0.0; double rv = array[0]; - for (int i = 1; i < size; i++) - rv = qwtMin(rv, array[i]); + for ( int i = 1; i < size; i++ ) + rv = qMin( rv, array[i] ); return rv; } @@ -34,14 +32,43 @@ double qwtGetMin(const double *array, int size) \param array Pointer to an array \param size Array size */ -double qwtGetMax(const double *array, int size) +double qwtGetMax( const double *array, int size ) { - if (size <= 0) + if ( size <= 0 ) return 0.0; double rv = array[0]; - for (int i = 1; i < size; i++) - rv = qwtMax(rv, array[i]); + for ( int i = 1; i < size; i++ ) + rv = qMax( rv, array[i] ); return rv; } + +/*! + \brief Normalize an angle to be int the range [0.0, 2 * PI[ + \param radians Angle in radians + \return Normalized angle in radians +*/ +double qwtNormalizeRadians( double radians ) +{ + double a = ::fmod( radians, 2.0 * M_PI ); + if ( a < 0.0 ) + a += 2.0 * M_PI; + + return a; + +} + +/*! + \brief Normalize an angle to be int the range [0.0, 360.0[ + \param radians Angle in degrees + \return Normalized angle in degrees +*/ +double qwtNormalizeDegrees( double degrees ) +{ + double a = ::fmod( degrees, 360.0 ); + if ( a < 0.0 ) + a += 360.0; + + return a; +} diff --git a/libs/qwt/qwt_math.h b/libs/qwt/qwt_math.h index 305b220a6..23ad20560 100644 --- a/libs/qwt/qwt_math.h +++ b/libs/qwt/qwt_math.h @@ -10,43 +10,30 @@ #ifndef QWT_MATH_H #define QWT_MATH_H -#include -#include #include "qwt_global.h" -#include "qwt_double_rect.h" -#if QT_VERSION < 0x040000 +#if defined(_MSC_VER) +/* + Microsoft says: -#define qwtMax QMAX -#define qwtMin QMIN -#define qwtAbs QABS - -#else // QT_VERSION >= 0x040000 - -#define qwtMax(x,y) qMax(qreal(x),qreal(y)) -#define qwtMin(x,y) qMin(qreal(x),qreal(y)) -#define qwtAbs qAbs - -#endif - -#ifndef LOG10_2 -#define LOG10_2 0.30102999566398119802 /* log10(2) */ -#endif - -#ifndef LOG10_3 -#define LOG10_3 0.47712125471966243540 /* log10(3) */ + Define _USE_MATH_DEFINES before including math.h to expose these macro + definitions for common math constants. These are placed under an #ifdef + since these commonly-defined names are not part of the C/C++ standards. +*/ +#define _USE_MATH_DEFINES 1 #endif -#ifndef LOG10_5 -#define LOG10_5 0.69897000433601885749 /* log10(5) */ -#endif +#include +#include "qwt_global.h" -#ifndef M_2PI -#define M_2PI 6.28318530717958623200 /* 2 pi */ +#ifndef M_PI_2 +// For Qt <= 4.8.4 M_PI_2 is not known by MinGW-w64 +// when compiling with -std=c++11 +#define M_PI_2 (1.57079632679489661923) #endif #ifndef LOG_MIN -//! Mininum value for logarithmic scales +//! Minimum value for logarithmic scales #define LOG_MIN 1.0e-100 #endif @@ -55,138 +42,108 @@ #define LOG_MAX 1.0e100 #endif -#ifndef M_E -#define M_E 2.7182818284590452354 /* e */ -#endif +QWT_EXPORT double qwtGetMin( const double *array, int size ); +QWT_EXPORT double qwtGetMax( const double *array, int size ); -#ifndef M_LOG2E -#define M_LOG2E 1.4426950408889634074 /* log_2 e */ -#endif - -#ifndef M_LOG10E -#define M_LOG10E 0.43429448190325182765 /* log_10 e */ -#endif +QWT_EXPORT double qwtNormalizeRadians( double radians ); +QWT_EXPORT double qwtNormalizeDegrees( double degrees ); -#ifndef M_LN2 -#define M_LN2 0.69314718055994530942 /* log_e 2 */ -#endif - -#ifndef M_LN10 -#define M_LN10 2.30258509299404568402 /* log_e 10 */ -#endif +/*! + \brief Compare 2 values, relative to an interval -#ifndef M_PI -#define M_PI 3.14159265358979323846 /* pi */ -#endif + Values are "equal", when : + \f$\cdot value2 - value1 <= abs(intervalSize * 10e^{-6})\f$ -#ifndef M_PI_2 -#define M_PI_2 1.57079632679489661923 /* pi/2 */ -#endif + \param value1 First value to compare + \param value2 Second value to compare + \param intervalSize interval size -#ifndef M_PI_4 -#define M_PI_4 0.78539816339744830962 /* pi/4 */ -#endif - -#ifndef M_1_PI -#define M_1_PI 0.31830988618379067154 /* 1/pi */ -#endif + \return 0: if equal, -1: if value2 > value1, 1: if value1 > value2 +*/ +inline int qwtFuzzyCompare( double value1, double value2, double intervalSize ) +{ + const double eps = qAbs( 1.0e-6 * intervalSize ); -#ifndef M_2_PI -#define M_2_PI 0.63661977236758134308 /* 2/pi */ -#endif + if ( value2 - value1 > eps ) + return -1; -#ifndef M_2_SQRTPI -#define M_2_SQRTPI 1.12837916709551257390 /* 2/sqrt(pi) */ -#endif + if ( value1 - value2 > eps ) + return 1; -#ifndef M_SQRT2 -#define M_SQRT2 1.41421356237309504880 /* sqrt(2) */ -#endif + return 0; +} -#ifndef M_SQRT1_2 -#define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */ -#endif -QWT_EXPORT double qwtGetMin(const double *array, int size); -QWT_EXPORT double qwtGetMax(const double *array, int size); +inline bool qwtFuzzyGreaterOrEqual( double d1, double d2 ) +{ + return ( d1 >= d2 ) || qFuzzyCompare( d1, d2 ); +} +inline bool qwtFuzzyLessOrEqual( double d1, double d2 ) +{ + return ( d1 <= d2 ) || qFuzzyCompare( d1, d2 ); +} //! Return the sign -inline int qwtSign(double x) +inline int qwtSign( double x ) { - if (x > 0.0) + if ( x > 0.0 ) return 1; - else if (x < 0.0) - return (-1); + else if ( x < 0.0 ) + return ( -1 ); else return 0; } //! Return the square of a number -inline double qwtSqr(const double x) +inline double qwtSqr( double x ) { - return x*x; + return x * x; } -/*! - \brief Limit a value to fit into a specified interval - \param x Input value - \param x1 First interval boundary - \param x2 Second interval boundary -*/ -template -T qwtLim(const T& x, const T& x1, const T& x2) +//! Approximation of arc tangent ( error below 0,005 radians ) +inline double qwtFastAtan( double x ) { - T rv; - T xmin, xmax; + if ( x < -1.0 ) + return -M_PI_2 - x / ( x * x + 0.28 ); - xmin = qwtMin(x1, x2); - xmax = qwtMax(x1, x2); + if ( x > 1.0 ) + return M_PI_2 - x / ( x * x + 0.28 ); - if ( x < xmin ) - rv = xmin; - else if ( x > xmax ) - rv = xmax; - else - rv = x; - - return rv; + return x / ( 1.0 + x * x * 0.28 ); } -inline QPoint qwtPolar2Pos(const QPoint &pole, - double radius, double angle) +//! Approximation of arc tangent ( error below 0,005 radians ) +inline double qwtFastAtan2( double y, double x ) { - const double x = pole.x() + radius * ::cos(angle); - const double y = pole.y() - radius * ::sin(angle); + if ( x > 0 ) + return qwtFastAtan( y / x ); - return QPoint(qRound(x), qRound(y)); -} + if ( x < 0 ) + { + const double d = qwtFastAtan( y / x ); + return ( y >= 0 ) ? d + M_PI : d - M_PI; + } -inline QPoint qwtDegree2Pos(const QPoint &pole, - double radius, double angle) -{ - return qwtPolar2Pos(pole, radius, angle / 180.0 * M_PI); -} + if ( y < 0.0 ) + return -M_PI_2; -inline QwtDoublePoint qwtPolar2Pos(const QwtDoublePoint &pole, - double radius, double angle) -{ - const double x = pole.x() + radius * ::cos(angle); - const double y = pole.y() - radius * ::sin(angle); + if ( y > 0.0 ) + return M_PI_2; - return QPoint(qRound(x), qRound(y)); + return 0.0; } -inline QwtDoublePoint qwtDegree2Pos(const QwtDoublePoint &pole, - double radius, double angle) +// Translate degrees into radians +inline double qwtRadians( double degrees ) { - return qwtPolar2Pos(pole, radius, angle / 180.0 * M_PI); + return degrees * M_PI / 180.0; } -//! Rounding of doubles, like qRound for integers -inline double qwtRound(double value) +// Translate radians into degrees +inline double qwtDegrees( double degrees ) { - return ::floor(value + 0.5); // MSVC has no ::round(). + return degrees * 180.0 / M_PI; } #endif diff --git a/libs/qwt/qwt_matrix_raster_data.cpp b/libs/qwt/qwt_matrix_raster_data.cpp new file mode 100644 index 000000000..69355adb3 --- /dev/null +++ b/libs/qwt/qwt_matrix_raster_data.cpp @@ -0,0 +1,298 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_matrix_raster_data.h" +#include +#include + +class QwtMatrixRasterData::PrivateData +{ +public: + PrivateData(): + resampleMode(QwtMatrixRasterData::NearestNeighbour), + numColumns(0) + { + } + + inline double value(int row, int col) const + { + return values.data()[ row * numColumns + col ]; + } + + QwtMatrixRasterData::ResampleMode resampleMode; + + QVector values; + int numColumns; + int numRows; + + double dx; + double dy; +}; + +//! Constructor +QwtMatrixRasterData::QwtMatrixRasterData() +{ + d_data = new PrivateData(); + update(); +} + +//! Destructor +QwtMatrixRasterData::~QwtMatrixRasterData() +{ + delete d_data; +} + +/*! + \brief Set the resampling algorithm + + \param mode Resampling mode + \sa resampleMode(), value() +*/ +void QwtMatrixRasterData::setResampleMode( ResampleMode mode ) +{ + d_data->resampleMode = mode; +} + +/*! + \return resampling algorithm + \sa setResampleMode(), value() +*/ +QwtMatrixRasterData::ResampleMode QwtMatrixRasterData::resampleMode() const +{ + return d_data->resampleMode; +} + +/*! + \brief Assign the bounding interval for an axis + + Setting the bounding intervals for the X/Y axis is mandatory + to define the positions for the values of the value matrix. + The interval in Z direction defines the possible range for + the values in the matrix, what is f.e used by QwtPlotSpectrogram + to map values to colors. The Z-interval might be the bounding + interval of the values in the matrix, but usually it isn't. + ( f.e a interval of 0.0-100.0 for values in percentage ) + + \param axis X, Y or Z axis + \param interval Interval + + \sa QwtRasterData::interval(), setValueMatrix() +*/ +void QwtMatrixRasterData::setInterval( + Qt::Axis axis, const QwtInterval &interval ) +{ + QwtRasterData::setInterval( axis, interval ); + update(); +} + +/*! + \brief Assign a value matrix + + The positions of the values are calculated by dividing + the bounding rectangle of the X/Y intervals into equidistant + rectangles ( pixels ). Each value corresponds to the center of + a pixel. + + \param values Vector of values + \param numColumns Number of columns + + \sa valueMatrix(), numColumns(), numRows(), setInterval()() +*/ +void QwtMatrixRasterData::setValueMatrix( + const QVector &values, int numColumns ) +{ + d_data->values = values; + d_data->numColumns = qMax( numColumns, 0 ); + update(); +} + +/*! + \return Value matrix + \sa setValueMatrix(), numColumns(), numRows(), setInterval() +*/ +const QVector QwtMatrixRasterData::valueMatrix() const +{ + return d_data->values; +} + +/*! + \brief Change a single value in the matrix + + \param row Row index + \param col Column index + \param value New value + + \sa value(), setValueMatrix() +*/ +void QwtMatrixRasterData::setValue( int row, int col, double value ) +{ + if ( row >= 0 && row < d_data->numRows && + col >= 0 && col < d_data->numColumns ) + { + const int index = row * d_data->numColumns + col; + d_data->values.data()[ index ] = value; + } +} + +/*! + \return Number of columns of the value matrix + \sa valueMatrix(), numRows(), setValueMatrix() +*/ +int QwtMatrixRasterData::numColumns() const +{ + return d_data->numColumns; +} + +/*! + \return Number of rows of the value matrix + \sa valueMatrix(), numColumns(), setValueMatrix() +*/ +int QwtMatrixRasterData::numRows() const +{ + return d_data->numRows; +} + +/*! + \brief Calculate the pixel hint + + pixelHint() returns the geometry of a pixel, that can be used + to calculate the resolution and alignment of the plot item, that is + representing the data. + + - NearestNeighbour\n + pixelHint() returns the surrounding pixel of the top left value + in the matrix. + + - BilinearInterpolation\n + Returns an empty rectangle recommending + to render in target device ( f.e. screen ) resolution. + + \param area Requested area, ignored + \return Calculated hint + + \sa ResampleMode, setMatrix(), setInterval() +*/ +QRectF QwtMatrixRasterData::pixelHint( const QRectF &area ) const +{ + Q_UNUSED( area ) + + QRectF rect; + if ( d_data->resampleMode == NearestNeighbour ) + { + const QwtInterval intervalX = interval( Qt::XAxis ); + const QwtInterval intervalY = interval( Qt::YAxis ); + if ( intervalX.isValid() && intervalY.isValid() ) + { + rect = QRectF( intervalX.minValue(), intervalY.minValue(), + d_data->dx, d_data->dy ); + } + } + + return rect; +} + +/*! + \return the value at a raster position + + \param x X value in plot coordinates + \param y Y value in plot coordinates + + \sa ResampleMode +*/ +double QwtMatrixRasterData::value( double x, double y ) const +{ + const QwtInterval xInterval = interval( Qt::XAxis ); + const QwtInterval yInterval = interval( Qt::YAxis ); + + if ( !( xInterval.contains(x) && yInterval.contains(y) ) ) + return qQNaN(); + + double value; + + switch( d_data->resampleMode ) + { + case BilinearInterpolation: + { + int col1 = qRound( (x - xInterval.minValue() ) / d_data->dx ) - 1; + int row1 = qRound( (y - yInterval.minValue() ) / d_data->dy ) - 1; + int col2 = col1 + 1; + int row2 = row1 + 1; + + if ( col1 < 0 ) + col1 = col2; + else if ( col2 >= static_cast( d_data->numColumns ) ) + col2 = col1; + + if ( row1 < 0 ) + row1 = row2; + else if ( row2 >= static_cast( d_data->numRows ) ) + row2 = row1; + + const double v11 = d_data->value( row1, col1 ); + const double v21 = d_data->value( row1, col2 ); + const double v12 = d_data->value( row2, col1 ); + const double v22 = d_data->value( row2, col2 ); + + const double x2 = xInterval.minValue() + + ( col2 + 0.5 ) * d_data->dx; + const double y2 = yInterval.minValue() + + ( row2 + 0.5 ) * d_data->dy; + + const double rx = ( x2 - x ) / d_data->dx; + const double ry = ( y2 - y ) / d_data->dy; + + const double vr1 = rx * v11 + ( 1.0 - rx ) * v21; + const double vr2 = rx * v12 + ( 1.0 - rx ) * v22; + + value = ry * vr1 + ( 1.0 - ry ) * vr2; + + break; + } + case NearestNeighbour: + default: + { + int row = int( (y - yInterval.minValue() ) / d_data->dy ); + int col = int( (x - xInterval.minValue() ) / d_data->dx ); + + // In case of intervals, where the maximum is included + // we get out of bound for row/col, when the value for the + // maximum is requested. Instead we return the value + // from the last row/col + + if ( row >= d_data->numRows ) + row = d_data->numRows - 1; + + if ( col >= d_data->numColumns ) + col = d_data->numColumns - 1; + + value = d_data->value( row, col ); + } + } + + return value; +} + +void QwtMatrixRasterData::update() +{ + d_data->numRows = 0; + d_data->dx = 0.0; + d_data->dy = 0.0; + + if ( d_data->numColumns > 0 ) + { + d_data->numRows = d_data->values.size() / d_data->numColumns; + + const QwtInterval xInterval = interval( Qt::XAxis ); + const QwtInterval yInterval = interval( Qt::YAxis ); + if ( xInterval.isValid() ) + d_data->dx = xInterval.width() / d_data->numColumns; + if ( yInterval.isValid() ) + d_data->dy = yInterval.width() / d_data->numRows; + } +} diff --git a/libs/qwt/qwt_matrix_raster_data.h b/libs/qwt/qwt_matrix_raster_data.h new file mode 100644 index 000000000..0b107c9fd --- /dev/null +++ b/libs/qwt/qwt_matrix_raster_data.h @@ -0,0 +1,74 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_MATRIX_RASTER_DATA_H +#define QWT_MATRIX_RASTER_DATA_H 1 + +#include "qwt_global.h" +#include "qwt_raster_data.h" +#include + +/*! + \brief A class representing a matrix of values as raster data + + QwtMatrixRasterData implements an interface for a matrix of + equidistant values, that can be used by a QwtPlotRasterItem. + It implements a couple of resampling algorithms, to provide + values for positions, that or not on the value matrix. +*/ +class QWT_EXPORT QwtMatrixRasterData: public QwtRasterData +{ +public: + /*! + \brief Resampling algorithm + The default setting is NearestNeighbour; + */ + enum ResampleMode + { + /*! + Return the value from the matrix, that is nearest to the + the requested position. + */ + NearestNeighbour, + + /*! + Interpolate the value from the distances and values of the + 4 surrounding values in the matrix, + */ + BilinearInterpolation + }; + + QwtMatrixRasterData(); + virtual ~QwtMatrixRasterData(); + + void setResampleMode(ResampleMode mode); + ResampleMode resampleMode() const; + + virtual void setInterval( Qt::Axis, const QwtInterval & ); + + void setValueMatrix( const QVector &values, int numColumns ); + const QVector valueMatrix() const; + + void setValue( int row, int col, double value ); + + int numColumns() const; + int numRows() const; + + virtual QRectF pixelHint( const QRectF & ) const; + + virtual double value( double x, double y ) const; + +private: + void update(); + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/libs/qwt/qwt_null_paintdevice.cpp b/libs/qwt/qwt_null_paintdevice.cpp new file mode 100644 index 000000000..db1611da2 --- /dev/null +++ b/libs/qwt/qwt_null_paintdevice.cpp @@ -0,0 +1,593 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_null_paintdevice.h" +#include +#include + +class QwtNullPaintDevice::PrivateData +{ +public: + PrivateData(): + mode( QwtNullPaintDevice::NormalMode ) + { + } + + QwtNullPaintDevice::Mode mode; +}; + +class QwtNullPaintDevice::PaintEngine: public QPaintEngine +{ +public: + PaintEngine(); + + virtual bool begin( QPaintDevice * ); + virtual bool end(); + + virtual Type type () const; + virtual void updateState(const QPaintEngineState &); + + virtual void drawRects(const QRect *, int ); + virtual void drawRects(const QRectF *, int ); + + virtual void drawLines(const QLine *, int ); + virtual void drawLines(const QLineF *, int ); + + virtual void drawEllipse(const QRectF &); + virtual void drawEllipse(const QRect &); + + virtual void drawPath(const QPainterPath &); + + virtual void drawPoints(const QPointF *, int ); + virtual void drawPoints(const QPoint *, int ); + + virtual void drawPolygon(const QPointF *, int , PolygonDrawMode ); + virtual void drawPolygon(const QPoint *, int , PolygonDrawMode ); + + virtual void drawPixmap(const QRectF &, + const QPixmap &, const QRectF &); + + virtual void drawTextItem(const QPointF &, const QTextItem &); + + virtual void drawTiledPixmap(const QRectF &, + const QPixmap &, const QPointF &s); + + virtual void drawImage(const QRectF &, + const QImage &, const QRectF &, Qt::ImageConversionFlags ); + +private: + QwtNullPaintDevice *nullDevice(); +}; + +QwtNullPaintDevice::PaintEngine::PaintEngine(): + QPaintEngine( QPaintEngine::AllFeatures ) +{ +} + +bool QwtNullPaintDevice::PaintEngine::begin( QPaintDevice * ) +{ + setActive( true ); + return true; +} + +bool QwtNullPaintDevice::PaintEngine::end() +{ + setActive( false ); + return true; +} + +QPaintEngine::Type QwtNullPaintDevice::PaintEngine::type() const +{ + return QPaintEngine::User; +} + +void QwtNullPaintDevice::PaintEngine::drawRects( + const QRect *rects, int rectCount) +{ + QwtNullPaintDevice *device = nullDevice(); + if ( device == NULL ) + return; + + if ( device->mode() != QwtNullPaintDevice::NormalMode ) + { + QPaintEngine::drawRects( rects, rectCount ); + return; + } + + device->drawRects( rects, rectCount ); +} + +void QwtNullPaintDevice::PaintEngine::drawRects( + const QRectF *rects, int rectCount) +{ + QwtNullPaintDevice *device = nullDevice(); + if ( device == NULL ) + return; + + if ( device->mode() != QwtNullPaintDevice::NormalMode ) + { + QPaintEngine::drawRects( rects, rectCount ); + return; + } + + device->drawRects( rects, rectCount ); +} + +void QwtNullPaintDevice::PaintEngine::drawLines( + const QLine *lines, int lineCount) +{ + QwtNullPaintDevice *device = nullDevice(); + if ( device == NULL ) + return; + + if ( device->mode() != QwtNullPaintDevice::NormalMode ) + { + QPaintEngine::drawLines( lines, lineCount ); + return; + } + + device->drawLines( lines, lineCount ); +} + +void QwtNullPaintDevice::PaintEngine::drawLines( + const QLineF *lines, int lineCount) +{ + QwtNullPaintDevice *device = nullDevice(); + if ( device == NULL ) + return; + + if ( device->mode() != QwtNullPaintDevice::NormalMode ) + { + QPaintEngine::drawLines( lines, lineCount ); + return; + } + + device->drawLines( lines, lineCount ); +} + +void QwtNullPaintDevice::PaintEngine::drawEllipse( + const QRectF &rect) +{ + QwtNullPaintDevice *device = nullDevice(); + if ( device == NULL ) + return; + + if ( device->mode() != QwtNullPaintDevice::NormalMode ) + { + QPaintEngine::drawEllipse( rect ); + return; + } + + device->drawEllipse( rect ); +} + +void QwtNullPaintDevice::PaintEngine::drawEllipse( + const QRect &rect) +{ + QwtNullPaintDevice *device = nullDevice(); + if ( device == NULL ) + return; + + if ( device->mode() != QwtNullPaintDevice::NormalMode ) + { + QPaintEngine::drawEllipse( rect ); + return; + } + + device->drawEllipse( rect ); +} + + +void QwtNullPaintDevice::PaintEngine::drawPath( + const QPainterPath &path) +{ + QwtNullPaintDevice *device = nullDevice(); + if ( device == NULL ) + return; + + device->drawPath( path ); +} + +void QwtNullPaintDevice::PaintEngine::drawPoints( + const QPointF *points, int pointCount) +{ + QwtNullPaintDevice *device = nullDevice(); + if ( device == NULL ) + return; + + if ( device->mode() != QwtNullPaintDevice::NormalMode ) + { + QPaintEngine::drawPoints( points, pointCount ); + return; + } + + device->drawPoints( points, pointCount ); +} + +void QwtNullPaintDevice::PaintEngine::drawPoints( + const QPoint *points, int pointCount) +{ + QwtNullPaintDevice *device = nullDevice(); + if ( device == NULL ) + return; + + if ( device->mode() != QwtNullPaintDevice::NormalMode ) + { + QPaintEngine::drawPoints( points, pointCount ); + return; + } + + device->drawPoints( points, pointCount ); +} + +void QwtNullPaintDevice::PaintEngine::drawPolygon( + const QPointF *points, int pointCount, PolygonDrawMode mode) +{ + QwtNullPaintDevice *device = nullDevice(); + if ( device == NULL ) + return; + + if ( device->mode() == QwtNullPaintDevice::PathMode ) + { + QPainterPath path; + + if ( pointCount > 0 ) + { + path.moveTo( points[0] ); + for ( int i = 1; i < pointCount; i++ ) + path.lineTo( points[i] ); + + if ( mode != PolylineMode ) + path.closeSubpath(); + } + + device->drawPath( path ); + return; + } + + device->drawPolygon( points, pointCount, mode ); +} + +void QwtNullPaintDevice::PaintEngine::drawPolygon( + const QPoint *points, int pointCount, PolygonDrawMode mode) +{ + QwtNullPaintDevice *device = nullDevice(); + if ( device == NULL ) + return; + + if ( device->mode() == QwtNullPaintDevice::PathMode ) + { + QPainterPath path; + + if ( pointCount > 0 ) + { + path.moveTo( points[0] ); + for ( int i = 1; i < pointCount; i++ ) + path.lineTo( points[i] ); + + if ( mode != PolylineMode ) + path.closeSubpath(); + } + + device->drawPath( path ); + return; + } + + device->drawPolygon( points, pointCount, mode ); +} + +void QwtNullPaintDevice::PaintEngine::drawPixmap( + const QRectF &rect, const QPixmap &pm, const QRectF &subRect ) +{ + QwtNullPaintDevice *device = nullDevice(); + if ( device == NULL ) + return; + + device->drawPixmap( rect, pm, subRect ); +} + +void QwtNullPaintDevice::PaintEngine::drawTextItem( + const QPointF &pos, const QTextItem &textItem) +{ + QwtNullPaintDevice *device = nullDevice(); + if ( device == NULL ) + return; + + if ( device->mode() != QwtNullPaintDevice::NormalMode ) + { + QPaintEngine::drawTextItem( pos, textItem ); + return; + } + + device->drawTextItem( pos, textItem ); +} + +void QwtNullPaintDevice::PaintEngine::drawTiledPixmap( + const QRectF &rect, const QPixmap &pixmap, + const QPointF &subRect) +{ + QwtNullPaintDevice *device = nullDevice(); + if ( device == NULL ) + return; + + if ( device->mode() != QwtNullPaintDevice::NormalMode ) + { + QPaintEngine::drawTiledPixmap( rect, pixmap, subRect ); + return; + } + + device->drawTiledPixmap( rect, pixmap, subRect ); +} + +void QwtNullPaintDevice::PaintEngine::drawImage( + const QRectF &rect, const QImage &image, + const QRectF &subRect, Qt::ImageConversionFlags flags) +{ + QwtNullPaintDevice *device = nullDevice(); + if ( device == NULL ) + return; + + device->drawImage( rect, image, subRect, flags ); +} + +void QwtNullPaintDevice::PaintEngine::updateState( + const QPaintEngineState &state) +{ + QwtNullPaintDevice *device = nullDevice(); + if ( device == NULL ) + return; + + device->updateState( state ); +} + +inline QwtNullPaintDevice *QwtNullPaintDevice::PaintEngine::nullDevice() +{ + if ( !isActive() ) + return NULL; + + return static_cast( paintDevice() ); +} + +//! Constructor +QwtNullPaintDevice::QwtNullPaintDevice(): + d_engine( NULL ) +{ + d_data = new PrivateData; +} + +//! Destructor +QwtNullPaintDevice::~QwtNullPaintDevice() +{ + delete d_engine; + delete d_data; +} + +/*! + Set the render mode + + \param mode New mode + \sa mode() + */ +void QwtNullPaintDevice::setMode( Mode mode ) +{ + d_data->mode = mode; +} + +/*! + \return Render mode + \sa setMode() +*/ +QwtNullPaintDevice::Mode QwtNullPaintDevice::mode() const +{ + return d_data->mode; +} + +//! See QPaintDevice::paintEngine() +QPaintEngine *QwtNullPaintDevice::paintEngine() const +{ + if ( d_engine == NULL ) + { + QwtNullPaintDevice *that = + const_cast< QwtNullPaintDevice * >( this ); + + that->d_engine = new PaintEngine(); + } + + return d_engine; +} + +/*! + See QPaintDevice::metric() + + \param deviceMetric Type of metric + \return Metric information for the given paint device metric. + + \sa sizeMetrics() +*/ +int QwtNullPaintDevice::metric( PaintDeviceMetric deviceMetric ) const +{ + int value; + + switch ( deviceMetric ) + { + case PdmWidth: + { + value = sizeMetrics().width(); + break; + } + case PdmHeight: + { + value = sizeMetrics().height(); + break; + } + case PdmNumColors: + { + value = 0xffffffff; + break; + } + case PdmDepth: + { + value = 32; + break; + } + case PdmPhysicalDpiX: + case PdmPhysicalDpiY: + case PdmDpiY: + case PdmDpiX: + { + value = 72; + break; + } + case PdmWidthMM: + { + value = qRound( metric( PdmWidth ) * 25.4 / metric( PdmDpiX ) ); + break; + } + case PdmHeightMM: + { + value = qRound( metric( PdmHeight ) * 25.4 / metric( PdmDpiY ) ); + break; + } + default: + value = 0; + } + return value; + +} + +//! See QPaintEngine::drawRects() +void QwtNullPaintDevice::drawRects( + const QRect *rects, int rectCount) +{ + Q_UNUSED(rects); + Q_UNUSED(rectCount); +} + +//! See QPaintEngine::drawRects() +void QwtNullPaintDevice::drawRects( + const QRectF *rects, int rectCount) +{ + Q_UNUSED(rects); + Q_UNUSED(rectCount); +} + +//! See QPaintEngine::drawLines() +void QwtNullPaintDevice::drawLines( + const QLine *lines, int lineCount) +{ + Q_UNUSED(lines); + Q_UNUSED(lineCount); +} + +//! See QPaintEngine::drawLines() +void QwtNullPaintDevice::drawLines( + const QLineF *lines, int lineCount) +{ + Q_UNUSED(lines); + Q_UNUSED(lineCount); +} + +//! See QPaintEngine::drawEllipse() +void QwtNullPaintDevice::drawEllipse( const QRectF &rect ) +{ + Q_UNUSED(rect); +} + +//! See QPaintEngine::drawEllipse() +void QwtNullPaintDevice::drawEllipse( const QRect &rect ) +{ + Q_UNUSED(rect); +} + +//! See QPaintEngine::drawPath() +void QwtNullPaintDevice::drawPath( const QPainterPath &path ) +{ + Q_UNUSED(path); +} + +//! See QPaintEngine::drawPoints() +void QwtNullPaintDevice::drawPoints( + const QPointF *points, int pointCount) +{ + Q_UNUSED(points); + Q_UNUSED(pointCount); +} + +//! See QPaintEngine::drawPoints() +void QwtNullPaintDevice::drawPoints( + const QPoint *points, int pointCount) +{ + Q_UNUSED(points); + Q_UNUSED(pointCount); +} + +//! See QPaintEngine::drawPolygon() +void QwtNullPaintDevice::drawPolygon( + const QPointF *points, int pointCount, + QPaintEngine::PolygonDrawMode mode) +{ + Q_UNUSED(points); + Q_UNUSED(pointCount); + Q_UNUSED(mode); +} + +//! See QPaintEngine::drawPolygon() +void QwtNullPaintDevice::drawPolygon( + const QPoint *points, int pointCount, + QPaintEngine::PolygonDrawMode mode) +{ + Q_UNUSED(points); + Q_UNUSED(pointCount); + Q_UNUSED(mode); +} + +//! See QPaintEngine::drawPixmap() +void QwtNullPaintDevice::drawPixmap( const QRectF &rect, + const QPixmap &pm, const QRectF &subRect ) +{ + Q_UNUSED(rect); + Q_UNUSED(pm); + Q_UNUSED(subRect); +} + +//! See QPaintEngine::drawTextItem() +void QwtNullPaintDevice::drawTextItem( + const QPointF &pos, const QTextItem &textItem) +{ + Q_UNUSED(pos); + Q_UNUSED(textItem); +} + +//! See QPaintEngine::drawTiledPixmap() +void QwtNullPaintDevice::drawTiledPixmap( + const QRectF &rect, const QPixmap &pixmap, + const QPointF &subRect) +{ + Q_UNUSED(rect); + Q_UNUSED(pixmap); + Q_UNUSED(subRect); +} + +//! See QPaintEngine::drawImage() +void QwtNullPaintDevice::drawImage( + const QRectF &rect, const QImage &image, + const QRectF &subRect, Qt::ImageConversionFlags flags) +{ + Q_UNUSED(rect); + Q_UNUSED(image); + Q_UNUSED(subRect); + Q_UNUSED(flags); +} + +//! See QPaintEngine::updateState() +void QwtNullPaintDevice::updateState( + const QPaintEngineState &state ) +{ + Q_UNUSED(state); +} diff --git a/libs/qwt/qwt_null_paintdevice.h b/libs/qwt/qwt_null_paintdevice.h new file mode 100644 index 000000000..d7f03beea --- /dev/null +++ b/libs/qwt/qwt_null_paintdevice.h @@ -0,0 +1,126 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_NULL_PAINT_DEVICE_H +#define QWT_NULL_PAINT_DEVICE_H 1 + +#include "qwt_global.h" +#include +#include + +/*! + \brief A null paint device doing nothing + + Sometimes important layout/rendering geometries are not + available or changeable from the public Qt class interface. + ( f.e hidden in the style implementation ). + + QwtNullPaintDevice can be used to manipulate or filter out + this information by analyzing the stream of paint primitives. + + F.e. QwtNullPaintDevice is used by QwtPlotCanvas to identify + styled backgrounds with rounded corners. +*/ + +class QWT_EXPORT QwtNullPaintDevice: public QPaintDevice +{ +public: + /*! + \brief Render mode + + \sa setMode(), mode() + */ + enum Mode + { + /*! + All vector graphic primitives are painted by + the corresponding draw methods + */ + NormalMode, + + /*! + Vector graphic primitives ( beside polygons ) are mapped to a QPainterPath + and are painted by drawPath. In PathMode mode + only a few draw methods are called: + + - drawPath() + - drawPixmap() + - drawImage() + - drawPolygon() + */ + PolygonPathMode, + + /*! + Vector graphic primitives are mapped to a QPainterPath + and are painted by drawPath. In PathMode mode + only a few draw methods are called: + + - drawPath() + - drawPixmap() + - drawImage() + */ + PathMode + }; + + QwtNullPaintDevice(); + virtual ~QwtNullPaintDevice(); + + void setMode( Mode ); + Mode mode() const; + + virtual QPaintEngine *paintEngine() const; + + virtual int metric( PaintDeviceMetric metric ) const; + + virtual void drawRects(const QRect *, int ); + virtual void drawRects(const QRectF *, int ); + + virtual void drawLines(const QLine *, int ); + virtual void drawLines(const QLineF *, int ); + + virtual void drawEllipse(const QRectF &); + virtual void drawEllipse(const QRect &); + + virtual void drawPath(const QPainterPath &); + + virtual void drawPoints(const QPointF *, int ); + virtual void drawPoints(const QPoint *, int ); + + virtual void drawPolygon( + const QPointF *, int , QPaintEngine::PolygonDrawMode ); + + virtual void drawPolygon( + const QPoint *, int , QPaintEngine::PolygonDrawMode ); + + virtual void drawPixmap(const QRectF &, + const QPixmap &, const QRectF &); + + virtual void drawTextItem(const QPointF &, const QTextItem &); + + virtual void drawTiledPixmap(const QRectF &, + const QPixmap &, const QPointF &s); + + virtual void drawImage(const QRectF &, + const QImage &, const QRectF &, Qt::ImageConversionFlags ); + + virtual void updateState( const QPaintEngineState &state ); + +protected: + //! \return Size needed to implement metric() + virtual QSize sizeMetrics() const = 0; + +private: + class PaintEngine; + PaintEngine *d_engine; + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/libs/qwt/qwt_painter.cpp b/libs/qwt/qwt_painter.cpp index dbca763b0..0bbf258c5 100644 --- a/libs/qwt/qwt_painter.cpp +++ b/libs/qwt/qwt_painter.cpp @@ -7,685 +7,1260 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ -// vim: expandtab - +#include "qwt_painter.h" +#include "qwt_math.h" +#include "qwt_clipper.h" +#include "qwt_color_map.h" +#include "qwt_scale_map.h" #include #include +#include #include #include #include #include #include #include -#if QT_VERSION < 0x040000 -#include -#else #include #include #include #include -#endif +#include +#include -#include "qwt_clipper.h" -#include "qwt_math.h" -#include "qwt_color_map.h" -#include "qwt_scale_map.h" -#include "qwt_painter.h" +#if QT_VERSION >= 0x050000 +#include +#endif -QwtMetricsMap QwtPainter::d_metricsMap; +#if QT_VERSION < 0x050000 -#if defined(Q_WS_X11) -bool QwtPainter::d_deviceClipping = true; -#else -bool QwtPainter::d_deviceClipping = false; +#ifdef Q_WS_X11 +#include #endif -#if QT_VERSION < 0x040000 -bool QwtPainter::d_SVGMode = false; #endif -static inline bool needDeviceClipping( - const QPainter *painter, bool deviceClipping) -{ - return deviceClipping && - (painter->device()->devType() == QInternal::Widget || - painter->device()->devType() == QInternal::Pixmap ); -} - -/*! - \brief En/Disable device clipping. +bool QwtPainter::d_polylineSplitting = true; +bool QwtPainter::d_roundingAlignment = true; - On X11 the default for device clipping is enabled, - otherwise it is disabled. - \sa QwtPainter::deviceClipping() -*/ -void QwtPainter::setDeviceClipping(bool enable) +static inline bool qwtIsClippingNeeded( + const QPainter *painter, QRectF &clipRect ) { - d_deviceClipping = enable; -} + bool doClipping = false; + const QPaintEngine *pe = painter->paintEngine(); + if ( pe && pe->type() == QPaintEngine::SVG ) + { + // The SVG paint engine ignores any clipping, -/*! - Returns whether device clipping is enabled. On X11 the default - is enabled, otherwise it is disabled. - \sa QwtPainter::setDeviceClipping() -*/ + if ( painter->hasClipping() ) + { + doClipping = true; + clipRect = painter->clipRegion().boundingRect(); + } + } -bool QwtPainter::deviceClipping() -{ - return d_deviceClipping; + return doClipping; } -/*! - Returns rect for device clipping - \sa QwtPainter::setDeviceClipping() -*/ -const QRect &QwtPainter::deviceClipRect() +template +static inline void qwtDrawPolyline( QPainter *painter, + const T *points, int pointCount, bool polylineSplitting ) { - static QRect clip; + bool doSplit = false; + if ( polylineSplitting ) + { + const QPaintEngine *pe = painter->paintEngine(); + if ( pe && pe->type() == QPaintEngine::Raster ) + { + /* + The raster paint engine seems to use some algo with O(n*n). + ( Qt 4.3 is better than Qt 4.2, but remains unacceptable) + To work around this problem, we have to split the polygon into + smaller pieces. + */ + doSplit = true; + } + } - if ( !clip.isValid() ) { - clip.setCoords(QWT_COORD_MIN, QWT_COORD_MIN, - QWT_COORD_MAX, QWT_COORD_MAX); + if ( doSplit ) + { + const int splitSize = 20; + for ( int i = 0; i < pointCount; i += splitSize ) + { + const int n = qMin( splitSize + 1, pointCount - i ); + painter->drawPolyline( points + i, n ); + } } - return clip; + else + painter->drawPolyline( points, pointCount ); } -//! Clip a point array -QwtPolygon QwtPainter::clip(const QwtPolygon &pa) +static inline void qwtUnscaleFont( QPainter *painter ) { - return QwtClipper::clipPolygon(deviceClipRect(), pa); -} - -#if QT_VERSION < 0x040000 - -/*! - \brief En/Disable SVG mode. + if ( painter->font().pixelSize() >= 0 ) + return; - When saving a QPicture to a SVG some texts are misaligned. - In SVGMode QwtPainter tries to fix them. + static QSize screenResolution; + if ( !screenResolution.isValid() ) + { + QDesktopWidget *desktop = QApplication::desktop(); + if ( desktop ) + { + screenResolution.setWidth( desktop->logicalDpiX() ); + screenResolution.setHeight( desktop->logicalDpiY() ); + } + } - \sa QwtPainter::isSVGMode() - \note A QPicture that is created in SVG mode and saved to the - native format, will be misaligned. Also it is not possible to - reload and play a SVG document, that was created in SVG mode. -*/ -void QwtPainter::setSVGMode(bool on) -{ - d_SVGMode = on; -} + const QPaintDevice *pd = painter->device(); + if ( pd->logicalDpiX() != screenResolution.width() || + pd->logicalDpiY() != screenResolution.height() ) + { + QFont pixelFont( painter->font(), QApplication::desktop() ); + pixelFont.setPixelSize( QFontInfo( pixelFont ).pixelSize() ); -bool QwtPainter::isSVGMode() -{ - return d_SVGMode; + painter->setFont( pixelFont ); + } } -#endif // QT_VERSION < 0x040000 - /*! - Scale all QwtPainter drawing operations using the ratio - QwtPaintMetrics(from).logicalDpiX() / QwtPaintMetrics(to).logicalDpiX() - and QwtPaintMetrics(from).logicalDpiY() / QwtPaintMetrics(to).logicalDpiY() + Check is the application is running with the X11 graphics system + that has some special capabilities that can be used for incremental + painting to a widget. - \sa QwtPainter::resetScaleMetrics(), QwtPainter::scaleMetricsX, - QwtPainter::scaleMetricsY() + \return True, when the graphics system is X11 */ -void QwtPainter::setMetricsMap(const QPaintDevice *layout, - const QPaintDevice *device) +bool QwtPainter::isX11GraphicsSystem() { - d_metricsMap.setMetrics(layout, device); + static int onX11 = -1; + if ( onX11 < 0 ) + { + QPixmap pm( 1, 1 ); + QPainter painter( &pm ); + + onX11 = ( painter.paintEngine()->type() == QPaintEngine::X11 ) ? 1 : 0; + } + + return onX11 == 1; } /*! - Change the metrics map - \sa QwtPainter::resetMetricsMap, QwtPainter::metricsMap + Check if the painter is using a paint engine, that aligns + coordinates to integers. Today these are all paint engines + beside QPaintEngine::Pdf and QPaintEngine::SVG. + + If we have an integer based paint engine it is also + checked if the painter has a transformation matrix, + that rotates or scales. + + \param painter Painter + \return true, when the painter is aligning + + \sa setRoundingAlignment() */ -void QwtPainter::setMetricsMap(const QwtMetricsMap &map) +bool QwtPainter::isAligning( QPainter *painter ) { - d_metricsMap = map; + if ( painter && painter->isActive() ) + { + switch ( painter->paintEngine()->type() ) + { + case QPaintEngine::Pdf: + case QPaintEngine::SVG: + return false; + + default:; + } + + const QTransform tr = painter->transform(); + if ( tr.isRotating() || tr.isScaling() ) + { + // we might have to check translations too + return false; + } + } + + return true; } /*! - Reset the metrics map to the ratio 1:1 - \sa QwtPainter::setMetricsMap, QwtPainter::resetMetricsMap + Enable whether coordinates should be rounded, before they are painted + to a paint engine that floors to integer values. For other paint engines + this ( PDF, SVG ), this flag has no effect. + QwtPainter stores this flag only, the rounding itself is done in + the painting code ( f.e the plot items ). + + The default setting is true. + + \sa roundingAlignment(), isAligning() */ -void QwtPainter::resetMetricsMap() +void QwtPainter::setRoundingAlignment( bool enable ) { - d_metricsMap = QwtMetricsMap(); + d_roundingAlignment = enable; } /*! - \return Metrics map + \brief En/Disable line splitting for the raster paint engine + + In some Qt versions the raster paint engine paints polylines of many points + much faster when they are split in smaller chunks: f.e all supported Qt versions + >= Qt 5.0 when drawing an antialiased polyline with a pen width >=2. + + The default setting is true. + + \sa polylineSplitting() */ -const QwtMetricsMap &QwtPainter::metricsMap() +void QwtPainter::setPolylineSplitting( bool enable ) { - return d_metricsMap; + d_polylineSplitting = enable; } -/*! - Wrapper for QPainter::setClipRect() -*/ -void QwtPainter::setClipRect(QPainter *painter, const QRect &rect) +//! Wrapper for QPainter::drawPath() +void QwtPainter::drawPath( QPainter *painter, const QPainterPath &path ) { - painter->setClipRect(d_metricsMap.layoutToDevice(rect, painter)); + painter->drawPath( path ); } -/*! - Wrapper for QPainter::drawRect() -*/ -void QwtPainter::drawRect(QPainter *painter, int x, int y, int w, int h) +//! Wrapper for QPainter::drawRect() +void QwtPainter::drawRect( QPainter *painter, double x, double y, double w, double h ) { - drawRect(painter, QRect(x, y, w, h)); + drawRect( painter, QRectF( x, y, w, h ) ); } -/*! - Wrapper for QPainter::drawRect() -*/ -void QwtPainter::drawRect(QPainter *painter, const QRect &rect) +//! Wrapper for QPainter::drawRect() +void QwtPainter::drawRect( QPainter *painter, const QRectF &rect ) { - const QRect r = d_metricsMap.layoutToDevice(rect, painter); + const QRectF r = rect; - QRect clipRect; + QRectF clipRect; + const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); - const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping); if ( deviceClipping ) - clipRect = deviceClipRect(); - - if ( clipRect.isValid() ) { - if ( !clipRect.intersects(r) ) + { + if ( !clipRect.intersects( r ) ) return; - if ( !clipRect.contains(r) ) { - fillRect(painter, r & clipRect, painter->brush()); - - int pw = painter->pen().width(); - pw = pw % 2 + pw / 2; - - QwtPolygon pa(5); - pa.setPoint(0, r.left(), r.top()); - pa.setPoint(1, r.right() - pw, r.top()); - pa.setPoint(2, r.right() - pw, r.bottom() - pw); - pa.setPoint(3, r.left(), r.bottom() - pw); - pa.setPoint(4, r.left(), r.top()); + if ( !clipRect.contains( r ) ) + { + fillRect( painter, r & clipRect, painter->brush() ); painter->save(); - painter->setBrush(Qt::NoBrush); - drawPolyline(painter, pa); + painter->setBrush( Qt::NoBrush ); + drawPolyline( painter, QPolygonF( r ) ); painter->restore(); return; } } - painter->drawRect(r); + painter->drawRect( r ); } -/*! - Wrapper for QPainter::fillRect() -*/ -void QwtPainter::fillRect(QPainter *painter, - const QRect &rect, const QBrush &brush) +//! Wrapper for QPainter::fillRect() +void QwtPainter::fillRect( QPainter *painter, + const QRectF &rect, const QBrush &brush ) { if ( !rect.isValid() ) return; - const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping); - - QRect clipRect; -#if QT_VERSION >= 0x040000 + QRectF clipRect; + const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); /* - Performance of Qt4 is horrible for non trivial brushs. Without - clipping expect minutes or hours for repainting large rects + Performance of Qt4 is horrible for a non trivial brush. Without + clipping expect minutes or hours for repainting large rectangles (might result from zooming) */ - clipRect = painter->window(); + if ( deviceClipping ) + clipRect &= painter->window(); + else + clipRect = painter->window(); + if ( painter->hasClipping() ) clipRect &= painter->clipRegion().boundingRect(); - if ( deviceClipping ) - clipRect &= deviceClipRect(); -#else - if ( deviceClipping ) - clipRect = deviceClipRect(); -#endif - QRect r = d_metricsMap.layoutToDevice(rect, painter); - if ( clipRect.isValid() ) - r = r.intersect(clipRect); + QRectF r = rect; + if ( deviceClipping ) + r = r.intersected( clipRect ); if ( r.isValid() ) - painter->fillRect(r, brush); + painter->fillRect( r, brush ); } -/*! - Wrapper for QPainter::drawPie() -*/ -void QwtPainter::drawPie(QPainter *painter, const QRect &rect, - int a, int alen) +//! Wrapper for QPainter::drawPie() +void QwtPainter::drawPie( QPainter *painter, const QRectF &rect, + int a, int alen ) { - QRect r = d_metricsMap.layoutToDevice(rect, painter); - - const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping); - if ( deviceClipping && !deviceClipRect().contains(rect) ) + QRectF clipRect; + const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); + if ( deviceClipping && !clipRect.contains( rect ) ) return; - painter->drawPie(r, a, alen); + painter->drawPie( rect, a, alen ); } -/*! - Wrapper for QPainter::drawEllipse() -*/ -void QwtPainter::drawEllipse(QPainter *painter, const QRect &rect) +//! Wrapper for QPainter::drawEllipse() +void QwtPainter::drawEllipse( QPainter *painter, const QRectF &rect ) { - QRect r = d_metricsMap.layoutToDevice(rect, painter); - - const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping); + QRectF clipRect; + const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); - if ( deviceClipping && !deviceClipRect().contains(rect) ) + if ( deviceClipping && !clipRect.contains( rect ) ) return; -#if QT_VERSION >= 0x040000 - if ( painter->pen().style() != Qt::NoPen && - painter->pen().color().isValid() ) { - // Qt4 adds the pen to the rect, Qt3 not. - int pw = painter->pen().width(); - if ( pw == 0 ) - pw = 1; - - r.setWidth(r.width() - pw); - r.setHeight(r.height() - pw); - } -#endif - - painter->drawEllipse(r); + painter->drawEllipse( rect ); } -/*! - Wrapper for QPainter::drawText() -*/ -void QwtPainter::drawText(QPainter *painter, int x, int y, - const QString &text) +//! Wrapper for QPainter::drawText() +void QwtPainter::drawText( QPainter *painter, double x, double y, + const QString &text ) { - drawText(painter, QPoint(x, y), text); + drawText( painter, QPointF( x, y ), text ); } -/*! - Wrapper for QPainter::drawText() -*/ -void QwtPainter::drawText(QPainter *painter, const QPoint &pos, - const QString &text) +//! Wrapper for QPainter::drawText() +void QwtPainter::drawText( QPainter *painter, const QPointF &pos, + const QString &text ) { - const QPoint p = d_metricsMap.layoutToDevice(pos, painter); + QRectF clipRect; + const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); - const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping); - - if ( deviceClipping && !deviceClipRect().contains(p) ) + if ( deviceClipping && !clipRect.contains( pos ) ) return; - painter->drawText(p, text); + + painter->save(); + qwtUnscaleFont( painter ); + painter->drawText( pos, text ); + painter->restore(); } -/*! - Wrapper for QPainter::drawText() -*/ -void QwtPainter::drawText(QPainter *painter, int x, int y, int w, int h, - int flags, const QString &text) +//! Wrapper for QPainter::drawText() +void QwtPainter::drawText( QPainter *painter, + double x, double y, double w, double h, + int flags, const QString &text ) { - drawText(painter, QRect(x, y, w, h), flags, text); + drawText( painter, QRectF( x, y, w, h ), flags, text ); } -/*! - Wrapper for QPainter::drawText() -*/ -void QwtPainter::drawText(QPainter *painter, const QRect &rect, - int flags, const QString &text) -{ - QRect textRect = d_metricsMap.layoutToDevice(rect, painter); -#if QT_VERSION < 0x040000 - if ( d_SVGMode && - ( flags == 0 || flags & Qt::AlignVCenter ) - && painter->device()->devType() == QInternal::Picture ) { - /* - Qt3 misalignes texts, when saving a text - to a SVG image. - */ - textRect.setY(textRect.y() - painter->fontMetrics().height() / 4); - } -#endif - painter->drawText(textRect, flags, text); +//! Wrapper for QPainter::drawText() +void QwtPainter::drawText( QPainter *painter, const QRectF &rect, + int flags, const QString &text ) +{ + painter->save(); + qwtUnscaleFont( painter ); + painter->drawText( rect, flags, text ); + painter->restore(); } #ifndef QT_NO_RICHTEXT /*! - Wrapper for QSimpleRichText::draw() -*/ -#if QT_VERSION < 0x040000 + Draw a text document into a rectangle -void QwtPainter::drawSimpleRichText(QPainter *painter, const QRect &rect, - int flags, QSimpleRichText &text) + \param painter Painter + \param rect Traget rectangle + \param flags Alignments/Text flags, see QPainter::drawText() + \param text Text document +*/ +void QwtPainter::drawSimpleRichText( QPainter *painter, const QRectF &rect, + int flags, const QTextDocument &text ) { - QColorGroup cg; - cg.setColor(QColorGroup::Text, painter->pen().color()); + QTextDocument *txt = text.clone(); - const QRect scaledRect = d_metricsMap.layoutToDevice(rect, painter); - - text.setWidth(painter, scaledRect.width()); + painter->save(); - // QSimpleRichText is Qt::AlignTop by default + painter->setFont( txt->defaultFont() ); + qwtUnscaleFont( painter ); - int y = scaledRect.y(); - if (flags & Qt::AlignBottom) - y += (scaledRect.height() - text.height()); - else if (flags & Qt::AlignVCenter) - y += (scaledRect.height() - text.height())/2; + txt->setDefaultFont( painter->font() ); + txt->setPageSize( QSizeF( rect.width(), QWIDGETSIZE_MAX ) ); - text.draw(painter, scaledRect.x(), y, scaledRect, cg); -} -#else -void QwtPainter::drawSimpleRichText(QPainter *painter, const QRect &rect, - int flags, QTextDocument &text) -{ - const QRect scaledRect = d_metricsMap.layoutToDevice(rect, painter); - text.setPageSize(QSize(scaledRect.width(), QWIDGETSIZE_MAX)); + QAbstractTextDocumentLayout* layout = txt->documentLayout(); - QAbstractTextDocumentLayout* layout = text.documentLayout(); - - const int height = qRound(layout->documentSize().height()); - int y = scaledRect.y(); - if (flags & Qt::AlignBottom) - y += (scaledRect.height() - height); - else if (flags & Qt::AlignVCenter) - y += (scaledRect.height() - height)/2; + const double height = layout->documentSize().height(); + double y = rect.y(); + if ( flags & Qt::AlignBottom ) + y += ( rect.height() - height ); + else if ( flags & Qt::AlignVCenter ) + y += ( rect.height() - height ) / 2; QAbstractTextDocumentLayout::PaintContext context; - context.palette.setColor(QPalette::Text, painter->pen().color()); - - painter->save(); + context.palette.setColor( QPalette::Text, painter->pen().color() ); - painter->translate(scaledRect.x(), y); - layout->draw(painter, context); + painter->translate( rect.x(), y ); + layout->draw( painter, context ); painter->restore(); + delete txt; } -#endif #endif // !QT_NO_RICHTEXT -/*! - Wrapper for QPainter::drawLine() -*/ -void QwtPainter::drawLine(QPainter *painter, int x1, int y1, int x2, int y2) +//! Wrapper for QPainter::drawLine() +void QwtPainter::drawLine( QPainter *painter, + const QPointF &p1, const QPointF &p2 ) { - const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping); + QRectF clipRect; + const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); if ( deviceClipping && - !(deviceClipRect().contains(x1, y1) && deviceClipRect().contains(x2, y2)) ) { - QwtPolygon pa(2); - pa.setPoint(0, x1, y1); - pa.setPoint(1, x2, y2); - drawPolyline(painter, pa); + !( clipRect.contains( p1 ) && clipRect.contains( p2 ) ) ) + { + QPolygonF polygon; + polygon += p1; + polygon += p2; + drawPolyline( painter, polygon ); return; } - if ( d_metricsMap.isIdentity() ) { -#if QT_VERSION >= 0x030200 && QT_VERSION < 0x040000 - if ( !painter->device()->isExtDev() ) -#endif - { - painter->drawLine(x1, y1, x2, y2); - return; - } - } + painter->drawLine( p1, p2 ); +} - const QPoint p1 = d_metricsMap.layoutToDevice(QPoint(x1, y1)); - const QPoint p2 = d_metricsMap.layoutToDevice(QPoint(x2, y2)); +//! Wrapper for QPainter::drawPolygon() +void QwtPainter::drawPolygon( QPainter *painter, const QPolygonF &polygon ) +{ + QRectF clipRect; + const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); -#if QT_VERSION >= 0x030200 && QT_VERSION < 0x040000 - if ( painter->device()->isExtDev() ) { - // Strange: the postscript driver of QPrinter adds an offset - // of 0.5 to the start/endpoint when using drawLine, but not - // for lines painted with drawLineSegments. + QPolygonF cpa = polygon; + if ( deviceClipping ) + cpa = QwtClipper::clipPolygonF( clipRect, polygon ); - QwtPolygon pa(2); - pa.setPoint(0, p1); - pa.setPoint(1, p2); - painter->drawLineSegments(pa); - } else - painter->drawLine(p1, p2); -#else - painter->drawLine(p1, p2); -#endif + painter->drawPolygon( cpa ); } -/*! - Wrapper for QPainter::drawPolygon() -*/ -void QwtPainter::drawPolygon(QPainter *painter, const QwtPolygon &pa) +//! Wrapper for QPainter::drawPolyline() +void QwtPainter::drawPolyline( QPainter *painter, const QPolygonF &polygon ) { - const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping); + QRectF clipRect; + const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); - QwtPolygon cpa = d_metricsMap.layoutToDevice(pa); - if ( deviceClipping ) { -#ifdef __GNUC__ -#endif - cpa = clip(cpa); - } - painter->drawPolygon(cpa); + QPolygonF cpa = polygon; + if ( deviceClipping ) + cpa = QwtClipper::clipPolygonF( clipRect, cpa ); + + qwtDrawPolyline( painter, + cpa.constData(), cpa.size(), d_polylineSplitting ); } -/*! - Wrapper for QPainter::drawPolyline() -*/ -void QwtPainter::drawPolyline(QPainter *painter, const QwtPolygon &pa) +//! Wrapper for QPainter::drawPolyline() +void QwtPainter::drawPolyline( QPainter *painter, + const QPointF *points, int pointCount ) { - const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping); + QRectF clipRect; + const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); - QwtPolygon cpa = d_metricsMap.layoutToDevice(pa); if ( deviceClipping ) - cpa = clip(cpa); + { + QPolygonF polygon( pointCount ); + ::memcpy( polygon.data(), points, pointCount * sizeof( QPointF ) ); -#if QT_VERSION >= 0x040000 && QT_VERSION < 0x040400 - bool doSplit = false; - if ( painter->paintEngine()->type() == QPaintEngine::Raster && - painter->pen().width() >= 2 ) { - /* - The raster paint engine seems to use some algo with O(n*n). - ( Qt 4.3 is better than Qt 4.2, but remains unacceptable) - To work around this problem, we have to split the polygon into - smaller pieces. - */ - doSplit = true; + polygon = QwtClipper::clipPolygonF( clipRect, polygon ); + qwtDrawPolyline( painter, + polygon.constData(), polygon.size(), d_polylineSplitting ); } + else + { + qwtDrawPolyline( painter, points, pointCount, d_polylineSplitting ); + } +} - if ( doSplit ) { - const int numPoints = cpa.size(); - const QPoint *points = cpa.data(); +//! Wrapper for QPainter::drawPolygon() +void QwtPainter::drawPolygon( QPainter *painter, const QPolygon &polygon ) +{ + QRectF clipRect; + const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); - const int splitSize = 20; - for ( int i = 0; i < numPoints; i += splitSize ) { - const int n = qwtMin(splitSize + 1, cpa.size() - i); - painter->drawPolyline(points + i, n); - } - } else -#endif - painter->drawPolyline(cpa); + QPolygon cpa = polygon; + if ( deviceClipping ) + cpa = QwtClipper::clipPolygon( clipRect, polygon ); + + painter->drawPolygon( cpa ); } -/*! - Wrapper for QPainter::drawPoint() -*/ +//! Wrapper for QPainter::drawPolyline() +void QwtPainter::drawPolyline( QPainter *painter, const QPolygon &polygon ) +{ + QRectF clipRect; + const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); + + QPolygon cpa = polygon; + if ( deviceClipping ) + cpa = QwtClipper::clipPolygon( clipRect, cpa ); + + qwtDrawPolyline( painter, + cpa.constData(), cpa.size(), d_polylineSplitting ); +} -void QwtPainter::drawPoint(QPainter *painter, int x, int y) +//! Wrapper for QPainter::drawPolyline() +void QwtPainter::drawPolyline( QPainter *painter, + const QPoint *points, int pointCount ) { - const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping); + QRectF clipRect; + const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); - const QPoint pos = d_metricsMap.layoutToDevice(QPoint(x, y)); + if ( deviceClipping ) + { + QPolygon polygon( pointCount ); + ::memcpy( polygon.data(), points, pointCount * sizeof( QPoint ) ); + + polygon = QwtClipper::clipPolygon( clipRect, polygon ); + qwtDrawPolyline( painter, + polygon.constData(), polygon.size(), d_polylineSplitting ); + } + else + qwtDrawPolyline( painter, points, pointCount, d_polylineSplitting ); +} + +//! Wrapper for QPainter::drawPoint() +void QwtPainter::drawPoint( QPainter *painter, const QPointF &pos ) +{ + QRectF clipRect; + const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); - if ( deviceClipping && !deviceClipRect().contains(pos) ) + if ( deviceClipping && !clipRect.contains( pos ) ) return; - painter->drawPoint(pos); + painter->drawPoint( pos ); } -void QwtPainter::drawColoredArc(QPainter *painter, const QRect &rect, - int peak, int arc, int interval, const QColor &c1, const QColor &c2) +//! Wrapper for QPainter::drawPoint() +void QwtPainter::drawPoint( QPainter *painter, const QPoint &pos ) { - int h1, s1, v1; - int h2, s2, v2; + QRectF clipRect; + const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); -#if QT_VERSION < 0x040000 - c1.hsv(&h1, &s1, &v1); - c2.hsv(&h2, &s2, &v2); -#else - c1.getHsv(&h1, &s1, &v1); - c2.getHsv(&h2, &s2, &v2); -#endif + if ( deviceClipping ) + { + const int minX = qCeil( clipRect.left() ); + const int maxX = qFloor( clipRect.right() ); + const int minY = qCeil( clipRect.top() ); + const int maxY = qFloor( clipRect.bottom() ); + + if ( pos.x() < minX || pos.x() > maxX + || pos.y() < minY || pos.y() > maxY ) + { + return; + } + } - arc /= 2; - for ( int angle = -arc; angle < arc; angle += interval) { - double ratio; - if ( angle >= 0 ) - ratio = 1.0 - angle / double(arc); - else - ratio = 1.0 + angle / double(arc); + painter->drawPoint( pos ); +} + +//! Wrapper for QPainter::drawPoints() +void QwtPainter::drawPoints( QPainter *painter, + const QPoint *points, int pointCount ) +{ + QRectF clipRect; + const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); + + if ( deviceClipping ) + { + const int minX = qCeil( clipRect.left() ); + const int maxX = qFloor( clipRect.right() ); + const int minY = qCeil( clipRect.top() ); + const int maxY = qFloor( clipRect.bottom() ); + const QRect r( minX, minY, maxX - minX, maxY - minY ); - QColor c; - c.setHsv( h1 + qRound(ratio * (h2 - h1)), - s1 + qRound(ratio * (s2 - s1)), - v1 + qRound(ratio * (v2 - v1)) ); + QPolygon clippedPolygon( pointCount ); + QPoint *clippedData = clippedPolygon.data(); - painter->setPen(QPen(c, painter->pen().width())); - painter->drawArc(rect, (peak + angle) * 16, interval * 16); + int numClippedPoints = 0; + for ( int i = 0; i < pointCount; i++ ) + { + if ( r.contains( points[i] ) ) + clippedData[ numClippedPoints++ ] = points[i]; + } + painter->drawPoints( clippedData, numClippedPoints ); + } + else + { + painter->drawPoints( points, pointCount ); } } -void QwtPainter::drawFocusRect(QPainter *painter, QWidget *widget) +//! Wrapper for QPainter::drawPoints() +void QwtPainter::drawPoints( QPainter *painter, + const QPointF *points, int pointCount ) { - drawFocusRect(painter, widget, widget->rect()); + QRectF clipRect; + const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); + + if ( deviceClipping ) + { + QPolygonF clippedPolygon( pointCount ); + QPointF *clippedData = clippedPolygon.data(); + + int numClippedPoints = 0; + for ( int i = 0; i < pointCount; i++ ) + { + if ( clipRect.contains( points[i] ) ) + clippedData[ numClippedPoints++ ] = points[i]; + } + painter->drawPoints( clippedData, numClippedPoints ); + } + else + { + painter->drawPoints( points, pointCount ); + } } -void QwtPainter::drawFocusRect(QPainter *painter, QWidget *widget, - const QRect &rect) +//! Wrapper for QPainter::drawImage() +void QwtPainter::drawImage( QPainter *painter, + const QRectF &rect, const QImage &image ) +{ + const QRect alignedRect = rect.toAlignedRect(); + + if ( alignedRect != rect ) + { + const QRectF clipRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 ); + + painter->save(); + painter->setClipRect( clipRect, Qt::IntersectClip ); + painter->drawImage( alignedRect, image ); + painter->restore(); + } + else + { + painter->drawImage( alignedRect, image ); + } +} + +//! Wrapper for QPainter::drawPixmap() +void QwtPainter::drawPixmap( QPainter *painter, + const QRectF &rect, const QPixmap &pixmap ) +{ + const QRect alignedRect = rect.toAlignedRect(); + + if ( alignedRect != rect ) + { + const QRectF clipRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 ); + + painter->save(); + painter->setClipRect( clipRect, Qt::IntersectClip ); + painter->drawPixmap( alignedRect, pixmap ); + painter->restore(); + } + else + { + painter->drawPixmap( alignedRect, pixmap ); + } +} + +//! Draw a focus rectangle on a widget using its style. +void QwtPainter::drawFocusRect( QPainter *painter, const QWidget *widget ) +{ + drawFocusRect( painter, widget, widget->rect() ); +} + +//! Draw a focus rectangle on a widget using its style. +void QwtPainter::drawFocusRect( QPainter *painter, const QWidget *widget, + const QRect &rect ) { -#if QT_VERSION < 0x040000 - widget->style().drawPrimitive(QStyle::PE_FocusRect, painter, - rect, widget->colorGroup()); -#else QStyleOptionFocusRect opt; - opt.init(widget); + opt.init( widget ); opt.rect = rect; opt.state |= QStyle::State_HasFocus; - widget->style()->drawPrimitive(QStyle::PE_FrameFocusRect, - &opt, painter, widget); + widget->style()->drawPrimitive( QStyle::PE_FrameFocusRect, + &opt, painter, widget ); +} + +/*! + Draw a round frame + + \param painter Painter + \param rect Frame rectangle + \param palette QPalette::WindowText is used for plain borders + QPalette::Dark and QPalette::Light for raised + or sunken borders + \param lineWidth Line width + \param frameStyle bitwise OR´ed value of QFrame::Shape and QFrame::Shadow +*/ +void QwtPainter::drawRoundFrame( QPainter *painter, + const QRectF &rect, const QPalette &palette, + int lineWidth, int frameStyle ) +{ + enum Style + { + Plain, + Sunken, + Raised + }; + + Style style = Plain; + if ( (frameStyle & QFrame::Sunken) == QFrame::Sunken ) + style = Sunken; + else if ( (frameStyle & QFrame::Raised) == QFrame::Raised ) + style = Raised; + + const double lw2 = 0.5 * lineWidth; + QRectF r = rect.adjusted( lw2, lw2, -lw2, -lw2 ); + + QBrush brush; + + if ( style != Plain ) + { + QColor c1 = palette.color( QPalette::Light ); + QColor c2 = palette.color( QPalette::Dark ); + + if ( style == Sunken ) + qSwap( c1, c2 ); + + QLinearGradient gradient( r.topLeft(), r.bottomRight() ); + gradient.setColorAt( 0.0, c1 ); +#if 0 + gradient.setColorAt( 0.3, c1 ); + gradient.setColorAt( 0.7, c2 ); #endif + gradient.setColorAt( 1.0, c2 ); + + brush = QBrush( gradient ); + } + else // Plain + { + brush = palette.brush( QPalette::WindowText ); + } + + painter->save(); + + painter->setPen( QPen( brush, lineWidth ) ); + painter->setBrush( Qt::NoBrush ); + + painter->drawEllipse( r ); + painter->restore(); } -//! Draw a round frame -#if QT_VERSION < 0x040000 -void QwtPainter::drawRoundFrame(QPainter *painter, const QRect &rect, - int width, const QColorGroup &cg, bool sunken) -#else -void QwtPainter::drawRoundFrame(QPainter *painter, const QRect &rect, - int width, const QPalette &palette, bool sunken) -#endif +/*! + Draw a rectangular frame + + \param painter Painter + \param rect Frame rectangle + \param palette Palette + \param foregroundRole Foreground role used for QFrame::Plain + \param frameWidth Frame width + \param midLineWidth Used for QFrame::Box + \param frameStyle bitwise OR´ed value of QFrame::Shape and QFrame::Shadow +*/ +void QwtPainter::drawFrame( QPainter *painter, const QRectF &rect, + const QPalette &palette, QPalette::ColorRole foregroundRole, + int frameWidth, int midLineWidth, int frameStyle ) { + if ( frameWidth <= 0 || rect.isEmpty() ) + return; -#if QT_VERSION < 0x040000 - QColor c0 = cg.mid(); - QColor c1, c2; - if ( sunken ) { - c1 = cg.dark(); - c2 = cg.light(); - } else { - c1 = cg.light(); - c2 = cg.dark(); - } -#else - QColor c0 = palette.color(QPalette::Mid); - QColor c1, c2; - if ( sunken ) { - c1 = palette.color(QPalette::Dark); - c2 = palette.color(QPalette::Light); - } else { - c1 = palette.color(QPalette::Light); - c2 = palette.color(QPalette::Dark); + const int shadow = frameStyle & QFrame::Shadow_Mask; + + painter->save(); + + if ( shadow == QFrame::Plain ) + { + const QRectF outerRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 ); + const QRectF innerRect = outerRect.adjusted( + frameWidth, frameWidth, -frameWidth, -frameWidth ); + + QPainterPath path; + path.addRect( outerRect ); + path.addRect( innerRect ); + + painter->setPen( Qt::NoPen ); + painter->setBrush( palette.color( foregroundRole ) ); + + painter->drawPath( path ); } + else + { + const int shape = frameStyle & QFrame::Shape_Mask; + + if ( shape == QFrame::Box ) + { + const QRectF outerRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 ); + const QRectF midRect1 = outerRect.adjusted( + frameWidth, frameWidth, -frameWidth, -frameWidth ); + const QRectF midRect2 = midRect1.adjusted( + midLineWidth, midLineWidth, -midLineWidth, -midLineWidth ); + + const QRectF innerRect = midRect2.adjusted( + frameWidth, frameWidth, -frameWidth, -frameWidth ); + + QPainterPath path1; + path1.moveTo( outerRect.bottomLeft() ); + path1.lineTo( outerRect.topLeft() ); + path1.lineTo( outerRect.topRight() ); + path1.lineTo( midRect1.topRight() ); + path1.lineTo( midRect1.topLeft() ); + path1.lineTo( midRect1.bottomLeft() ); + + QPainterPath path2; + path2.moveTo( outerRect.bottomLeft() ); + path2.lineTo( outerRect.bottomRight() ); + path2.lineTo( outerRect.topRight() ); + path2.lineTo( midRect1.topRight() ); + path2.lineTo( midRect1.bottomRight() ); + path2.lineTo( midRect1.bottomLeft() ); + + QPainterPath path3; + path3.moveTo( midRect2.bottomLeft() ); + path3.lineTo( midRect2.topLeft() ); + path3.lineTo( midRect2.topRight() ); + path3.lineTo( innerRect.topRight() ); + path3.lineTo( innerRect.topLeft() ); + path3.lineTo( innerRect.bottomLeft() ); + + QPainterPath path4; + path4.moveTo( midRect2.bottomLeft() ); + path4.lineTo( midRect2.bottomRight() ); + path4.lineTo( midRect2.topRight() ); + path4.lineTo( innerRect.topRight() ); + path4.lineTo( innerRect.bottomRight() ); + path4.lineTo( innerRect.bottomLeft() ); + + QPainterPath path5; + path5.addRect( midRect1 ); + path5.addRect( midRect2 ); + + painter->setPen( Qt::NoPen ); + + QBrush brush1 = palette.dark().color(); + QBrush brush2 = palette.light().color(); + + if ( shadow == QFrame::Raised ) + qSwap( brush1, brush2 ); + + painter->setBrush( brush1 ); + painter->drawPath( path1 ); + painter->drawPath( path4 ); + + painter->setBrush( brush2 ); + painter->drawPath( path2 ); + painter->drawPath( path3 ); + + painter->setBrush( palette.mid() ); + painter->drawPath( path5 ); + } +#if 0 + // qDrawWinPanel doesn't result in something nice + // on a scalable document like PDF. Better draw a + // Panel. + + else if ( shape == QFrame::WinPanel ) + { + painter->setRenderHint( QPainter::NonCosmeticDefaultPen, true ); + qDrawWinPanel ( painter, rect.toRect(), palette, + frameStyle & QFrame::Sunken ); + } + else if ( shape == QFrame::StyledPanel ) + { + } #endif + else + { + const QRectF outerRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 ); + const QRectF innerRect = outerRect.adjusted( + frameWidth - 1.0, frameWidth - 1.0, + -( frameWidth - 1.0 ), -( frameWidth - 1.0 ) ); + + QPainterPath path1; + path1.moveTo( outerRect.bottomLeft() ); + path1.lineTo( outerRect.topLeft() ); + path1.lineTo( outerRect.topRight() ); + path1.lineTo( innerRect.topRight() ); + path1.lineTo( innerRect.topLeft() ); + path1.lineTo( innerRect.bottomLeft() ); + + + QPainterPath path2; + path2.moveTo( outerRect.bottomLeft() ); + path2.lineTo( outerRect.bottomRight() ); + path2.lineTo( outerRect.topRight() ); + path2.lineTo( innerRect.topRight() ); + path2.lineTo( innerRect.bottomRight() ); + path2.lineTo( innerRect.bottomLeft() ); + + painter->setPen( Qt::NoPen ); - painter->setPen(QPen(c0, width)); - painter->drawArc(rect, 0, 360 * 16); // full + QBrush brush1 = palette.dark().color(); + QBrush brush2 = palette.light().color(); - const int peak = 150; - const int interval = 2; + if ( shadow == QFrame::Raised ) + qSwap( brush1, brush2 ); + + painter->setBrush( brush1 ); + painter->drawPath( path1 ); + + painter->setBrush( brush2 ); + painter->drawPath( path2 ); + } + + } - if ( c0 != c1 ) - drawColoredArc(painter, rect, peak, 160, interval, c0, c1); - if ( c0 != c2 ) - drawColoredArc(painter, rect, peak + 180, 120, interval, c0, c2); + painter->restore(); } -void QwtPainter::drawColorBar(QPainter *painter, - const QwtColorMap &colorMap, const QwtDoubleInterval &interval, - const QwtScaleMap &scaleMap, Qt::Orientation orientation, - const QRect &rect) +/*! + Draw a rectangular frame with rounded borders + + \param painter Painter + \param rect Frame rectangle + \param xRadius x-radius of the ellipses defining the corners + \param yRadius y-radius of the ellipses defining the corners + \param palette QPalette::WindowText is used for plain borders + QPalette::Dark and QPalette::Light for raised + or sunken borders + \param lineWidth Line width + \param frameStyle bitwise OR´ed value of QFrame::Shape and QFrame::Shadow +*/ + +void QwtPainter::drawRoundedFrame( QPainter *painter, + const QRectF &rect, double xRadius, double yRadius, + const QPalette &palette, int lineWidth, int frameStyle ) +{ + painter->save(); + painter->setRenderHint( QPainter::Antialiasing, true ); + painter->setBrush( Qt::NoBrush ); + + double lw2 = lineWidth * 0.5; + QRectF r = rect.adjusted( lw2, lw2, -lw2, -lw2 ); + + QPainterPath path; + path.addRoundedRect( r, xRadius, yRadius ); + + enum Style + { + Plain, + Sunken, + Raised + }; + + Style style = Plain; + if ( (frameStyle & QFrame::Sunken) == QFrame::Sunken ) + style = Sunken; + else if ( (frameStyle & QFrame::Raised) == QFrame::Raised ) + style = Raised; + + if ( style != Plain && path.elementCount() == 17 ) + { + // move + 4 * ( cubicTo + lineTo ) + QPainterPath pathList[8]; + + for ( int i = 0; i < 4; i++ ) + { + const int j = i * 4 + 1; + + pathList[ 2 * i ].moveTo( + path.elementAt(j - 1).x, path.elementAt( j - 1 ).y + ); + + pathList[ 2 * i ].cubicTo( + path.elementAt(j + 0).x, path.elementAt(j + 0).y, + path.elementAt(j + 1).x, path.elementAt(j + 1).y, + path.elementAt(j + 2).x, path.elementAt(j + 2).y ); + + pathList[ 2 * i + 1 ].moveTo( + path.elementAt(j + 2).x, path.elementAt(j + 2).y + ); + pathList[ 2 * i + 1 ].lineTo( + path.elementAt(j + 3).x, path.elementAt(j + 3).y + ); + } + + QColor c1( palette.color( QPalette::Dark ) ); + QColor c2( palette.color( QPalette::Light ) ); + + if ( style == Raised ) + qSwap( c1, c2 ); + + for ( int i = 0; i < 4; i++ ) + { + QRectF r = pathList[2 * i].controlPointRect(); + + QPen arcPen; + arcPen.setCapStyle( Qt::FlatCap ); + arcPen.setWidth( lineWidth ); + + QPen linePen; + linePen.setCapStyle( Qt::FlatCap ); + linePen.setWidth( lineWidth ); + + switch( i ) + { + case 0: + { + arcPen.setColor( c1 ); + linePen.setColor( c1 ); + break; + } + case 1: + { + QLinearGradient gradient; + gradient.setStart( r.topLeft() ); + gradient.setFinalStop( r.bottomRight() ); + gradient.setColorAt( 0.0, c1 ); + gradient.setColorAt( 1.0, c2 ); + + arcPen.setBrush( gradient ); + linePen.setColor( c2 ); + break; + } + case 2: + { + arcPen.setColor( c2 ); + linePen.setColor( c2 ); + break; + } + case 3: + { + QLinearGradient gradient; + + gradient.setStart( r.bottomRight() ); + gradient.setFinalStop( r.topLeft() ); + gradient.setColorAt( 0.0, c2 ); + gradient.setColorAt( 1.0, c1 ); + + arcPen.setBrush( gradient ); + linePen.setColor( c1 ); + break; + } + } + + + painter->setPen( arcPen ); + painter->drawPath( pathList[ 2 * i] ); + + painter->setPen( linePen ); + painter->drawPath( pathList[ 2 * i + 1] ); + } + } + else + { + QPen pen( palette.color( QPalette::WindowText ), lineWidth ); + painter->setPen( pen ); + painter->drawPath( path ); + } + + painter->restore(); +} + +/*! + Draw a color bar into a rectangle + + \param painter Painter + \param colorMap Color map + \param interval Value range + \param scaleMap Scale map + \param orientation Orientation + \param rect Traget rectangle +*/ +void QwtPainter::drawColorBar( QPainter *painter, + const QwtColorMap &colorMap, const QwtInterval &interval, + const QwtScaleMap &scaleMap, Qt::Orientation orientation, + const QRectF &rect ) { -#if QT_VERSION < 0x040000 - QValueVector colorTable; -#else QVector colorTable; -#endif if ( colorMap.format() == QwtColorMap::Indexed ) - colorTable = colorMap.colorTable(interval); + colorTable = colorMap.colorTable( interval ); QColor c; - const QRect devRect = d_metricsMap.layoutToDevice(rect); + const QRect devRect = rect.toAlignedRect(); /* We paint to a pixmap first to have something scalable for printing ( f.e. in a Pdf document ) */ - QPixmap pixmap(devRect.size()); - QPainter pmPainter(&pixmap); - pmPainter.translate(-devRect.x(), -devRect.y()); + QPixmap pixmap( devRect.size() ); + QPainter pmPainter( &pixmap ); + pmPainter.translate( -devRect.x(), -devRect.y() ); - if ( orientation == Qt::Horizontal ) { + if ( orientation == Qt::Horizontal ) + { QwtScaleMap sMap = scaleMap; - sMap.setPaintInterval(devRect.left(), devRect.right()); + sMap.setPaintInterval( rect.left(), rect.right() ); - for ( int x = devRect.left(); x <= devRect.right(); x++ ) { - const double value = sMap.invTransform(x); + for ( int x = devRect.left(); x <= devRect.right(); x++ ) + { + const double value = sMap.invTransform( x ); if ( colorMap.format() == QwtColorMap::RGB ) - c.setRgb(colorMap.rgb(interval, value)); + c.setRgba( colorMap.rgb( interval, value ) ); else - c = colorTable[colorMap.colorIndex(interval, value)]; + c = colorTable[colorMap.colorIndex( interval, value )]; - pmPainter.setPen(c); - pmPainter.drawLine(x, devRect.top(), x, devRect.bottom()); + pmPainter.setPen( c ); + pmPainter.drawLine( x, devRect.top(), x, devRect.bottom() ); } - } else { // Vertical + } + else // Vertical + { QwtScaleMap sMap = scaleMap; - sMap.setPaintInterval(devRect.bottom(), devRect.top()); + sMap.setPaintInterval( rect.bottom(), rect.top() ); - for ( int y = devRect.top(); y <= devRect.bottom(); y++ ) { - const double value = sMap.invTransform(y); + for ( int y = devRect.top(); y <= devRect.bottom(); y++ ) + { + const double value = sMap.invTransform( y ); if ( colorMap.format() == QwtColorMap::RGB ) - c.setRgb(colorMap.rgb(interval, value)); + c.setRgb( colorMap.rgb( interval, value ) ); else - c = colorTable[colorMap.colorIndex(interval, value)]; + c = colorTable[colorMap.colorIndex( interval, value )]; - pmPainter.setPen(c); - pmPainter.drawLine(devRect.left(), y, devRect.right(), y); + pmPainter.setPen( c ); + pmPainter.drawLine( devRect.left(), y, devRect.right(), y ); } } pmPainter.end(); - painter->drawPixmap(devRect, pixmap); + + drawPixmap( painter, rect, pixmap ); +} + +static inline void qwtFillRect( const QWidget *widget, QPainter *painter, + const QRect &rect, const QBrush &brush) +{ + if ( brush.style() == Qt::TexturePattern ) + { + painter->save(); + + painter->setClipRect( rect ); + painter->drawTiledPixmap(rect, brush.texture(), rect.topLeft()); + + painter->restore(); + } + else if ( brush.gradient() ) + { + painter->save(); + + painter->setClipRect( rect ); + painter->fillRect(0, 0, widget->width(), + widget->height(), brush); + + painter->restore(); + } + else + { + painter->fillRect(rect, brush); + } } + +/*! + Fill a pixmap with the content of a widget + + In Qt >= 5.0 QPixmap::fill() is a nop, in Qt 4.x it is buggy + for backgrounds with gradients. Thus fillPixmap() offers + an alternative implementation. + + \param widget Widget + \param pixmap Pixmap to be filled + \param offset Offset + + \sa QPixmap::fill() + */ +void QwtPainter::fillPixmap( const QWidget *widget, + QPixmap &pixmap, const QPoint &offset ) +{ + const QRect rect( offset, pixmap.size() ); + + QPainter painter( &pixmap ); + painter.translate( -offset ); + + const QBrush autoFillBrush = + widget->palette().brush( widget->backgroundRole() ); + + if ( !( widget->autoFillBackground() && autoFillBrush.isOpaque() ) ) + { + const QBrush bg = widget->palette().brush( QPalette::Window ); + qwtFillRect( widget, &painter, rect, bg); + } + + if ( widget->autoFillBackground() ) + qwtFillRect( widget, &painter, rect, autoFillBrush); + + if ( widget->testAttribute(Qt::WA_StyledBackground) ) + { + painter.setClipRegion( rect ); + + QStyleOption opt; + opt.initFrom( widget ); + widget->style()->drawPrimitive( QStyle::PE_Widget, + &opt, &painter, widget ); + } +} + +/*! + Fill rect with the background of a widget + + \param painter Painter + \param rect Rectangle to be filled + \param widget Widget + + \sa QStyle::PE_Widget, QWidget::backgroundRole() + */ +void QwtPainter::drawBackgound( QPainter *painter, + const QRectF &rect, const QWidget *widget ) +{ + if ( widget->testAttribute( Qt::WA_StyledBackground ) ) + { + QStyleOption opt; + opt.initFrom( widget ); + opt.rect = rect.toAlignedRect(); + + widget->style()->drawPrimitive( + QStyle::PE_Widget, &opt, painter, widget); + } + else + { + const QBrush brush = + widget->palette().brush( widget->backgroundRole() ); + + painter->fillRect( rect, brush ); + } +} + +/*! + \return A pixmap that can be used as backing store + + \param widget Widget, for which the backinstore is intended + \param size Size of the pixmap + */ +QPixmap QwtPainter::backingStore( QWidget *widget, const QSize &size ) +{ + QPixmap pm; + +#define QWT_HIGH_DPI 1 + +#if QT_VERSION >= 0x050000 && QWT_HIGH_DPI + qreal pixelRatio = 1.0; + + if ( widget && widget->windowHandle() ) + { + pixelRatio = widget->windowHandle()->devicePixelRatio(); + } + else + { + if ( qApp ) + pixelRatio = qApp->devicePixelRatio(); + } + + pm = QPixmap( size * pixelRatio ); + pm.setDevicePixelRatio( pixelRatio ); +#else + Q_UNUSED( widget ) + pm = QPixmap( size ); +#endif + +#if QT_VERSION < 0x050000 +#ifdef Q_WS_X11 + if ( widget && isX11GraphicsSystem() ) + { + if ( pm.x11Info().screen() != widget->x11Info().screen() ) + pm.x11SetScreen( widget->x11Info().screen() ); + } +#endif +#endif + + return pm; +} + diff --git a/libs/qwt/qwt_painter.h b/libs/qwt/qwt_painter.h index 5d5abe375..9609b6935 100644 --- a/libs/qwt/qwt_painter.h +++ b/libs/qwt/qwt_painter.h @@ -10,140 +10,179 @@ #ifndef QWT_PAINTER_H #define QWT_PAINTER_H +#include "qwt_global.h" + #include #include -#include "qwt_global.h" -#include "qwt_layout_metrics.h" -#include "qwt_polygon.h" +#include +#include +#include class QPainter; class QBrush; class QColor; class QWidget; +class QPolygonF; +class QRectF; +class QImage; +class QPixmap; class QwtScaleMap; class QwtColorMap; -class QwtDoubleInterval; +class QwtInterval; -#if QT_VERSION < 0x040000 -class QColorGroup; -class QSimpleRichText; -#else -class QPalette; class QTextDocument; -#endif - -#if defined(Q_WS_X11) -// Warning: QCOORD_MIN, QCOORD_MAX are wrong on X11. -#define QWT_COORD_MAX 16384 -#define QWT_COORD_MIN (-QWT_COORD_MAX - 1) -#else -#define QWT_COORD_MAX 2147483647 -#define QWT_COORD_MIN -QWT_COORD_MAX - 1 -#endif +class QPainterPath; /*! \brief A collection of QPainter workarounds - - 1) Clipping to coordinate system limits (Qt3 only) - - On X11 pixel coordinates are stored in shorts. Qt - produces overruns when mapping QCOORDS to shorts. - - 2) Scaling to device metrics - - QPainter scales fonts, line and fill patterns to the metrics - of the paint device. Other values like the geometries of rects, points - remain device independend. To enable a device independent widget - implementation, QwtPainter adds scaling of these geometries. - (Unfortunately QPainter::scale scales both types of paintings, - so the objects of the first type would be scaled twice). */ - class QWT_EXPORT QwtPainter { public: - static void setMetricsMap(const QPaintDevice *layout, - const QPaintDevice *device); - static void setMetricsMap(const QwtMetricsMap &); - static void resetMetricsMap(); - static const QwtMetricsMap &metricsMap(); - - static void setDeviceClipping(bool); - static bool deviceClipping(); - - static void setClipRect(QPainter *, const QRect &); - - static void drawText(QPainter *, int x, int y, - const QString &); - static void drawText(QPainter *, const QPoint &, - const QString &); - static void drawText(QPainter *, int x, int y, int w, int h, - int flags, const QString &); - static void drawText(QPainter *, const QRect &, - int flags, const QString &); + static void setPolylineSplitting( bool ); + static bool polylineSplitting(); + + static void setRoundingAlignment( bool ); + static bool roundingAlignment(); + static bool roundingAlignment(QPainter *); + + static void drawText( QPainter *, double x, double y, const QString & ); + static void drawText( QPainter *, const QPointF &, const QString & ); + static void drawText( QPainter *, double x, double y, double w, double h, + int flags, const QString & ); + static void drawText( QPainter *, const QRectF &, + int flags, const QString & ); #ifndef QT_NO_RICHTEXT -#if QT_VERSION < 0x040000 - static void drawSimpleRichText(QPainter *, const QRect &, - int flags, QSimpleRichText &); -#else - static void drawSimpleRichText(QPainter *, const QRect &, - int flags, QTextDocument &); -#endif + static void drawSimpleRichText( QPainter *, const QRectF &, + int flags, const QTextDocument & ); #endif - static void drawRect(QPainter *, int x, int y, int w, int h); - static void drawRect(QPainter *, const QRect &rect); - static void fillRect(QPainter *, const QRect &, const QBrush &); - - static void drawEllipse(QPainter *, const QRect &); - static void drawPie(QPainter *, const QRect & r, int a, int alen); - - static void drawLine(QPainter *, int x1, int y1, int x2, int y2); - static void drawLine(QPainter *, const QPoint &p1, const QPoint &p2); - static void drawPolygon(QPainter *, const QwtPolygon &pa); - static void drawPolyline(QPainter *, const QwtPolygon &pa); - static void drawPoint(QPainter *, int x, int y); - -#if QT_VERSION < 0x040000 - static void drawRoundFrame(QPainter *, const QRect &, - int width, const QColorGroup &cg, bool sunken); -#else - static void drawRoundFrame(QPainter *, const QRect &, - int width, const QPalette &, bool sunken); -#endif - static void drawFocusRect(QPainter *, QWidget *); - static void drawFocusRect(QPainter *, QWidget *, const QRect &); + static void drawRect( QPainter *, double x, double y, double w, double h ); + static void drawRect( QPainter *, const QRectF &rect ); + static void fillRect( QPainter *, const QRectF &, const QBrush & ); - static QwtPolygon clip(const QwtPolygon &); + static void drawEllipse( QPainter *, const QRectF & ); + static void drawPie( QPainter *, const QRectF & r, int a, int alen ); - static void drawColorBar(QPainter *painter, - const QwtColorMap &, const QwtDoubleInterval &, - const QwtScaleMap &, Qt::Orientation, const QRect &); + static void drawLine( QPainter *, double x1, double y1, double x2, double y2 ); + static void drawLine( QPainter *, const QPointF &p1, const QPointF &p2 ); + static void drawLine( QPainter *, const QLineF & ); -#if QT_VERSION < 0x040000 - static void setSVGMode(bool on); - static bool isSVGMode(); -#endif + static void drawPolygon( QPainter *, const QPolygonF & ); + static void drawPolyline( QPainter *, const QPolygonF & ); + static void drawPolyline( QPainter *, const QPointF *, int pointCount ); -private: - static void drawColoredArc(QPainter *, const QRect &, - int peak, int arc, int intervall, const QColor &c1, const QColor &c2); + static void drawPolygon( QPainter *, const QPolygon & ); + static void drawPolyline( QPainter *, const QPolygon & ); + static void drawPolyline( QPainter *, const QPoint *, int pointCount ); - static const QRect &deviceClipRect(); + static void drawPoint( QPainter *, const QPoint & ); + static void drawPoints( QPainter *, const QPolygon & ); + static void drawPoints( QPainter *, const QPoint *, int pointCount ); - static bool d_deviceClipping; - static QwtMetricsMap d_metricsMap; -#if QT_VERSION < 0x040000 - static bool d_SVGMode; -#endif + static void drawPoint( QPainter *, double x, double y ); + static void drawPoint( QPainter *, const QPointF & ); + static void drawPoints( QPainter *, const QPolygonF & ); + static void drawPoints( QPainter *, const QPointF *, int pointCount ); + + static void drawPath( QPainter *, const QPainterPath & ); + static void drawImage( QPainter *, const QRectF &, const QImage & ); + static void drawPixmap( QPainter *, const QRectF &, const QPixmap & ); + + static void drawRoundFrame( QPainter *, + const QRectF &, const QPalette &, int lineWidth, int frameStyle ); + + static void drawRoundedFrame( QPainter *, + const QRectF &, double xRadius, double yRadius, + const QPalette &, int lineWidth, int frameStyle ); + + static void drawFrame( QPainter *, const QRectF &rect, + const QPalette &palette, QPalette::ColorRole foregroundRole, + int lineWidth, int midLineWidth, int frameStyle ); + + static void drawFocusRect( QPainter *, const QWidget * ); + static void drawFocusRect( QPainter *, const QWidget *, const QRect & ); + + static void drawColorBar( QPainter *painter, + const QwtColorMap &, const QwtInterval &, + const QwtScaleMap &, Qt::Orientation, const QRectF & ); + + static bool isAligning( QPainter *painter ); + static bool isX11GraphicsSystem(); + + static void fillPixmap( const QWidget *, + QPixmap &, const QPoint &offset = QPoint() ); + + static void drawBackgound( QPainter *painter, + const QRectF &rect, const QWidget *widget ); + + static QPixmap backingStore( QWidget *, const QSize & ); + +private: + static bool d_polylineSplitting; + static bool d_roundingAlignment; }; +//! Wrapper for QPainter::drawPoint() +inline void QwtPainter::drawPoint( QPainter *painter, double x, double y ) +{ + QwtPainter::drawPoint( painter, QPointF( x, y ) ); +} + +//! Wrapper for QPainter::drawPoints() +inline void QwtPainter::drawPoints( QPainter *painter, const QPolygon &polygon ) +{ + drawPoints( painter, polygon.data(), polygon.size() ); +} + +//! Wrapper for QPainter::drawPoints() +inline void QwtPainter::drawPoints( QPainter *painter, const QPolygonF &polygon ) +{ + drawPoints( painter, polygon.data(), polygon.size() ); +} + //! Wrapper for QPainter::drawLine() -inline void QwtPainter::drawLine(QPainter *painter, - const QPoint &p1, const QPoint &p2) +inline void QwtPainter::drawLine( QPainter *painter, + double x1, double y1, double x2, double y2 ) { - drawLine(painter, p1.x(), p1.y(), p2.x(), p2.y()); + QwtPainter::drawLine( painter, QPointF( x1, y1 ), QPointF( x2, y2 ) ); } +//! Wrapper for QPainter::drawLine() +inline void QwtPainter::drawLine( QPainter *painter, const QLineF &line ) +{ + QwtPainter::drawLine( painter, line.p1(), line.p2() ); +} + +/*! + \return True, when line splitting for the raster paint engine is enabled. + \sa setPolylineSplitting() +*/ +inline bool QwtPainter::polylineSplitting() +{ + return d_polylineSplitting; +} + +/*! + Check whether coordinates should be rounded, before they are painted + to a paint engine that rounds to integer values. For other paint engines + ( PDF, SVG ), this flag has no effect. + + \return True, when rounding is enabled + \sa setRoundingAlignment(), isAligning() +*/ +inline bool QwtPainter::roundingAlignment() +{ + return d_roundingAlignment; +} + +/*! + \return roundingAlignment() && isAligning(painter); + \param painter Painter +*/ +inline bool QwtPainter::roundingAlignment(QPainter *painter) +{ + return d_roundingAlignment && isAligning(painter); +} #endif diff --git a/libs/qwt/qwt_painter_command.cpp b/libs/qwt/qwt_painter_command.cpp new file mode 100644 index 000000000..f6affae37 --- /dev/null +++ b/libs/qwt/qwt_painter_command.cpp @@ -0,0 +1,237 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_painter_command.h" + +//! Construct an invalid command +QwtPainterCommand::QwtPainterCommand(): + d_type( Invalid ) +{ +} + +//! Copy constructor +QwtPainterCommand::QwtPainterCommand( const QPainterPath &path ): + d_type( Path ) +{ + d_path = new QPainterPath( path ); +} + +/*! + Constructor for Pixmap paint operation + + \param rect Target rectangle + \param pixmap Pixmap + \param subRect Rectangle inside the pixmap + + \sa QPainter::drawPixmap() + */ +QwtPainterCommand::QwtPainterCommand( const QRectF &rect, + const QPixmap &pixmap, const QRectF& subRect ): + d_type( Pixmap ) +{ + d_pixmapData = new PixmapData(); + d_pixmapData->rect = rect; + d_pixmapData->pixmap = pixmap; + d_pixmapData->subRect = subRect; +} + +/*! + Constructor for Image paint operation + + \param rect Target rectangle + \param image Image + \param subRect Rectangle inside the image + \param flags Conversion flags + + \sa QPainter::drawImage() + */ +QwtPainterCommand::QwtPainterCommand( const QRectF &rect, + const QImage &image, const QRectF& subRect, + Qt::ImageConversionFlags flags ): + d_type( Image ) +{ + d_imageData = new ImageData(); + d_imageData->rect = rect; + d_imageData->image = image; + d_imageData->subRect = subRect; + d_imageData->flags = flags; +} + +/*! + Constructor for State paint operation + \param state Paint engine state + */ +QwtPainterCommand::QwtPainterCommand( const QPaintEngineState &state ): + d_type( State ) +{ + d_stateData = new StateData(); + + d_stateData->flags = state.state(); + + if ( d_stateData->flags & QPaintEngine::DirtyPen ) + d_stateData->pen = state.pen(); + + if ( d_stateData->flags & QPaintEngine::DirtyBrush ) + d_stateData->brush = state.brush(); + + if ( d_stateData->flags & QPaintEngine::DirtyBrushOrigin ) + d_stateData->brushOrigin = state.brushOrigin(); + + if ( d_stateData->flags & QPaintEngine::DirtyFont ) + d_stateData->font = state.font(); + + if ( d_stateData->flags & QPaintEngine::DirtyBackground ) + { + d_stateData->backgroundMode = state.backgroundMode(); + d_stateData->backgroundBrush = state.backgroundBrush(); + } + + if ( d_stateData->flags & QPaintEngine::DirtyTransform ) + d_stateData->transform = state.transform(); + + if ( d_stateData->flags & QPaintEngine::DirtyClipEnabled ) + d_stateData->isClipEnabled = state.isClipEnabled(); + + if ( d_stateData->flags & QPaintEngine::DirtyClipRegion ) + { + d_stateData->clipRegion = state.clipRegion(); + d_stateData->clipOperation = state.clipOperation(); + } + + if ( d_stateData->flags & QPaintEngine::DirtyClipPath ) + { + d_stateData->clipPath = state.clipPath(); + d_stateData->clipOperation = state.clipOperation(); + } + + if ( d_stateData->flags & QPaintEngine::DirtyHints ) + d_stateData->renderHints = state.renderHints(); + + if ( d_stateData->flags & QPaintEngine::DirtyCompositionMode ) + d_stateData->compositionMode = state.compositionMode(); + + if ( d_stateData->flags & QPaintEngine::DirtyOpacity ) + d_stateData->opacity = state.opacity(); +} + +/*! + Copy constructor + \param other Command to be copied + + */ +QwtPainterCommand::QwtPainterCommand(const QwtPainterCommand &other) +{ + copy( other ); +} + +//! Destructor +QwtPainterCommand::~QwtPainterCommand() +{ + reset(); +} + +/*! + Assignment operator + + \param other Command to be copied + \return Modified command + */ +QwtPainterCommand &QwtPainterCommand::operator=(const QwtPainterCommand &other) +{ + reset(); + copy( other ); + + return *this; +} + +void QwtPainterCommand::copy( const QwtPainterCommand &other ) +{ + d_type = other.d_type; + + switch( other.d_type ) + { + case Path: + { + d_path = new QPainterPath( *other.d_path ); + break; + } + case Pixmap: + { + d_pixmapData = new PixmapData( *other.d_pixmapData ); + break; + } + case Image: + { + d_imageData = new ImageData( *other.d_imageData ); + break; + } + case State: + { + d_stateData = new StateData( *other.d_stateData ); + break; + } + default: + break; + } +} + +void QwtPainterCommand::reset() +{ + switch( d_type ) + { + case Path: + { + delete d_path; + break; + } + case Pixmap: + { + delete d_pixmapData; + break; + } + case Image: + { + delete d_imageData; + break; + } + case State: + { + delete d_stateData; + break; + } + default: + break; + } + + d_type = Invalid; +} + +//! \return Painter path to be painted +QPainterPath *QwtPainterCommand::path() +{ + return d_path; +} + +//! \return Attributes how to paint a QPixmap +QwtPainterCommand::PixmapData* QwtPainterCommand::pixmapData() +{ + return d_pixmapData; +} + +//! \return Attributes how to paint a QImage +QwtPainterCommand::ImageData* QwtPainterCommand::imageData() +{ + return d_imageData; +} + +//! \return Attributes of a state change +QwtPainterCommand::StateData* QwtPainterCommand::stateData() +{ + return d_stateData; +} diff --git a/libs/qwt/qwt_painter_command.h b/libs/qwt/qwt_painter_command.h new file mode 100644 index 000000000..2da597a7f --- /dev/null +++ b/libs/qwt/qwt_painter_command.h @@ -0,0 +1,173 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_PAINTER_COMMAND_H +#define QWT_PAINTER_COMMAND_H + +#include "qwt_global.h" +#include +#include +#include +#include + +class QPainterPath; + +/*! + QwtPainterCommand represents the attributes of a paint operation + how it is used between QPainter and QPaintDevice + + It is used by QwtGraphic to record and replay paint operations + + \sa QwtGraphic::commands() + */ + +class QWT_EXPORT QwtPainterCommand +{ +public: + //! Type of the paint command + enum Type + { + //! Invalid command + Invalid = -1, + + //! Draw a QPainterPath + Path, + + //! Draw a QPixmap + Pixmap, + + //! Draw a QImage + Image, + + //! QPainter state change + State + }; + + //! Attributes how to paint a QPixmap + struct PixmapData + { + QRectF rect; + QPixmap pixmap; + QRectF subRect; + }; + + //! Attributes how to paint a QImage + struct ImageData + { + QRectF rect; + QImage image; + QRectF subRect; + Qt::ImageConversionFlags flags; + }; + + //! Attributes of a state change + struct StateData + { + QPaintEngine::DirtyFlags flags; + + QPen pen; + QBrush brush; + QPointF brushOrigin; + QBrush backgroundBrush; + Qt::BGMode backgroundMode; + QFont font; + QMatrix matrix; + QTransform transform; + + Qt::ClipOperation clipOperation; + QRegion clipRegion; + QPainterPath clipPath; + bool isClipEnabled; + + QPainter::RenderHints renderHints; + QPainter::CompositionMode compositionMode; + qreal opacity; + }; + + QwtPainterCommand(); + QwtPainterCommand(const QwtPainterCommand &); + + QwtPainterCommand( const QPainterPath & ); + + QwtPainterCommand( const QRectF &rect, + const QPixmap &, const QRectF& subRect ); + + QwtPainterCommand( const QRectF &rect, + const QImage &, const QRectF& subRect, + Qt::ImageConversionFlags ); + + QwtPainterCommand( const QPaintEngineState & ); + + ~QwtPainterCommand(); + + QwtPainterCommand &operator=(const QwtPainterCommand & ); + + Type type() const; + + QPainterPath *path(); + const QPainterPath *path() const; + + PixmapData* pixmapData(); + const PixmapData* pixmapData() const; + + ImageData* imageData(); + const ImageData* imageData() const; + + StateData* stateData(); + const StateData* stateData() const; + +private: + void copy( const QwtPainterCommand & ); + void reset(); + + Type d_type; + + union + { + QPainterPath *d_path; + PixmapData *d_pixmapData; + ImageData *d_imageData; + StateData *d_stateData; + }; +}; + +//! \return Type of the command +inline QwtPainterCommand::Type QwtPainterCommand::type() const +{ + return d_type; +} + +//! \return Painter path to be painted +inline const QPainterPath *QwtPainterCommand::path() const +{ + return d_path; +} + +//! \return Attributes how to paint a QPixmap +inline const QwtPainterCommand::PixmapData* +QwtPainterCommand::pixmapData() const +{ + return d_pixmapData; +} + +//! \return Attributes how to paint a QImage +inline const QwtPainterCommand::ImageData * +QwtPainterCommand::imageData() const +{ + return d_imageData; +} + +//! \return Attributes of a state change +inline const QwtPainterCommand::StateData * +QwtPainterCommand::stateData() const +{ + return d_stateData; +} + +#endif diff --git a/libs/qwt/qwt_panner.cpp b/libs/qwt/qwt_panner.cpp index deae92918..18497a916 100644 --- a/libs/qwt/qwt_panner.cpp +++ b/libs/qwt/qwt_panner.cpp @@ -7,49 +7,26 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ -// vim: expandtab - +#include "qwt_panner.h" +#include "qwt_picker.h" +#include "qwt_painter.h" #include #include #include -#include #include -#if QT_VERSION < 0x040000 -#include -#endif -#include "qwt_picker.h" -#include "qwt_array.h" -#include "qwt_panner.h" +#include -static QwtArray activePickers(QWidget *w) +static QVector qwtActivePickers( QWidget *w ) { - QwtArray pickers; + QVector pickers; -#if QT_VERSION >= 0x040000 QObjectList children = w->children(); - for ( int i = 0; i < children.size(); i++ ) { - QObject *obj = children[i]; - if ( obj->inherits("QwtPicker") ) { - QwtPicker *picker = (QwtPicker *)obj; - if ( picker->isEnabled() ) - pickers += picker; - } - } -#else - QObjectList *children = (QObjectList *)w->children(); - if ( children ) { - for ( QObjectListIterator it(*children); it.current(); ++it ) { - QObject *obj = (QObject *)it.current(); - if ( obj->inherits("QwtPicker") ) { - QwtPicker *picker = (QwtPicker *)obj; - if ( picker->isEnabled() ) { - pickers.resize(pickers.size() + 1); - pickers[int(pickers.size()) - 1] = picker; - } - } - } + for ( int i = 0; i < children.size(); i++ ) + { + QwtPicker *picker = qobject_cast( children[i] ); + if ( picker && picker->isEnabled() ) + pickers += picker; } -#endif return pickers; } @@ -58,51 +35,47 @@ class QwtPanner::PrivateData { public: PrivateData(): - button(Qt::LeftButton), - buttonState(Qt::NoButton), - abortKey(Qt::Key_Escape), - abortKeyState(Qt::NoButton), + button( Qt::LeftButton ), + buttonModifiers( Qt::NoModifier ), + abortKey( Qt::Key_Escape ), + abortKeyModifiers( Qt::NoModifier ), #ifndef QT_NO_CURSOR - cursor(NULL), - restoreCursor(NULL), - hasCursor(false), + cursor( NULL ), + restoreCursor( NULL ), + hasCursor( false ), #endif - isEnabled(false) { -#if QT_VERSION >= 0x040000 + isEnabled( false ) + { orientations = Qt::Vertical | Qt::Horizontal; -#else - orientations[Qt::Vertical] = true; - orientations[Qt::Horizontal] = true; -#endif } - ~PrivateData() { + ~PrivateData() + { #ifndef QT_NO_CURSOR delete cursor; delete restoreCursor; #endif } - int button; - int buttonState; + Qt::MouseButton button; + Qt::KeyboardModifiers buttonModifiers; + int abortKey; - int abortKeyState; + Qt::KeyboardModifiers abortKeyModifiers; QPoint initialPos; QPoint pos; QPixmap pixmap; + QBitmap contentsMask; + #ifndef QT_NO_CURSOR QCursor *cursor; QCursor *restoreCursor; bool hasCursor; #endif bool isEnabled; -#if QT_VERSION >= 0x040000 Qt::Orientations orientations; -#else - bool orientations[2]; -#endif }; /*! @@ -110,22 +83,17 @@ public: \param parent Parent widget to be panned */ -QwtPanner::QwtPanner(QWidget *parent): - QWidget(parent) +QwtPanner::QwtPanner( QWidget *parent ): + QWidget( parent ) { d_data = new PrivateData(); -#if QT_VERSION >= 0x040000 - setAttribute(Qt::WA_TransparentForMouseEvents); - setAttribute(Qt::WA_NoSystemBackground); - setFocusPolicy(Qt::NoFocus); -#else - setBackgroundMode(Qt::NoBackground); - setFocusPolicy(QWidget::NoFocus); -#endif + setAttribute( Qt::WA_TransparentForMouseEvents ); + setAttribute( Qt::WA_NoSystemBackground ); + setFocusPolicy( Qt::NoFocus ); hide(); - setEnabled(true); + setEnabled( true ); } //! Destructor @@ -135,37 +103,44 @@ QwtPanner::~QwtPanner() } /*! - Change the mouse button - The defaults are Qt::LeftButton and Qt::NoButton + Change the mouse button and modifiers used for panning + The defaults are Qt::LeftButton and Qt::NoModifier */ -void QwtPanner::setMouseButton(int button, int buttonState) +void QwtPanner::setMouseButton( Qt::MouseButton button, + Qt::KeyboardModifiers modifiers ) { d_data->button = button; - d_data->buttonState = buttonState; + d_data->buttonModifiers = modifiers; } -//! Get the mouse button -void QwtPanner::getMouseButton(int &button, int &buttonState) const +//! Get mouse button and modifiers used for panning +void QwtPanner::getMouseButton( Qt::MouseButton &button, + Qt::KeyboardModifiers &modifiers ) const { button = d_data->button; - buttonState = d_data->buttonState; + modifiers = d_data->buttonModifiers; } /*! Change the abort key - The defaults are Qt::Key_Escape and Qt::NoButton + The defaults are Qt::Key_Escape and Qt::NoModifiers + + \param key Key ( See Qt::Keycode ) + \param modifiers Keyboard modifiers */ -void QwtPanner::setAbortKey(int key, int state) +void QwtPanner::setAbortKey( int key, + Qt::KeyboardModifiers modifiers ) { d_data->abortKey = key; - d_data->abortKeyState = state; + d_data->abortKeyModifiers = modifiers; } -//! Get the abort key -void QwtPanner::getAbortKey(int &key, int &state) const +//! Get the abort key and modifiers +void QwtPanner::getAbortKey( int &key, + Qt::KeyboardModifiers &modifiers ) const { key = d_data->abortKey; - state = d_data->abortKeyState; + modifiers = d_data->abortKeyModifiers; } /*! @@ -177,9 +152,9 @@ void QwtPanner::getAbortKey(int &key, int &state) const \sa setCursor() */ #ifndef QT_NO_CURSOR -void QwtPanner::setCursor(const QCursor &cursor) +void QwtPanner::setCursor( const QCursor &cursor ) { - d_data->cursor = new QCursor(cursor); + d_data->cursor = new QCursor( cursor ); } #endif @@ -209,31 +184,35 @@ const QCursor QwtPanner::cursor() const \param on true or false \sa isEnabled(), eventFilter() */ -void QwtPanner::setEnabled(bool on) +void QwtPanner::setEnabled( bool on ) { - if ( d_data->isEnabled != on ) { + if ( d_data->isEnabled != on ) + { d_data->isEnabled = on; QWidget *w = parentWidget(); - if ( w ) { - if ( d_data->isEnabled ) { - w->installEventFilter(this); - } else { - w->removeEventFilter(this); + if ( w ) + { + if ( d_data->isEnabled ) + { + w->installEventFilter( this ); + } + else + { + w->removeEventFilter( this ); hide(); } } } } -#if QT_VERSION >= 0x040000 /*! Set the orientations, where panning is enabled The default value is in both directions: Qt::Horizontal | Qt::Vertical /param o Orientation */ -void QwtPanner::setOrientations(Qt::Orientations o) +void QwtPanner::setOrientations( Qt::Orientations o ) { d_data->orientations = o; } @@ -244,27 +223,13 @@ Qt::Orientations QwtPanner::orientations() const return d_data->orientations; } -#else -void QwtPanner::enableOrientation(Qt::Orientation o, bool enable) -{ - if ( o == Qt::Vertical || o == Qt::Horizontal ) - d_data->orientations[o] = enable; -} -#endif - /*! - Return true if a orientatio is enabled + \return True if an orientation is enabled \sa orientations(), setOrientations() */ -bool QwtPanner::isOrientationEnabled(Qt::Orientation o) const +bool QwtPanner::isOrientationEnabled( Qt::Orientation o ) const { -#if QT_VERSION >= 0x040000 return d_data->orientations & o; -#else - if ( o == Qt::Vertical || o == Qt::Horizontal ) - return d_data->orientations[o]; - return false; -#endif } /*! @@ -284,80 +249,121 @@ bool QwtPanner::isEnabled() const \param pe Paint event */ -void QwtPanner::paintEvent(QPaintEvent *pe) +void QwtPanner::paintEvent( QPaintEvent *pe ) { - QPixmap pm(size()); + int dx = d_data->pos.x() - d_data->initialPos.x(); + int dy = d_data->pos.y() - d_data->initialPos.y(); - QPainter painter(&pm); + QRect r( 0, 0, d_data->pixmap.width(), d_data->pixmap.height() ); + r.moveCenter( QPoint( r.center().x() + dx, r.center().y() + dy ) ); - const QColor bg = -#if QT_VERSION < 0x040000 - parentWidget()->palette().color( - QPalette::Normal, QColorGroup::Background); -#else - parentWidget()->palette().color( - QPalette::Normal, QPalette::Background); -#endif + QPixmap pm( size() ); + QwtPainter::fillPixmap( parentWidget(), pm ); - painter.setPen(Qt::NoPen); - painter.setBrush(QBrush(bg)); - painter.drawRect(0, 0, pm.width(), pm.height()); + QPainter painter( &pm ); - int dx = d_data->pos.x() - d_data->initialPos.x(); - int dy = d_data->pos.y() - d_data->initialPos.y(); - - QRect r(0, 0, d_data->pixmap.width(), d_data->pixmap.height()); - r.moveCenter(QPoint(r.center().x() + dx, r.center().y() + dy)); + if ( !d_data->contentsMask.isNull() ) + { + QPixmap masked = d_data->pixmap; + masked.setMask( d_data->contentsMask ); + painter.drawPixmap( r, masked ); + } + else + { + painter.drawPixmap( r, d_data->pixmap ); + } - painter.drawPixmap(r, d_data->pixmap); painter.end(); - painter.begin(this); - painter.setClipRegion(pe->region()); - painter.drawPixmap(0, 0, pm); + if ( !d_data->contentsMask.isNull() ) + pm.setMask( d_data->contentsMask ); + + painter.begin( this ); + painter.setClipRegion( pe->region() ); + painter.drawPixmap( 0, 0, pm ); +} + +/*! + \brief Calculate a mask for the contents of the panned widget + + Sometimes only parts of the contents of a widget should be + panned. F.e. for a widget with a styled background with rounded borders + only the area inside of the border should be panned. + + \return An empty bitmap, indicating no mask +*/ +QBitmap QwtPanner::contentsMask() const +{ + return QBitmap(); +} + +/*! + Grab the widget into a pixmap. + \return Grabbed pixmap +*/ +QPixmap QwtPanner::grab() const +{ +#if QT_VERSION >= 0x050000 + return parentWidget()->grab( parentWidget()->rect() ); +#else + return QPixmap::grabWidget( parentWidget() ); +#endif } /*! \brief Event filter - When isEnabled() the mouse events of the observed widget are filtered. + When isEnabled() is true mouse events of the + observed widget are filtered. + + \param object Object to be filtered + \param event Event + + \return Always false, beside for paint events for the + parent widget. \sa widgetMousePressEvent(), widgetMouseReleaseEvent(), widgetMouseMoveEvent() */ -bool QwtPanner::eventFilter(QObject *o, QEvent *e) +bool QwtPanner::eventFilter( QObject *object, QEvent *event ) { - if ( o == NULL || o != parentWidget() ) + if ( object == NULL || object != parentWidget() ) return false; - switch(e->type()) { - case QEvent::MouseButtonPress: { - widgetMousePressEvent((QMouseEvent *)e); - break; - } - case QEvent::MouseMove: { - widgetMouseMoveEvent((QMouseEvent *)e); - break; - } - case QEvent::MouseButtonRelease: { - widgetMouseReleaseEvent((QMouseEvent *)e); - break; - } - case QEvent::KeyPress: { - widgetKeyPressEvent((QKeyEvent *)e); - break; - } - case QEvent::KeyRelease: { - widgetKeyReleaseEvent((QKeyEvent *)e); - break; - } - case QEvent::Paint: { - if ( isVisible() ) - return true; - break; - } - default: - ; + switch ( event->type() ) + { + case QEvent::MouseButtonPress: + { + widgetMousePressEvent( static_cast( event ) ); + break; + } + case QEvent::MouseMove: + { + widgetMouseMoveEvent( static_cast( event ) ); + break; + } + case QEvent::MouseButtonRelease: + { + widgetMouseReleaseEvent( static_cast( event ) ); + break; + } + case QEvent::KeyPress: + { + widgetKeyPressEvent( static_cast( event ) ); + break; + } + case QEvent::KeyRelease: + { + widgetKeyReleaseEvent( static_cast( event ) ); + break; + } + case QEvent::Paint: + { + if ( isVisible() ) + return true; + break; + } + default:; } return false; @@ -366,53 +372,40 @@ bool QwtPanner::eventFilter(QObject *o, QEvent *e) /*! Handle a mouse press event for the observed widget. - \param me Mouse event + \param mouseEvent Mouse event \sa eventFilter(), widgetMouseReleaseEvent(), widgetMouseMoveEvent(), */ -void QwtPanner::widgetMousePressEvent(QMouseEvent *me) +void QwtPanner::widgetMousePressEvent( QMouseEvent *mouseEvent ) { - if ( me->button() != d_data->button ) + if ( ( mouseEvent->button() != d_data->button ) + || ( mouseEvent->modifiers() != d_data->buttonModifiers ) ) + { return; + } QWidget *w = parentWidget(); if ( w == NULL ) return; -#if QT_VERSION < 0x040000 - if ( (me->state() & Qt::KeyButtonMask) != - (d_data->buttonState & Qt::KeyButtonMask) ) -#else - if ( (me->modifiers() & Qt::KeyboardModifierMask) != - (int)(d_data->buttonState & Qt::KeyboardModifierMask) ) -#endif - { - return; - } - #ifndef QT_NO_CURSOR - showCursor(true); + showCursor( true ); #endif - d_data->initialPos = d_data->pos = me->pos(); + d_data->initialPos = d_data->pos = mouseEvent->pos(); - QRect cr = parentWidget()->rect(); - if ( parentWidget()->inherits("QFrame") ) { - const QFrame* frame = (QFrame*)parentWidget(); - cr = frame->contentsRect(); - } - setGeometry(cr); + setGeometry( parentWidget()->rect() ); // We don't want to grab the picker ! - QwtArray pickers = activePickers(parentWidget()); - for ( int i = 0; i < (int)pickers.size(); i++ ) - pickers[i]->setEnabled(false); + QVector pickers = qwtActivePickers( parentWidget() ); + for ( int i = 0; i < pickers.size(); i++ ) + pickers[i]->setEnabled( false ); - d_data->pixmap = QPixmap::grabWidget(parentWidget(), - cr.x(), cr.y(), cr.width(), cr.height()); + d_data->pixmap = grab(); + d_data->contentsMask = contentsMask(); - for ( int i = 0; i < (int)pickers.size(); i++ ) - pickers[i]->setEnabled(true); + for ( int i = 0; i < pickers.size(); i++ ) + pickers[i]->setEnabled( true ); show(); } @@ -420,56 +413,60 @@ void QwtPanner::widgetMousePressEvent(QMouseEvent *me) /*! Handle a mouse move event for the observed widget. - \param me Mouse event + \param mouseEvent Mouse event \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent() */ -void QwtPanner::widgetMouseMoveEvent(QMouseEvent *me) +void QwtPanner::widgetMouseMoveEvent( QMouseEvent *mouseEvent ) { if ( !isVisible() ) return; - QPoint pos = me->pos(); - if ( !isOrientationEnabled(Qt::Horizontal) ) - pos.setX(d_data->initialPos.x()); - if ( !isOrientationEnabled(Qt::Vertical) ) - pos.setY(d_data->initialPos.y()); + QPoint pos = mouseEvent->pos(); + if ( !isOrientationEnabled( Qt::Horizontal ) ) + pos.setX( d_data->initialPos.x() ); + if ( !isOrientationEnabled( Qt::Vertical ) ) + pos.setY( d_data->initialPos.y() ); - if ( pos != d_data->pos && rect().contains(pos) ) { + if ( pos != d_data->pos && rect().contains( pos ) ) + { d_data->pos = pos; update(); - emit moved(d_data->pos.x() - d_data->initialPos.x(), - d_data->pos.y() - d_data->initialPos.y()); + Q_EMIT moved( d_data->pos.x() - d_data->initialPos.x(), + d_data->pos.y() - d_data->initialPos.y() ); } } /*! Handle a mouse release event for the observed widget. - \param me Mouse event + \param mouseEvent Mouse event \sa eventFilter(), widgetMousePressEvent(), widgetMouseMoveEvent(), */ -void QwtPanner::widgetMouseReleaseEvent(QMouseEvent *me) +void QwtPanner::widgetMouseReleaseEvent( QMouseEvent *mouseEvent ) { - if ( isVisible() ) { + if ( isVisible() ) + { hide(); #ifndef QT_NO_CURSOR - showCursor(false); + showCursor( false ); #endif - QPoint pos = me->pos(); - if ( !isOrientationEnabled(Qt::Horizontal) ) - pos.setX(d_data->initialPos.x()); - if ( !isOrientationEnabled(Qt::Vertical) ) - pos.setY(d_data->initialPos.y()); + QPoint pos = mouseEvent->pos(); + if ( !isOrientationEnabled( Qt::Horizontal ) ) + pos.setX( d_data->initialPos.x() ); + if ( !isOrientationEnabled( Qt::Vertical ) ) + pos.setY( d_data->initialPos.y() ); d_data->pixmap = QPixmap(); + d_data->contentsMask = QBitmap(); d_data->pos = pos; - if ( d_data->pos != d_data->initialPos ) { - emit panned(d_data->pos.x() - d_data->initialPos.x(), - d_data->pos.y() - d_data->initialPos.y()); + if ( d_data->pos != d_data->initialPos ) + { + Q_EMIT panned( d_data->pos.x() - d_data->initialPos.x(), + d_data->pos.y() - d_data->initialPos.y() ); } } } @@ -477,42 +474,36 @@ void QwtPanner::widgetMouseReleaseEvent(QMouseEvent *me) /*! Handle a key press event for the observed widget. - \param ke Key event + \param keyEvent Key event \sa eventFilter(), widgetKeyReleaseEvent() */ -void QwtPanner::widgetKeyPressEvent(QKeyEvent *ke) +void QwtPanner::widgetKeyPressEvent( QKeyEvent *keyEvent ) { - if ( ke->key() == d_data->abortKey ) { - const bool matched = -#if QT_VERSION < 0x040000 - (ke->state() & Qt::KeyButtonMask) == - (d_data->abortKeyState & Qt::KeyButtonMask); -#else - (ke->modifiers() & Qt::KeyboardModifierMask) == - (int)(d_data->abortKeyState & Qt::KeyboardModifierMask); -#endif - if ( matched ) { - hide(); + if ( ( keyEvent->key() == d_data->abortKey ) + && ( keyEvent->modifiers() == d_data->abortKeyModifiers ) ) + { + hide(); + #ifndef QT_NO_CURSOR - showCursor(false); + showCursor( false ); #endif - d_data->pixmap = QPixmap(); - } + d_data->pixmap = QPixmap(); } } /*! Handle a key release event for the observed widget. - \param ke Key event + \param keyEvent Key event \sa eventFilter(), widgetKeyReleaseEvent() */ -void QwtPanner::widgetKeyReleaseEvent(QKeyEvent *) +void QwtPanner::widgetKeyReleaseEvent( QKeyEvent *keyEvent ) { + Q_UNUSED( keyEvent ); } #ifndef QT_NO_CURSOR -void QwtPanner::showCursor(bool on) +void QwtPanner::showCursor( bool on ) { if ( on == d_data->hasCursor ) return; @@ -523,23 +514,24 @@ void QwtPanner::showCursor(bool on) d_data->hasCursor = on; - if ( on ) { -#if QT_VERSION < 0x040000 - if ( w->testWState(WState_OwnCursor) ) -#else - if ( w->testAttribute(Qt::WA_SetCursor) ) -#endif + if ( on ) + { + if ( w->testAttribute( Qt::WA_SetCursor ) ) { delete d_data->restoreCursor; - d_data->restoreCursor = new QCursor(w->cursor()); + d_data->restoreCursor = new QCursor( w->cursor() ); } - w->setCursor(*d_data->cursor); - } else { - if ( d_data->restoreCursor ) { - w->setCursor(*d_data->restoreCursor); + w->setCursor( *d_data->cursor ); + } + else + { + if ( d_data->restoreCursor ) + { + w->setCursor( *d_data->restoreCursor ); delete d_data->restoreCursor; d_data->restoreCursor = NULL; - } else + } + else w->unsetCursor(); } } diff --git a/libs/qwt/qwt_panner.h b/libs/qwt/qwt_panner.h index d648f07da..a0c6873ab 100644 --- a/libs/qwt/qwt_panner.h +++ b/libs/qwt/qwt_panner.h @@ -10,9 +10,9 @@ #ifndef QWT_PANNER_H #define QWT_PANNER_H 1 -#include -#include #include "qwt_global.h" +#include +#include class QCursor; @@ -25,8 +25,8 @@ class QCursor; QwtPanner grabs the content of the widget into a pixmap and moves the pixmap around, without initiating any repaint events for the widget. - Areas, that are not part of content are not painted while panning - in in process. This makes panning fast enough for widgets, where + Areas, that are not part of content are not painted while panning. + This makes panning fast enough for widgets, where repaints are too slow for mouse movements. For widgets, where repaints are very fast it might be better to @@ -37,39 +37,38 @@ class QWT_EXPORT QwtPanner: public QWidget Q_OBJECT public: - QwtPanner(QWidget* parent); + QwtPanner( QWidget* parent ); virtual ~QwtPanner(); - void setEnabled(bool); + void setEnabled( bool ); bool isEnabled() const; - void setMouseButton(int button, int buttonState = Qt::NoButton); - void getMouseButton(int &button, int &buttonState) const; - void setAbortKey(int key, int state = Qt::NoButton); - void getAbortKey(int &key, int &state) const; + void setMouseButton( Qt::MouseButton, + Qt::KeyboardModifiers = Qt::NoModifier ); + void getMouseButton( Qt::MouseButton &button, + Qt::KeyboardModifiers & ) const; + + void setAbortKey( int key, Qt::KeyboardModifiers = Qt::NoModifier ); + void getAbortKey( int &key, Qt::KeyboardModifiers & ) const; - void setCursor(const QCursor &); + void setCursor( const QCursor & ); const QCursor cursor() const; -#if QT_VERSION >= 0x040000 - void setOrientations(Qt::Orientations); + void setOrientations( Qt::Orientations ); Qt::Orientations orientations() const; -#else - void enableOrientation(Qt::Orientation, bool enable); -#endif - bool isOrientationEnabled(Qt::Orientation) const; + bool isOrientationEnabled( Qt::Orientation ) const; - virtual bool eventFilter(QObject *, QEvent *); + virtual bool eventFilter( QObject *, QEvent * ); -signals: +Q_SIGNALS: /*! Signal emitted, when panning is done \param dx Offset in horizontal direction \param dy Offset in vertical direction */ - void panned(int dx, int dy); + void panned( int dx, int dy ); /*! Signal emitted, while the widget moved, but panning @@ -78,20 +77,23 @@ signals: \param dx Offset in horizontal direction \param dy Offset in vertical direction */ - void moved(int dx, int dy); + void moved( int dx, int dy ); protected: - virtual void widgetMousePressEvent(QMouseEvent *); - virtual void widgetMouseReleaseEvent(QMouseEvent *); - virtual void widgetMouseMoveEvent(QMouseEvent *); - virtual void widgetKeyPressEvent(QKeyEvent *); - virtual void widgetKeyReleaseEvent(QKeyEvent *); + virtual void widgetMousePressEvent( QMouseEvent * ); + virtual void widgetMouseReleaseEvent( QMouseEvent * ); + virtual void widgetMouseMoveEvent( QMouseEvent * ); + virtual void widgetKeyPressEvent( QKeyEvent * ); + virtual void widgetKeyReleaseEvent( QKeyEvent * ); + + virtual void paintEvent( QPaintEvent * ); - virtual void paintEvent(QPaintEvent *); + virtual QBitmap contentsMask() const; + virtual QPixmap grab() const; private: #ifndef QT_NO_CURSOR - void showCursor(bool); + void showCursor( bool ); #endif class PrivateData; diff --git a/libs/qwt/qwt_picker.cpp b/libs/qwt/qwt_picker.cpp index 82752cb74..335ddf0cd 100644 --- a/libs/qwt/qwt_picker.cpp +++ b/libs/qwt/qwt_picker.cpp @@ -7,56 +7,108 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ +#include "qwt_picker.h" +#include "qwt_picker_machine.h" +#include "qwt_painter.h" +#include "qwt_math.h" +#include "qwt_widget_overlay.h" #include #include #include #include #include #include -#include "qwt_math.h" -#include "qwt_painter.h" -#include "qwt_picker_machine.h" -#include "qwt_picker.h" -#if QT_VERSION < 0x040000 -#include -#else #include #include -#endif +#include -class QwtPicker::PickerWidget: public QWidget +static inline QRegion qwtMaskRegion( const QRect &r, int penWidth ) { -public: - enum Type { - RubberBand, - Text - }; + const int pw = qMax( penWidth, 1 ); + const int pw2 = penWidth / 2; + + int x1 = r.left() - pw2; + int x2 = r.right() + 1 + pw2 + ( pw % 2 ); + + int y1 = r.top() - pw2; + int y2 = r.bottom() + 1 + pw2 + ( pw % 2 ); + + QRegion region; + + region += QRect( x1, y1, x2 - x1, pw ); + region += QRect( x1, y1, pw, y2 - y1 ); + region += QRect( x1, y2 - pw, x2 - x1, pw ); + region += QRect( x2 - pw, y1, pw, y2 - y1 ); + + return region; +} + +static inline QRegion qwtMaskRegion( const QLine &l, int penWidth ) +{ + const int pw = qMax( penWidth, 1 ); + const int pw2 = penWidth / 2; + + QRegion region; + + if ( l.x1() == l.x2() ) + { + region += QRect( l.x1() - pw2, l.y1(), + pw, l.y2() ).normalized(); + } + else if ( l.y1() == l.y2() ) + { + region += QRect( l.x1(), l.y1() - pw2, + l.x2(), pw ).normalized(); + } - PickerWidget(QwtPicker *, QWidget *, Type); - virtual void updateMask(); + return region; +} - /* - For a tracker text with a background we can use the background - rect as mask. Also for "regular" Qt widgets >= 4.3.0 we - don't need to mask the text anymore. - */ - bool d_hasTextMask; +class QwtPickerRubberband: public QwtWidgetOverlay +{ +public: + QwtPickerRubberband( QwtPicker *, QWidget * ); protected: - virtual void paintEvent(QPaintEvent *); + virtual void drawOverlay( QPainter * ) const; + virtual QRegion maskHint() const; QwtPicker *d_picker; - Type d_type; }; +class QwtPickerTracker: public QwtWidgetOverlay +{ +public: + QwtPickerTracker( QwtPicker *, QWidget * ); + +protected: + virtual void drawOverlay( QPainter * ) const; + virtual QRegion maskHint() const; + + QwtPicker *d_picker; +}; + + class QwtPicker::PrivateData { public: + PrivateData(): + enabled( false ), + stateMachine( NULL ), + resizeMode( QwtPicker::Stretch ), + rubberBand( QwtPicker::NoRubberBand ), + trackerMode( QwtPicker::AlwaysOff ), + isActive( false ), + trackerPosition( -1, -1 ), + mouseTracking( false ), + openGL( false ) + { + } + bool enabled; QwtPickerMachine *stateMachine; - int selectionFlags; QwtPicker::ResizeMode resizeMode; QwtPicker::RubberBand rubberBand; @@ -66,246 +118,130 @@ public: QPen trackerPen; QFont trackerFont; - QwtPolygon selection; + QPolygon pickedPoints; bool isActive; QPoint trackerPosition; bool mouseTracking; // used to save previous value - /* - On X11 the widget below the picker widgets gets paint events - with a region that is the bounding rect of the mask, if it is complex. - In case of (f.e) a CrossRubberBand and a text this creates complete - repaints of the widget. So we better use two different widgets. - */ - -#if QT_VERSION < 0x040000 - QGuardedPtr rubberBandWidget; - QGuardedPtr trackerWidget; -#else - QPointer rubberBandWidget; - QPointer trackerWidget; -#endif + QPointer< QwtPickerRubberband > rubberBandOverlay; + QPointer< QwtPickerTracker> trackerOverlay; + + bool openGL; }; -QwtPicker::PickerWidget::PickerWidget( - QwtPicker *picker, QWidget *parent, Type type): - QWidget(parent), - d_hasTextMask(false), - d_picker(picker), - d_type(type) +QwtPickerRubberband::QwtPickerRubberband( + QwtPicker *picker, QWidget *parent ): + QwtWidgetOverlay( parent ), + d_picker( picker ) { -#if QT_VERSION >= 0x040000 - setAttribute(Qt::WA_TransparentForMouseEvents); - setAttribute(Qt::WA_NoSystemBackground); - setFocusPolicy(Qt::NoFocus); -#else - setBackgroundMode(Qt::NoBackground); - setFocusPolicy(QWidget::NoFocus); - setMouseTracking(true); -#endif - hide(); + setMaskMode( QwtWidgetOverlay::MaskHint ); } -void QwtPicker::PickerWidget::updateMask() +QRegion QwtPickerRubberband::maskHint() const { - QRegion mask; - - if ( d_type == RubberBand ) { - QBitmap bm(width(), height()); - bm.fill(Qt::color0); - - QPainter painter(&bm); - QPen pen = d_picker->rubberBandPen(); - pen.setColor(Qt::color1); - painter.setPen(pen); - - d_picker->drawRubberBand(&painter); - - mask = QRegion(bm); - } - if ( d_type == Text ) { - d_hasTextMask = true; -#if QT_VERSION >= 0x040300 - if ( !parentWidget()->testAttribute(Qt::WA_PaintOnScreen) ) { -#if 0 - if ( parentWidget()->paintEngine()->type() != QPaintEngine::OpenGL ) -#endif - { - // With Qt >= 4.3 drawing of the tracker can be implemented in an - // easier way, using the textRect as mask. - - d_hasTextMask = false; - } - } -#endif - - if ( d_hasTextMask ) { - const QwtText label = d_picker->trackerText( - d_picker->trackerPosition()); - if ( label.testPaintAttribute(QwtText::PaintBackground) - && label.backgroundBrush().style() != Qt::NoBrush ) { -#if QT_VERSION >= 0x040300 - if ( label.backgroundBrush().color().alpha() > 0 ) -#endif - // We don't need a text mask, when we have a background - d_hasTextMask = false; - } - } - - if ( d_hasTextMask ) { - QBitmap bm(width(), height()); - bm.fill(Qt::color0); - - QPainter painter(&bm); - painter.setFont(font()); - - QPen pen = d_picker->trackerPen(); - pen.setColor(Qt::color1); - painter.setPen(pen); - - d_picker->drawTracker(&painter); - - mask = QRegion(bm); - } else { - mask = d_picker->trackerRect(font()); - } - } - -#if QT_VERSION < 0x040000 - QWidget *w = parentWidget(); - const bool doUpdate = w->isUpdatesEnabled(); - const Qt::BackgroundMode bgMode = w->backgroundMode(); - w->setUpdatesEnabled(false); - if ( bgMode != Qt::NoBackground ) - w->setBackgroundMode(Qt::NoBackground); -#endif - - setMask(mask); - -#if QT_VERSION < 0x040000 - if ( bgMode != Qt::NoBackground ) - w->setBackgroundMode(bgMode); - - w->setUpdatesEnabled(doUpdate); -#endif + return d_picker->rubberBandMask(); +} - setShown(!mask.isEmpty()); +void QwtPickerRubberband::drawOverlay( QPainter *painter ) const +{ + painter->setPen( d_picker->rubberBandPen() ); + d_picker->drawRubberBand( painter ); } -void QwtPicker::PickerWidget::paintEvent(QPaintEvent *e) +QwtPickerTracker::QwtPickerTracker( + QwtPicker *picker, QWidget *parent ): + QwtWidgetOverlay( parent ), + d_picker( picker ) { - QPainter painter(this); - painter.setClipRegion(e->region()); + setMaskMode( QwtWidgetOverlay::MaskHint ); +} - if ( d_type == RubberBand ) { - painter.setPen(d_picker->rubberBandPen()); - d_picker->drawRubberBand(&painter); - } +QRegion QwtPickerTracker::maskHint() const +{ + return d_picker->trackerRect( font() ); +} - if ( d_type == Text ) { - /* - If we have a text mask we simply fill the region of - the mask. This gives better results for antialiased fonts. - */ - bool doDrawTracker = !d_hasTextMask; -#if QT_VERSION < 0x040000 - if ( !doDrawTracker && QPainter::redirect(this) ) { - // setMask + painter redirection doesn't work - doDrawTracker = true; - } -#endif - if ( doDrawTracker ) { - painter.setPen(d_picker->trackerPen()); - d_picker->drawTracker(&painter); - } else - painter.fillRect(e->rect(), QBrush(d_picker->trackerPen().color())); - } +void QwtPickerTracker::drawOverlay( QPainter *painter ) const +{ + painter->setPen( d_picker->trackerPen() ); + d_picker->drawTracker( painter ); } /*! Constructor - Creates an picker that is enabled, but where selection flag - is set to NoSelection, rubberband and tracker are disabled. + Creates an picker that is enabled, but without a state machine. + rubber band and tracker are disabled. \param parent Parent widget, that will be observed */ -QwtPicker::QwtPicker(QWidget *parent): - QObject(parent) +QwtPicker::QwtPicker( QWidget *parent ): + QObject( parent ) { - init(parent, NoSelection, NoRubberBand, AlwaysOff); + init( parent, NoRubberBand, AlwaysOff ); } /*! Constructor - \param selectionFlags Or'd value of SelectionType, RectSelectionType and - SelectionMode - \param rubberBand Rubberband style + \param rubberBand Rubber band style \param trackerMode Tracker mode \param parent Parent widget, that will be observed */ -QwtPicker::QwtPicker(int selectionFlags, RubberBand rubberBand, - DisplayMode trackerMode, QWidget *parent): - QObject(parent) +QwtPicker::QwtPicker( RubberBand rubberBand, + DisplayMode trackerMode, QWidget *parent ): + QObject( parent ) { - init(parent, selectionFlags, rubberBand, trackerMode); + init( parent, rubberBand, trackerMode ); } //! Destructor QwtPicker::~QwtPicker() { - setMouseTracking(false); + setMouseTracking( false ); + delete d_data->stateMachine; - delete d_data->rubberBandWidget; - delete d_data->trackerWidget; + delete d_data->rubberBandOverlay; + delete d_data->trackerOverlay; + delete d_data; } -//! Init the picker, used by the constructors -void QwtPicker::init(QWidget *parent, int selectionFlags, - RubberBand rubberBand, DisplayMode trackerMode) +//! Initialize the picker - used by the constructors +void QwtPicker::init( QWidget *parent, + RubberBand rubberBand, DisplayMode trackerMode ) { d_data = new PrivateData; - d_data->rubberBandWidget = NULL; - d_data->trackerWidget = NULL; - d_data->rubberBand = rubberBand; - d_data->enabled = false; - d_data->resizeMode = Stretch; - d_data->trackerMode = AlwaysOff; - d_data->isActive = false; - d_data->trackerPosition = QPoint(-1, -1); - d_data->mouseTracking = false; - - d_data->stateMachine = NULL; - setSelectionFlags(selectionFlags); - - if ( parent ) { -#if QT_VERSION >= 0x040000 + + if ( parent ) + { if ( parent->focusPolicy() == Qt::NoFocus ) - parent->setFocusPolicy(Qt::WheelFocus); -#else - if ( parent->focusPolicy() == QWidget::NoFocus ) - parent->setFocusPolicy(QWidget::WheelFocus); -#endif + parent->setFocusPolicy( Qt::WheelFocus ); + d_data->openGL = parent->inherits( "QGLWidget" ); d_data->trackerFont = parent->font(); d_data->mouseTracking = parent->hasMouseTracking(); - setEnabled(true); + + setEnabled( true ); } - setTrackerMode(trackerMode); + + setTrackerMode( trackerMode ); } /*! - Set a state machine and delete the previous one + Set a state machine and delete the previous one + + \param stateMachine State machine + \sa stateMachine() */ -void QwtPicker::setStateMachine(QwtPickerMachine *stateMachine) +void QwtPicker::setStateMachine( QwtPickerMachine *stateMachine ) { - if ( d_data->stateMachine != stateMachine ) { + if ( d_data->stateMachine != stateMachine ) + { reset(); delete d_data->stateMachine; @@ -317,39 +253,21 @@ void QwtPicker::setStateMachine(QwtPickerMachine *stateMachine) } /*! - Create a state machine depending on the selection flags. - - - PointSelection | ClickSelection\n - QwtPickerClickPointMachine() - - PointSelection | DragSelection\n - QwtPickerDragPointMachine() - - RectSelection | ClickSelection\n - QwtPickerClickRectMachine() - - RectSelection | DragSelection\n - QwtPickerDragRectMachine() - - PolygonSelection\n - QwtPickerPolygonMachine() - - \sa setSelectionFlags() + \return Assigned state machine + \sa setStateMachine() */ -QwtPickerMachine *QwtPicker::stateMachine(int flags) const +QwtPickerMachine *QwtPicker::stateMachine() { - if ( flags & PointSelection ) { - if ( flags & ClickSelection ) - return new QwtPickerClickPointMachine; - else - return new QwtPickerDragPointMachine; - } - if ( flags & RectSelection ) { - if ( flags & ClickSelection ) - return new QwtPickerClickRectMachine; - else - return new QwtPickerDragRectMachine; - } - if ( flags & PolygonSelection ) { - return new QwtPickerPolygonMachine(); - } - return NULL; + return d_data->stateMachine; +} + +/*! + \return Assigned state machine + \sa setStateMachine() +*/ +const QwtPickerMachine *QwtPicker::stateMachine() const +{ + return d_data->stateMachine; } //! Return the parent widget, where the selection happens @@ -357,7 +275,7 @@ QWidget *QwtPicker::parentWidget() { QObject *obj = parent(); if ( obj && obj->isWidgetType() ) - return (QWidget *)obj; + return static_cast( obj ); return NULL; } @@ -367,51 +285,26 @@ const QWidget *QwtPicker::parentWidget() const { QObject *obj = parent(); if ( obj && obj->isWidgetType() ) - return (QWidget *)obj; + return static_cast< const QWidget *>( obj ); return NULL; } /*! - Set the selection flags - - \param flags Or'd value of SelectionType, RectSelectionType and - SelectionMode. The default value is NoSelection. - - \sa selectionFlags(), SelectionType, RectSelectionType, SelectionMode -*/ - -void QwtPicker::setSelectionFlags(int flags) -{ - d_data->selectionFlags = flags; - setStateMachine(stateMachine(flags)); -} - -/*! - \return Selection flags, an Or'd value of SelectionType, RectSelectionType and - SelectionMode. - \sa setSelectionFlags(), SelectionType, RectSelectionType, SelectionMode -*/ -int QwtPicker::selectionFlags() const -{ - return d_data->selectionFlags; -} - -/*! - Set the rubberband style + Set the rubber band style - \param rubberBand Rubberband style + \param rubberBand Rubber band style The default value is NoRubberBand. \sa rubberBand(), RubberBand, setRubberBandPen() */ -void QwtPicker::setRubberBand(RubberBand rubberBand) +void QwtPicker::setRubberBand( RubberBand rubberBand ) { d_data->rubberBand = rubberBand; } /*! - \return Rubberband style + \return Rubber band style \sa setRubberBand(), RubberBand, rubberBandPen() */ QwtPicker::RubberBand QwtPicker::rubberBand() const @@ -435,11 +328,12 @@ QwtPicker::RubberBand QwtPicker::rubberBand() const \sa trackerMode(), DisplayMode */ -void QwtPicker::setTrackerMode(DisplayMode mode) +void QwtPicker::setTrackerMode( DisplayMode mode ) { - if ( d_data->trackerMode != mode ) { + if ( d_data->trackerMode != mode ) + { d_data->trackerMode = mode; - setMouseTracking(d_data->trackerMode == AlwaysOn); + setMouseTracking( d_data->trackerMode == AlwaysOn ); } } @@ -466,7 +360,7 @@ QwtPicker::DisplayMode QwtPicker::trackerMode() const \param mode Resize mode \sa resizeMode(), ResizeMode */ -void QwtPicker::setResizeMode(ResizeMode mode) +void QwtPicker::setResizeMode( ResizeMode mode ) { d_data->resizeMode = mode; } @@ -490,17 +384,19 @@ QwtPicker::ResizeMode QwtPicker::resizeMode() const \param enabled true or false \sa isEnabled(), eventFilter() */ -void QwtPicker::setEnabled(bool enabled) +void QwtPicker::setEnabled( bool enabled ) { - if ( d_data->enabled != enabled ) { + if ( d_data->enabled != enabled ) + { d_data->enabled = enabled; QWidget *w = parentWidget(); - if ( w ) { + if ( w ) + { if ( enabled ) - w->installEventFilter(this); + w->installEventFilter( this ); else - w->removeEventFilter(this); + w->removeEventFilter( this ); } updateDisplay(); @@ -509,7 +405,7 @@ void QwtPicker::setEnabled(bool enabled) /*! \return true when enabled, false otherwise - \sa setEnabled, eventFilter() + \sa setEnabled(), eventFilter() */ bool QwtPicker::isEnabled() const @@ -523,9 +419,10 @@ bool QwtPicker::isEnabled() const \param font Tracker font \sa trackerFont(), setTrackerMode(), setTrackerPen() */ -void QwtPicker::setTrackerFont(const QFont &font) +void QwtPicker::setTrackerFont( const QFont &font ) { - if ( font != d_data->trackerFont ) { + if ( font != d_data->trackerFont ) + { d_data->trackerFont = font; updateDisplay(); } @@ -547,9 +444,10 @@ QFont QwtPicker::trackerFont() const \param pen Tracker pen \sa trackerPen(), setTrackerMode(), setTrackerFont() */ -void QwtPicker::setTrackerPen(const QPen &pen) +void QwtPicker::setTrackerPen( const QPen &pen ) { - if ( pen != d_data->trackerPen ) { + if ( pen != d_data->trackerPen ) + { d_data->trackerPen = pen; updateDisplay(); } @@ -567,19 +465,20 @@ QPen QwtPicker::trackerPen() const /*! Set the pen for the rubberband - \param pen Rubberband pen + \param pen Rubber band pen \sa rubberBandPen(), setRubberBand() */ -void QwtPicker::setRubberBandPen(const QPen &pen) +void QwtPicker::setRubberBandPen( const QPen &pen ) { - if ( pen != d_data->rubberBandPen ) { + if ( pen != d_data->rubberBandPen ) + { d_data->rubberBandPen = pen; updateDisplay(); } } /*! - \return Rubberband pen + \return Rubber band pen \sa setRubberBandPen(), rubberBand() */ QPen QwtPicker::rubberBandPen() const @@ -600,106 +499,227 @@ QPen QwtPicker::rubberBandPen() const \return Converted position as string */ -QwtText QwtPicker::trackerText(const QPoint &pos) const +QwtText QwtPicker::trackerText( const QPoint &pos ) const { QString label; - switch(rubberBand()) { - case HLineRubberBand: - label.sprintf("%d", pos.y()); - break; - case VLineRubberBand: - label.sprintf("%d", pos.x()); - break; - default: - label.sprintf("%d, %d", pos.x(), pos.y()); + switch ( rubberBand() ) + { + case HLineRubberBand: + label.sprintf( "%d", pos.y() ); + break; + case VLineRubberBand: + label.sprintf( "%d", pos.x() ); + break; + default: + label.sprintf( "%d, %d", pos.x(), pos.y() ); } return label; } /*! - Draw a rubberband , depending on rubberBand() and selectionFlags() - - \param painter Painter, initialized with clip rect + Calculate the mask for the rubber band overlay - \sa rubberBand(), RubberBand, selectionFlags() -*/ - -void QwtPicker::drawRubberBand(QPainter *painter) const + \return Region for the mask + \sa QWidget::setMask() + */ +QRegion QwtPicker::rubberBandMask() const { + QRegion mask; + if ( !isActive() || rubberBand() == NoRubberBand || - rubberBandPen().style() == Qt::NoPen ) { - return; + rubberBandPen().style() == Qt::NoPen ) + { + return mask; } - const QRect &pRect = pickRect(); - const QwtPolygon &pa = d_data->selection; + const QPolygon pa = adjustedPoints( d_data->pickedPoints ); - if ( selectionFlags() & PointSelection ) { - if ( pa.count() < 1 ) - return; + QwtPickerMachine::SelectionType selectionType = + QwtPickerMachine::NoSelection; - const QPoint pos = pa[0]; + if ( d_data->stateMachine ) + selectionType = d_data->stateMachine->selectionType(); - switch(rubberBand()) { - case VLineRubberBand: - QwtPainter::drawLine(painter, pos.x(), - pRect.top(), pos.x(), pRect.bottom()); + switch ( selectionType ) + { + case QwtPickerMachine::NoSelection: + case QwtPickerMachine::PointSelection: + { + if ( pa.count() < 1 ) + return mask; + + const QPoint pos = pa[0]; + const int pw = rubberBandPen().width(); + + const QRect pRect = pickArea().boundingRect().toRect(); + switch ( rubberBand() ) + { + case VLineRubberBand: + { + mask += qwtMaskRegion( QLine( pos.x(), pRect.top(), + pos.x(), pRect.bottom() ), pw ); + break; + } + case HLineRubberBand: + { + mask += qwtMaskRegion( QLine( pRect.left(), pos.y(), + pRect.right(), pos.y() ), pw ); + break; + } + case CrossRubberBand: + { + mask += qwtMaskRegion( QLine( pos.x(), pRect.top(), + pos.x(), pRect.bottom() ), pw ); + mask += qwtMaskRegion( QLine( pRect.left(), pos.y(), + pRect.right(), pos.y() ), pw ); + break; + } + default: + break; + } break; + } + case QwtPickerMachine::RectSelection: + { + if ( pa.count() < 2 ) + return mask; - case HLineRubberBand: - QwtPainter::drawLine(painter, pRect.left(), - pos.y(), pRect.right(), pos.y()); + const int pw = rubberBandPen().width(); + + switch ( rubberBand() ) + { + case RectRubberBand: + { + const QRect r = QRect( pa.first(), pa.last() ); + mask = qwtMaskRegion( r.normalized(), pw ); + break; + } + case EllipseRubberBand: + { + const QRect r = QRect( pa.first(), pa.last() ); + mask += r.adjusted( -pw, -pw, pw, pw ); + break; + } + default: + break; + } break; + } + case QwtPickerMachine::PolygonSelection: + { + const int pw = rubberBandPen().width(); + if ( pw <= 1 ) + { + // because of the join style we better + // return a mask for a pen width <= 1 only - case CrossRubberBand: - QwtPainter::drawLine(painter, pos.x(), - pRect.top(), pos.x(), pRect.bottom()); - QwtPainter::drawLine(painter, pRect.left(), - pos.y(), pRect.right(), pos.y()); + const int off = 2 * pw; + const QRect r = pa.boundingRect(); + mask += r.adjusted( -off, -off, off, off ); + } break; + } default: break; - } } - else if ( selectionFlags() & RectSelection ) { - if ( pa.count() < 2 ) - return; - - QPoint p1 = pa[0]; - QPoint p2 = pa[int(pa.count() - 1)]; - - if ( selectionFlags() & CenterToCorner ) { - p1.setX(p1.x() - (p2.x() - p1.x())); - p1.setY(p1.y() - (p2.y() - p1.y())); - } else if ( selectionFlags() & CenterToRadius ) { - const int radius = qwtMax(qwtAbs(p2.x() - p1.x()), - qwtAbs(p2.y() - p1.y())); - p2.setX(p1.x() + radius); - p2.setY(p1.y() + radius); - p1.setX(p1.x() - radius); - p1.setY(p1.y() - radius); + return mask; +} + +/*! + Draw a rubber band, depending on rubberBand() + + \param painter Painter, initialized with a clip region + + \sa rubberBand(), RubberBand +*/ + +void QwtPicker::drawRubberBand( QPainter *painter ) const +{ + if ( !isActive() || rubberBand() == NoRubberBand || + rubberBandPen().style() == Qt::NoPen ) + { + return; + } + + const QPolygon pa = adjustedPoints( d_data->pickedPoints ); + + QwtPickerMachine::SelectionType selectionType = + QwtPickerMachine::NoSelection; + + if ( d_data->stateMachine ) + selectionType = d_data->stateMachine->selectionType(); + + switch ( selectionType ) + { + case QwtPickerMachine::NoSelection: + case QwtPickerMachine::PointSelection: + { + if ( pa.count() < 1 ) + return; + + const QPoint pos = pa[0]; + + const QRect pRect = pickArea().boundingRect().toRect(); + switch ( rubberBand() ) + { + case VLineRubberBand: + { + QwtPainter::drawLine( painter, pos.x(), + pRect.top(), pos.x(), pRect.bottom() ); + break; + } + case HLineRubberBand: + { + QwtPainter::drawLine( painter, pRect.left(), + pos.y(), pRect.right(), pos.y() ); + break; + } + case CrossRubberBand: + { + QwtPainter::drawLine( painter, pos.x(), + pRect.top(), pos.x(), pRect.bottom() ); + QwtPainter::drawLine( painter, pRect.left(), + pos.y(), pRect.right(), pos.y() ); + break; + } + default: + break; + } + break; } + case QwtPickerMachine::RectSelection: + { + if ( pa.count() < 2 ) + return; -#if QT_VERSION < 0x040000 - const QRect rect = QRect(p1, p2).normalize(); -#else - const QRect rect = QRect(p1, p2).normalized(); -#endif - switch(rubberBand()) { - case EllipseRubberBand: - QwtPainter::drawEllipse(painter, rect); + const QRect rect = QRect( pa.first(), pa.last() ).normalized(); + switch ( rubberBand() ) + { + case EllipseRubberBand: + { + QwtPainter::drawEllipse( painter, rect ); + break; + } + case RectRubberBand: + { + QwtPainter::drawRect( painter, rect ); + break; + } + default: + break; + } break; - case RectRubberBand: - QwtPainter::drawRect(painter, rect); + } + case QwtPickerMachine::PolygonSelection: + { + if ( rubberBand() == PolygonRubberBand ) + painter->drawPolyline( pa ); break; + } default: break; - } - } else if ( selectionFlags() & PolygonSelection ) { - if ( rubberBand() == PolygonRubberBand ) - painter->drawPolyline(pa); } } @@ -710,63 +730,114 @@ void QwtPicker::drawRubberBand(QPainter *painter) const \sa trackerRect(), trackerText() */ -void QwtPicker::drawTracker(QPainter *painter) const -{ - const QRect textRect = trackerRect(painter->font()); - if ( !textRect.isEmpty() ) { - QwtText label = trackerText(d_data->trackerPosition); - if ( !label.isEmpty() ) { - painter->save(); - -#if defined(Q_WS_MAC) - // Antialiased fonts are broken on the Mac. -#if QT_VERSION >= 0x040000 - painter->setRenderHint(QPainter::TextAntialiasing, false); -#else - QFont fnt = label.usedFont(painter->font()); - fnt.setStyleStrategy(QFont::NoAntialias); - label.setFont(fnt); -#endif -#endif - label.draw(painter, textRect); - - painter->restore(); - } +void QwtPicker::drawTracker( QPainter *painter ) const +{ + const QRect textRect = trackerRect( painter->font() ); + if ( !textRect.isEmpty() ) + { + const QwtText label = trackerText( d_data->trackerPosition ); + if ( !label.isEmpty() ) + label.draw( painter, textRect ); } } +/*! + \brief Map the pickedPoints() into a selection() + + adjustedPoints() maps the points, that have been collected on + the parentWidget() into a selection(). The default implementation + simply returns the points unmodified. + + The reason, why a selection() differs from the picked points + depends on the application requirements. F.e. : + + - A rectangular selection might need to have a specific aspect ratio only.\n + - A selection could accept non intersecting polygons only.\n + - ...\n + + The example below is for a rectangular selection, where the first + point is the center of the selected rectangle. + \par Example + \verbatim QPolygon MyPicker::adjustedPoints(const QPolygon &points) const +{ + QPolygon adjusted; + if ( points.size() == 2 ) + { + const int width = qAbs(points[1].x() - points[0].x()); + const int height = qAbs(points[1].y() - points[0].y()); + + QRect rect(0, 0, 2 * width, 2 * height); + rect.moveCenter(points[0]); + + adjusted += rect.topLeft(); + adjusted += rect.bottomRight(); + } + return adjusted; +}\endverbatim\n + + \param points Selected points + \return Selected points unmodified +*/ +QPolygon QwtPicker::adjustedPoints( const QPolygon &points ) const +{ + return points; +} + +/*! + \return Selected points + \sa pickedPoints(), adjustedPoints() +*/ +QPolygon QwtPicker::selection() const +{ + return adjustedPoints( d_data->pickedPoints ); +} + +//! \return Current position of the tracker QPoint QwtPicker::trackerPosition() const { return d_data->trackerPosition; } -QRect QwtPicker::trackerRect(const QFont &font) const +/*! + Calculate the bounding rectangle for the tracker text + from the current position of the tracker + + \param font Font of the tracker text + \return Bounding rectangle of the tracker text + + \sa trackerPosition() +*/ +QRect QwtPicker::trackerRect( const QFont &font ) const { if ( trackerMode() == AlwaysOff || - (trackerMode() == ActiveOnly && !isActive() ) ) { + ( trackerMode() == ActiveOnly && !isActive() ) ) + { return QRect(); } if ( d_data->trackerPosition.x() < 0 || d_data->trackerPosition.y() < 0 ) return QRect(); - QwtText text = trackerText(d_data->trackerPosition); + QwtText text = trackerText( d_data->trackerPosition ); if ( text.isEmpty() ) return QRect(); - QRect textRect(QPoint(0, 0), text.textSize(font)); + const QSizeF textSize = text.textSize( font ); + QRect textRect( 0, 0, qCeil( textSize.width() ), qCeil( textSize.height() ) ); const QPoint &pos = d_data->trackerPosition; int alignment = 0; - if ( isActive() && d_data->selection.count() > 1 - && rubberBand() != NoRubberBand ) { + if ( isActive() && d_data->pickedPoints.count() > 1 + && rubberBand() != NoRubberBand ) + { const QPoint last = - d_data->selection[int(d_data->selection.count()) - 2]; + d_data->pickedPoints[int( d_data->pickedPoints.count() ) - 2]; - alignment |= (pos.x() >= last.x()) ? Qt::AlignRight : Qt::AlignLeft; - alignment |= (pos.y() > last.y()) ? Qt::AlignBottom : Qt::AlignTop; - } else + alignment |= ( pos.x() >= last.x() ) ? Qt::AlignRight : Qt::AlignLeft; + alignment |= ( pos.y() > last.y() ) ? Qt::AlignBottom : Qt::AlignTop; + } + else alignment = Qt::AlignTop | Qt::AlignRight; const int margin = 5; @@ -783,15 +854,17 @@ QRect QwtPicker::trackerRect(const QFont &font) const else if ( alignment & Qt::AlignTop ) y -= textRect.height() + margin; - textRect.moveTopLeft(QPoint(x, y)); + textRect.moveTopLeft( QPoint( x, y ) ); - int right = qwtMin(textRect.right(), pickRect().right() - margin); - int bottom = qwtMin(textRect.bottom(), pickRect().bottom() - margin); - textRect.moveBottomRight(QPoint(right, bottom)); + const QRect pickRect = pickArea().boundingRect().toRect(); - int left = qwtMax(textRect.left(), pickRect().left() + margin); - int top = qwtMax(textRect.top(), pickRect().top() + margin); - textRect.moveTopLeft(QPoint(left, top)); + int right = qMin( textRect.right(), pickRect.right() - margin ); + int bottom = qMin( textRect.bottom(), pickRect.bottom() - margin ); + textRect.moveBottomRight( QPoint( right, bottom ) ); + + int left = qMax( textRect.left(), pickRect.left() + margin ); + int top = qMax( textRect.top(), pickRect.top() + margin ); + textRect.moveTopLeft( QPoint( left, top ) ); return textRect; } @@ -799,57 +872,83 @@ QRect QwtPicker::trackerRect(const QFont &font) const /*! \brief Event filter - When isEnabled() == true all events of the observed widget are filtered. + When isEnabled() is true all events of the observed widget are filtered. Mouse and keyboard events are translated into widgetMouse- and widgetKey- and widgetWheel-events. Paint and Resize events are handled to keep - rubberband and tracker up to date. + rubber band and tracker up to date. + + \param object Object to be filtered + \param event Event - \sa event(), widgetMousePressEvent(), widgetMouseReleaseEvent(), + \return Always false. + + \sa widgetEnterEvent(), widgetLeaveEvent(), + widgetMousePressEvent(), widgetMouseReleaseEvent(), widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(), - widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent() + widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent(), + QObject::installEventFilter(), QObject::event() */ -bool QwtPicker::eventFilter(QObject *o, QEvent *e) +bool QwtPicker::eventFilter( QObject *object, QEvent *event ) { - if ( o && o == parentWidget() ) { - switch(e->type()) { - case QEvent::Resize: { - const QResizeEvent *re = (QResizeEvent *)e; - if ( d_data->resizeMode == Stretch ) - stretchSelection(re->oldSize(), re->size()); - - if ( d_data->rubberBandWidget ) - d_data->rubberBandWidget->resize(re->size()); + if ( object && object == parentWidget() ) + { + switch ( event->type() ) + { + case QEvent::Resize: + { + const QResizeEvent *re = static_cast( event ); + if ( d_data->resizeMode == Stretch ) + stretchSelection( re->oldSize(), re->size() ); - if ( d_data->trackerWidget ) - d_data->trackerWidget->resize(re->size()); - break; - } - case QEvent::Leave: - widgetLeaveEvent(e); - break; - case QEvent::MouseButtonPress: - widgetMousePressEvent((QMouseEvent *)e); - break; - case QEvent::MouseButtonRelease: - widgetMouseReleaseEvent((QMouseEvent *)e); - break; - case QEvent::MouseButtonDblClick: - widgetMouseDoubleClickEvent((QMouseEvent *)e); - break; - case QEvent::MouseMove: - widgetMouseMoveEvent((QMouseEvent *)e); - break; - case QEvent::KeyPress: - widgetKeyPressEvent((QKeyEvent *)e); - break; - case QEvent::KeyRelease: - widgetKeyReleaseEvent((QKeyEvent *)e); - break; - case QEvent::Wheel: - widgetWheelEvent((QWheelEvent *)e); - break; - default: - break; + break; + } + case QEvent::Enter: + { + widgetEnterEvent( event ); + break; + } + case QEvent::Leave: + { + widgetLeaveEvent( event ); + break; + } + case QEvent::MouseButtonPress: + { + widgetMousePressEvent( static_cast( event ) ); + break; + } + case QEvent::MouseButtonRelease: + { + widgetMouseReleaseEvent( static_cast( event ) ); + break; + } + case QEvent::MouseButtonDblClick: + { + widgetMouseDoubleClickEvent( static_cast( event ) ); + break; + } + case QEvent::MouseMove: + { + widgetMouseMoveEvent( static_cast( event ) ); + break; + } + case QEvent::KeyPress: + { + widgetKeyPressEvent( static_cast( event ) ); + break; + } + case QEvent::KeyRelease: + { + widgetKeyReleaseEvent( static_cast( event ) ); + break; + } + case QEvent::Wheel: + { + widgetWheelEvent( static_cast( event ) ); + break; + } + default: + break; } } return false; @@ -858,81 +957,97 @@ bool QwtPicker::eventFilter(QObject *o, QEvent *e) /*! Handle a mouse press event for the observed widget. - Begin and/or end a selection depending on the selection flags. + \param mouseEvent Mouse event - \sa QwtPicker, selectionFlags() \sa eventFilter(), widgetMouseReleaseEvent(), widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(), widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent() */ -void QwtPicker::widgetMousePressEvent(QMouseEvent *e) +void QwtPicker::widgetMousePressEvent( QMouseEvent *mouseEvent ) { - transition(e); + transition( mouseEvent ); } /*! Handle a mouse move event for the observed widget. - Move the last point of the selection in case of isActive() == true + \param mouseEvent Mouse event \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(), widgetMouseDoubleClickEvent(), widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent() */ -void QwtPicker::widgetMouseMoveEvent(QMouseEvent *e) +void QwtPicker::widgetMouseMoveEvent( QMouseEvent *mouseEvent ) { - if ( pickRect().contains(e->pos()) ) - d_data->trackerPosition = e->pos(); + if ( pickArea().contains( mouseEvent->pos() ) ) + d_data->trackerPosition = mouseEvent->pos(); else - d_data->trackerPosition = QPoint(-1, -1); + d_data->trackerPosition = QPoint( -1, -1 ); if ( !isActive() ) updateDisplay(); - transition(e); + transition( mouseEvent ); +} + +/*! + Handle a enter event for the observed widget. + + \param event Qt event + + \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(), + widgetMouseDoubleClickEvent(), + widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent() +*/ +void QwtPicker::widgetEnterEvent( QEvent *event ) +{ + transition( event ); } /*! Handle a leave event for the observed widget. + \param event Qt event + \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(), widgetMouseDoubleClickEvent(), widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent() */ -void QwtPicker::widgetLeaveEvent(QEvent *) +void QwtPicker::widgetLeaveEvent( QEvent *event ) { - d_data->trackerPosition = QPoint(-1, -1); + transition( event ); + + d_data->trackerPosition = QPoint( -1, -1 ); if ( !isActive() ) updateDisplay(); } /*! - Handle a mouse relase event for the observed widget. + Handle a mouse release event for the observed widget. - End a selection depending on the selection flags. + \param mouseEvent Mouse event - \sa QwtPicker, selectionFlags() \sa eventFilter(), widgetMousePressEvent(), widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(), widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent() */ -void QwtPicker::widgetMouseReleaseEvent(QMouseEvent *e) +void QwtPicker::widgetMouseReleaseEvent( QMouseEvent *mouseEvent ) { - transition(e); + transition( mouseEvent ); } /*! Handle mouse double click event for the observed widget. - Empty implementation, does nothing. + \param mouseEvent Mouse event \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(), widgetMouseMoveEvent(), widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent() */ -void QwtPicker::widgetMouseDoubleClickEvent(QMouseEvent *me) +void QwtPicker::widgetMouseDoubleClickEvent( QMouseEvent *mouseEvent ) { - transition(me); + transition( mouseEvent ); } @@ -941,20 +1056,22 @@ void QwtPicker::widgetMouseDoubleClickEvent(QMouseEvent *me) Move the last point of the selection in case of isActive() == true + \param wheelEvent Wheel event + \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(), widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent() */ -void QwtPicker::widgetWheelEvent(QWheelEvent *e) +void QwtPicker::widgetWheelEvent( QWheelEvent *wheelEvent ) { - if ( pickRect().contains(e->pos()) ) - d_data->trackerPosition = e->pos(); + if ( pickArea().contains( wheelEvent->pos() ) ) + d_data->trackerPosition = wheelEvent->pos(); else - d_data->trackerPosition = QPoint(-1, -1); + d_data->trackerPosition = QPoint( -1, -1 ); updateDisplay(); - transition(e); + transition( wheelEvent ); } /*! @@ -964,47 +1081,51 @@ void QwtPicker::widgetWheelEvent(QWheelEvent *e) move the cursor, the abort key aborts a selection. All other keys are handled by the current state machine. - \sa QwtPicker, selectionFlags() + \param keyEvent Key event + \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(), widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(), widgetWheelEvent(), widgetKeyReleaseEvent(), stateMachine(), QwtEventPattern::KeyPatternCode */ -void QwtPicker::widgetKeyPressEvent(QKeyEvent *ke) +void QwtPicker::widgetKeyPressEvent( QKeyEvent *keyEvent ) { int dx = 0; int dy = 0; int offset = 1; - if ( ke->isAutoRepeat() ) + if ( keyEvent->isAutoRepeat() ) offset = 5; - if ( keyMatch(KeyLeft, ke) ) + if ( keyMatch( KeyLeft, keyEvent ) ) dx = -offset; - else if ( keyMatch(KeyRight, ke) ) + else if ( keyMatch( KeyRight, keyEvent ) ) dx = offset; - else if ( keyMatch(KeyUp, ke) ) + else if ( keyMatch( KeyUp, keyEvent ) ) dy = -offset; - else if ( keyMatch(KeyDown, ke) ) + else if ( keyMatch( KeyDown, keyEvent ) ) dy = offset; - else if ( keyMatch(KeyAbort, ke) ) { + else if ( keyMatch( KeyAbort, keyEvent ) ) + { reset(); - } else - transition(ke); + } + else + transition( keyEvent ); - if ( dx != 0 || dy != 0 ) { - const QRect rect = pickRect(); - const QPoint pos = parentWidget()->mapFromGlobal(QCursor::pos()); + if ( dx != 0 || dy != 0 ) + { + const QRect rect = pickArea().boundingRect().toRect(); + const QPoint pos = parentWidget()->mapFromGlobal( QCursor::pos() ); int x = pos.x() + dx; - x = qwtMax(rect.left(), x); - x = qwtMin(rect.right(), x); + x = qMax( rect.left(), x ); + x = qMin( rect.right(), x ); int y = pos.y() + dy; - y = qwtMax(rect.top(), y); - y = qwtMin(rect.bottom(), y); + y = qMax( rect.top(), y ); + y = qMin( rect.bottom(), y ); - QCursor::setPos(parentWidget()->mapToGlobal(QPoint(x, y))); + QCursor::setPos( parentWidget()->mapToGlobal( QPoint( x, y ) ) ); } } @@ -1013,62 +1134,78 @@ void QwtPicker::widgetKeyPressEvent(QKeyEvent *ke) Passes the event to the state machine. + \param keyEvent Key event + \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(), widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(), widgetWheelEvent(), widgetKeyPressEvent(), stateMachine() */ -void QwtPicker::widgetKeyReleaseEvent(QKeyEvent *ke) +void QwtPicker::widgetKeyReleaseEvent( QKeyEvent *keyEvent ) { - transition(ke); + transition( keyEvent ); } /*! Passes an event to the state machine and executes the resulting commands. Append and Move commands use the current position - of the cursor (QCursor::pos()). + of the cursor ( QCursor::pos() ). - \param e Event + \param event Event */ -void QwtPicker::transition(const QEvent *e) +void QwtPicker::transition( const QEvent *event ) { if ( !d_data->stateMachine ) return; - QwtPickerMachine::CommandList commandList = - d_data->stateMachine->transition(*this, e); + const QList commandList = + d_data->stateMachine->transition( *this, event ); QPoint pos; - switch(e->type()) { - case QEvent::MouseButtonDblClick: - case QEvent::MouseButtonPress: - case QEvent::MouseButtonRelease: - case QEvent::MouseMove: { - const QMouseEvent *me = (QMouseEvent *)e; - pos = me->pos(); - break; - } - default: - pos = parentWidget()->mapFromGlobal(QCursor::pos()); - } - - for ( uint i = 0; i < (uint)commandList.count(); i++ ) { - switch(commandList[i]) { - case QwtPickerMachine::Begin: { - begin(); - break; - } - case QwtPickerMachine::Append: { - append(pos); - break; - } - case QwtPickerMachine::Move: { - move(pos); - break; - } - case QwtPickerMachine::End: { - end(); + switch ( event->type() ) + { + case QEvent::MouseButtonDblClick: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseMove: + { + const QMouseEvent *me = + static_cast< const QMouseEvent * >( event ); + pos = me->pos(); break; } + default: + pos = parentWidget()->mapFromGlobal( QCursor::pos() ); + } + + for ( int i = 0; i < commandList.count(); i++ ) + { + switch ( commandList[i] ) + { + case QwtPickerMachine::Begin: + { + begin(); + break; + } + case QwtPickerMachine::Append: + { + append( pos ); + break; + } + case QwtPickerMachine::Move: + { + move( pos ); + break; + } + case QwtPickerMachine::Remove: + { + remove(); + break; + } + case QwtPickerMachine::End: + { + end(); + break; + } } } } @@ -1076,93 +1213,99 @@ void QwtPicker::transition(const QEvent *e) /*! Open a selection setting the state to active - \sa isActive, end(), append(), move() + \sa isActive(), end(), append(), move() */ void QwtPicker::begin() { if ( d_data->isActive ) return; - d_data->selection.resize(0); + d_data->pickedPoints.resize( 0 ); d_data->isActive = true; + Q_EMIT activated( true ); - if ( trackerMode() != AlwaysOff ) { - if ( d_data->trackerPosition.x() < 0 || d_data->trackerPosition.y() < 0 ) { + if ( trackerMode() != AlwaysOff ) + { + if ( d_data->trackerPosition.x() < 0 || d_data->trackerPosition.y() < 0 ) + { QWidget *w = parentWidget(); if ( w ) - d_data->trackerPosition = w->mapFromGlobal(QCursor::pos()); + d_data->trackerPosition = w->mapFromGlobal( QCursor::pos() ); } } updateDisplay(); - setMouseTracking(true); + setMouseTracking( true ); } /*! \brief Close a selection setting the state to inactive. - The selection is validated and maybe fixed by QwtPicker::accept(). + The selection is validated and maybe fixed by accept(). \param ok If true, complete the selection and emit a selected signal otherwise discard the selection. \return true if the selection is accepted, false otherwise - \sa isActive, begin(), append(), move(), selected(), accept() + \sa isActive(), begin(), append(), move(), selected(), accept() */ -bool QwtPicker::end(bool ok) +bool QwtPicker::end( bool ok ) { - if ( d_data->isActive ) { - setMouseTracking(false); + if ( d_data->isActive ) + { + setMouseTracking( false ); d_data->isActive = false; + Q_EMIT activated( false ); if ( trackerMode() == ActiveOnly ) - d_data->trackerPosition = QPoint(-1, -1); + d_data->trackerPosition = QPoint( -1, -1 ); if ( ok ) - ok = accept(d_data->selection); + ok = accept( d_data->pickedPoints ); if ( ok ) - emit selected(d_data->selection); + Q_EMIT selected( d_data->pickedPoints ); else - d_data->selection.resize(0); + d_data->pickedPoints.resize( 0 ); updateDisplay(); - } else + } + else ok = false; return ok; } /*! - Reset the state machine and terminate (end(false)) the selection + Reset the state machine and terminate ( end(false) ) the selection */ void QwtPicker::reset() { if ( d_data->stateMachine ) d_data->stateMachine->reset(); - if (isActive()) - end(false); + if ( isActive() ) + end( false ); } /*! - Append a point to the selection and update rubberband and tracker. + Append a point to the selection and update rubber band and tracker. The appended() signal is emitted. \param pos Additional point - \sa isActive, begin(), end(), move(), appended() + \sa isActive(), begin(), end(), move(), appended() */ -void QwtPicker::append(const QPoint &pos) +void QwtPicker::append( const QPoint &pos ) { - if ( d_data->isActive ) { - const int idx = d_data->selection.count(); - d_data->selection.resize(idx + 1); - d_data->selection[idx] = pos; + if ( d_data->isActive ) + { + const int idx = d_data->pickedPoints.count(); + d_data->pickedPoints.resize( idx + 1 ); + d_data->pickedPoints[idx] = pos; updateDisplay(); - - emit appended(pos); + Q_EMIT appended( pos ); } } @@ -1171,27 +1314,61 @@ void QwtPicker::append(const QPoint &pos) The moved() signal is emitted. \param pos New position - \sa isActive, begin(), end(), append() - + \sa isActive(), begin(), end(), append() */ -void QwtPicker::move(const QPoint &pos) +void QwtPicker::move( const QPoint &pos ) { - if ( d_data->isActive ) { - const int idx = d_data->selection.count() - 1; - if ( idx >= 0 ) { - if ( d_data->selection[idx] != pos ) { - d_data->selection[idx] = pos; + if ( d_data->isActive ) + { + const int idx = d_data->pickedPoints.count() - 1; + if ( idx >= 0 ) + { + if ( d_data->pickedPoints[idx] != pos ) + { + d_data->pickedPoints[idx] = pos; updateDisplay(); - - emit moved(pos); + Q_EMIT moved( pos ); } } } } -bool QwtPicker::accept(QwtPolygon &) const +/*! + Remove the last point of the selection + The removed() signal is emitted. + + \sa isActive(), begin(), end(), append(), move() +*/ +void QwtPicker::remove() +{ + if ( d_data->isActive ) + { + const int idx = d_data->pickedPoints.count() - 1; + if ( idx > 0 ) + { + const int idx = d_data->pickedPoints.count(); + + const QPoint pos = d_data->pickedPoints[idx - 1]; + d_data->pickedPoints.resize( idx - 1 ); + + updateDisplay(); + Q_EMIT removed( pos ); + } + } +} + +/*! + \brief Validate and fix up the selection + + Accepts all selections unmodified + + \param selection Selection to validate and fix up + \return true, when accepted, false otherwise +*/ +bool QwtPicker::accept( QPolygon &selection ) const { + Q_UNUSED( selection ); return true; } @@ -1204,10 +1381,14 @@ bool QwtPicker::isActive() const return d_data->isActive; } -//! Return Selected points -const QwtPolygon &QwtPicker::selection() const +/*! + Return the points, that have been collected so far. The selection() + is calculated from the pickedPoints() in adjustedPoints(). + \return Picked points +*/ +const QPolygon &QwtPicker::pickedPoints() const { - return d_data->selection; + return d_data->pickedPoints; } /*! @@ -1219,25 +1400,27 @@ const QwtPolygon &QwtPicker::selection() const \sa ResizeMode, setResizeMode(), resizeMode() */ -void QwtPicker::stretchSelection(const QSize &oldSize, const QSize &newSize) +void QwtPicker::stretchSelection( const QSize &oldSize, const QSize &newSize ) { - if ( oldSize.isEmpty() ) { + if ( oldSize.isEmpty() ) + { // avoid division by zero. But scaling for small sizes also // doesn't make much sense, because of rounding losses. TODO ... return; } const double xRatio = - double(newSize.width()) / double(oldSize.width()); + double( newSize.width() ) / double( oldSize.width() ); const double yRatio = - double(newSize.height()) / double(oldSize.height()); + double( newSize.height() ) / double( oldSize.height() ); - for ( int i = 0; i < int(d_data->selection.count()); i++ ) { - QPoint &p = d_data->selection[i]; - p.setX(qRound(p.x() * xRatio)); - p.setY(qRound(p.y() * yRatio)); + for ( int i = 0; i < int( d_data->pickedPoints.count() ); i++ ) + { + QPoint &p = d_data->pickedPoints[i]; + p.setX( qRound( p.x() * xRatio ) ); + p.setY( qRound( p.y() * yRatio ) ); - emit changed(d_data->selection); + Q_EMIT changed( d_data->pickedPoints ); } } @@ -1254,98 +1437,141 @@ void QwtPicker::stretchSelection(const QSize &oldSize, const QSize &newSize) be restored. */ -void QwtPicker::setMouseTracking(bool enable) +void QwtPicker::setMouseTracking( bool enable ) { QWidget *widget = parentWidget(); if ( !widget ) return; - if ( enable ) { + if ( enable ) + { d_data->mouseTracking = widget->hasMouseTracking(); - widget->setMouseTracking(true); - } else { - widget->setMouseTracking(d_data->mouseTracking); + widget->setMouseTracking( true ); + } + else + { + widget->setMouseTracking( d_data->mouseTracking ); } } /*! Find the area of the observed widget, where selection might happen. - \return QFrame::contentsRect() if it is a QFrame, QWidget::rect() otherwise. + \return parentWidget()->contentsRect() */ -QRect QwtPicker::pickRect() const +QPainterPath QwtPicker::pickArea() const { - QRect rect; + QPainterPath path; const QWidget *widget = parentWidget(); - if ( !widget ) - return rect; - - if ( widget->inherits("QFrame") ) - rect = ((QFrame *)widget)->contentsRect(); - else - rect = widget->rect(); + if ( widget ) + path.addRect( widget->contentsRect() ); - return rect; + return path; } +//! Update the state of rubber band and tracker label void QwtPicker::updateDisplay() { QWidget *w = parentWidget(); bool showRubberband = false; bool showTracker = false; - if ( w && w->isVisible() && d_data->enabled ) { + + if ( w && w->isVisible() && d_data->enabled ) + { if ( rubberBand() != NoRubberBand && isActive() && - rubberBandPen().style() != Qt::NoPen ) { + rubberBandPen().style() != Qt::NoPen ) + { showRubberband = true; } if ( trackerMode() == AlwaysOn || - (trackerMode() == ActiveOnly && isActive() ) ) { - if ( trackerPen() != Qt::NoPen ) + ( trackerMode() == ActiveOnly && isActive() ) ) + { + if ( trackerPen() != Qt::NoPen + && !trackerRect( QFont() ).isEmpty() ) + { showTracker = true; + } } } -#if QT_VERSION < 0x040000 - QGuardedPtr &rw = d_data->rubberBandWidget; -#else - QPointer &rw = d_data->rubberBandWidget; -#endif - if ( showRubberband ) { - if ( rw.isNull() ) { - rw = new PickerWidget( this, w, PickerWidget::RubberBand); - rw->resize(w->size()); + QPointer< QwtPickerRubberband > &rw = d_data->rubberBandOverlay; + if ( showRubberband ) + { + if ( rw.isNull() ) + { + rw = new QwtPickerRubberband( this, w ); + rw->setObjectName( "PickerRubberBand" ); + rw->resize( w->size() ); } - rw->updateMask(); - rw->update(); // Needed, when the mask doesn't change - } else - delete rw; - -#if QT_VERSION < 0x040000 - QGuardedPtr &tw = d_data->trackerWidget; -#else - QPointer &tw = d_data->trackerWidget; -#endif - if ( showTracker ) { - if ( tw.isNull() ) { - tw = new PickerWidget( this, w, PickerWidget::Text); - tw->resize(w->size()); + + if ( d_data->rubberBand <= RectRubberBand ) + rw->setMaskMode( QwtWidgetOverlay::MaskHint ); + else + rw->setMaskMode( QwtWidgetOverlay::AlphaMask ); + + rw->updateOverlay(); + } + else + { + if ( d_data->openGL ) + { + // Qt 4.8 crashes for a delete + if ( !rw.isNull() ) + { + rw->hide(); + rw->deleteLater(); + rw = NULL; + } + } + else + { + delete rw; } - tw->updateMask(); - tw->update(); // Needed, when the mask doesn't change - } else - delete tw; + } + + QPointer< QwtPickerTracker > &tw = d_data->trackerOverlay; + if ( showTracker ) + { + if ( tw.isNull() ) + { + tw = new QwtPickerTracker( this, w ); + tw->setObjectName( "PickerTracker" ); + tw->resize( w->size() ); + } + tw->setFont( d_data->trackerFont ); + tw->updateOverlay(); + } + else + { + if ( d_data->openGL ) + { + // Qt 4.8 crashes for a delete + if ( !tw.isNull() ) + { + tw->hide(); + tw->deleteLater(); + tw = NULL; + } + } + else + { + delete tw; + } + } } -const QWidget *QwtPicker::rubberBandWidget() const +//! \return Overlay displaying the rubber band +const QwtWidgetOverlay *QwtPicker::rubberBandOverlay() const { - return d_data->rubberBandWidget; + return d_data->rubberBandOverlay; } -const QWidget *QwtPicker::trackerWidget() const +//! \return Overlay displaying the tracker text +const QwtWidgetOverlay *QwtPicker::trackerOverlay() const { - return d_data->trackerWidget; + return d_data->trackerOverlay; } diff --git a/libs/qwt/qwt_picker.h b/libs/qwt/qwt_picker.h index f3e6580ba..87d6805e9 100644 --- a/libs/qwt/qwt_picker.h +++ b/libs/qwt/qwt_picker.h @@ -10,62 +10,77 @@ #ifndef QWT_PICKER #define QWT_PICKER 1 +#include "qwt_global.h" +#include "qwt_text.h" +#include "qwt_event_pattern.h" #include #include #include #include -#include "qwt_global.h" -#include "qwt_text.h" -#include "qwt_polygon.h" -#include "qwt_event_pattern.h" +#include class QWidget; class QMouseEvent; class QWheelEvent; class QKeyEvent; class QwtPickerMachine; +class QwtWidgetOverlay; /*! \brief QwtPicker provides selections on a widget - QwtPicker filters all mouse and keyboard events of a widget - and translates them into an array of selected points. Depending - on the QwtPicker::SelectionType the selection might be a single point, - a rectangle or a polygon. The selection process is supported by - optional rubberbands (rubberband selection) and position trackers. + QwtPicker filters all enter, leave, mouse and keyboard events of a widget + and translates them into an array of selected points. + + The way how the points are collected depends on type of state machine + that is connected to the picker. Qwt offers a couple of predefined + state machines for selecting: - QwtPicker is useful for widgets where the event handlers - can't be overloaded, like for components of composite widgets. - It offers alternative handlers for mouse and key events. + - Nothing\n + QwtPickerTrackerMachine + - Single points\n + QwtPickerClickPointMachine, QwtPickerDragPointMachine + - Rectangles\n + QwtPickerClickRectMachine, QwtPickerDragRectMachine + - Polygons\n + QwtPickerPolygonMachine + + While these state machines cover the most common ways to collect points + it is also possible to implement individual machines as well. + + QwtPicker translates the picked points into a selection using the + adjustedPoints() method. adjustedPoints() is intended to be reimplemented + to fix up the selection according to application specific requirements. + (F.e. when an application accepts rectangles of a fixed aspect ratio only.) + + Optionally QwtPicker support the process of collecting points by a + rubber band and tracker displaying a text for the current mouse + position. \par Example \verbatim #include +#include QwtPicker *picker = new QwtPicker(widget); +picker->setStateMachine(new QwtPickerDragRectMachine); picker->setTrackerMode(QwtPicker::ActiveOnly); -connect(picker, SIGNAL(selected(const QwtPolygon &)), ...); - -// emit the position of clicks on widget -picker->setSelectionFlags(QwtPicker::PointSelection | QwtPicker::ClickSelection); - - ... - -// now select rectangles -picker->setSelectionFlags(QwtPicker::RectSelection | QwtPicker::DragSelection); picker->setRubberBand(QwtPicker::RectRubberBand); \endverbatim\n - The selection process uses the commands begin(), append(), move() and end(). - append() adds a new point to the selection, move() changes the position of - the latest point. + The state machine triggers the following commands: - The commands are initiated from a small state machine (QwtPickerMachine) - that translates mouse and key events. There are a couple of predefined - state machines for point, rect and polygon selections. The selectionFlags() - control which one should be used. It is possible to use other machines - by overloading stateMachine(). + - begin()\n + Activate/Initialize the selection. + - append()\n + Add a new point + - move() \n + Change the position of the last point. + - remove()\n + Remove the last point. + - end()\n + Terminate the selection and call accept to validate the picked points. The picker is active (isActive()), between begin() and end(). - In active state the rubberband is displayed, and the tracker is visible + In active state the rubber band is displayed, and the tracker is visible in case of trackerMode is ActiveOnly or AlwaysOn. The cursor can be moved using the arrow keys. All selections can be aborted @@ -73,7 +88,7 @@ picker->setRubberBand(QwtPicker::RectRubberBand); \endverbatim\n \warning In case of QWidget::NoFocus the focus policy of the observed widget is set to QWidget::WheelFocus and mouse tracking - will be manipulated for ClickSelection while the picker is active, + will be manipulated while the picker is active, or if trackerMode() is AlwayOn. */ @@ -81,214 +96,158 @@ class QWT_EXPORT QwtPicker: public QObject, public QwtEventPattern { Q_OBJECT - Q_ENUMS(RubberBand) - Q_ENUMS(DisplayMode) - Q_ENUMS(ResizeMode) + Q_ENUMS( RubberBand DisplayMode ResizeMode ) - Q_PROPERTY(int selectionFlags READ selectionFlags WRITE setSelectionFlags) - Q_PROPERTY(DisplayMode trackerMode READ trackerMode WRITE setTrackerMode) - Q_PROPERTY(QFont trackerFont READ trackerFont WRITE setTrackerFont) - Q_PROPERTY(RubberBand rubberBand READ rubberBand WRITE setRubberBand) - Q_PROPERTY(ResizeMode resizeMode READ resizeMode WRITE setResizeMode) - Q_PROPERTY(bool isEnabled READ isEnabled WRITE setEnabled) + Q_PROPERTY( bool isEnabled READ isEnabled WRITE setEnabled ) + Q_PROPERTY( ResizeMode resizeMode READ resizeMode WRITE setResizeMode ) - Q_PROPERTY(QPen trackerPen READ trackerPen WRITE setTrackerPen) - Q_PROPERTY(QPen rubberBandPen READ rubberBandPen WRITE setRubberBandPen) + Q_PROPERTY( DisplayMode trackerMode READ trackerMode WRITE setTrackerMode ) + Q_PROPERTY( QPen trackerPen READ trackerPen WRITE setTrackerPen ) + Q_PROPERTY( QFont trackerFont READ trackerFont WRITE setTrackerFont ) -public: - /*! - This enum type describes the type of a selection. It can be or'd - with QwtPicker::RectSelectionType and QwtPicker::SelectionMode - and passed to QwtPicker::setSelectionFlags() - - NoSelection\n - Selection is disabled. Note this is different to the disabled - state, as you might have a tracker. - - PointSelection\n - Select a single point. - - RectSelection\n - Select a rectangle. - - PolygonSelection\n - Select a polygon. - - The default value is NoSelection. - \sa QwtPicker::setSelectionFlags(), QwtPicker::selectionFlags() - */ - - enum SelectionType { - NoSelection = 0, - PointSelection = 1, - RectSelection = 2, - PolygonSelection = 4 - }; - - /*! - \brief Selection subtype for RectSelection - This enum type describes the type of rectangle selections. - It can be or'd with QwtPicker::RectSelectionType and - QwtPicker::SelectionMode and passed to QwtPicker::setSelectionFlags(). - - CornerToCorner\n - The first and the second selected point are the corners - of the rectangle. - - CenterToCorner\n - The first point is the center, the second a corner of the - rectangle. - - CenterToRadius\n - The first point is the center of a quadrat, calculated by the maximum - of the x- and y-distance. - - The default value is CornerToCorner. - \sa QwtPicker::setSelectionFlags(), QwtPicker::selectionFlags() - */ - enum RectSelectionType { - CornerToCorner = 64, - CenterToCorner = 128, - CenterToRadius = 256 - }; + Q_PROPERTY( RubberBand rubberBand READ rubberBand WRITE setRubberBand ) + Q_PROPERTY( QPen rubberBandPen READ rubberBandPen WRITE setRubberBandPen ) +public: /*! - Values of this enum type or'd together with a SelectionType value - identifies which state machine should be used for the selection. - - The default value is ClickSelection. - \sa stateMachine() - */ - enum SelectionMode { - ClickSelection = 1024, - DragSelection = 2048 - }; + Rubber band style - /*! - Rubberband style - - NoRubberBand\n - No rubberband. - - HLineRubberBand & PointSelection\n - A horizontal line. - - VLineRubberBand & PointSelection\n - A vertical line. - - CrossRubberBand & PointSelection\n - A horizontal and a vertical line. - - RectRubberBand & RectSelection\n - A rectangle. - - EllipseRubberBand & RectSelection\n - An ellipse. - - PolygonRubberBand &PolygonSelection\n - A polygon. - - UserRubberBand\n - Values >= UserRubberBand can be used to define additional - rubber bands. - - The default value is NoRubberBand. - \sa QwtPicker::setRubberBand(), QwtPicker::rubberBand() + The default value is QwtPicker::NoRubberBand. + \sa setRubberBand(), rubberBand() */ - enum RubberBand { + enum RubberBand + { + //! No rubberband. NoRubberBand = 0, - // Point + //! A horizontal line ( only for QwtPickerMachine::PointSelection ) HLineRubberBand, + + //! A vertical line ( only for QwtPickerMachine::PointSelection ) VLineRubberBand, + + //! A crosshair ( only for QwtPickerMachine::PointSelection ) CrossRubberBand, - // Rect + //! A rectangle ( only for QwtPickerMachine::RectSelection ) RectRubberBand, + + //! An ellipse ( only for QwtPickerMachine::RectSelection ) EllipseRubberBand, - // Polygon + //! A polygon ( only for QwtPickerMachine::PolygonSelection ) PolygonRubberBand, + /*! + Values >= UserRubberBand can be used to define additional + rubber bands. + */ UserRubberBand = 100 }; /*! - - AlwaysOff\n - Display never. - - AlwaysOn\n - Display always. - - ActiveOnly\n - Display only when the selection is active. - - \sa QwtPicker::setTrackerMode(), QwtPicker::trackerMode(), - QwtPicker::isActive() + \brief Display mode + \sa setTrackerMode(), trackerMode(), isActive() */ - enum DisplayMode { + enum DisplayMode + { + //! Display never AlwaysOff, + + //! Display always AlwaysOn, + + //! Display only when the selection is active ActiveOnly }; /*! Controls what to do with the selected points of an active selection when the observed widget is resized. - - Stretch\n - All points are scaled according to the new size, - - KeepSize\n - All points remain unchanged. - The default value is Stretch. - \sa QwtPicker::setResizeMode(), QwtPicker::resize() + The default value is QwtPicker::Stretch. + \sa setResizeMode() */ - enum ResizeMode { + enum ResizeMode + { + //! All points are scaled according to the new size, Stretch, + + //! All points remain unchanged. KeepSize }; - explicit QwtPicker(QWidget *parent); - explicit QwtPicker(int selectionFlags, RubberBand rubberBand, - DisplayMode trackerMode, QWidget *); + explicit QwtPicker( QWidget *parent ); + explicit QwtPicker( RubberBand rubberBand, + DisplayMode trackerMode, QWidget * ); virtual ~QwtPicker(); - virtual void setSelectionFlags(int); - int selectionFlags() const; + void setStateMachine( QwtPickerMachine * ); + const QwtPickerMachine *stateMachine() const; + QwtPickerMachine *stateMachine(); - virtual void setRubberBand(RubberBand); + void setRubberBand( RubberBand ); RubberBand rubberBand() const; - virtual void setTrackerMode(DisplayMode); + void setTrackerMode( DisplayMode ); DisplayMode trackerMode() const; - virtual void setResizeMode(ResizeMode); + void setResizeMode( ResizeMode ); ResizeMode resizeMode() const; - virtual void setRubberBandPen(const QPen &); + void setRubberBandPen( const QPen & ); QPen rubberBandPen() const; - virtual void setTrackerPen(const QPen &); + void setTrackerPen( const QPen & ); QPen trackerPen() const; - virtual void setTrackerFont(const QFont &); + void setTrackerFont( const QFont & ); QFont trackerFont() const; bool isEnabled() const; - virtual void setEnabled(bool); - bool isActive() const; - virtual bool eventFilter(QObject *, QEvent *); + virtual bool eventFilter( QObject *, QEvent * ); QWidget *parentWidget(); const QWidget *parentWidget() const; - virtual QRect pickRect() const; - const QwtPolygon &selection() const; + virtual QPainterPath pickArea() const; + + virtual void drawRubberBand( QPainter * ) const; + virtual void drawTracker( QPainter * ) const; - virtual void drawRubberBand(QPainter *) const; - virtual void drawTracker(QPainter *) const; + virtual QRegion rubberBandMask() const; - virtual QwtText trackerText(const QPoint &pos) const; + virtual QwtText trackerText( const QPoint &pos ) const; QPoint trackerPosition() const; - QRect trackerRect(const QFont &) const; + virtual QRect trackerRect( const QFont & ) const; + QPolygon selection() const; + +public Q_SLOTS: + void setEnabled( bool ); + +Q_SIGNALS: + /*! + A signal indicating, when the picker has been activated. + Together with setEnabled() it can be used to implement + selections with more than one picker. + + \param on True, when the picker has been activated + */ + void activated( bool on ); -signals: /*! A signal emitting the selected points, at the end of a selection. - \param pa Selected points + \param polygon Selected points */ - void selected(const QwtPolygon &pa); + void selected( const QPolygon &polygon ); /*! A signal emitted when a point has been appended to the selection @@ -296,7 +255,7 @@ signals: \param pos Position of the appended point. \sa append(). moved() */ - void appended(const QPoint &pos); + void appended( const QPoint &pos ); /*! A signal emitted whenever the last appended point of the @@ -305,64 +264,64 @@ signals: \param pos Position of the moved last point of the selection. \sa move(), appended() */ - void moved(const QPoint &pos); + void moved( const QPoint &pos ); + + /*! + A signal emitted whenever the last appended point of the + selection has been removed. + \param pos Position of the point, that has been removed + \sa remove(), appended() + */ + void removed( const QPoint &pos ); /*! A signal emitted when the active selection has been changed. This might happen when the observed widget is resized. - \param pa Changed selection + \param selection Changed selection \sa stretchSelection() */ - void changed(const QwtPolygon &pa); + void changed( const QPolygon &selection ); protected: - /*! - \brief Validate and fixup the selection - - Accepts all selections unmodified - - \param selection Selection to validate and fixup - \return true, when accepted, false otherwise - */ - virtual bool accept(QwtPolygon &selection) const; + virtual QPolygon adjustedPoints( const QPolygon & ) const; - virtual void transition(const QEvent *); + virtual void transition( const QEvent * ); virtual void begin(); - virtual void append(const QPoint &); - virtual void move(const QPoint &); - virtual bool end(bool ok = true); + virtual void append( const QPoint & ); + virtual void move( const QPoint & ); + virtual void remove(); + virtual bool end( bool ok = true ); + virtual bool accept( QPolygon & ) const; virtual void reset(); - virtual void widgetMousePressEvent(QMouseEvent *); - virtual void widgetMouseReleaseEvent(QMouseEvent *); - virtual void widgetMouseDoubleClickEvent(QMouseEvent *); - virtual void widgetMouseMoveEvent(QMouseEvent *); - virtual void widgetWheelEvent(QWheelEvent *); - virtual void widgetKeyPressEvent(QKeyEvent *); - virtual void widgetKeyReleaseEvent(QKeyEvent *); - virtual void widgetLeaveEvent(QEvent *); - - virtual void stretchSelection(const QSize &oldSize, - const QSize &newSize); + virtual void widgetMousePressEvent( QMouseEvent * ); + virtual void widgetMouseReleaseEvent( QMouseEvent * ); + virtual void widgetMouseDoubleClickEvent( QMouseEvent * ); + virtual void widgetMouseMoveEvent( QMouseEvent * ); + virtual void widgetWheelEvent( QWheelEvent * ); + virtual void widgetKeyPressEvent( QKeyEvent * ); + virtual void widgetKeyReleaseEvent( QKeyEvent * ); + virtual void widgetEnterEvent( QEvent * ); + virtual void widgetLeaveEvent( QEvent * ); - virtual QwtPickerMachine *stateMachine(int) const; + virtual void stretchSelection( const QSize &oldSize, + const QSize &newSize ); virtual void updateDisplay(); - const QWidget *rubberBandWidget() const; - const QWidget *trackerWidget() const; + const QwtWidgetOverlay *rubberBandOverlay() const; + const QwtWidgetOverlay *trackerOverlay() const; + + const QPolygon &pickedPoints() const; private: - void init(QWidget *, int selectionFlags, RubberBand rubberBand, - DisplayMode trackerMode); + void init( QWidget *, RubberBand rubberBand, DisplayMode trackerMode ); - void setStateMachine(QwtPickerMachine *); - void setMouseTracking(bool); + void setMouseTracking( bool ); - class PickerWidget; class PrivateData; PrivateData *d_data; }; diff --git a/libs/qwt/qwt_picker_machine.cpp b/libs/qwt/qwt_picker_machine.cpp index 6752b1c47..b6644bf93 100644 --- a/libs/qwt/qwt_picker_machine.cpp +++ b/libs/qwt/qwt_picker_machine.cpp @@ -7,13 +7,14 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ -#include -#include "qwt_event_pattern.h" #include "qwt_picker_machine.h" +#include "qwt_event_pattern.h" +#include //! Constructor -QwtPickerMachine::QwtPickerMachine(): - d_state(0) +QwtPickerMachine::QwtPickerMachine( SelectionType type ): + d_selectionType( type ), + d_state( 0 ) { } @@ -22,6 +23,12 @@ QwtPickerMachine::~QwtPickerMachine() { } +//! Return the selection type +QwtPickerMachine::SelectionType QwtPickerMachine::selectionType() const +{ + return d_selectionType; +} + //! Return the current state int QwtPickerMachine::state() const { @@ -29,7 +36,7 @@ int QwtPickerMachine::state() const } //! Change the current state -void QwtPickerMachine::setState(int state) +void QwtPickerMachine::setState( int state ) { d_state = state; } @@ -37,271 +44,483 @@ void QwtPickerMachine::setState(int state) //! Set the current state to 0. void QwtPickerMachine::reset() { - setState(0); + setState( 0 ); +} + +//! Constructor +QwtPickerTrackerMachine::QwtPickerTrackerMachine(): + QwtPickerMachine( NoSelection ) +{ } //! Transition -QwtPickerMachine::CommandList QwtPickerClickPointMachine::transition( - const QwtEventPattern &eventPattern, const QEvent *e) +QList QwtPickerTrackerMachine::transition( + const QwtEventPattern &, const QEvent *e ) { - QwtPickerMachine::CommandList cmdList; - - switch(e->type()) { - case QEvent::MouseButtonPress: { - if ( eventPattern.mouseMatch( - QwtEventPattern::MouseSelect1, (const QMouseEvent *)e) ) { - cmdList += Begin; - cmdList += Append; - cmdList += End; + QList cmdList; + + switch ( e->type() ) + { + case QEvent::Enter: + case QEvent::MouseMove: + { + if ( state() == 0 ) + { + cmdList += Begin; + cmdList += Append; + setState( 1 ); + } + else + { + cmdList += Move; + } + break; } - break; - } - case QEvent::KeyPress: { - if ( eventPattern.keyMatch( - QwtEventPattern::KeySelect1, (const QKeyEvent *)e) ) { - cmdList += Begin; - cmdList += Append; + case QEvent::Leave: + { + cmdList += Remove; cmdList += End; + setState( 0 ); } - break; - } - default: - break; + default: + break; } return cmdList; } +//! Constructor +QwtPickerClickPointMachine::QwtPickerClickPointMachine(): + QwtPickerMachine( PointSelection ) +{ +} + //! Transition -QwtPickerMachine::CommandList QwtPickerDragPointMachine::transition( - const QwtEventPattern &eventPattern, const QEvent *e) +QList QwtPickerClickPointMachine::transition( + const QwtEventPattern &eventPattern, const QEvent *event ) { - QwtPickerMachine::CommandList cmdList; + QList cmdList; - switch(e->type()) { - case QEvent::MouseButtonPress: { - if ( eventPattern.mouseMatch( - QwtEventPattern::MouseSelect1, (const QMouseEvent *)e) ) { - if ( state() == 0 ) { + switch ( event->type() ) + { + case QEvent::MouseButtonPress: + { + if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, + static_cast( event ) ) ) + { cmdList += Begin; cmdList += Append; - setState(1); + cmdList += End; } + break; } - break; - } - case QEvent::MouseMove: - case QEvent::Wheel: { - if ( state() != 0 ) - cmdList += Move; - break; - } - case QEvent::MouseButtonRelease: { - if ( state() != 0 ) { - cmdList += End; - setState(0); - } - break; - } - case QEvent::KeyPress: { - if ( eventPattern.keyMatch( - QwtEventPattern::KeySelect1, (const QKeyEvent *)e) ) { - if ( state() == 0 ) { + case QEvent::KeyPress: + { + if ( eventPattern.keyMatch( QwtEventPattern::KeySelect1, + static_cast ( event ) ) ) + { cmdList += Begin; cmdList += Append; - setState(1); - } else { cmdList += End; - setState(0); } + break; } - break; - } - default: - break; + default: + break; } return cmdList; } +//! Constructor +QwtPickerDragPointMachine::QwtPickerDragPointMachine(): + QwtPickerMachine( PointSelection ) +{ +} + //! Transition -QwtPickerMachine::CommandList QwtPickerClickRectMachine::transition( - const QwtEventPattern &eventPattern, const QEvent *e) +QList QwtPickerDragPointMachine::transition( + const QwtEventPattern &eventPattern, const QEvent *event ) { - QwtPickerMachine::CommandList cmdList; - - switch(e->type()) { - case QEvent::MouseButtonPress: { - if ( eventPattern.mouseMatch( - QwtEventPattern::MouseSelect1, (const QMouseEvent *)e) ) { - switch(state()) { - case 0: { - cmdList += Begin; - cmdList += Append; - setState(1); - break; - } - case 1: { - // Uh, strange we missed the MouseButtonRelease - break; + QList cmdList; + + switch ( event->type() ) + { + case QEvent::MouseButtonPress: + { + if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, + static_cast( event ) ) ) + { + if ( state() == 0 ) + { + cmdList += Begin; + cmdList += Append; + setState( 1 ); + } } - default: { + break; + } + case QEvent::MouseMove: + case QEvent::Wheel: + { + if ( state() != 0 ) + cmdList += Move; + break; + } + case QEvent::MouseButtonRelease: + { + if ( state() != 0 ) + { cmdList += End; - setState(0); + setState( 0 ); } + break; + } + case QEvent::KeyPress: + { + if ( eventPattern.keyMatch( QwtEventPattern::KeySelect1, + static_cast( event ) ) ) + { + if ( state() == 0 ) + { + cmdList += Begin; + cmdList += Append; + setState( 1 ); + } + else + { + cmdList += End; + setState( 0 ); + } } + break; } + default: + break; } - case QEvent::MouseMove: - case QEvent::Wheel: { - if ( state() != 0 ) - cmdList += Move; - break; - } - case QEvent::MouseButtonRelease: { - if ( eventPattern.mouseMatch( - QwtEventPattern::MouseSelect1, (const QMouseEvent *)e) ) { - if ( state() == 1 ) { - cmdList += Append; - setState(2); + + return cmdList; +} + +//! Constructor +QwtPickerClickRectMachine::QwtPickerClickRectMachine(): + QwtPickerMachine( RectSelection ) +{ +} + +//! Transition +QList QwtPickerClickRectMachine::transition( + const QwtEventPattern &eventPattern, const QEvent *event ) +{ + QList cmdList; + + switch ( event->type() ) + { + case QEvent::MouseButtonPress: + { + if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, + static_cast( event ) ) ) + { + switch ( state() ) + { + case 0: + { + cmdList += Begin; + cmdList += Append; + setState( 1 ); + break; + } + case 1: + { + // Uh, strange we missed the MouseButtonRelease + break; + } + default: + { + cmdList += End; + setState( 0 ); + } + } } + break; } - break; - } - case QEvent::KeyPress: { - if ( eventPattern.keyMatch( - QwtEventPattern::KeySelect1, (const QKeyEvent *)e) ) { - if ( state() == 0 ) { - cmdList += Begin; - cmdList += Append; - setState(1); - } else { - if ( state() == 1 ) { + case QEvent::MouseMove: + case QEvent::Wheel: + { + if ( state() != 0 ) + cmdList += Move; + break; + } + case QEvent::MouseButtonRelease: + { + if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, + static_cast( event ) ) ) + { + if ( state() == 1 ) + { cmdList += Append; - setState(2); - } else if ( state() == 2 ) { - cmdList += End; - setState(0); + setState( 2 ); } } + break; } - break; - } - default: - break; + case QEvent::KeyPress: + { + if ( eventPattern.keyMatch( QwtEventPattern::KeySelect1, + static_cast ( event ) ) ) + { + if ( state() == 0 ) + { + cmdList += Begin; + cmdList += Append; + setState( 1 ); + } + else + { + if ( state() == 1 ) + { + cmdList += Append; + setState( 2 ); + } + else if ( state() == 2 ) + { + cmdList += End; + setState( 0 ); + } + } + } + break; + } + default: + break; } return cmdList; } +//! Constructor +QwtPickerDragRectMachine::QwtPickerDragRectMachine(): + QwtPickerMachine( RectSelection ) +{ +} + //! Transition -QwtPickerMachine::CommandList QwtPickerDragRectMachine::transition( - const QwtEventPattern &eventPattern, const QEvent *e) +QList QwtPickerDragRectMachine::transition( + const QwtEventPattern &eventPattern, const QEvent *event ) { - QwtPickerMachine::CommandList cmdList; + QList cmdList; - switch(e->type()) { - case QEvent::MouseButtonPress: { - if ( eventPattern.mouseMatch( - QwtEventPattern::MouseSelect1, (const QMouseEvent *)e) ) { - if ( state() == 0 ) { - cmdList += Begin; - cmdList += Append; - cmdList += Append; - setState(2); + switch ( event->type() ) + { + case QEvent::MouseButtonPress: + { + if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, + static_cast( event ) ) ) + { + if ( state() == 0 ) + { + cmdList += Begin; + cmdList += Append; + cmdList += Append; + setState( 2 ); + } } + break; } - break; - } - case QEvent::MouseMove: - case QEvent::Wheel: { - if ( state() != 0 ) - cmdList += Move; - break; - } - case QEvent::MouseButtonRelease: { - if ( state() == 2 ) { - cmdList += End; - setState(0); + case QEvent::MouseMove: + case QEvent::Wheel: + { + if ( state() != 0 ) + cmdList += Move; + break; } - break; - } - case QEvent::KeyPress: { - if ( eventPattern.keyMatch( - QwtEventPattern::KeySelect1, (const QKeyEvent *)e) ) { - if ( state() == 0 ) { - cmdList += Begin; - cmdList += Append; - cmdList += Append; - setState(2); - } else { + case QEvent::MouseButtonRelease: + { + if ( state() == 2 ) + { cmdList += End; - setState(0); + setState( 0 ); } + break; } - break; - } - default: - break; + case QEvent::KeyPress: + { + if ( eventPattern.keyMatch( QwtEventPattern::KeySelect1, + static_cast ( event ) ) ) + { + if ( state() == 0 ) + { + cmdList += Begin; + cmdList += Append; + cmdList += Append; + setState( 2 ); + } + else + { + cmdList += End; + setState( 0 ); + } + } + break; + } + default: + break; } return cmdList; } +//! Constructor +QwtPickerPolygonMachine::QwtPickerPolygonMachine(): + QwtPickerMachine( PolygonSelection ) +{ +} + //! Transition -QwtPickerMachine::CommandList QwtPickerPolygonMachine::transition( - const QwtEventPattern &eventPattern, const QEvent *e) +QList QwtPickerPolygonMachine::transition( + const QwtEventPattern &eventPattern, const QEvent *event ) { - QwtPickerMachine::CommandList cmdList; + QList cmdList; - switch(e->type()) { - case QEvent::MouseButtonPress: { - if ( eventPattern.mouseMatch( - QwtEventPattern::MouseSelect1, (const QMouseEvent *)e) ) { - if (state() == 0) { - cmdList += Begin; - cmdList += Append; - cmdList += Append; - setState(1); - } else { - cmdList += End; - setState(0); + switch ( event->type() ) + { + case QEvent::MouseButtonPress: + { + if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, + static_cast( event ) ) ) + { + if ( state() == 0 ) + { + cmdList += Begin; + cmdList += Append; + cmdList += Append; + setState( 1 ); + } + else + { + cmdList += Append; + } + } + if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect2, + static_cast( event ) ) ) + { + if ( state() == 1 ) + { + cmdList += End; + setState( 0 ); + } } + break; } - if ( eventPattern.mouseMatch( - QwtEventPattern::MouseSelect2, (const QMouseEvent *)e) ) { - if (state() == 1) - cmdList += Append; + case QEvent::MouseMove: + case QEvent::Wheel: + { + if ( state() != 0 ) + cmdList += Move; + break; } - break; - } - case QEvent::MouseMove: - case QEvent::Wheel: { - if ( state() != 0 ) - cmdList += Move; - break; + case QEvent::KeyPress: + { + if ( eventPattern.keyMatch( QwtEventPattern::KeySelect1, + static_cast ( event ) ) ) + { + if ( state() == 0 ) + { + cmdList += Begin; + cmdList += Append; + cmdList += Append; + setState( 1 ); + } + else + { + cmdList += Append; + } + } + else if ( eventPattern.keyMatch( QwtEventPattern::KeySelect2, + static_cast ( event ) ) ) + { + if ( state() == 1 ) + { + cmdList += End; + setState( 0 ); + } + } + break; + } + default: + break; } - case QEvent::KeyPress: { - if ( eventPattern.keyMatch( - QwtEventPattern::KeySelect1, (const QKeyEvent *)e) ) { - if ( state() == 0 ) { - cmdList += Begin; - cmdList += Append; - cmdList += Append; - setState(1); - } else { + + return cmdList; +} + +//! Constructor +QwtPickerDragLineMachine::QwtPickerDragLineMachine(): + QwtPickerMachine( PolygonSelection ) +{ +} + +//! Transition +QList QwtPickerDragLineMachine::transition( + const QwtEventPattern &eventPattern, const QEvent *event ) +{ + QList cmdList; + + switch( event->type() ) + { + case QEvent::MouseButtonPress: + { + if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, + static_cast( event ) ) ) + { + if ( state() == 0 ) + { + cmdList += Begin; + cmdList += Append; + cmdList += Append; + setState( 1 ); + } + } + break; + } + case QEvent::KeyPress: + { + if ( eventPattern.keyMatch( QwtEventPattern::KeySelect1, + static_cast ( event ) ) ) + { + if ( state() == 0 ) + { + cmdList += Begin; + cmdList += Append; + cmdList += Append; + setState( 1 ); + } + else + { + cmdList += End; + setState( 0 ); + } + } + break; + } + case QEvent::MouseMove: + case QEvent::Wheel: + { + if ( state() != 0 ) + cmdList += Move; + + break; + } + case QEvent::MouseButtonRelease: + { + if ( state() != 0 ) + { cmdList += End; - setState(0); + setState( 0 ); } - } else if ( eventPattern.keyMatch( - QwtEventPattern::KeySelect2, (const QKeyEvent *)e) ) { - if ( state() == 1 ) - cmdList += Append; } - break; - } - default: - break; + default: + break; } return cmdList; diff --git a/libs/qwt/qwt_picker_machine.h b/libs/qwt/qwt_picker_machine.h index b6dd4b8cc..6164b93b5 100644 --- a/libs/qwt/qwt_picker_machine.h +++ b/libs/qwt/qwt_picker_machine.h @@ -11,11 +11,7 @@ #define QWT_PICKER_MACHINE 1 #include "qwt_global.h" -#if QT_VERSION < 0x040000 -#include -#else #include -#endif class QEvent; class QwtEventPattern; @@ -32,37 +28,69 @@ class QwtEventPattern; class QWT_EXPORT QwtPickerMachine { public: - //! Commands - the output of the state machine - enum Command { + /*! + Type of a selection. + \sa selectionType() + */ + enum SelectionType + { + //! The state machine not usable for any type of selection. + NoSelection = -1, + + //! The state machine is for selecting a single point. + PointSelection, + + //! The state machine is for selecting a rectangle (2 points). + RectSelection, + + //! The state machine is for selecting a polygon (many points). + PolygonSelection + }; + + //! Commands - the output of a state machine + enum Command + { Begin, Append, Move, + Remove, End }; -#if QT_VERSION < 0x040000 - typedef QValueList CommandList; -#else - typedef QList CommandList; -#endif - + QwtPickerMachine( SelectionType ); virtual ~QwtPickerMachine(); //! Transition - virtual CommandList transition( - const QwtEventPattern &, const QEvent *) = 0; + virtual QList transition( + const QwtEventPattern &, const QEvent * ) = 0; void reset(); int state() const; - void setState(int); + void setState( int ); -protected: - QwtPickerMachine(); + SelectionType selectionType() const; private: + const SelectionType d_selectionType; int d_state; }; +/*! + \brief A state machine for indicating mouse movements + + QwtPickerTrackerMachine supports displaying information + corresponding to mouse movements, but is not intended for + selecting anything. Begin/End are related to Enter/Leave events. +*/ +class QWT_EXPORT QwtPickerTrackerMachine: public QwtPickerMachine +{ +public: + QwtPickerTrackerMachine(); + + virtual QList transition( + const QwtEventPattern &, const QEvent * ); +}; + /*! \brief A state machine for point selections @@ -74,8 +102,10 @@ private: class QWT_EXPORT QwtPickerClickPointMachine: public QwtPickerMachine { public: - virtual CommandList transition( - const QwtEventPattern &, const QEvent *); + QwtPickerClickPointMachine(); + + virtual QList transition( + const QwtEventPattern &, const QEvent * ); }; /*! @@ -88,8 +118,10 @@ public: class QWT_EXPORT QwtPickerDragPointMachine: public QwtPickerMachine { public: - virtual CommandList transition( - const QwtEventPattern &, const QEvent *); + QwtPickerDragPointMachine(); + + virtual QList transition( + const QwtEventPattern &, const QEvent * ); }; /*! @@ -108,8 +140,10 @@ public: class QWT_EXPORT QwtPickerClickRectMachine: public QwtPickerMachine { public: - virtual CommandList transition( - const QwtEventPattern &, const QEvent *); + QwtPickerClickRectMachine(); + + virtual QList transition( + const QwtEventPattern &, const QEvent * ); }; /*! @@ -127,8 +161,34 @@ public: class QWT_EXPORT QwtPickerDragRectMachine: public QwtPickerMachine { public: - virtual CommandList transition( - const QwtEventPattern &, const QEvent *); + QwtPickerDragRectMachine(); + + virtual QList transition( + const QwtEventPattern &, const QEvent * ); +}; + +/*! + \brief A state machine for line selections + + Pressing QwtEventPattern::MouseSelect1 selects + the first point, releasing it the second point. + Pressing QwtEventPattern::KeySelect1 also selects the + first point, a second press selects the second point and terminates + the selection. + + A common use case of QwtPickerDragLineMachine are pickers for + distance measurements. + + \sa QwtEventPattern::MousePatternCode, QwtEventPattern::KeyPatternCode +*/ + +class QWT_EXPORT QwtPickerDragLineMachine: public QwtPickerMachine +{ +public: + QwtPickerDragLineMachine(); + + virtual QList transition( + const QwtEventPattern &, const QEvent * ); }; /*! @@ -145,8 +205,10 @@ public: class QWT_EXPORT QwtPickerPolygonMachine: public QwtPickerMachine { public: - virtual CommandList transition( - const QwtEventPattern &, const QEvent *); + QwtPickerPolygonMachine(); + + virtual QList transition( + const QwtEventPattern &, const QEvent * ); }; #endif diff --git a/libs/qwt/qwt_pixel_matrix.cpp b/libs/qwt/qwt_pixel_matrix.cpp new file mode 100644 index 000000000..627992c7a --- /dev/null +++ b/libs/qwt/qwt_pixel_matrix.cpp @@ -0,0 +1,51 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2003 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_pixel_matrix.h" + +/*! + \brief Constructor + + \param rect Bounding rectangle for the matrix +*/ +QwtPixelMatrix::QwtPixelMatrix( const QRect& rect ): + QBitArray( qMax( rect.width() * rect.height(), 0 ) ), + d_rect( rect ) +{ +} + +//! Destructor +QwtPixelMatrix::~QwtPixelMatrix() +{ +} + +/*! + Set the bounding rectangle of the matrix + + \param rect Bounding rectangle + + \note All bits are cleared + */ +void QwtPixelMatrix::setRect( const QRect& rect ) +{ + if ( rect != d_rect ) + { + d_rect = rect; + const int sz = qMax( rect.width() * rect.height(), 0 ); + resize( sz ); + } + + fill( false ); +} + +//! \return Bounding rectangle +QRect QwtPixelMatrix::rect() const +{ + return d_rect; +} diff --git a/libs/qwt/qwt_pixel_matrix.h b/libs/qwt/qwt_pixel_matrix.h new file mode 100644 index 000000000..0fe9daf1c --- /dev/null +++ b/libs/qwt/qwt_pixel_matrix.h @@ -0,0 +1,98 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2003 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_PIXEL_MATRIX_H +#define QWT_PIXEL_MATRIX_H + +#include "qwt_global.h" +#include +#include + +/*! + \brief A bit field corresponding to the pixels of a rectangle + + QwtPixelMatrix is intended to filter out duplicates in an + unsorted array of points. +*/ +class QWT_EXPORT QwtPixelMatrix: public QBitArray +{ +public: + QwtPixelMatrix( const QRect& rect ); + ~QwtPixelMatrix(); + + void setRect( const QRect& rect ); + QRect rect() const; + + bool testPixel( int x, int y ) const; + bool testAndSetPixel( int x, int y, bool on ); + + int index( int x, int y ) const; + +private: + QRect d_rect; +}; + +/*! + \brief Test if a pixel has been set + + \param x X-coordinate + \param y Y-coordinate + + \return true, when pos is outside of rect(), or when the pixel + has already been set. + */ +inline bool QwtPixelMatrix::testPixel( int x, int y ) const +{ + const int idx = index( x, y ); + return ( idx >= 0 ) ? testBit( idx ) : true; +} + +/*! + \brief Set a pixel and test if a pixel has been set before + + \param x X-coordinate + \param y Y-coordinate + \param on Set/Clear the pixel + + \return true, when pos is outside of rect(), or when the pixel + was set before. + */ +inline bool QwtPixelMatrix::testAndSetPixel( int x, int y, bool on ) +{ + const int idx = index( x, y ); + if ( idx < 0 ) + return true; + + const bool onBefore = testBit( idx ); + setBit( idx, on ); + + return onBefore; +} + +/*! + \brief Calculate the index in the bit field corresponding to a position + + \param x X-coordinate + \param y Y-coordinate + \return Index, when rect() contains pos - otherwise -1. + */ +inline int QwtPixelMatrix::index( int x, int y ) const +{ + const int dx = x - d_rect.x(); + if ( dx < 0 || dx >= d_rect.width() ) + return -1; + + const int dy = y - d_rect.y(); + if ( dy < 0 || dy >= d_rect.height() ) + return -1; + + return dy * d_rect.width() + dx; +} + +#endif diff --git a/libs/qwt/qwt_plot.cpp b/libs/qwt/qwt_plot.cpp index 0816d2260..d4740c051 100644 --- a/libs/qwt/qwt_plot.cpp +++ b/libs/qwt/qwt_plot.cpp @@ -7,16 +7,6 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ -#include -#if QT_VERSION < 0x040000 -#include -#include -#else -#include -#include -#endif -#include -#include #include "qwt_plot.h" #include "qwt_plot_dict.h" #include "qwt_plot_layout.h" @@ -24,22 +14,90 @@ #include "qwt_scale_engine.h" #include "qwt_text_label.h" #include "qwt_legend.h" -#include "qwt_dyngrid_layout.h" +#include "qwt_legend_data.h" #include "qwt_plot_canvas.h" -#include "qwt_paint_buffer.h" +#include +#include +#include +#include +#include +#include + +static inline void qwtEnableLegendItems( QwtPlot *plot, bool on ) +{ + if ( on ) + { + QObject::connect( + plot, SIGNAL( legendDataChanged( + const QVariant &, const QList & ) ), + plot, SLOT( updateLegendItems( + const QVariant &, const QList & ) ) ); + } + else + { + QObject::disconnect( + plot, SIGNAL( legendDataChanged( + const QVariant &, const QList & ) ), + plot, SLOT( updateLegendItems( + const QVariant &, const QList & ) ) ); + } +} + +static void qwtSetTabOrder( + QWidget *first, QWidget *second, bool withChildren ) +{ + QList tabChain; + tabChain += first; + tabChain += second; + + if ( withChildren ) + { + QList children = second->findChildren(); + + QWidget *w = second->nextInFocusChain(); + while ( children.contains( w ) ) + { + children.removeAll( w ); + + tabChain += w; + w = w->nextInFocusChain(); + } + } + + for ( int i = 0; i < tabChain.size() - 1; i++ ) + { + QWidget *from = tabChain[i]; + QWidget *to = tabChain[i+1]; + + const Qt::FocusPolicy policy1 = from->focusPolicy(); + const Qt::FocusPolicy policy2 = to->focusPolicy(); + + QWidget *proxy1 = from->focusProxy(); + QWidget *proxy2 = to->focusProxy(); + + from->setFocusPolicy( Qt::TabFocus ); + from->setFocusProxy( NULL); + + to->setFocusPolicy( Qt::TabFocus ); + to->setFocusProxy( NULL); + + QWidget::setTabOrder( from, to ); + + from->setFocusPolicy( policy1 ); + from->setFocusProxy( proxy1); + + to->setFocusPolicy( policy2 ); + to->setFocusProxy( proxy2 ); + } +} class QwtPlot::PrivateData { public: -#if QT_VERSION < 0x040000 - QGuardedPtr lblTitle; - QGuardedPtr canvas; - QGuardedPtr legend; -#else - QPointer lblTitle; - QPointer canvas; - QPointer legend; -#endif + QPointer titleLabel; + QPointer footerLabel; + QPointer canvas; + QPointer legend; QwtPlotLayout *layout; bool autoReplot; @@ -49,10 +107,10 @@ public: \brief Constructor \param parent Parent widget */ -QwtPlot::QwtPlot(QWidget *parent): - QFrame(parent) +QwtPlot::QwtPlot( QWidget *parent ): + QFrame( parent ) { - initPlot(QwtText()); + initPlot( QwtText() ); } /*! @@ -60,30 +118,16 @@ QwtPlot::QwtPlot(QWidget *parent): \param title Title text \param parent Parent widget */ -QwtPlot::QwtPlot(const QwtText &title, QWidget *parent): - QFrame(parent) -{ - initPlot(title); -} - -#if QT_VERSION < 0x040000 -/*! - \brief Constructor - \param parent Parent widget - \param name Object name - */ -QwtPlot::QwtPlot(QWidget *parent, const char *name): - QFrame(parent, name) +QwtPlot::QwtPlot( const QwtText &title, QWidget *parent ): + QFrame( parent ) { - initPlot(QwtText()); + initPlot( title ); } -#endif - //! Destructor QwtPlot::~QwtPlot() { - detachItems(QwtPlotItem::Rtti_PlotItem, autoDelete()); + detachItems( QwtPlotItem::Rtti_PlotItem, autoDelete() ); delete d_data->layout; deleteAxesData(); @@ -94,75 +138,160 @@ QwtPlot::~QwtPlot() \brief Initializes a QwtPlot instance \param title Title text */ -void QwtPlot::initPlot(const QwtText &title) +void QwtPlot::initPlot( const QwtText &title ) { d_data = new PrivateData; -#if QT_VERSION < 0x040000 - setWFlags(Qt::WNoAutoErase); -#endif - d_data->layout = new QwtPlotLayout; - d_data->autoReplot = false; - d_data->lblTitle = new QwtTextLabel(title, this); - d_data->lblTitle->setFont(QFont(fontInfo().family(), 14, QFont::Bold)); + // title + d_data->titleLabel = new QwtTextLabel( this ); + d_data->titleLabel->setObjectName( "QwtPlotTitle" ); + d_data->titleLabel->setFont( QFont( fontInfo().family(), 14, QFont::Bold ) ); - QwtText text(title); - int flags = Qt::AlignCenter; -#if QT_VERSION < 0x040000 - flags |= Qt::WordBreak | Qt::ExpandTabs; -#else - flags |= Qt::TextWordWrap; -#endif - text.setRenderFlags(flags); - d_data->lblTitle->setText(text); + QwtText text( title ); + text.setRenderFlags( Qt::AlignCenter | Qt::TextWordWrap ); + d_data->titleLabel->setText( text ); + + // footer + d_data->footerLabel = new QwtTextLabel( this ); + d_data->footerLabel->setObjectName( "QwtPlotFooter" ); + + QwtText footer; + footer.setRenderFlags( Qt::AlignCenter | Qt::TextWordWrap ); + d_data->footerLabel->setText( footer ); + // legend d_data->legend = NULL; + // axis initAxesData(); - d_data->canvas = new QwtPlotCanvas(this); - d_data->canvas->setFrameStyle(QFrame::Panel|QFrame::Sunken); - d_data->canvas->setLineWidth(2); - d_data->canvas->setMidLineWidth(0); + // canvas + d_data->canvas = new QwtPlotCanvas( this ); + d_data->canvas->setObjectName( "QwtPlotCanvas" ); + d_data->canvas->installEventFilter( this ); + + setSizePolicy( QSizePolicy::MinimumExpanding, + QSizePolicy::MinimumExpanding ); + + resize( 200, 200 ); + + QList focusChain; + focusChain << this << d_data->titleLabel << axisWidget( xTop ) + << axisWidget( yLeft ) << d_data->canvas << axisWidget( yRight ) + << axisWidget( xBottom ) << d_data->footerLabel; + + for ( int i = 0; i < focusChain.size() - 1; i++ ) + qwtSetTabOrder( focusChain[i], focusChain[i+1], false ); + + qwtEnableLegendItems( this, true ); +} + +/*! + \brief Set the drawing canvas of the plot widget + + QwtPlot invokes methods of the canvas as meta methods ( see QMetaObject ). + In opposite to using conventional C++ techniques like virtual methods + they allow to use canvas implementations that are derived from + QWidget or QGLWidget. + + The following meta methods could be implemented: + + - replot() + When the canvas doesn't offer a replot method, QwtPlot calls + update() instead. + + - borderPath() + The border path is necessary to clip the content of the canvas + When the canvas doesn't have any special border ( f.e rounded corners ) + it is o.k. not to implement this method. + + The default canvas is a QwtPlotCanvas - updateTabOrder(); + \param canvas Canvas Widget + \sa canvas() + */ +void QwtPlot::setCanvas( QWidget *canvas ) +{ + if ( canvas == d_data->canvas ) + return; + + delete d_data->canvas; + d_data->canvas = canvas; + + if ( canvas ) + { + canvas->setParent( this ); + canvas->installEventFilter( this ); - setSizePolicy(QSizePolicy::MinimumExpanding, - QSizePolicy::MinimumExpanding); + if ( isVisible() ) + canvas->show(); + } } /*! \brief Adds handling of layout requests + \param event Event + + \return See QFrame::event() */ -bool QwtPlot::event(QEvent *e) -{ - bool ok = QFrame::event(e); - switch(e->type()) { -#if QT_VERSION < 0x040000 - case QEvent::LayoutHint: -#else - case QEvent::LayoutRequest: -#endif - updateLayout(); - break; -#if QT_VERSION >= 0x040000 - case QEvent::PolishRequest: - polish(); - break; -#endif - default: - ; +bool QwtPlot::event( QEvent *event ) +{ + bool ok = QFrame::event( event ); + switch ( event->type() ) + { + case QEvent::LayoutRequest: + updateLayout(); + break; + case QEvent::PolishRequest: + replot(); + break; + default:; } return ok; } -//! Replots the plot if QwtPlot::autoReplot() is \c true. +/*! + \brief Event filter + + The plot handles the following events for the canvas: + + - QEvent::Resize + The canvas margins might depend on its size + + - QEvent::ContentsRectChange + The layout needs to be recalculated + + \param object Object to be filtered + \param event Event + + \return See QFrame::eventFilter() + + \sa updateCanvasMargins(), updateLayout() +*/ +bool QwtPlot::eventFilter( QObject *object, QEvent *event ) +{ + if ( object == d_data->canvas ) + { + if ( event->type() == QEvent::Resize ) + { + updateCanvasMargins(); + } + else if ( event->type() == QEvent::ContentsRectChange ) + { + updateLayout(); + } + } + + return QFrame::eventFilter( object, event ); +} + +//! Replots the plot if autoReplot() is \c true. void QwtPlot::autoRefresh() { - if (d_data->autoReplot) + if ( d_data->autoReplot ) replot(); } @@ -181,12 +310,15 @@ void QwtPlot::autoRefresh() \param tf \c true or \c false. Defaults to \c true. \sa replot() */ -void QwtPlot::setAutoReplot(bool tf) +void QwtPlot::setAutoReplot( bool tf ) { d_data->autoReplot = tf; } -//! \return true if the autoReplot option is set. +/*! + \return true if the autoReplot option is set. + \sa setAutoReplot() +*/ bool QwtPlot::autoReplot() const { return d_data->autoReplot; @@ -196,10 +328,11 @@ bool QwtPlot::autoReplot() const Change the plot's title \param title New title */ -void QwtPlot::setTitle(const QString &title) +void QwtPlot::setTitle( const QString &title ) { - if ( title != d_data->lblTitle->text().text() ) { - d_data->lblTitle->setText(title); + if ( title != d_data->titleLabel->text().text() ) + { + d_data->titleLabel->setText( title ); updateLayout(); } } @@ -208,51 +341,111 @@ void QwtPlot::setTitle(const QString &title) Change the plot's title \param title New title */ -void QwtPlot::setTitle(const QwtText &title) +void QwtPlot::setTitle( const QwtText &title ) { - if ( title != d_data->lblTitle->text() ) { - d_data->lblTitle->setText(title); + if ( title != d_data->titleLabel->text() ) + { + d_data->titleLabel->setText( title ); updateLayout(); } } -//! \return the plot's title +//! \return Title of the plot QwtText QwtPlot::title() const { - return d_data->lblTitle->text(); + return d_data->titleLabel->text(); } -//! \return the plot's title -QwtPlotLayout *QwtPlot::plotLayout() +//! \return Title label widget. +QwtTextLabel *QwtPlot::titleLabel() { - return d_data->layout; + return d_data->titleLabel; } -//! \return the plot's titel label. -const QwtPlotLayout *QwtPlot::plotLayout() const +//! \return Title label widget. +const QwtTextLabel *QwtPlot::titleLabel() const { - return d_data->layout; + return d_data->titleLabel; } -//! \return the plot's titel label. -QwtTextLabel *QwtPlot::titleLabel() +/*! + Change the text the footer + \param text New text of the footer +*/ +void QwtPlot::setFooter( const QString &text ) { - return d_data->lblTitle; + if ( text != d_data->footerLabel->text().text() ) + { + d_data->footerLabel->setText( text ); + updateLayout(); + } } /*! - \return the plot's titel label. + Change the text the footer + \param text New text of the footer */ -const QwtTextLabel *QwtPlot::titleLabel() const +void QwtPlot::setFooter( const QwtText &text ) +{ + if ( text != d_data->footerLabel->text() ) + { + d_data->footerLabel->setText( text ); + updateLayout(); + } +} + +//! \return Text of the footer +QwtText QwtPlot::footer() const +{ + return d_data->footerLabel->text(); +} + +//! \return Footer label widget. +QwtTextLabel *QwtPlot::footerLabel() +{ + return d_data->footerLabel; +} + +//! \return Footer label widget. +const QwtTextLabel *QwtPlot::footerLabel() const +{ + return d_data->footerLabel; +} + +/*! + \brief Assign a new plot layout + + \param layout Layout() + \sa plotLayout() + */ +void QwtPlot::setPlotLayout( QwtPlotLayout *layout ) +{ + if ( layout != d_data->layout ) + { + delete d_data->layout; + d_data->layout = layout; + + updateLayout(); + } +} + +//! \return the plot's layout +QwtPlotLayout *QwtPlot::plotLayout() +{ + return d_data->layout; +} + +//! \return the plot's layout +const QwtPlotLayout *QwtPlot::plotLayout() const { - return d_data->lblTitle; + return d_data->layout; } /*! \return the plot's legend \sa insertLegend() */ -QwtLegend *QwtPlot::legend() +QwtAbstractLegend *QwtPlot::legend() { return d_data->legend; } @@ -261,7 +454,7 @@ QwtLegend *QwtPlot::legend() \return the plot's legend \sa insertLegend() */ -const QwtLegend *QwtPlot::legend() const +const QwtAbstractLegend *QwtPlot::legend() const { return d_data->legend; } @@ -270,7 +463,7 @@ const QwtLegend *QwtPlot::legend() const /*! \return the plot's canvas */ -QwtPlotCanvas *QwtPlot::canvas() +QWidget *QwtPlot::canvas() { return d_data->canvas; } @@ -278,51 +471,45 @@ QwtPlotCanvas *QwtPlot::canvas() /*! \return the plot's canvas */ -const QwtPlotCanvas *QwtPlot::canvas() const +const QWidget *QwtPlot::canvas() const { return d_data->canvas; } -//! Polish -void QwtPlot::polish() -{ - replot(); - -#if QT_VERSION < 0x040000 - QFrame::polish(); -#endif -} - /*! - Return sizeHint + \return Size hint for the plot widget \sa minimumSizeHint() */ - QSize QwtPlot::sizeHint() const { int dw = 0; int dh = 0; - for ( int axisId = 0; axisId < axisCnt; axisId++ ) { - if ( axisEnabled(axisId) ) { + for ( int axisId = 0; axisId < axisCnt; axisId++ ) + { + if ( axisEnabled( axisId ) ) + { const int niceDist = 40; - const QwtScaleWidget *scaleWidget = axisWidget(axisId); + const QwtScaleWidget *scaleWidget = axisWidget( axisId ); const QwtScaleDiv &scaleDiv = scaleWidget->scaleDraw()->scaleDiv(); - const int majCnt = scaleDiv.ticks(QwtScaleDiv::MajorTick).count(); + const int majCnt = scaleDiv.ticks( QwtScaleDiv::MajorTick ).count(); - if ( axisId == yLeft || axisId == yRight ) { - int hDiff = (majCnt - 1) * niceDist - - scaleWidget->minimumSizeHint().height(); + if ( axisId == yLeft || axisId == yRight ) + { + int hDiff = ( majCnt - 1 ) * niceDist + - scaleWidget->minimumSizeHint().height(); if ( hDiff > dh ) dh = hDiff; - } else { - int wDiff = (majCnt - 1) * niceDist - - scaleWidget->minimumSizeHint().width(); + } + else + { + int wDiff = ( majCnt - 1 ) * niceDist + - scaleWidget->minimumSizeHint().width(); if ( wDiff > dw ) dw = wDiff; } } } - return minimumSizeHint() + QSize(dw, dh); + return minimumSizeHint() + QSize( dw, dh ); } /*! @@ -330,16 +517,19 @@ QSize QwtPlot::sizeHint() const */ QSize QwtPlot::minimumSizeHint() const { - QSize hint = d_data->layout->minimumSizeHint(this); - hint += QSize(2 * frameWidth(), 2 * frameWidth()); + QSize hint = d_data->layout->minimumSizeHint( this ); + hint += QSize( 2 * frameWidth(), 2 * frameWidth() ); return hint; } -//! Resize and update internal layout -void QwtPlot::resizeEvent(QResizeEvent *e) +/*! + Resize and update internal layout + \param e Resize event +*/ +void QwtPlot::resizeEvent( QResizeEvent *e ) { - QFrame::resizeEvent(e); + QFrame::resizeEvent( e ); updateLayout(); } @@ -350,13 +540,12 @@ void QwtPlot::resizeEvent(QResizeEvent *e) or if any curves are attached to raw data, the plot has to be refreshed explicitly in order to make changes visible. - \sa setAutoReplot() - \warning Calls canvas()->repaint, take care of infinite recursions + \sa updateAxes(), setAutoReplot() */ void QwtPlot::replot() { bool doAutoReplot = autoReplot(); - setAutoReplot(false); + setAutoReplot( false ); updateAxes(); @@ -365,38 +554,20 @@ void QwtPlot::replot() axes labels. We need to process them here before painting to avoid that scales and canvas get out of sync. */ -#if QT_VERSION >= 0x040000 - QApplication::sendPostedEvents(this, QEvent::LayoutRequest); -#else - QApplication::sendPostedEvents(this, QEvent::LayoutHint); -#endif + QApplication::sendPostedEvents( this, QEvent::LayoutRequest ); - QwtPlotCanvas &canvas = *d_data->canvas; - - canvas.invalidatePaintCache(); - - /* - In case of cached or packed painting the canvas - is repainted completely and doesn't need to be erased. - */ - const bool erase = - !canvas.testPaintAttribute(QwtPlotCanvas::PaintPacked) - && !canvas.testPaintAttribute(QwtPlotCanvas::PaintCached); - -#if QT_VERSION >= 0x040000 - const bool noBackgroundMode = canvas.testAttribute(Qt::WA_NoBackground); - if ( !erase && !noBackgroundMode ) - canvas.setAttribute(Qt::WA_NoBackground, true); - - canvas.repaint(canvas.contentsRect()); - - if ( !erase && !noBackgroundMode ) - canvas.setAttribute(Qt::WA_NoBackground, false); -#else - canvas.repaint(canvas.contentsRect(), erase); -#endif + if ( d_data->canvas ) + { + const bool ok = QMetaObject::invokeMethod( + d_data->canvas, "replot", Qt::DirectConnection ); + if ( !ok ) + { + // fallback, when canvas has no a replot method + d_data->canvas->update( d_data->canvas->contentsRect() ); + } + } - setAutoReplot(doAutoReplot); + setAutoReplot( doAutoReplot ); } /*! @@ -405,119 +576,153 @@ void QwtPlot::replot() */ void QwtPlot::updateLayout() { - d_data->layout->activate(this, contentsRect()); + d_data->layout->activate( this, contentsRect() ); + + QRect titleRect = d_data->layout->titleRect().toRect(); + QRect footerRect = d_data->layout->footerRect().toRect(); + QRect scaleRect[QwtPlot::axisCnt]; + for ( int axisId = 0; axisId < axisCnt; axisId++ ) + scaleRect[axisId] = d_data->layout->scaleRect( axisId ).toRect(); + QRect legendRect = d_data->layout->legendRect().toRect(); + QRect canvasRect = d_data->layout->canvasRect().toRect(); - // // resize and show the visible widgets - // - if (!d_data->lblTitle->text().isEmpty()) { - d_data->lblTitle->setGeometry(d_data->layout->titleRect()); - if (!d_data->lblTitle->isVisible()) - d_data->lblTitle->show(); - } else - d_data->lblTitle->hide(); - - for (int axisId = 0; axisId < axisCnt; axisId++ ) { - if (axisEnabled(axisId) ) { - axisWidget(axisId)->setGeometry(d_data->layout->scaleRect(axisId)); - - if ( axisId == xBottom || axisId == xTop ) { - QRegion r(d_data->layout->scaleRect(axisId)); - if ( axisEnabled(yLeft) ) - r = r.subtract(QRegion(d_data->layout->scaleRect(yLeft))); - if ( axisEnabled(yRight) ) - r = r.subtract(QRegion(d_data->layout->scaleRect(yRight))); - r.translate(-d_data->layout->scaleRect(axisId).x(), - -d_data->layout->scaleRect(axisId).y()); - - axisWidget(axisId)->setMask(r); + + if ( !d_data->titleLabel->text().isEmpty() ) + { + d_data->titleLabel->setGeometry( titleRect ); + if ( !d_data->titleLabel->isVisibleTo( this ) ) + d_data->titleLabel->show(); + } + else + d_data->titleLabel->hide(); + + if ( !d_data->footerLabel->text().isEmpty() ) + { + d_data->footerLabel->setGeometry( footerRect ); + if ( !d_data->footerLabel->isVisibleTo( this ) ) + d_data->footerLabel->show(); + } + else + d_data->footerLabel->hide(); + + for ( int axisId = 0; axisId < axisCnt; axisId++ ) + { + if ( axisEnabled( axisId ) ) + { + axisWidget( axisId )->setGeometry( scaleRect[axisId] ); + +#if 1 + if ( axisId == xBottom || axisId == xTop ) + { + // do we need this code any longer ??? + + QRegion r( scaleRect[axisId] ); + if ( axisEnabled( yLeft ) ) + r = r.subtracted( QRegion( scaleRect[yLeft] ) ); + if ( axisEnabled( yRight ) ) + r = r.subtracted( QRegion( scaleRect[yRight] ) ); + r.translate( -scaleRect[ axisId ].x(), + -scaleRect[axisId].y() ); + + axisWidget( axisId )->setMask( r ); } - if (!axisWidget(axisId)->isVisible()) - axisWidget(axisId)->show(); - } else - axisWidget(axisId)->hide(); +#endif + if ( !axisWidget( axisId )->isVisibleTo( this ) ) + axisWidget( axisId )->show(); + } + else + axisWidget( axisId )->hide(); } - if ( d_data->legend && - d_data->layout->legendPosition() != ExternalLegend ) { - if (d_data->legend->itemCount() > 0) { - d_data->legend->setGeometry(d_data->layout->legendRect()); - d_data->legend->show(); - } else + if ( d_data->legend ) + { + if ( d_data->legend->isEmpty() ) + { d_data->legend->hide(); + } + else + { + d_data->legend->setGeometry( legendRect ); + d_data->legend->show(); + } } - d_data->canvas->setGeometry(d_data->layout->canvasRect()); + d_data->canvas->setGeometry( canvasRect ); } /*! - Update the focus tab order + \brief Calculate the canvas margins - The order is changed so that the canvas will be in front of the - first legend item, or behind the last legend item - depending - on the position of the legend. -*/ + \param maps QwtPlot::axisCnt maps, mapping between plot and paint device coordinates + \param canvasRect Bounding rectangle where to paint + \param left Return parameter for the left margin + \param top Return parameter for the top margin + \param right Return parameter for the right margin + \param bottom Return parameter for the bottom margin -void QwtPlot::updateTabOrder() + Plot items might indicate, that they need some extra space + at the borders of the canvas by the QwtPlotItem::Margins flag. + + updateCanvasMargins(), QwtPlotItem::getCanvasMarginHint() + */ +void QwtPlot::getCanvasMarginsHint( + const QwtScaleMap maps[], const QRectF &canvasRect, + double &left, double &top, double &right, double &bottom) const { -#if QT_VERSION >= 0x040000 - using namespace Qt; // QWidget::NoFocus/Qt::NoFocus -#else - if ( d_data->canvas->focusPolicy() == NoFocus ) - return; -#endif - if ( d_data->legend.isNull() - || d_data->layout->legendPosition() == ExternalLegend - || d_data->legend->legendItems().count() == 0 ) { - return; - } + left = top = right = bottom = -1.0; - // Depending on the position of the legend the - // tab order will be changed that the canvas is - // next to the last legend item, or before - // the first one. + const QwtPlotItemList& itmList = itemList(); + for ( QwtPlotItemIterator it = itmList.begin(); + it != itmList.end(); ++it ) + { + const QwtPlotItem *item = *it; + if ( item->testItemAttribute( QwtPlotItem::Margins ) ) + { + double m[ QwtPlot::axisCnt ]; + item->getCanvasMarginHint( + maps[ item->xAxis() ], maps[ item->yAxis() ], + canvasRect, m[yLeft], m[xTop], m[yRight], m[xBottom] ); + + left = qMax( left, m[yLeft] ); + top = qMax( top, m[xTop] ); + right = qMax( right, m[yRight] ); + bottom = qMax( bottom, m[xBottom] ); + } + } +} - const bool canvasFirst = - d_data->layout->legendPosition() == QwtPlot::BottomLegend || - d_data->layout->legendPosition() == QwtPlot::RightLegend; +/*! + \brief Update the canvas margins - QWidget *previous = NULL; + Plot items might indicate, that they need some extra space + at the borders of the canvas by the QwtPlotItem::Margins flag. - QWidget *w; -#if QT_VERSION >= 0x040000 - w = d_data->canvas; - while ( ( w = w->nextInFocusChain() ) != d_data->canvas ) -#else - if ( focusData() == NULL ) - return; + getCanvasMarginsHint(), QwtPlotItem::getCanvasMarginHint() + */ +void QwtPlot::updateCanvasMargins() +{ + QwtScaleMap maps[axisCnt]; + for ( int axisId = 0; axisId < axisCnt; axisId++ ) + maps[axisId] = canvasMap( axisId ); - while ( focusData()->next() != d_data->canvas ); - while ( (w = focusData()->next()) != d_data->canvas ) -#endif + double margins[axisCnt]; + getCanvasMarginsHint( maps, canvas()->contentsRect(), + margins[yLeft], margins[xTop], margins[yRight], margins[xBottom] ); + + bool doUpdate = false; + for ( int axisId = 0; axisId < axisCnt; axisId++ ) { - bool isLegendItem = false; - if ( w->focusPolicy() != NoFocus - && w->parent() && w->parent() == d_data->legend->contentsWidget() ) { - isLegendItem = true; - } - - if ( canvasFirst ) { - if ( isLegendItem ) - break; - - previous = w; - } else { - if ( isLegendItem ) - previous = w; - else { - if ( previous ) - break; - } + if ( margins[axisId] >= 0.0 ) + { + const int m = qCeil( margins[axisId] ); + plotLayout()->setCanvasMargin( m, axisId); + doUpdate = true; } } - if ( previous && previous != d_data->canvas) - setTabOrder(previous, d_data->canvas); + if ( doUpdate ) + updateLayout(); } /*! @@ -529,48 +734,48 @@ void QwtPlot::updateTabOrder() plot items better overload drawItems() \sa drawItems() */ -void QwtPlot::drawCanvas(QPainter *painter) +void QwtPlot::drawCanvas( QPainter *painter ) { QwtScaleMap maps[axisCnt]; for ( int axisId = 0; axisId < axisCnt; axisId++ ) - maps[axisId] = canvasMap(axisId); + maps[axisId] = canvasMap( axisId ); - drawItems(painter, - d_data->canvas->contentsRect(), maps, QwtPlotPrintFilter()); + drawItems( painter, d_data->canvas->contentsRect(), maps ); } /*! Redraw the canvas items. + \param painter Painter used for drawing - \param rect Bounding rectangle where to paint - \param map QwtPlot::axisCnt maps, mapping between plot and paint device coordinates - \param pfilter Plot print filter + \param canvasRect Bounding rectangle where to paint + \param maps QwtPlot::axisCnt maps, mapping between plot and paint device coordinates + + \note Usually canvasRect is contentsRect() of the plot canvas. + Due to a bug in Qt this rectangle might be wrong for certain + frame styles ( f.e QFrame::Box ) and it might be necessary to + fix the margins manually using QWidget::setContentsMargins() */ -void QwtPlot::drawItems(QPainter *painter, const QRect &rect, - const QwtScaleMap map[axisCnt], - const QwtPlotPrintFilter &pfilter) const +void QwtPlot::drawItems( QPainter *painter, const QRectF &canvasRect, + const QwtScaleMap maps[axisCnt] ) const { const QwtPlotItemList& itmList = itemList(); for ( QwtPlotItemIterator it = itmList.begin(); - it != itmList.end(); ++it ) { + it != itmList.end(); ++it ) + { QwtPlotItem *item = *it; - if ( item && item->isVisible() ) { - if ( !(pfilter.options() & QwtPlotPrintFilter::PrintGrid) - && item->rtti() == QwtPlotItem::Rtti_PlotGrid ) { - continue; - } - + if ( item && item->isVisible() ) + { painter->save(); -#if QT_VERSION >= 0x040000 - painter->setRenderHint(QPainter::Antialiasing, - item->testRenderHint(QwtPlotItem::RenderAntialiased) ); -#endif + painter->setRenderHint( QPainter::Antialiasing, + item->testRenderHint( QwtPlotItem::RenderAntialiased ) ); + painter->setRenderHint( QPainter::HighQualityAntialiasing, + item->testRenderHint( QwtPlotItem::RenderAntialiased ) ); - item->draw(painter, - map[item->xAxis()], map[item->yAxis()], - rect); + item->draw( painter, + maps[item->xAxis()], maps[item->yAxis()], + canvasRect ); painter->restore(); } @@ -584,174 +789,92 @@ void QwtPlot::drawItems(QPainter *painter, const QRect &rect, \sa QwtScaleMap, transform(), invTransform() */ -QwtScaleMap QwtPlot::canvasMap(int axisId) const +QwtScaleMap QwtPlot::canvasMap( int axisId ) const { QwtScaleMap map; if ( !d_data->canvas ) return map; - map.setTransformation(axisScaleEngine(axisId)->transformation()); - - const QwtScaleDiv *sd = axisScaleDiv(axisId); - map.setScaleInterval(sd->lBound(), sd->hBound()); - - if ( axisEnabled(axisId) ) { - const QwtScaleWidget *s = axisWidget(axisId); - if ( axisId == yLeft || axisId == yRight ) { - int y = s->y() + s->startBorderDist() - d_data->canvas->y(); - int h = s->height() - s->startBorderDist() - s->endBorderDist(); - map.setPaintInterval(y + h, y); - } else { - int x = s->x() + s->startBorderDist() - d_data->canvas->x(); - int w = s->width() - s->startBorderDist() - s->endBorderDist(); - map.setPaintInterval(x, x + w); + map.setTransformation( axisScaleEngine( axisId )->transformation() ); + + const QwtScaleDiv &sd = axisScaleDiv( axisId ); + map.setScaleInterval( sd.lowerBound(), sd.upperBound() ); + + if ( axisEnabled( axisId ) ) + { + const QwtScaleWidget *s = axisWidget( axisId ); + if ( axisId == yLeft || axisId == yRight ) + { + double y = s->y() + s->startBorderDist() - d_data->canvas->y(); + double h = s->height() - s->startBorderDist() - s->endBorderDist(); + map.setPaintInterval( y + h, y ); + } + else + { + double x = s->x() + s->startBorderDist() - d_data->canvas->x(); + double w = s->width() - s->startBorderDist() - s->endBorderDist(); + map.setPaintInterval( x, x + w ); } - } else { - const int margin = plotLayout()->canvasMargin(axisId); + } + else + { + int margin = 0; + if ( !plotLayout()->alignCanvasToScale( axisId ) ) + margin = plotLayout()->canvasMargin( axisId ); const QRect &canvasRect = d_data->canvas->contentsRect(); - if ( axisId == yLeft || axisId == yRight ) { - map.setPaintInterval(canvasRect.bottom() - margin, - canvasRect.top() + margin); - } else { - map.setPaintInterval(canvasRect.left() + margin, - canvasRect.right() - margin); + if ( axisId == yLeft || axisId == yRight ) + { + map.setPaintInterval( canvasRect.bottom() - margin, + canvasRect.top() + margin ); + } + else + { + map.setPaintInterval( canvasRect.left() + margin, + canvasRect.right() - margin ); } } return map; } -/*! - Change the margin of the plot. The margin is the space - around all components. - - \param margin new margin - \sa QwtPlotLayout::setMargin(), margin(), plotLayout() -*/ -void QwtPlot::setMargin(int margin) -{ - if ( margin < 0 ) - margin = 0; - - if ( margin != d_data->layout->margin() ) { - d_data->layout->setMargin(margin); - updateLayout(); - } -} - -/*! - \return margin - \sa setMargin(), QwtPlotLayout::margin(), plotLayout() -*/ -int QwtPlot::margin() const -{ - return d_data->layout->margin(); -} - /*! \brief Change the background of the plotting area - Sets c to QColorGroup::Background of all colorgroups of + Sets brush to QPalette::Window of all color groups of the palette of the canvas. Using canvas()->setPalette() is a more powerful way to set these colors. - \param c new background color + + \param brush New background brush + \sa canvasBackground() */ -void QwtPlot::setCanvasBackground(const QColor &c) +void QwtPlot::setCanvasBackground( const QBrush &brush ) { - QPalette p = d_data->canvas->palette(); - - for ( int i = 0; i < QPalette::NColorGroups; i++ ) { -#if QT_VERSION < 0x040000 - p.setColor((QPalette::ColorGroup)i, QColorGroup::Background, c); -#else - p.setColor((QPalette::ColorGroup)i, QPalette::Background, c); -#endif - } + QPalette pal = d_data->canvas->palette(); + pal.setBrush( QPalette::Window, brush ); - canvas()->setPalette(p); + canvas()->setPalette( pal ); } /*! - Nothing else than: canvas()->palette().color( - QPalette::Normal, QColorGroup::Background); - - \return the background color of the plotting area. -*/ -const QColor & QwtPlot::canvasBackground() const -{ -#if QT_VERSION < 0x040000 - return canvas()->palette().color( - QPalette::Normal, QColorGroup::Background); -#else - return canvas()->palette().color( - QPalette::Normal, QPalette::Background); -#endif -} - -/*! - \brief Change the border width of the plotting area - Nothing else than canvas()->setLineWidth(w), - left for compatibility only. - \param w new border width -*/ -void QwtPlot::setCanvasLineWidth(int w) -{ - canvas()->setLineWidth(w); - updateLayout(); -} + Nothing else than: canvas()->palette().brush( + QPalette::Normal, QPalette::Window); -/*! - Nothing else than: canvas()->lineWidth(), - left for compatibility only. - \return the border width of the plotting area + \return Background brush of the plotting area. + \sa setCanvasBackground() */ -int QwtPlot::canvasLineWidth() const +QBrush QwtPlot::canvasBackground() const { - return canvas()->lineWidth(); + return canvas()->palette().brush( + QPalette::Normal, QPalette::Window ); } /*! \return \c true if the specified axis exists, otherwise \c false \param axisId axis index */ -bool QwtPlot::axisValid(int axisId) +bool QwtPlot::axisValid( int axisId ) { - return ((axisId >= QwtPlot::yLeft) && (axisId < QwtPlot::axisCnt)); -} - -/*! - Called internally when the legend has been clicked on. - Emits a legendClicked() signal. -*/ -void QwtPlot::legendItemClicked() -{ - if ( d_data->legend && sender()->isWidgetType() ) { - QwtPlotItem *plotItem = - (QwtPlotItem*)d_data->legend->find((QWidget *)sender()); - if ( plotItem ) - emit legendClicked(plotItem); - } -} - -/*! - Called internally when the legend has been checked - Emits a legendClicked() signal. -*/ -void QwtPlot::legendItemChecked(bool on) -{ - if ( d_data->legend && sender()->isWidgetType() ) { - QwtPlotItem *plotItem = - (QwtPlotItem*)d_data->legend->find((QWidget *)sender()); - if ( plotItem ) - emit legendChecked(plotItem, on); - } -} - -//! Remove all curves and markers -void QwtPlot::clear() -{ - detachItems(QwtPlotItem::Rtti_PlotCurve); - detachItems(QwtPlotItem::Rtti_PlotMarker); + return ( ( axisId >= QwtPlot::yLeft ) && ( axisId < QwtPlot::axisCnt ) ); } /*! @@ -762,17 +885,23 @@ void QwtPlot::clear() Otherwise the legend items will be placed in a table with a best fit number of columns from left to right. - If pos != QwtPlot::ExternalLegend the plot widget will become - parent of the legend. It will be deleted when the plot is deleted, - or another legend is set with insertLegend(). + insertLegend() will set the plot widget as parent for the legend. + The legend will be deleted in the destructor of the plot or when + another legend is inserted. + + Legends, that are not inserted into the layout of the plot widget + need to connect to the legendDataChanged() signal. Calling updateLegend() + initiates this signal for an initial update. When the application code + wants to implement its own layout this also needs to be done for + rendering plots to a document ( see QwtPlotRenderer ). \param legend Legend \param pos The legend's position. For top/left position the number - of colums will be limited to 1, otherwise it will be set to + of columns will be limited to 1, otherwise it will be set to unlimited. - \param ratio Ratio between legend and the bounding rect - of title, canvas and axes. The legend will be shrinked + \param ratio Ratio between legend and the bounding rectangle + of title, canvas and axes. The legend will be shrunk if it would need more space than the given ratio. The ratio is limited to ]0.0 .. 1.0]. In case of <= 0.0 it will be reset to the default ratio. @@ -781,53 +910,254 @@ void QwtPlot::clear() \sa legend(), QwtPlotLayout::legendPosition(), QwtPlotLayout::setLegendPosition() */ -void QwtPlot::insertLegend(QwtLegend *legend, - QwtPlot::LegendPosition pos, double ratio) +void QwtPlot::insertLegend( QwtAbstractLegend *legend, + QwtPlot::LegendPosition pos, double ratio ) { - d_data->layout->setLegendPosition(pos, ratio); + d_data->layout->setLegendPosition( pos, ratio ); - if ( legend != d_data->legend ) { + if ( legend != d_data->legend ) + { if ( d_data->legend && d_data->legend->parent() == this ) delete d_data->legend; d_data->legend = legend; - if ( d_data->legend ) { - if ( pos != ExternalLegend ) { - if ( d_data->legend->parent() != this ) { -#if QT_VERSION < 0x040000 - d_data->legend->reparent(this, QPoint(0, 0)); -#else - d_data->legend->setParent(this); -#endif + if ( d_data->legend ) + { + connect( this, + SIGNAL( legendDataChanged( + const QVariant &, const QList & ) ), + d_data->legend, + SLOT( updateLegend( + const QVariant &, const QList & ) ) + ); + + if ( d_data->legend->parent() != this ) + d_data->legend->setParent( this ); + + qwtEnableLegendItems( this, false ); + updateLegend(); + qwtEnableLegendItems( this, true ); + + QwtLegend *lgd = qobject_cast( legend ); + if ( lgd ) + { + switch ( d_data->layout->legendPosition() ) + { + case LeftLegend: + case RightLegend: + { + if ( lgd->maxColumns() == 0 ) + lgd->setMaxColumns( 1 ); // 1 column: align vertical + break; + } + case TopLegend: + case BottomLegend: + { + lgd->setMaxColumns( 0 ); // unlimited + break; + } + default: + break; } } - const QwtPlotItemList& itmList = itemList(); - for ( QwtPlotItemIterator it = itmList.begin(); - it != itmList.end(); ++it ) { - (*it)->updateLegend(d_data->legend); - } - - QLayout *l = d_data->legend->contentsWidget()->layout(); - if ( l && l->inherits("QwtDynGridLayout") ) { - QwtDynGridLayout *tl = (QwtDynGridLayout *)l; - switch(d_data->layout->legendPosition()) { + QWidget *previousInChain = NULL; + switch ( d_data->layout->legendPosition() ) + { case LeftLegend: - case RightLegend: - tl->setMaxCols(1); // 1 column: align vertical + { + previousInChain = axisWidget( QwtPlot::xTop ); break; + } case TopLegend: - case BottomLegend: - tl->setMaxCols(0); // unlimited + { + previousInChain = this; break; - case ExternalLegend: + } + case RightLegend: + { + previousInChain = axisWidget( QwtPlot::yRight ); + break; + } + case BottomLegend: + { + previousInChain = footerLabel(); break; } } + + if ( previousInChain ) + qwtSetTabOrder( previousInChain, legend, true ); } - updateTabOrder(); } updateLayout(); } + +/*! + Emit legendDataChanged() for all plot item + + \sa QwtPlotItem::legendData(), legendDataChanged() + */ +void QwtPlot::updateLegend() +{ + const QwtPlotItemList& itmList = itemList(); + for ( QwtPlotItemIterator it = itmList.begin(); + it != itmList.end(); ++it ) + { + updateLegend( *it ); + } +} + +/*! + Emit legendDataChanged() for a plot item + + \param plotItem Plot item + \sa QwtPlotItem::legendData(), legendDataChanged() + */ +void QwtPlot::updateLegend( const QwtPlotItem *plotItem ) +{ + if ( plotItem == NULL ) + return; + + QList legendData; + + if ( plotItem->testItemAttribute( QwtPlotItem::Legend ) ) + legendData = plotItem->legendData(); + + const QVariant itemInfo = itemToInfo( const_cast< QwtPlotItem *>( plotItem) ); + Q_EMIT legendDataChanged( itemInfo, legendData ); +} + +/*! + \brief Update all plot items interested in legend attributes + + Call QwtPlotItem::updateLegend(), when the QwtPlotItem::LegendInterest + flag is set. + + \param itemInfo Info about the plot item + \param legendData Entries to be displayed for the plot item ( usually 1 ) + + \sa QwtPlotItem::LegendInterest, + QwtPlotLegendItem, QwtPlotItem::updateLegend() + */ +void QwtPlot::updateLegendItems( const QVariant &itemInfo, + const QList &legendData ) +{ + QwtPlotItem *plotItem = infoToItem( itemInfo ); + if ( plotItem ) + { + const QwtPlotItemList& itmList = itemList(); + for ( QwtPlotItemIterator it = itmList.begin(); + it != itmList.end(); ++it ) + { + QwtPlotItem *item = *it; + if ( item->testItemInterest( QwtPlotItem::LegendInterest ) ) + item->updateLegend( plotItem, legendData ); + } + } +} + +/*! + \brief Attach/Detach a plot item + + \param plotItem Plot item + \param on When true attach the item, otherwise detach it + */ +void QwtPlot::attachItem( QwtPlotItem *plotItem, bool on ) +{ + if ( plotItem->testItemInterest( QwtPlotItem::LegendInterest ) ) + { + // plotItem is some sort of legend + + const QwtPlotItemList& itmList = itemList(); + for ( QwtPlotItemIterator it = itmList.begin(); + it != itmList.end(); ++it ) + { + QwtPlotItem *item = *it; + + QList legendData; + if ( on && item->testItemAttribute( QwtPlotItem::Legend ) ) + { + legendData = item->legendData(); + plotItem->updateLegend( item, legendData ); + } + } + } + + if ( on ) + insertItem( plotItem ); + else + removeItem( plotItem ); + + Q_EMIT itemAttached( plotItem, on ); + + if ( plotItem->testItemAttribute( QwtPlotItem::Legend ) ) + { + // the item wants to be represented on the legend + + if ( on ) + { + updateLegend( plotItem ); + } + else + { + const QVariant itemInfo = itemToInfo( plotItem ); + Q_EMIT legendDataChanged( itemInfo, QList() ); + } + } + + if ( autoReplot() ) + update(); +} + +/*! + \brief Build an information, that can be used to identify + a plot item on the legend. + + The default implementation simply wraps the plot item + into a QVariant object. When overloading itemToInfo() + usually infoToItem() needs to reimplemeted too. + +\code + QVariant itemInfo; + qVariantSetValue( itemInfo, plotItem ); +\endcode + + \param plotItem Plot item + \return Plot item embedded in a QVariant + \sa infoToItem() + */ +QVariant QwtPlot::itemToInfo( QwtPlotItem *plotItem ) const +{ + QVariant itemInfo; + qVariantSetValue( itemInfo, plotItem ); + + return itemInfo; +} + +/*! + \brief Identify the plot item according to an item info object, + that has bee generated from itemToInfo(). + + The default implementation simply tries to unwrap a QwtPlotItem + pointer: + +\code + if ( itemInfo.canConvert() ) + return qvariant_cast( itemInfo ); +\endcode + \param itemInfo Plot item + \return A plot item, when successful, otherwise a NULL pointer. + \sa itemToInfo() +*/ +QwtPlotItem *QwtPlot::infoToItem( const QVariant &itemInfo ) const +{ + if ( itemInfo.canConvert() ) + return qvariant_cast( itemInfo ); + + return NULL; +} + + diff --git a/libs/qwt/qwt_plot.h b/libs/qwt/qwt_plot.h index 7efa080e3..d6626136d 100644 --- a/libs/qwt/qwt_plot.h +++ b/libs/qwt/qwt_plot.h @@ -10,23 +10,22 @@ #ifndef QWT_PLOT_H #define QWT_PLOT_H -#include #include "qwt_global.h" -#include "qwt_array.h" #include "qwt_text.h" #include "qwt_plot_dict.h" #include "qwt_scale_map.h" -#include "qwt_plot_printfilter.h" +#include "qwt_interval.h" +#include +#include +#include class QwtPlotLayout; -class QwtLegend; +class QwtAbstractLegend; class QwtScaleWidget; class QwtScaleEngine; class QwtScaleDiv; class QwtScaleDraw; class QwtTextLabel; -class QwtPlotCanvas; -class QwtPlotPrintFilter; /*! \brief A 2-D plotting widget @@ -37,10 +36,13 @@ class QwtPlotPrintFilter; (QwtPlotMarker), the grid (QwtPlotGrid), or anything else derived from QwtPlotItem. A plot can have up to four axes, with each plot item attached to an x- and - a y axis. The scales at the axes can be explicitely set (QwtScaleDiv), or + a y axis. The scales at the axes can be explicitly set (QwtScaleDiv), or are calculated from the plot items, using algorithms (QwtScaleEngine) which can be configured separately for each axis. + The simpleplot example is a good starting point to see how to set up a + plot widget. + \image html plot.png \par Example @@ -51,20 +53,15 @@ class QwtPlotPrintFilter; #include #include -QwtPlot *myPlot; -double x[100], y1[100], y2[100]; // x and y values - -myPlot = new QwtPlot("Two Curves", parent); +QwtPlot *myPlot = new QwtPlot("Two Curves", parent); // add curves QwtPlotCurve *curve1 = new QwtPlotCurve("Curve 1"); QwtPlotCurve *curve2 = new QwtPlotCurve("Curve 2"); -getSomeValues(x, y1, y2); - -// copy the data into the curves -curve1->setData(x, y1, 100); -curve2->setData(x, y2, 100); +// connect or copy the data to the curves +curve1->setData(...); +curve2->setData(...); curve1->attach(myPlot); curve2->attach(myPlot); @@ -76,227 +73,234 @@ myPlot->replot(); class QWT_EXPORT QwtPlot: public QFrame, public QwtPlotDict { - friend class QwtPlotCanvas; - Q_OBJECT + + Q_PROPERTY( QBrush canvasBackground + READ canvasBackground WRITE setCanvasBackground ) + Q_PROPERTY( bool autoReplot READ autoReplot WRITE setAutoReplot ) + +#if 0 + // This property is intended to configure the plot + // widget from a special dialog in the deigner plugin. + // Disabled until such a dialog has been implemented. + Q_PROPERTY( QString propertiesDocument - READ grabProperties WRITE applyProperties ) + READ grabProperties WRITE applyProperties ) +#endif public: - //! Axis index - enum Axis { + //! \brief Axis index + enum Axis + { + //! Y axis left of the canvas yLeft, + + //! Y axis right of the canvas yRight, + + //! X axis below the canvas xBottom, + + //! X axis above the canvas xTop, + //! Number of axes axisCnt }; /*! - \brief Position of the legend, relative to the canvas. + Position of the legend, relative to the canvas. - ExternalLegend means that only the content of the legend - will be handled by QwtPlot, but not its geometry. - This might be interesting if an application wants to - have a legend in an external window. + \sa insertLegend() */ - enum LegendPosition { + enum LegendPosition + { + //! The legend will be left from the QwtPlot::yLeft axis. LeftLegend, + + //! The legend will be right from the QwtPlot::yRight axis. RightLegend, + + //! The legend will be below the footer BottomLegend, - TopLegend, - ExternalLegend + //! The legend will be above the title + TopLegend }; - explicit QwtPlot(QWidget * = NULL); - explicit QwtPlot(const QwtText &title, QWidget *p = NULL); -#if QT_VERSION < 0x040000 - explicit QwtPlot(QWidget *, const char* name); -#endif + explicit QwtPlot( QWidget * = NULL ); + explicit QwtPlot( const QwtText &title, QWidget * = NULL ); virtual ~QwtPlot(); - void applyProperties(const QString &); + void applyProperties( const QString & ); QString grabProperties() const; - void setAutoReplot(bool tf = true); + void setAutoReplot( bool = true ); bool autoReplot() const; - void print(QPaintDevice &p, - const QwtPlotPrintFilter & = QwtPlotPrintFilter()) const; - virtual void print(QPainter *, const QRect &rect, - const QwtPlotPrintFilter & = QwtPlotPrintFilter()) const; - // Layout + void setPlotLayout( QwtPlotLayout * ); + QwtPlotLayout *plotLayout(); const QwtPlotLayout *plotLayout() const; - void setMargin(int margin); - int margin() const; - // Title - void setTitle(const QString &); - void setTitle(const QwtText &t); + void setTitle( const QString & ); + void setTitle( const QwtText &t ); QwtText title() const; QwtTextLabel *titleLabel(); const QwtTextLabel *titleLabel() const; + // Footer + + void setFooter( const QString & ); + void setFooter( const QwtText &t ); + QwtText footer() const; + + QwtTextLabel *footerLabel(); + const QwtTextLabel *footerLabel() const; + // Canvas - QwtPlotCanvas *canvas(); - const QwtPlotCanvas *canvas() const; + void setCanvas( QWidget * ); - void setCanvasBackground (const QColor &c); - const QColor& canvasBackground() const; + QWidget *canvas(); + const QWidget *canvas() const; - void setCanvasLineWidth(int w); - int canvasLineWidth() const; + void setCanvasBackground( const QBrush & ); + QBrush canvasBackground() const; - virtual QwtScaleMap canvasMap(int axisId) const; + virtual QwtScaleMap canvasMap( int axisId ) const; - double invTransform(int axisId, int pos) const; - int transform(int axisId, double value) const; + double invTransform( int axisId, int pos ) const; + double transform( int axisId, double value ) const; // Axes - QwtScaleEngine *axisScaleEngine(int axisId); - const QwtScaleEngine *axisScaleEngine(int axisId) const; - void setAxisScaleEngine(int axisId, QwtScaleEngine *); + QwtScaleEngine *axisScaleEngine( int axisId ); + const QwtScaleEngine *axisScaleEngine( int axisId ) const; + void setAxisScaleEngine( int axisId, QwtScaleEngine * ); - void setAxisAutoScale(int axisId); - bool axisAutoScale(int axisId) const; + void setAxisAutoScale( int axisId, bool on = true ); + bool axisAutoScale( int axisId ) const; - void enableAxis(int axisId, bool tf = true); - bool axisEnabled(int axisId) const; + void enableAxis( int axisId, bool tf = true ); + bool axisEnabled( int axisId ) const; - void setAxisFont(int axisId, const QFont &f); - QFont axisFont(int axisId) const; + void setAxisFont( int axisId, const QFont &f ); + QFont axisFont( int axisId ) const; - void setAxisScale(int axisId, double min, double max, double step = 0); - void setAxisScaleDiv(int axisId, const QwtScaleDiv &); - void setAxisScaleDraw(int axisId, QwtScaleDraw *); + void setAxisScale( int axisId, double min, double max, double step = 0 ); + void setAxisScaleDiv( int axisId, const QwtScaleDiv & ); + void setAxisScaleDraw( int axisId, QwtScaleDraw * ); - double axisStepSize(int axisId) const; + double axisStepSize( int axisId ) const; + QwtInterval axisInterval( int axisId ) const; - const QwtScaleDiv *axisScaleDiv(int axisId) const; - QwtScaleDiv *axisScaleDiv(int axisId); + const QwtScaleDiv &axisScaleDiv( int axisId ) const; - const QwtScaleDraw *axisScaleDraw(int axisId) const; - QwtScaleDraw *axisScaleDraw(int axisId); + const QwtScaleDraw *axisScaleDraw( int axisId ) const; + QwtScaleDraw *axisScaleDraw( int axisId ); - const QwtScaleWidget *axisWidget(int axisId) const; - QwtScaleWidget *axisWidget(int axisId); + const QwtScaleWidget *axisWidget( int axisId ) const; + QwtScaleWidget *axisWidget( int axisId ); -#if QT_VERSION < 0x040000 - void setAxisLabelAlignment(int axisId, int); -#else - void setAxisLabelAlignment(int axisId, Qt::Alignment); -#endif - void setAxisLabelRotation(int axisId, double rotation); + void setAxisLabelAlignment( int axisId, Qt::Alignment ); + void setAxisLabelRotation( int axisId, double rotation ); + + void setAxisTitle( int axisId, const QString & ); + void setAxisTitle( int axisId, const QwtText & ); + QwtText axisTitle( int axisId ) const; - void setAxisTitle(int axisId, const QString &); - void setAxisTitle(int axisId, const QwtText &); - QwtText axisTitle(int axisId) const; + void setAxisMaxMinor( int axisId, int maxMinor ); + int axisMaxMinor( int axisId ) const; - void setAxisMaxMinor(int axisId, int maxMinor); - int axisMaxMajor(int axisId) const; - void setAxisMaxMajor(int axisId, int maxMajor); - int axisMaxMinor(int axisId) const; + void setAxisMaxMajor( int axisId, int maxMajor ); + int axisMaxMajor( int axisId ) const; // Legend - void insertLegend(QwtLegend *, LegendPosition = QwtPlot::RightLegend, - double ratio = -1.0); + void insertLegend( QwtAbstractLegend *, + LegendPosition = QwtPlot::RightLegend, double ratio = -1.0 ); + + QwtAbstractLegend *legend(); + const QwtAbstractLegend *legend() const; - QwtLegend *legend(); - const QwtLegend *legend() const; + void updateLegend(); + void updateLegend( const QwtPlotItem * ); // Misc - virtual void polish(); virtual QSize sizeHint() const; virtual QSize minimumSizeHint() const; virtual void updateLayout(); + virtual void drawCanvas( QPainter * ); - virtual bool event(QEvent *); + void updateAxes(); + void updateCanvasMargins(); -signals: - /*! - A signal which is emitted when the user has clicked on - a legend item, which is in QwtLegend::ClickableItem mode. + virtual void getCanvasMarginsHint( + const QwtScaleMap maps[], const QRectF &canvasRect, + double &left, double &top, double &right, double &bottom) const; - \param plotItem Corresponding plot item of the - selected legend item + virtual bool event( QEvent * ); + virtual bool eventFilter( QObject *, QEvent * ); - \note clicks are disabled as default - \sa QwtLegend::setItemMode, QwtLegend::itemMode - */ - void legendClicked(QwtPlotItem *plotItem); + virtual void drawItems( QPainter *, const QRectF &, + const QwtScaleMap maps[axisCnt] ) const; - /*! - A signal which is emitted when the user has clicked on - a legend item, which is in QwtLegend::CheckableItem mode + virtual QVariant itemToInfo( QwtPlotItem * ) const; + virtual QwtPlotItem *infoToItem( const QVariant & ) const; - \param plotItem Corresponding plot item of the - selected legend item - \param on True when the legen item is checked +Q_SIGNALS: + /*! + A signal indicating, that an item has been attached/detached - \note clicks are disabled as default - \sa QwtLegend::setItemMode, QwtLegend::itemMode + \param plotItem Plot item + \param on Attached/Detached */ + void itemAttached( QwtPlotItem *plotItem, bool on ); - void legendChecked(QwtPlotItem *plotItem, bool on); + /*! + A signal with the attributes how to update + the legend entries for a plot item. -public slots: - virtual void clear(); + \param itemInfo Info about a plot item, build from itemToInfo() + \param data Attributes of the entries ( usually <= 1 ) for + the plot item. + + \sa itemToInfo(), infoToItem(), QwtAbstractLegend::updateLegend() + */ + void legendDataChanged( const QVariant &itemInfo, + const QList &data ); +public Q_SLOTS: virtual void replot(); void autoRefresh(); -protected slots: - virtual void legendItemClicked(); - virtual void legendItemChecked(bool); - protected: - static bool axisValid(int axisId); - - virtual void drawCanvas(QPainter *); - virtual void drawItems(QPainter *, const QRect &, - const QwtScaleMap maps[axisCnt], - const QwtPlotPrintFilter &) const; - - virtual void updateTabOrder(); - - void updateAxes(); + static bool axisValid( int axisId ); - virtual void resizeEvent(QResizeEvent *e); + virtual void resizeEvent( QResizeEvent *e ); - virtual void printLegendItem(QPainter *, - const QWidget *, const QRect &) const; - - virtual void printTitle(QPainter *, const QRect &) const; - - virtual void printScale(QPainter *, int axisId, int startDist, int endDist, - int baseDist, const QRect &) const; - - virtual void printCanvas(QPainter *, - const QRect &boundingRect, const QRect &canvasRect, - const QwtScaleMap maps[axisCnt], const QwtPlotPrintFilter &) const; - - virtual void printLegend(QPainter *, const QRect &) const; +private Q_SLOTS: + void updateLegendItems( const QVariant &itemInfo, + const QList &data ); private: + friend class QwtPlotItem; + void attachItem( QwtPlotItem *, bool ); + void initAxesData(); void deleteAxesData(); void updateScaleDiv(); - void initPlot(const QwtText &title); + void initPlot( const QwtText &title ); class AxisData; AxisData *d_axisData[axisCnt]; diff --git a/libs/qwt/qwt_plot_abstract_barchart.cpp b/libs/qwt/qwt_plot_abstract_barchart.cpp new file mode 100644 index 000000000..9a72fdd5b --- /dev/null +++ b/libs/qwt/qwt_plot_abstract_barchart.cpp @@ -0,0 +1,367 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_plot_abstract_barchart.h" +#include "qwt_scale_map.h" + +static inline double qwtTransformWidth( + const QwtScaleMap &map, double value, double width ) +{ + const double w2 = 0.5 * width; + + const double v1 = map.transform( value - w2 ); + const double v2 = map.transform( value + w2 ); + + return qAbs( v2 - v1 ); +} + +class QwtPlotAbstractBarChart::PrivateData +{ +public: + PrivateData(): + layoutPolicy( QwtPlotAbstractBarChart::AutoAdjustSamples ), + layoutHint( 0.5 ), + spacing( 10 ), + margin( 5 ), + baseline( 0.0 ) + { + } + + QwtPlotAbstractBarChart::LayoutPolicy layoutPolicy; + double layoutHint; + int spacing; + int margin; + double baseline; +}; + +/*! + Constructor + \param title Title of the chart +*/ +QwtPlotAbstractBarChart::QwtPlotAbstractBarChart( const QwtText &title ): + QwtPlotSeriesItem( title ) +{ + d_data = new PrivateData; + + setItemAttribute( QwtPlotItem::Legend, true ); + setItemAttribute( QwtPlotItem::AutoScale, true ); + setItemAttribute( QwtPlotItem::Margins, true ); + setZ( 19.0 ); +} + +//! Destructor +QwtPlotAbstractBarChart::~QwtPlotAbstractBarChart() +{ + delete d_data; +} + +/*! + The combination of layoutPolicy() and layoutHint() define how the width + of the bars is calculated + + \param policy Layout policy + + \sa layoutPolicy(), layoutHint() + */ +void QwtPlotAbstractBarChart::setLayoutPolicy( LayoutPolicy policy ) +{ + if ( policy != d_data->layoutPolicy ) + { + d_data->layoutPolicy = policy; + itemChanged(); + } +} + +/*! + The combination of layoutPolicy() and layoutHint() define how the width + of the bars is calculated + + \return Layout policy of the chart item + \sa setLayoutPolicy(), layoutHint() + */ +QwtPlotAbstractBarChart::LayoutPolicy QwtPlotAbstractBarChart::layoutPolicy() const +{ + return d_data->layoutPolicy; +} + +/*! + The combination of layoutPolicy() and layoutHint() define how the width + of the bars is calculated + + \param hint Layout hint + + \sa LayoutPolicy, layoutPolicy(), layoutHint() + */ +void QwtPlotAbstractBarChart::setLayoutHint( double hint ) +{ + hint = qMax( 0.0, hint ); + if ( hint != d_data->layoutHint ) + { + d_data->layoutHint = hint; + itemChanged(); + } +} + +/*! + The combination of layoutPolicy() and layoutHint() define how the width + of the bars is calculated + + \return Layout policy of the chart item + \sa LayoutPolicy, setLayoutHint(), layoutPolicy() +*/ +double QwtPlotAbstractBarChart::layoutHint() const +{ + return d_data->layoutHint; +} + +/*! + \brief Set the spacing + + The spacing is the distance between 2 samples ( bars for QwtPlotBarChart or + a group of bars for QwtPlotMultiBarChart ) in paint device coordinates. + + \sa spacing() + */ +void QwtPlotAbstractBarChart::setSpacing( int spacing ) +{ + spacing = qMax( spacing, 0 ); + if ( spacing != d_data->spacing ) + { + d_data->spacing = spacing; + itemChanged(); + } +} + +/*! + \return Spacing between 2 samples ( bars or groups of bars ) + \sa setSpacing(), margin() + */ +int QwtPlotAbstractBarChart::spacing() const +{ + return d_data->spacing; +} +/*! + \brief Set the margin + + The margin is the distance between the outmost bars and the contentsRect() + of the canvas. The default setting is 5 pixels. + + \param margin Margin + + \sa spacing(), margin() + */ +void QwtPlotAbstractBarChart::setMargin( int margin ) +{ + margin = qMax( margin, 0 ); + if ( margin != d_data->margin ) + { + d_data->margin = margin; + itemChanged(); + } +} + +/*! + \return Margin between the outmost bars and the contentsRect() + of the canvas. + + \sa setMargin(), spacing() + */ +int QwtPlotAbstractBarChart::margin() const +{ + return d_data->margin; +} + +/*! + \brief Set the baseline + + The baseline is the origin for the chart. Each bar is + painted from the baseline in the direction of the sample + value. In case of a horizontal orientation() the baseline + is interpreted as x - otherwise as y - value. + + The default value for the baseline is 0. + + \param value Value for the baseline + + \sa baseline(), QwtPlotSeriesItem::orientation() +*/ +void QwtPlotAbstractBarChart::setBaseline( double value ) +{ + if ( value != d_data->baseline ) + { + d_data->baseline = value; + itemChanged(); + } +} + +/*! + \return Value for the origin of the bar chart + \sa setBaseline(), QwtPlotSeriesItem::orientation() + */ +double QwtPlotAbstractBarChart::baseline() const +{ + return d_data->baseline; +} + +/*! + Calculate the width for a sample in paint device coordinates + + \param map Scale map for the corresponding scale + \param canvasSize Size of the canvas in paint device coordinates + \param boundingSize Bounding size of the chart in plot coordinates + ( used in AutoAdjustSamples mode ) + \param value Value of the sample + + \return Sample width + \sa layoutPolicy(), layoutHint() +*/ +double QwtPlotAbstractBarChart::sampleWidth( const QwtScaleMap &map, + double canvasSize, double boundingSize, double value ) const +{ + double width; + + switch( d_data->layoutPolicy ) + { + case ScaleSamplesToAxes: + { + width = qwtTransformWidth( map, value, d_data->layoutHint ); + break; + } + case ScaleSampleToCanvas: + { + width = canvasSize * d_data->layoutHint; + break; + } + case FixedSampleSize: + { + width = d_data->layoutHint; + break; + } + case AutoAdjustSamples: + default: + { + const size_t numSamples = dataSize(); + + double w = 1.0; + if ( numSamples > 1 ) + { + w = qAbs( boundingSize / ( numSamples - 1 ) ); + } + + width = qwtTransformWidth( map, value, w ); + width -= d_data->spacing; + } + } + + return width; +} + +/*! + \brief Calculate a hint for the canvas margin + + Bar charts need to reserve some space for displaying the bars + for the first and the last sample. The hint is calculated + from the layoutHint() depending on the layoutPolicy(). + + The margins are in target device coordinates ( pixels on screen ) + + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param canvasRect Contents rectangle of the canvas in painter coordinates + \param left Returns the left margin + \param top Returns the top margin + \param right Returns the right margin + \param bottom Returns the bottom margin + + \return Margin + + \sa layoutPolicy(), layoutHint(), QwtPlotItem::Margins + QwtPlot::getCanvasMarginsHint(), QwtPlot::updateCanvasMargins() + */ +void QwtPlotAbstractBarChart::getCanvasMarginHint( const QwtScaleMap &xMap, + const QwtScaleMap &yMap, const QRectF &canvasRect, + double &left, double &top, double &right, double &bottom ) const +{ + double hint = -1.0; + + switch( layoutPolicy() ) + { + case ScaleSampleToCanvas: + { + if ( orientation() == Qt::Vertical ) + hint = 0.5 * canvasRect.width() * d_data->layoutHint; + else + hint = 0.5 * canvasRect.height() * d_data->layoutHint; + + break; + } + case FixedSampleSize: + { + hint = 0.5 * d_data->layoutHint; + break; + } + case AutoAdjustSamples: + case ScaleSamplesToAxes: + default: + { + const size_t numSamples = dataSize(); + if ( numSamples <= 0 ) + break; + + // doesn't work for nonlinear scales + + const QRectF br = dataRect(); + double spacing = 0.0; + double sampleWidthS = 1.0; + + if ( layoutPolicy() == ScaleSamplesToAxes ) + { + sampleWidthS = qMax( d_data->layoutHint, 0.0 ); + } + else + { + spacing = d_data->spacing; + + if ( numSamples > 1 ) + { + sampleWidthS = qAbs( br.width() / ( numSamples - 1 ) ); + } + } + + double ds, w; + if ( orientation() == Qt::Vertical ) + { + ds = qAbs( xMap.sDist() ); + w = canvasRect.width(); + } + else + { + ds = qAbs( yMap.sDist() ); + w = canvasRect.height(); + } + + const double sampleWidthP = ( w - spacing * ds ) + * sampleWidthS / ( ds + sampleWidthS ); + + hint = 0.5 * sampleWidthP; + hint += qMax( d_data->margin, 0 ); + } + } + + if ( orientation() == Qt::Vertical ) + { + left = right = hint; + top = bottom = -1.0; // no hint + } + else + { + left = right = -1.0; // no hint + top = bottom = hint; + } +} diff --git a/libs/qwt/qwt_plot_abstract_barchart.h b/libs/qwt/qwt_plot_abstract_barchart.h new file mode 100644 index 000000000..78b98a531 --- /dev/null +++ b/libs/qwt/qwt_plot_abstract_barchart.h @@ -0,0 +1,97 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_PLOT_ABSTRACT_BAR_CHART_H +#define QWT_PLOT_ABSTRACT_BAR_CHART_H + +#include "qwt_global.h" +#include "qwt_plot_seriesitem.h" +#include "qwt_series_data.h" + +/*! + \brief Abstract base class for bar chart items + + In opposite to almost all other plot items bar charts can't be + displayed inside of their bounding rectangle and need a special + API how to calculate the width of the bars and how they affect + the layout of the attached plot. + */ +class QWT_EXPORT QwtPlotAbstractBarChart: public QwtPlotSeriesItem +{ +public: + /*! + \brief Mode how to calculate the bar width + + setLayoutPolicy(), setLayoutHint(), barWidthHint() + */ + enum LayoutPolicy + { + /*! + The sample width is calculated by dividing the bounding rectangle + by the number of samples. + + \sa boundingRectangle() + \note The layoutHint() is ignored + */ + AutoAdjustSamples, + + /*! + layoutHint() defines an interval in axis coordinates + */ + ScaleSamplesToAxes, + + /*! + The bar width is calculated by multiplying layoutHint() + with the height or width of the canvas. + + \sa boundingRectangle() + */ + ScaleSampleToCanvas, + + /*! + layoutHint() defines a fixed width in paint device coordinates. + */ + FixedSampleSize + }; + + explicit QwtPlotAbstractBarChart( const QwtText &title ); + virtual ~QwtPlotAbstractBarChart(); + + void setLayoutPolicy( LayoutPolicy ); + LayoutPolicy layoutPolicy() const; + + void setLayoutHint( double ); + double layoutHint() const; + + void setSpacing( int ); + int spacing() const; + + void setMargin( int ); + int margin() const; + + void setBaseline( double ); + double baseline() const; + + virtual void getCanvasMarginHint( + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, + double &left, double &top, double &right, double &bottom) const; + + +protected: + double sampleWidth( const QwtScaleMap &map, + double canvasSize, double dataSize, + double value ) const; + +private: + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/libs/qwt/qwt_plot_axis.cpp b/libs/qwt/qwt_plot_axis.cpp index 5f1a9a501..e3802f68e 100644 --- a/libs/qwt/qwt_plot_axis.cpp +++ b/libs/qwt/qwt_plot_axis.cpp @@ -26,6 +26,8 @@ public: int maxMajor; int maxMinor; + bool isValid; + QwtScaleDiv scaleDiv; QwtScaleEngine *scaleEngine; QwtScaleWidget *scaleWidget; @@ -36,31 +38,44 @@ void QwtPlot::initAxesData() { int axisId; - for( axisId = 0; axisId < axisCnt; axisId++) + for ( axisId = 0; axisId < axisCnt; axisId++ ) d_axisData[axisId] = new AxisData; d_axisData[yLeft]->scaleWidget = - new QwtScaleWidget(QwtScaleDraw::LeftScale, this); + new QwtScaleWidget( QwtScaleDraw::LeftScale, this ); d_axisData[yRight]->scaleWidget = - new QwtScaleWidget(QwtScaleDraw::RightScale, this); + new QwtScaleWidget( QwtScaleDraw::RightScale, this ); d_axisData[xTop]->scaleWidget = - new QwtScaleWidget(QwtScaleDraw::TopScale, this); + new QwtScaleWidget( QwtScaleDraw::TopScale, this ); d_axisData[xBottom]->scaleWidget = - new QwtScaleWidget(QwtScaleDraw::BottomScale, this); + new QwtScaleWidget( QwtScaleDraw::BottomScale, this ); + d_axisData[yLeft]->scaleWidget->setObjectName( "QwtPlotAxisYLeft" ); + d_axisData[yRight]->scaleWidget->setObjectName( "QwtPlotAxisYRight" ); + d_axisData[xTop]->scaleWidget->setObjectName( "QwtPlotAxisXTop" ); + d_axisData[xBottom]->scaleWidget->setObjectName( "QwtPlotAxisXBottom" ); - QFont fscl(fontInfo().family(), 10); - QFont fttl(fontInfo().family(), 12, QFont::Bold); +#if 1 + // better find the font sizes from the application font + QFont fscl( fontInfo().family(), 10 ); + QFont fttl( fontInfo().family(), 12, QFont::Bold ); +#endif - for(axisId = 0; axisId < axisCnt; axisId++) { + for ( axisId = 0; axisId < axisCnt; axisId++ ) + { AxisData &d = *d_axisData[axisId]; - d.scaleWidget->setFont(fscl); - d.scaleWidget->setMargin(2); + d.scaleEngine = new QwtLinearScaleEngine; + + d.scaleWidget->setTransformation( + d.scaleEngine->transformation() ); + + d.scaleWidget->setFont( fscl ); + d.scaleWidget->setMargin( 2 ); QwtText text = d.scaleWidget->title(); - text.setFont(fttl); - d.scaleWidget->setTitle(text); + text.setFont( fttl ); + d.scaleWidget->setTitle( text ); d.doAutoScale = true; @@ -71,9 +86,8 @@ void QwtPlot::initAxesData() d.maxMinor = 5; d.maxMajor = 8; - d.scaleEngine = new QwtLinearScaleEngine; - d.scaleDiv.invalidate(); + d.isValid = false; } d_axisData[yLeft]->isEnabled = true; @@ -84,7 +98,8 @@ void QwtPlot::initAxesData() void QwtPlot::deleteAxesData() { - for( int axisId = 0; axisId < axisCnt; axisId++) { + for ( int axisId = 0; axisId < axisCnt; axisId++ ) + { delete d_axisData[axisId]->scaleEngine; delete d_axisData[axisId]; d_axisData[axisId] = NULL; @@ -92,75 +107,85 @@ void QwtPlot::deleteAxesData() } /*! - \return specified axis, or NULL if axisId is invalid. - \param axisId axis index + \return Scale widget of the specified axis, or NULL if axisId is invalid. + \param axisId Axis index */ -const QwtScaleWidget *QwtPlot::axisWidget(int axisId) const +const QwtScaleWidget *QwtPlot::axisWidget( int axisId ) const { - if (axisValid(axisId)) + if ( axisValid( axisId ) ) return d_axisData[axisId]->scaleWidget; return NULL; } /*! - \return specified axis, or NULL if axisId is invalid. - \param axisId axis index + \return Scale widget of the specified axis, or NULL if axisId is invalid. + \param axisId Axis index */ -QwtScaleWidget *QwtPlot::axisWidget(int axisId) +QwtScaleWidget *QwtPlot::axisWidget( int axisId ) { - if (axisValid(axisId)) + if ( axisValid( axisId ) ) return d_axisData[axisId]->scaleWidget; return NULL; } /*! - Change the scale engine for an axis + Change the scale engine for an axis - \param axisId axis index + \param axisId Axis index \param scaleEngine Scale engine \sa axisScaleEngine() */ -void QwtPlot::setAxisScaleEngine(int axisId, QwtScaleEngine *scaleEngine) +void QwtPlot::setAxisScaleEngine( int axisId, QwtScaleEngine *scaleEngine ) { - if (axisValid(axisId) && scaleEngine != NULL ) { + if ( axisValid( axisId ) && scaleEngine != NULL ) + { AxisData &d = *d_axisData[axisId]; delete d.scaleEngine; d.scaleEngine = scaleEngine; - d.scaleDiv.invalidate(); + d_axisData[axisId]->scaleWidget->setTransformation( + scaleEngine->transformation() ); + + d.isValid = false; autoRefresh(); } } -//! \return Scale engine for a specific axis -QwtScaleEngine *QwtPlot::axisScaleEngine(int axisId) +/*! + \param axisId Axis index + \return Scale engine for a specific axis +*/ +QwtScaleEngine *QwtPlot::axisScaleEngine( int axisId ) { - if (axisValid(axisId)) + if ( axisValid( axisId ) ) return d_axisData[axisId]->scaleEngine; else return NULL; } -//! \return Scale engine for a specific axis -const QwtScaleEngine *QwtPlot::axisScaleEngine(int axisId) const +/*! + \param axisId Axis index + \return Scale engine for a specific axis +*/ +const QwtScaleEngine *QwtPlot::axisScaleEngine( int axisId ) const { - if (axisValid(axisId)) + if ( axisValid( axisId ) ) return d_axisData[axisId]->scaleEngine; else return NULL; } /*! - \return \c true if autoscaling is enabled - \param axisId axis index + \return \c True, if autoscaling is enabled + \param axisId Axis index */ -bool QwtPlot::axisAutoScale(int axisId) const +bool QwtPlot::axisAutoScale( int axisId ) const { - if (axisValid(axisId)) + if ( axisValid( axisId ) ) return d_axisData[axisId]->doAutoScale; else return false; @@ -168,38 +193,38 @@ bool QwtPlot::axisAutoScale(int axisId) const } /*! - \return \c true if a specified axis is enabled - \param axisId axis index + \return \c True, if a specified axis is enabled + \param axisId Axis index */ -bool QwtPlot::axisEnabled(int axisId) const +bool QwtPlot::axisEnabled( int axisId ) const { - if (axisValid(axisId)) + if ( axisValid( axisId ) ) return d_axisData[axisId]->isEnabled; else return false; } /*! - \return the font of the scale labels for a specified axis - \param axisId axis index + \return The font of the scale labels for a specified axis + \param axisId Axis index */ -QFont QwtPlot::axisFont(int axisId) const +QFont QwtPlot::axisFont( int axisId ) const { - if (axisValid(axisId)) - return axisWidget(axisId)->font(); + if ( axisValid( axisId ) ) + return axisWidget( axisId )->font(); else return QFont(); } /*! - \return the maximum number of major ticks for a specified axis - \param axisId axis index - sa setAxisMaxMajor() + \return The maximum number of major ticks for a specified axis + \param axisId Axis index + \sa setAxisMaxMajor(), QwtScaleEngine::divideScale() */ -int QwtPlot::axisMaxMajor(int axisId) const +int QwtPlot::axisMaxMajor( int axisId ) const { - if (axisValid(axisId)) + if ( axisValid( axisId ) ) return d_axisData[axisId]->maxMajor; else return 0; @@ -207,12 +232,12 @@ int QwtPlot::axisMaxMajor(int axisId) const /*! \return the maximum number of minor ticks for a specified axis - \param axisId axis index - sa setAxisMaxMinor() + \param axisId Axis index + \sa setAxisMaxMinor(), QwtScaleEngine::divideScale() */ -int QwtPlot::axisMaxMinor(int axisId) const +int QwtPlot::axisMaxMinor( int axisId ) const { - if (axisValid(axisId)) + if ( axisValid( axisId ) ) return d_axisData[axisId]->maxMinor; else return 0; @@ -221,95 +246,91 @@ int QwtPlot::axisMaxMinor(int axisId) const /*! \brief Return the scale division of a specified axis - axisScaleDiv(axisId)->lBound(), axisScaleDiv(axisId)->hBound() + axisScaleDiv(axisId).lowerBound(), axisScaleDiv(axisId).upperBound() are the current limits of the axis scale. - \param axisId axis index + \param axisId Axis index \return Scale division - \sa QwtScaleDiv, setAxisScaleDiv + \sa QwtScaleDiv, setAxisScaleDiv(), QwtScaleEngine::divideScale() */ -const QwtScaleDiv *QwtPlot::axisScaleDiv(int axisId) const +const QwtScaleDiv &QwtPlot::axisScaleDiv( int axisId ) const { - if (!axisValid(axisId)) - return NULL; - - return &d_axisData[axisId]->scaleDiv; + return d_axisData[axisId]->scaleDiv; } /*! - \brief Return the scale division of a specified axis - - axisScaleDiv(axisId)->lBound(), axisScaleDiv(axisId)->hBound() - are the current limits of the axis scale. - - \param axisId axis index - \return Scale division + \brief Return the scale draw of a specified axis - \sa QwtScaleDiv, setAxisScaleDiv + \param axisId Axis index + \return Specified scaleDraw for axis, or NULL if axis is invalid. */ -QwtScaleDiv *QwtPlot::axisScaleDiv(int axisId) +const QwtScaleDraw *QwtPlot::axisScaleDraw( int axisId ) const { - if (!axisValid(axisId)) + if ( !axisValid( axisId ) ) return NULL; - return &d_axisData[axisId]->scaleDiv; + return axisWidget( axisId )->scaleDraw(); } /*! - \returns the scale draw of a specified axis - \param axisId axis index - \return specified scaleDraw for axis, or NULL if axis is invalid. - \sa QwtScaleDraw + \brief Return the scale draw of a specified axis + + \param axisId Axis index + \return Specified scaleDraw for axis, or NULL if axis is invalid. */ -const QwtScaleDraw *QwtPlot::axisScaleDraw(int axisId) const +QwtScaleDraw *QwtPlot::axisScaleDraw( int axisId ) { - if (!axisValid(axisId)) + if ( !axisValid( axisId ) ) return NULL; - return axisWidget(axisId)->scaleDraw(); + return axisWidget( axisId )->scaleDraw(); } /*! - \returns the scale draw of a specified axis - \param axisId axis index - \return specified scaleDraw for axis, or NULL if axis is invalid. - \sa QwtScaleDraw + \brief Return the step size parameter that has been set in setAxisScale. + + This doesn't need to be the step size of the current scale. + + \param axisId Axis index + \return step size parameter value + + \sa setAxisScale(), QwtScaleEngine::divideScale() */ -QwtScaleDraw *QwtPlot::axisScaleDraw(int axisId) +double QwtPlot::axisStepSize( int axisId ) const { - if (!axisValid(axisId)) - return NULL; + if ( !axisValid( axisId ) ) + return 0; - return axisWidget(axisId)->scaleDraw(); + return d_axisData[axisId]->stepSize; } /*! - Return the step size parameter, that has been set - in setAxisScale. This doesn't need to be the step size - of the current scale. + \brief Return the current interval of the specified axis - \param axisId axis index - \return step size parameter value + This is only a convenience function for axisScaleDiv( axisId )->interval(); + + \param axisId Axis index + \return Scale interval - \sa setAxisScale + \sa QwtScaleDiv, axisScaleDiv() */ -double QwtPlot::axisStepSize(int axisId) const +QwtInterval QwtPlot::axisInterval( int axisId ) const { - if (!axisValid(axisId)) - return 0; + if ( !axisValid( axisId ) ) + return QwtInterval(); - return d_axisData[axisId]->stepSize; + return d_axisData[axisId]->scaleDiv.interval(); } /*! - \return the title of a specified axis - \param axisId axis index + \return Title of a specified axis + \param axisId Axis index */ -QwtText QwtPlot::axisTitle(int axisId) const +QwtText QwtPlot::axisTitle( int axisId ) const { - if (axisValid(axisId)) - return axisWidget(axisId)->title(); + if ( axisValid( axisId ) ) + return axisWidget( axisId )->title(); else return QwtText(); } @@ -323,12 +344,14 @@ QwtText QwtPlot::axisTitle(int axisId) const into values works as normal. Only xBottom and yLeft are enabled by default. - \param axisId axis index + + \param axisId Axis index \param tf \c true (enabled) or \c false (disabled) */ -void QwtPlot::enableAxis(int axisId, bool tf) +void QwtPlot::enableAxis( int axisId, bool tf ) { - if (axisValid(axisId) && tf != d_axisData[axisId]->isEnabled) { + if ( axisValid( axisId ) && tf != d_axisData[axisId]->isEnabled ) + { d_axisData[axisId]->isEnabled = tf; updateLayout(); } @@ -337,15 +360,19 @@ void QwtPlot::enableAxis(int axisId, bool tf) /*! Transform the x or y coordinate of a position in the drawing region into a value. - \param axisId axis index + + \param axisId Axis index \param pos position + + \return Position as axis coordinate + \warning The position can be an x or a y coordinate, depending on the specified axis. */ -double QwtPlot::invTransform(int axisId, int pos) const +double QwtPlot::invTransform( int axisId, int pos ) const { - if (axisValid(axisId)) - return(canvasMap(axisId).invTransform(pos)); + if ( axisValid( axisId ) ) + return( canvasMap( axisId ).invTransform( pos ) ); else return 0.0; } @@ -353,31 +380,32 @@ double QwtPlot::invTransform(int axisId, int pos) const /*! \brief Transform a value into a coordinate in the plotting region - \param axisId axis index + + \param axisId Axis index \param value value - \return X or y coordinate in the plotting region corresponding + \return X or Y coordinate in the plotting region corresponding to the value. */ -int QwtPlot::transform(int axisId, double value) const +double QwtPlot::transform( int axisId, double value ) const { - if (axisValid(axisId)) - return(canvasMap(axisId).transform(value)); + if ( axisValid( axisId ) ) + return( canvasMap( axisId ).transform( value ) ); else - return 0; - + return 0.0; } /*! \brief Change the font of an axis - \param axisId axis index - \param f font + + \param axisId Axis index + \param font Font \warning This function changes the font of the tick labels, not of the axis title. */ -void QwtPlot::setAxisFont(int axisId, const QFont &f) +void QwtPlot::setAxisFont( int axisId, const QFont &font ) { - if (axisValid(axisId)) - axisWidget(axisId)->setFont(f); + if ( axisValid( axisId ) ) + axisWidget( axisId )->setFont( font ); } /*! @@ -386,33 +414,45 @@ void QwtPlot::setAxisFont(int axisId, const QFont &f) This member function is used to switch back to autoscaling mode after a fixed scale has been set. Autoscaling is enabled by default. - \param axisId axis index - \sa QwtPlot::setAxisScale(), QwtPlot::setAxisScaleDiv() + \param axisId Axis index + \param on On/Off + \sa setAxisScale(), setAxisScaleDiv(), updateAxes() + + \note The autoscaling flag has no effect until updateAxes() is executed + ( called by replot() ). */ -void QwtPlot::setAxisAutoScale(int axisId) +void QwtPlot::setAxisAutoScale( int axisId, bool on ) { - if (axisValid(axisId) && !d_axisData[axisId]->doAutoScale ) { - d_axisData[axisId]->doAutoScale = true; + if ( axisValid( axisId ) && ( d_axisData[axisId]->doAutoScale != on ) ) + { + d_axisData[axisId]->doAutoScale = on; autoRefresh(); } } /*! \brief Disable autoscaling and specify a fixed scale for a selected axis. - \param axisId axis index - \param min - \param max minimum and maximum of the scale + + In updateAxes() the scale engine calculates a scale division from the + specified parameters, that will be assigned to the scale widget. So + updates of the scale widget usually happen delayed with the next replot. + + \param axisId Axis index + \param min Minimum of the scale + \param max Maximum of the scale \param stepSize Major step size. If step == 0, the step size is - calculated automatically using the maxMajor setting. - \sa setAxisMaxMajor(), setAxisAutoScale() + calculated automatically using the maxMajor setting. + + \sa setAxisMaxMajor(), setAxisAutoScale(), axisStepSize(), QwtScaleEngine::divideScale() */ -void QwtPlot::setAxisScale(int axisId, double min, double max, double stepSize) +void QwtPlot::setAxisScale( int axisId, double min, double max, double stepSize ) { - if (axisValid(axisId)) { + if ( axisValid( axisId ) ) + { AxisData &d = *d_axisData[axisId]; d.doAutoScale = false; - d.scaleDiv.invalidate(); + d.isValid = false; d.minValue = min; d.maxValue = max; @@ -424,17 +464,25 @@ void QwtPlot::setAxisScale(int axisId, double min, double max, double stepSize) /*! \brief Disable autoscaling and specify a fixed scale for a selected axis. - \param axisId axis index + + The scale division will be stored locally only until the next call + of updateAxes(). So updates of the scale widget usually happen delayed with + the next replot. + + \param axisId Axis index \param scaleDiv Scale division + \sa setAxisScale(), setAxisAutoScale() */ -void QwtPlot::setAxisScaleDiv(int axisId, const QwtScaleDiv &scaleDiv) +void QwtPlot::setAxisScaleDiv( int axisId, const QwtScaleDiv &scaleDiv ) { - if (axisValid(axisId)) { + if ( axisValid( axisId ) ) + { AxisData &d = *d_axisData[axisId]; d.doAutoScale = false; d.scaleDiv = scaleDiv; + d.isValid = true; autoRefresh(); } @@ -442,8 +490,9 @@ void QwtPlot::setAxisScaleDiv(int axisId, const QwtScaleDiv &scaleDiv) /*! \brief Set a scale draw - \param axisId axis index - \param scaleDraw object responsible for drawing scales. + + \param axisId Axis index + \param scaleDraw Object responsible for drawing scales. By passing scaleDraw it is possible to extend QwtScaleDraw functionality and let it take place in QwtPlot. Please note @@ -455,63 +504,63 @@ void QwtPlot::setAxisScaleDiv(int axisId, const QwtScaleDiv &scaleDiv) previous QwtScaleDraw. */ -void QwtPlot::setAxisScaleDraw(int axisId, QwtScaleDraw *scaleDraw) +void QwtPlot::setAxisScaleDraw( int axisId, QwtScaleDraw *scaleDraw ) { - if (axisValid(axisId)) { - axisWidget(axisId)->setScaleDraw(scaleDraw); + if ( axisValid( axisId ) ) + { + axisWidget( axisId )->setScaleDraw( scaleDraw ); autoRefresh(); } } /*! Change the alignment of the tick labels - \param axisId axis index - \param alignment Or'd Qt::AlignmentFlags + + \param axisId Axis index + \param alignment Or'd Qt::AlignmentFlags see + \sa QwtScaleDraw::setLabelAlignment() */ -#if QT_VERSION < 0x040000 -void QwtPlot::setAxisLabelAlignment(int axisId, int alignment) -#else -void QwtPlot::setAxisLabelAlignment(int axisId, Qt::Alignment alignment) -#endif +void QwtPlot::setAxisLabelAlignment( int axisId, Qt::Alignment alignment ) { - if (axisValid(axisId)) - axisWidget(axisId)->setLabelAlignment(alignment); + if ( axisValid( axisId ) ) + axisWidget( axisId )->setLabelAlignment( alignment ); } /*! Rotate all tick labels - \param axisId axis index + + \param axisId Axis index \param rotation Angle in degrees. When changing the label rotation, the label alignment might be adjusted too. - \sa QwtScaleDraw::setLabelRotation(), QwtPlot::setAxisLabelAlignment + + \sa QwtScaleDraw::setLabelRotation(), setAxisLabelAlignment() */ -void QwtPlot::setAxisLabelRotation(int axisId, double rotation) +void QwtPlot::setAxisLabelRotation( int axisId, double rotation ) { - if (axisValid(axisId)) - axisWidget(axisId)->setLabelRotation(rotation); + if ( axisValid( axisId ) ) + axisWidget( axisId )->setLabelRotation( rotation ); } /*! Set the maximum number of minor scale intervals for a specified axis - \param axisId axis index - \param maxMinor maximum number of minor steps + \param axisId Axis index + \param maxMinor Maximum number of minor steps + \sa axisMaxMinor() */ -void QwtPlot::setAxisMaxMinor(int axisId, int maxMinor) +void QwtPlot::setAxisMaxMinor( int axisId, int maxMinor ) { - if (axisValid(axisId)) { - if ( maxMinor < 0 ) - maxMinor = 0; - if ( maxMinor > 100 ) - maxMinor = 100; + if ( axisValid( axisId ) ) + { + maxMinor = qBound( 0, maxMinor, 100 ); AxisData &d = *d_axisData[axisId]; - - if ( maxMinor != d.maxMinor ) { + if ( maxMinor != d.maxMinor ) + { d.maxMinor = maxMinor; - d.scaleDiv.invalidate(); + d.isValid = false; autoRefresh(); } } @@ -520,22 +569,22 @@ void QwtPlot::setAxisMaxMinor(int axisId, int maxMinor) /*! Set the maximum number of major scale intervals for a specified axis - \param axisId axis index - \param maxMajor maximum number of major steps + \param axisId Axis index + \param maxMajor Maximum number of major steps + \sa axisMaxMajor() */ -void QwtPlot::setAxisMaxMajor(int axisId, int maxMajor) +void QwtPlot::setAxisMaxMajor( int axisId, int maxMajor ) { - if (axisValid(axisId)) { - if ( maxMajor < 1 ) - maxMajor = 1; - if ( maxMajor > 1000 ) - maxMajor = 10000; + if ( axisValid( axisId ) ) + { + maxMajor = qBound( 1, maxMajor, 10000 ); AxisData &d = *d_axisData[axisId]; - if ( maxMajor != d.maxMinor ) { + if ( maxMajor != d.maxMajor ) + { d.maxMajor = maxMajor; - d.scaleDiv.invalidate(); + d.isValid = false; autoRefresh(); } } @@ -543,87 +592,128 @@ void QwtPlot::setAxisMaxMajor(int axisId, int maxMajor) /*! \brief Change the title of a specified axis - \param axisId axis index + + \param axisId Axis index \param title axis title */ -void QwtPlot::setAxisTitle(int axisId, const QString &title) +void QwtPlot::setAxisTitle( int axisId, const QString &title ) { - if (axisValid(axisId)) - axisWidget(axisId)->setTitle(title); + if ( axisValid( axisId ) ) + axisWidget( axisId )->setTitle( title ); } /*! \brief Change the title of a specified axis - \param axisId axis index - \param title axis title + + \param axisId Axis index + \param title Axis title */ -void QwtPlot::setAxisTitle(int axisId, const QwtText &title) +void QwtPlot::setAxisTitle( int axisId, const QwtText &title ) { - if (axisValid(axisId)) - axisWidget(axisId)->setTitle(title); + if ( axisValid( axisId ) ) + axisWidget( axisId )->setTitle( title ); } -//! Rebuild the scales +/*! + \brief Rebuild the axes scales + + In case of autoscaling the boundaries of a scale are calculated + from the bounding rectangles of all plot items, having the + QwtPlotItem::AutoScale flag enabled ( QwtScaleEngine::autoScale() ). + Then a scale division is calculated ( QwtScaleEngine::didvideScale() ) + and assigned to scale widget. + + When the scale boundaries have been assigned with setAxisScale() a + scale division is calculated ( QwtScaleEngine::didvideScale() ) + for this interval and assigned to the scale widget. + + When the scale has been set explicitly by setAxisScaleDiv() the + locally stored scale division gets assigned to the scale widget. + + The scale widget indicates modifications by emitting a + QwtScaleWidget::scaleDivChanged() signal. + + updateAxes() is usually called by replot(). + + \sa setAxisAutoScale(), setAxisScale(), setAxisScaleDiv(), replot() + QwtPlotItem::boundingRect() + */ void QwtPlot::updateAxes() { // Find bounding interval of the item data // for all axes, where autoscaling is enabled - QwtDoubleInterval intv[axisCnt]; + QwtInterval intv[axisCnt]; const QwtPlotItemList& itmList = itemList(); QwtPlotItemIterator it; - for ( it = itmList.begin(); it != itmList.end(); ++it ) { + for ( it = itmList.begin(); it != itmList.end(); ++it ) + { const QwtPlotItem *item = *it; - if ( !item->testItemAttribute(QwtPlotItem::AutoScale) ) + if ( !item->testItemAttribute( QwtPlotItem::AutoScale ) ) + continue; + + if ( !item->isVisible() ) continue; - if ( axisAutoScale(item->xAxis()) || axisAutoScale(item->yAxis()) ) { - const QwtDoubleRect rect = item->boundingRect(); - intv[item->xAxis()] |= QwtDoubleInterval(rect.left(), rect.right()); - intv[item->yAxis()] |= QwtDoubleInterval(rect.top(), rect.bottom()); + if ( axisAutoScale( item->xAxis() ) || axisAutoScale( item->yAxis() ) ) + { + const QRectF rect = item->boundingRect(); + + if ( rect.width() >= 0.0 ) + intv[item->xAxis()] |= QwtInterval( rect.left(), rect.right() ); + + if ( rect.height() >= 0.0 ) + intv[item->yAxis()] |= QwtInterval( rect.top(), rect.bottom() ); } } // Adjust scales - for (int axisId = 0; axisId < axisCnt; axisId++) { + for ( int axisId = 0; axisId < axisCnt; axisId++ ) + { AxisData &d = *d_axisData[axisId]; double minValue = d.minValue; double maxValue = d.maxValue; double stepSize = d.stepSize; - if ( d.doAutoScale && intv[axisId].isValid() ) { - d.scaleDiv.invalidate(); + if ( d.doAutoScale && intv[axisId].isValid() ) + { + d.isValid = false; minValue = intv[axisId].minValue(); maxValue = intv[axisId].maxValue(); - d.scaleEngine->autoScale(d.maxMajor, - minValue, maxValue, stepSize); + d.scaleEngine->autoScale( d.maxMajor, + minValue, maxValue, stepSize ); } - if ( !d.scaleDiv.isValid() ) { + if ( !d.isValid ) + { d.scaleDiv = d.scaleEngine->divideScale( - minValue, maxValue, - d.maxMajor, d.maxMinor, stepSize); + minValue, maxValue, + d.maxMajor, d.maxMinor, stepSize ); + d.isValid = true; } - QwtScaleWidget *scaleWidget = axisWidget(axisId); - scaleWidget->setScaleDiv( - d.scaleEngine->transformation(), d.scaleDiv); + QwtScaleWidget *scaleWidget = axisWidget( axisId ); + scaleWidget->setScaleDiv( d.scaleDiv ); int startDist, endDist; - scaleWidget->getBorderDistHint(startDist, endDist); - scaleWidget->setBorderDist(startDist, endDist); + scaleWidget->getBorderDistHint( startDist, endDist ); + scaleWidget->setBorderDist( startDist, endDist ); } - for ( it = itmList.begin(); it != itmList.end(); ++it ) { + for ( it = itmList.begin(); it != itmList.end(); ++it ) + { QwtPlotItem *item = *it; - item->updateScaleDiv( *axisScaleDiv(item->xAxis()), - *axisScaleDiv(item->yAxis())); + if ( item->testItemInterest( QwtPlotItem::ScaleInterest ) ) + { + item->updateScaleDiv( axisScaleDiv( item->xAxis() ), + axisScaleDiv( item->yAxis() ) ); + } } } diff --git a/libs/qwt/qwt_plot_barchart.cpp b/libs/qwt/qwt_plot_barchart.cpp new file mode 100644 index 000000000..b9db8e22f --- /dev/null +++ b/libs/qwt/qwt_plot_barchart.cpp @@ -0,0 +1,459 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_plot_barchart.h" +#include "qwt_scale_map.h" +#include "qwt_column_symbol.h" +#include "qwt_painter.h" +#include + +class QwtPlotBarChart::PrivateData +{ +public: + PrivateData(): + symbol( NULL ), + legendMode( QwtPlotBarChart::LegendChartTitle ) + { + } + + ~PrivateData() + { + delete symbol; + } + + QwtColumnSymbol *symbol; + QwtPlotBarChart::LegendMode legendMode; +}; + +/*! + Constructor + \param title Title of the curve +*/ +QwtPlotBarChart::QwtPlotBarChart( const QwtText &title ): + QwtPlotAbstractBarChart( title ) +{ + init(); +} + +/*! + Constructor + \param title Title of the curve +*/ +QwtPlotBarChart::QwtPlotBarChart( const QString &title ): + QwtPlotAbstractBarChart( QwtText( title ) ) +{ + init(); +} + +//! Destructor +QwtPlotBarChart::~QwtPlotBarChart() +{ + delete d_data; +} + +void QwtPlotBarChart::init() +{ + d_data = new PrivateData; + setData( new QwtPointSeriesData() ); +} + +//! \return QwtPlotItem::Rtti_PlotBarChart +int QwtPlotBarChart::rtti() const +{ + return QwtPlotItem::Rtti_PlotBarChart; +} + +/*! + Initialize data with an array of points + + \param samples Vector of points + \note QVector is implicitly shared + \note QPolygonF is derived from QVector +*/ +void QwtPlotBarChart::setSamples( + const QVector &samples ) +{ + setData( new QwtPointSeriesData( samples ) ); +} + +/*! + Initialize data with an array of doubles + + The indices in the array are taken as x coordinate, + while the doubles are interpreted as y values. + + \param samples Vector of y coordinates + \note QVector is implicitly shared +*/ +void QwtPlotBarChart::setSamples( + const QVector &samples ) +{ + QVector points; + for ( int i = 0; i < samples.size(); i++ ) + points += QPointF( i, samples[ i ] ); + + setData( new QwtPointSeriesData( points ) ); +} + +/*! + Assign a series of samples + + setSamples() is just a wrapper for setData() without any additional + value - beside that it is easier to find for the developer. + + \param data Data + \warning The item takes ownership of the data object, deleting + it when its not used anymore. +*/ +void QwtPlotBarChart::setSamples( QwtSeriesData *data ) +{ + setData( data ); +} + +/*! + \brief Assign a symbol + + The bar chart will take the ownership of the symbol, hence the previously + set symbol will be delete by setting a new one. If \p symbol is + \c NULL no symbol will be drawn. + + \param symbol Symbol + \sa symbol() +*/ +void QwtPlotBarChart::setSymbol( QwtColumnSymbol *symbol ) +{ + if ( symbol != d_data->symbol ) + { + delete d_data->symbol; + d_data->symbol = symbol; + + legendChanged(); + itemChanged(); + } +} + +/*! + \return Current symbol or NULL, when no symbol has been assigned + \sa setSymbol() +*/ +const QwtColumnSymbol *QwtPlotBarChart::symbol() const +{ + return d_data->symbol; +} + +/*! + Set the mode that decides what to display on the legend + + In case of LegendBarTitles barTitle() needs to be overloaded + to return individual titles for each bar. + + \param mode New mode + \sa legendMode(), legendData(), barTitle(), QwtPlotItem::ItemAttribute + */ +void QwtPlotBarChart::setLegendMode( LegendMode mode ) +{ + if ( mode != d_data->legendMode ) + { + d_data->legendMode = mode; + legendChanged(); + } +} + +/*! + \return Legend mode + \sa setLegendMode() + */ +QwtPlotBarChart::LegendMode QwtPlotBarChart::legendMode() const +{ + return d_data->legendMode; +} + +/*! + \return Bounding rectangle of all samples. + For an empty series the rectangle is invalid. +*/ +QRectF QwtPlotBarChart::boundingRect() const +{ + const size_t numSamples = dataSize(); + if ( numSamples == 0 ) + return QwtPlotSeriesItem::boundingRect(); + + QRectF rect = QwtPlotSeriesItem::boundingRect(); + if ( rect.height() >= 0 ) + { + const double baseLine = baseline(); + + if ( rect.bottom() < baseLine ) + rect.setBottom( baseLine ); + + if ( rect.top() > baseLine ) + rect.setTop( baseLine ); + } + + if ( orientation() == Qt::Horizontal ) + rect.setRect( rect.y(), rect.x(), rect.height(), rect.width() ); + + return rect; +} + +/*! + Draw an interval of the bar chart + + \param painter Painter + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param canvasRect Contents rect of the canvas + \param from Index of the first point to be painted + \param to Index of the last point to be painted. If to < 0 the + curve will be painted to its last point. + + \sa drawSymbols() +*/ +void QwtPlotBarChart::drawSeries( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const +{ + if ( to < 0 ) + to = dataSize() - 1; + + if ( from < 0 ) + from = 0; + + if ( from > to ) + return; + + + const QRectF br = data()->boundingRect(); + const QwtInterval interval( br.left(), br.right() ); + + painter->save(); + + for ( int i = from; i <= to; i++ ) + { + drawSample( painter, xMap, yMap, + canvasRect, interval, i, sample( i ) ); + } + + painter->restore(); +} + +/*! + Draw a sample + + \param painter Painter + \param xMap x map + \param yMap y map + \param canvasRect Contents rect of the canvas + \param boundingInterval Bounding interval of sample values + \param index Index of the sample + \param sample Value of the sample + + \sa drawSeries() +*/ +void QwtPlotBarChart::drawSample( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, const QwtInterval &boundingInterval, + int index, const QPointF &sample ) const +{ + QwtColumnRect barRect; + + if ( orientation() == Qt::Horizontal ) + { + const double barHeight = sampleWidth( yMap, canvasRect.height(), + boundingInterval.width(), sample.y() ); + + const double x1 = xMap.transform( baseline() ); + const double x2 = xMap.transform( sample.y() ); + + const double y = yMap.transform( sample.x() ); + const double y1 = y - 0.5 * barHeight; + const double y2 = y + 0.5 * barHeight; + + barRect.direction = ( x1 < x2 ) ? + QwtColumnRect::LeftToRight : QwtColumnRect::RightToLeft; + + barRect.hInterval = QwtInterval( x1, x2 ).normalized(); + barRect.vInterval = QwtInterval( y1, y2 ); + } + else + { + const double barWidth = sampleWidth( xMap, canvasRect.width(), + boundingInterval.width(), sample.y() ); + + const double x = xMap.transform( sample.x() ); + const double x1 = x - 0.5 * barWidth; + const double x2 = x + 0.5 * barWidth; + + const double y1 = yMap.transform( baseline() ); + const double y2 = yMap.transform( sample.y() ); + + barRect.direction = ( y1 < y2 ) ? + QwtColumnRect::TopToBottom : QwtColumnRect::BottomToTop; + + barRect.hInterval = QwtInterval( x1, x2 ); + barRect.vInterval = QwtInterval( y1, y2 ).normalized(); + } + + drawBar( painter, index, sample, barRect ); +} + +/*! + Draw a bar + + \param painter Painter + \param sampleIndex Index of the sample represented by the bar + \param sample Value of the sample + \param rect Bounding rectangle of the bar + */ +void QwtPlotBarChart::drawBar( QPainter *painter, + int sampleIndex, const QPointF &sample, + const QwtColumnRect &rect ) const +{ + const QwtColumnSymbol *specialSym = + specialSymbol( sampleIndex, sample ); + + const QwtColumnSymbol *sym = specialSym; + if ( sym == NULL ) + sym = d_data->symbol; + + if ( sym ) + { + sym->draw( painter, rect ); + } + else + { + // we build a temporary default symbol + QwtColumnSymbol sym( QwtColumnSymbol::Box ); + sym.setLineWidth( 1 ); + sym.setFrameStyle( QwtColumnSymbol::Plain ); + sym.draw( painter, rect ); + } + + delete specialSym; +} + +/*! + Needs to be overloaded to return a + non default symbol for a specific sample + + \param sampleIndex Index of the sample represented by the bar + \param sample Value of the sample + + \return NULL, indicating to use the default symbol + */ +QwtColumnSymbol *QwtPlotBarChart::specialSymbol( + int sampleIndex, const QPointF &sample ) const +{ + Q_UNUSED( sampleIndex ); + Q_UNUSED( sample ); + + return NULL; +} + +/*! + \brief Return the title of a bar + + In LegendBarTitles mode the title is displayed on + the legend entry corresponding to a bar. + + The default implementation is a dummy, that is intended + to be overloaded. + + \param sampleIndex Index of the bar + \return An empty text + \sa LegendBarTitles + */ +QwtText QwtPlotBarChart::barTitle( int sampleIndex ) const +{ + Q_UNUSED( sampleIndex ); + return QwtText(); +} + +/*! + \brief Return all information, that is needed to represent + the item on the legend + + In case of LegendBarTitles an entry for each bar is returned, + otherwise the chart is represented like any other plot item + from its title() and the legendIcon(). + + \return Information, that is needed to represent the item on the legend + \sa title(), setLegendMode(), barTitle(), QwtLegend, QwtPlotLegendItem + */ +QList QwtPlotBarChart::legendData() const +{ + QList list; + + if ( d_data->legendMode == LegendBarTitles ) + { + const size_t numSamples = dataSize(); + for ( size_t i = 0; i < numSamples; i++ ) + { + QwtLegendData data; + + QVariant titleValue; + qVariantSetValue( titleValue, barTitle( i ) ); + data.setValue( QwtLegendData::TitleRole, titleValue ); + + if ( !legendIconSize().isEmpty() ) + { + QVariant iconValue; + qVariantSetValue( iconValue, + legendIcon( i, legendIconSize() ) ); + + data.setValue( QwtLegendData::IconRole, iconValue ); + } + + list += data; + } + } + else + { + return QwtPlotAbstractBarChart::legendData(); + } + + return list; +} + +/*! + \return Icon representing a bar or the chart on the legend + + When the legendMode() is LegendBarTitles the icon shows + the bar corresponding to index - otherwise the bar + displays the default symbol. + + \param index Index of the legend entry + \param size Icon size + + \sa setLegendMode(), drawBar(), + QwtPlotItem::setLegendIconSize(), QwtPlotItem::legendData() + */ +QwtGraphic QwtPlotBarChart::legendIcon( + int index, const QSizeF &size ) const +{ + QwtColumnRect column; + column.hInterval = QwtInterval( 0.0, size.width() - 1.0 ); + column.vInterval = QwtInterval( 0.0, size.height() - 1.0 ); + + QwtGraphic icon; + icon.setDefaultSize( size ); + icon.setRenderHint( QwtGraphic::RenderPensUnscaled, true ); + + QPainter painter( &icon ); + painter.setRenderHint( QPainter::Antialiasing, + testRenderHint( QwtPlotItem::RenderAntialiased ) ); + + int barIndex = -1; + if ( d_data->legendMode == QwtPlotBarChart::LegendBarTitles ) + barIndex = index; + + drawBar( &painter, barIndex, QPointF(), column ); + + return icon; +} diff --git a/libs/qwt/qwt_plot_barchart.h b/libs/qwt/qwt_plot_barchart.h new file mode 100644 index 000000000..d47bfb972 --- /dev/null +++ b/libs/qwt/qwt_plot_barchart.h @@ -0,0 +1,118 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_PLOT_BAR_CHART_H +#define QWT_PLOT_BAR_CHART_H + +#include "qwt_global.h" +#include "qwt_plot_abstract_barchart.h" +#include "qwt_series_data.h" + +class QwtColumnRect; +class QwtColumnSymbol; + +/*! + \brief QwtPlotBarChart displays a series of a values as bars. + + Each bar might be customized individually by implementing + a specialSymbol(). Otherwise it is rendered using a default symbol. + + Depending on its orientation() the bars are displayed horizontally + or vertically. The bars cover the interval between the baseline() + and the value. + + By activating the LegendBarTitles mode each sample will have + its own entry on the legend. + + The most common use case of a bar chart is to display a + list of y coordinates, where the x coordinate is simply the index + in the list. But for other situations ( f.e. when values are related + to dates ) it is also possible to set x coordinates explicitly. + + \sa QwtPlotMultiBarChart, QwtPlotHistogram, QwtPlotCurve::Sticks, + QwtPlotSeriesItem::orientation(), QwtPlotAbstractBarChart::baseline() + */ +class QWT_EXPORT QwtPlotBarChart: + public QwtPlotAbstractBarChart, public QwtSeriesStore +{ +public: + /*! + \brief Legend modes. + + The default setting is QwtPlotBarChart::LegendChartTitle. + \sa setLegendMode(), legendMode() + */ + enum LegendMode + { + /*! + One entry on the legend showing the default symbol + and the title() of the chart + + \sa QwtPlotItem::title() + */ + LegendChartTitle, + + /*! + One entry for each value showing the individual symbol + of the corresponding bar and the bar title. + + \sa specialSymbol(), barTitle() + */ + LegendBarTitles + }; + + explicit QwtPlotBarChart( const QString &title = QString::null ); + explicit QwtPlotBarChart( const QwtText &title ); + + virtual ~QwtPlotBarChart(); + + virtual int rtti() const; + + void setSamples( const QVector & ); + void setSamples( const QVector & ); + void setSamples( QwtSeriesData *series ); + + void setSymbol( QwtColumnSymbol * ); + const QwtColumnSymbol *symbol() const; + + void setLegendMode( LegendMode ); + LegendMode legendMode() const; + + virtual void drawSeries( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + + virtual QRectF boundingRect() const; + + virtual QwtColumnSymbol *specialSymbol( + int sampleIndex, const QPointF& ) const; + + virtual QwtText barTitle( int sampleIndex ) const; + +protected: + virtual void drawSample( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, const QwtInterval &boundingInterval, + int index, const QPointF& sample ) const; + + virtual void drawBar( QPainter *, + int sampleIndex, const QPointF& point, + const QwtColumnRect & ) const; + + QList legendData() const; + QwtGraphic legendIcon( int index, const QSizeF & ) const; + +private: + void init(); + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/libs/qwt/qwt_plot_canvas.cpp b/libs/qwt/qwt_plot_canvas.cpp index 1980bb46f..0271713a8 100644 --- a/libs/qwt/qwt_plot_canvas.cpp +++ b/libs/qwt/qwt_plot_canvas.cpp @@ -7,66 +7,541 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ -// vim: expandtab - +#include "qwt_plot_canvas.h" +#include "qwt_painter.h" +#include "qwt_null_paintdevice.h" +#include "qwt_math.h" +#include "qwt_plot.h" #include #include -#if QT_VERSION >= 0x040000 #include #include -#ifdef Q_WS_X11 -#include +#include + +class QwtStyleSheetRecorder: public QwtNullPaintDevice +{ +public: + QwtStyleSheetRecorder( const QSize &size ): + d_size( size ) + { + } + + virtual void updateState( const QPaintEngineState &state ) + { + if ( state.state() & QPaintEngine::DirtyPen ) + { + d_pen = state.pen(); + } + if ( state.state() & QPaintEngine::DirtyBrush ) + { + d_brush = state.brush(); + } + if ( state.state() & QPaintEngine::DirtyBrushOrigin ) + { + d_origin = state.brushOrigin(); + } + } + + virtual void drawRects(const QRectF *rects, int count ) + { + for ( int i = 0; i < count; i++ ) + border.rectList += rects[i]; + } + + virtual void drawPath( const QPainterPath &path ) + { + const QRectF rect( QPointF( 0.0, 0.0 ), d_size ); + if ( path.controlPointRect().contains( rect.center() ) ) + { + setCornerRects( path ); + alignCornerRects( rect ); + + background.path = path; + background.brush = d_brush; + background.origin = d_origin; + } + else + { + border.pathList += path; + } + } + + void setCornerRects( const QPainterPath &path ) + { + QPointF pos( 0.0, 0.0 ); + + for ( int i = 0; i < path.elementCount(); i++ ) + { + QPainterPath::Element el = path.elementAt(i); + switch( el.type ) + { + case QPainterPath::MoveToElement: + case QPainterPath::LineToElement: + { + pos.setX( el.x ); + pos.setY( el.y ); + break; + } + case QPainterPath::CurveToElement: + { + QRectF r( pos, QPointF( el.x, el.y ) ); + clipRects += r.normalized(); + + pos.setX( el.x ); + pos.setY( el.y ); + + break; + } + case QPainterPath::CurveToDataElement: + { + if ( clipRects.size() > 0 ) + { + QRectF r = clipRects.last(); + r.setCoords( + qMin( r.left(), el.x ), + qMin( r.top(), el.y ), + qMax( r.right(), el.x ), + qMax( r.bottom(), el.y ) + ); + clipRects.last() = r.normalized(); + } + break; + } + } + } + } + +protected: + virtual QSize sizeMetrics() const + { + return d_size; + } + +private: + void alignCornerRects( const QRectF &rect ) + { + for ( int i = 0; i < clipRects.size(); i++ ) + { + QRectF &r = clipRects[i]; + if ( r.center().x() < rect.center().x() ) + r.setLeft( rect.left() ); + else + r.setRight( rect.right() ); + + if ( r.center().y() < rect.center().y() ) + r.setTop( rect.top() ); + else + r.setBottom( rect.bottom() ); + } + } + + +public: + QVector clipRects; + + struct Border + { + QList pathList; + QList rectList; + QRegion clipRegion; + } border; + + struct Background + { + QPainterPath path; + QBrush brush; + QPointF origin; + } background; + +private: + const QSize d_size; + + QPen d_pen; + QBrush d_brush; + QPointF d_origin; +}; + +static void qwtDrawBackground( QPainter *painter, QwtPlotCanvas *canvas ) +{ + painter->save(); + + const QPainterPath borderClip = canvas->borderPath( canvas->rect() ); + if ( !borderClip.isEmpty() ) + painter->setClipPath( borderClip, Qt::IntersectClip ); + + const QBrush &brush = + canvas->palette().brush( canvas->backgroundRole() ); + + if ( brush.style() == Qt::TexturePattern ) + { + QPixmap pm( canvas->size() ); + QwtPainter::fillPixmap( canvas, pm ); + painter->drawPixmap( 0, 0, pm ); + } + else if ( brush.gradient() ) + { + QVector rects; + + if ( brush.gradient()->coordinateMode() == QGradient::ObjectBoundingMode ) + { + rects += canvas->rect(); + } + else + { + rects = painter->clipRegion().rects(); + } + +#if 1 + bool useRaster = false; + + if ( painter->paintEngine()->type() == QPaintEngine::X11 ) + { + // Qt 4.7.1: gradients on X11 are broken ( subrects + + // QGradient::StretchToDeviceMode ) and horrible slow. + // As workaround we have to use the raster paintengine. + // Even if the QImage -> QPixmap translation is slow + // it is three times faster, than using X11 directly + + useRaster = true; + } #endif + if ( useRaster ) + { + QImage::Format format = QImage::Format_RGB32; + + const QGradientStops stops = brush.gradient()->stops(); + for ( int i = 0; i < stops.size(); i++ ) + { + if ( stops[i].second.alpha() != 255 ) + { + // don't use Format_ARGB32_Premultiplied. It's + // recommended by the Qt docs, but QPainter::drawImage() + // is horrible slow on X11. + + format = QImage::Format_ARGB32; + break; + } + } + + QImage image( canvas->size(), format ); + + QPainter p( &image ); + p.setPen( Qt::NoPen ); + p.setBrush( brush ); + + p.drawRects( rects ); + + p.end(); + + painter->drawImage( 0, 0, image ); + } + else + { + painter->setPen( Qt::NoPen ); + painter->setBrush( brush ); + + painter->drawRects( rects ); + } + } + else + { + painter->setPen( Qt::NoPen ); + painter->setBrush( brush ); + + painter->drawRects( painter->clipRegion().rects() ); + + } + + painter->restore(); +} + +static inline void qwtRevertPath( QPainterPath &path ) +{ + if ( path.elementCount() == 4 ) + { + QPainterPath::Element el0 = path.elementAt(0); + QPainterPath::Element el3 = path.elementAt(3); + + path.setElementPositionAt( 0, el3.x, el3.y ); + path.setElementPositionAt( 3, el0.x, el0.y ); + } +} + +static QPainterPath qwtCombinePathList( const QRectF &rect, + const QList &pathList ) +{ + if ( pathList.isEmpty() ) + return QPainterPath(); + + QPainterPath ordered[8]; // starting top left + + for ( int i = 0; i < pathList.size(); i++ ) + { + int index = -1; + QPainterPath subPath = pathList[i]; + + const QRectF br = pathList[i].controlPointRect(); + if ( br.center().x() < rect.center().x() ) + { + if ( br.center().y() < rect.center().y() ) + { + if ( qAbs( br.top() - rect.top() ) < + qAbs( br.left() - rect.left() ) ) + { + index = 1; + } + else + { + index = 0; + } + } + else + { + if ( qAbs( br.bottom() - rect.bottom() ) < + qAbs( br.left() - rect.left() ) ) + { + index = 6; + } + else + { + index = 7; + } + } + + if ( subPath.currentPosition().y() > br.center().y() ) + qwtRevertPath( subPath ); + } + else + { + if ( br.center().y() < rect.center().y() ) + { + if ( qAbs( br.top() - rect.top() ) < + qAbs( br.right() - rect.right() ) ) + { + index = 2; + } + else + { + index = 3; + } + } + else + { + if ( qAbs( br.bottom() - rect.bottom() ) < + qAbs( br.right() - rect.right() ) ) + { + index = 5; + } + else + { + index = 4; + } + } + if ( subPath.currentPosition().y() < br.center().y() ) + qwtRevertPath( subPath ); + } + ordered[index] = subPath; + } + + for ( int i = 0; i < 4; i++ ) + { + if ( ordered[ 2 * i].isEmpty() != ordered[2 * i + 1].isEmpty() ) + { + // we don't accept incomplete rounded borders + return QPainterPath(); + } + } + + + const QPolygonF corners( rect ); + + QPainterPath path; + //path.moveTo( rect.topLeft() ); + + for ( int i = 0; i < 4; i++ ) + { + if ( ordered[2 * i].isEmpty() ) + { + path.lineTo( corners[i] ); + } + else + { + path.connectPath( ordered[2 * i] ); + path.connectPath( ordered[2 * i + 1] ); + } + } + + path.closeSubpath(); + +#if 0 + return path.simplified(); +#else + return path; #endif -#include -#include "qwt_painter.h" -#include "qwt_math.h" -#include "qwt_plot.h" -#include "qwt_paint_buffer.h" -#include "qwt_plot_canvas.h" +} + +static inline void qwtDrawStyledBackground( + QWidget *w, QPainter *painter ) +{ + QStyleOption opt; + opt.initFrom(w); + w->style()->drawPrimitive( QStyle::PE_Widget, &opt, painter, w); +} + +static QWidget *qwtBackgroundWidget( QWidget *w ) +{ + if ( w->parentWidget() == NULL ) + return w; + + if ( w->autoFillBackground() ) + { + const QBrush brush = w->palette().brush( w->backgroundRole() ); + if ( brush.color().alpha() > 0 ) + return w; + } + + if ( w->testAttribute( Qt::WA_StyledBackground ) ) + { + QImage image( 1, 1, QImage::Format_ARGB32 ); + image.fill( Qt::transparent ); + + QPainter painter( &image ); + painter.translate( -w->rect().center() ); + qwtDrawStyledBackground( w, &painter ); + painter.end(); + + if ( qAlpha( image.pixel( 0, 0 ) ) != 0 ) + return w; + } + + return qwtBackgroundWidget( w->parentWidget() ); +} + +static void qwtFillBackground( QPainter *painter, + QWidget *widget, const QVector &fillRects ) +{ + if ( fillRects.isEmpty() ) + return; + + QRegion clipRegion; + if ( painter->hasClipping() ) + clipRegion = painter->transform().map( painter->clipRegion() ); + else + clipRegion = widget->contentsRect(); + + // Try to find out which widget fills + // the unfilled areas of the styled background + + QWidget *bgWidget = qwtBackgroundWidget( widget->parentWidget() ); + + for ( int i = 0; i < fillRects.size(); i++ ) + { + const QRect rect = fillRects[i].toAlignedRect(); + if ( clipRegion.intersects( rect ) ) + { + QPixmap pm( rect.size() ); + QwtPainter::fillPixmap( bgWidget, pm, widget->mapTo( bgWidget, rect.topLeft() ) ); + painter->drawPixmap( rect, pm ); + } + } +} + +static void qwtFillBackground( QPainter *painter, QwtPlotCanvas *canvas ) +{ + QVector rects; + + if ( canvas->testAttribute( Qt::WA_StyledBackground ) ) + { + QwtStyleSheetRecorder recorder( canvas->size() ); + + QPainter p( &recorder ); + qwtDrawStyledBackground( canvas, &p ); + p.end(); + + if ( recorder.background.brush.isOpaque() ) + rects = recorder.clipRects; + else + rects += canvas->rect(); + } + else + { + const QRectF r = canvas->rect(); + const double radius = canvas->borderRadius(); + if ( radius > 0.0 ) + { + QSizeF sz( radius, radius ); + + rects += QRectF( r.topLeft(), sz ); + rects += QRectF( r.topRight() - QPointF( radius, 0 ), sz ); + rects += QRectF( r.bottomRight() - QPointF( radius, radius ), sz ); + rects += QRectF( r.bottomLeft() - QPointF( 0, radius ), sz ); + } + } + + qwtFillBackground( painter, canvas, rects); +} + class QwtPlotCanvas::PrivateData { public: PrivateData(): - focusIndicator(NoFocusIndicator), - paintAttributes(0), - cache(NULL) { + focusIndicator( NoFocusIndicator ), + borderRadius( 0 ), + paintAttributes( 0 ), + backingStore( NULL ) + { + styleSheet.hasBorder = false; } - ~PrivateData() { - delete cache; + ~PrivateData() + { + delete backingStore; } FocusIndicator focusIndicator; - int paintAttributes; - QPixmap *cache; + double borderRadius; + QwtPlotCanvas::PaintAttributes paintAttributes; + QPixmap *backingStore; + + struct StyleSheet + { + bool hasBorder; + QPainterPath borderPath; + QVector cornerRects; + + struct StyleSheetBackground + { + QBrush brush; + QPointF origin; + } background; + + } styleSheet; + }; -//! Sets a cross cursor, enables QwtPlotCanvas::PaintCached +/*! + \brief Constructor -QwtPlotCanvas::QwtPlotCanvas(QwtPlot *plot): - QFrame(plot) + \param plot Parent plot widget + \sa QwtPlot::setCanvas() +*/ +QwtPlotCanvas::QwtPlotCanvas( QwtPlot *plot ): + QFrame( plot ) { - d_data = new PrivateData; + setFrameStyle( QFrame::Panel | QFrame::Sunken ); + setLineWidth( 2 ); -#if QT_VERSION >= 0x040100 - setAutoFillBackground(true); -#endif + d_data = new PrivateData; -#if QT_VERSION < 0x040000 - setWFlags(Qt::WNoAutoErase); -#ifndef QT_NO_CURSOR - setCursor(Qt::crossCursor); -#endif -#else #ifndef QT_NO_CURSOR - setCursor(Qt::CrossCursor); + setCursor( Qt::CrossCursor ); #endif -#endif // >= 0x040000 - setPaintAttribute(PaintCached, true); - setPaintAttribute(PaintPacked, true); + setAutoFillBackground( true ); + setPaintAttribute( QwtPlotCanvas::BackingStore, true ); + setPaintAttribute( QwtPlotCanvas::Opaque, true ); + setPaintAttribute( QwtPlotCanvas::HackStyledBackground, true ); } //! Destructor @@ -78,21 +553,13 @@ QwtPlotCanvas::~QwtPlotCanvas() //! Return parent plot widget QwtPlot *QwtPlotCanvas::plot() { - QWidget *w = parentWidget(); - if ( w && w->inherits("QwtPlot") ) - return (QwtPlot *)w; - - return NULL; + return qobject_cast( parent() ); } //! Return parent plot widget const QwtPlot *QwtPlotCanvas::plot() const { - const QWidget *w = parentWidget(); - if ( w && w->inherits("QwtPlot") ) - return (QwtPlot *)w; - - return NULL; + return qobject_cast( parent() ); } /*! @@ -101,13 +568,11 @@ const QwtPlot *QwtPlotCanvas::plot() const \param attribute Paint attribute \param on On/Off - The default setting enables PaintCached and PaintPacked - - \sa testPaintAttribute(), drawCanvas(), drawContents(), paintCache() + \sa testPaintAttribute(), backingStore() */ -void QwtPlotCanvas::setPaintAttribute(PaintAttribute attribute, bool on) +void QwtPlotCanvas::setPaintAttribute( PaintAttribute attribute, bool on ) { - if ( bool(d_data->paintAttributes & attribute) == on ) + if ( bool( d_data->paintAttributes & attribute ) == on ) return; if ( on ) @@ -115,76 +580,78 @@ void QwtPlotCanvas::setPaintAttribute(PaintAttribute attribute, bool on) else d_data->paintAttributes &= ~attribute; - switch(attribute) { - case PaintCached: { - if ( on ) { - if ( d_data->cache == NULL ) - d_data->cache = new QPixmap(); - - if ( isVisible() ) { - const QRect cr = contentsRect(); - *d_data->cache = QPixmap::grabWidget(this, - cr.x(), cr.y(), cr.width(), cr.height() ); + switch ( attribute ) + { + case BackingStore: + { + if ( on ) + { + if ( d_data->backingStore == NULL ) + d_data->backingStore = new QPixmap(); + + if ( isVisible() ) + { +#if QT_VERSION >= 0x050000 + *d_data->backingStore = grab( rect() ); +#else + *d_data->backingStore = + QPixmap::grabWidget( this, rect() ); +#endif + } } - } else { - delete d_data->cache; - d_data->cache = NULL; + else + { + delete d_data->backingStore; + d_data->backingStore = NULL; + } + break; } - break; - } - case PaintPacked: { - /* - If not visible, changing of the background mode - is delayed until it becomes visible. This tries to avoid - looking through the canvas when the canvas is shown the first - time. - */ - - if ( on == false || isVisible() ) - QwtPlotCanvas::setSystemBackground(!on); + case Opaque: + { + if ( on ) + setAttribute( Qt::WA_OpaquePaintEvent, true ); - break; - } + break; + } + case HackStyledBackground: + case ImmediatePaint: + { + break; + } } } /*! - Test wether a paint attribute is enabled + Test whether a paint attribute is enabled \param attribute Paint attribute - \return true if the attribute is enabled + \return true, when attribute is enabled \sa setPaintAttribute() */ -bool QwtPlotCanvas::testPaintAttribute(PaintAttribute attribute) const -{ - return (d_data->paintAttributes & attribute) != 0; -} - -//! Return the paint cache, might be null -QPixmap *QwtPlotCanvas::paintCache() +bool QwtPlotCanvas::testPaintAttribute( PaintAttribute attribute ) const { - return d_data->cache; + return d_data->paintAttributes & attribute; } -//! Return the paint cache, might be null -const QPixmap *QwtPlotCanvas::paintCache() const +//! \return Backing store, might be null +const QPixmap *QwtPlotCanvas::backingStore() const { - return d_data->cache; + return d_data->backingStore; } -//! Invalidate the internal paint cache -void QwtPlotCanvas::invalidatePaintCache() +//! Invalidate the internal backing store +void QwtPlotCanvas::invalidateBackingStore() { - if ( d_data->cache ) - *d_data->cache = QPixmap(); + if ( d_data->backingStore ) + *d_data->backingStore = QPixmap(); } /*! Set the focus indicator - \sa FocusIndicator, focusIndicator + \sa FocusIndicator, focusIndicator() */ -void QwtPlotCanvas::setFocusIndicator(FocusIndicator focusIndicator) +void QwtPlotCanvas::setFocusIndicator( FocusIndicator focusIndicator ) { d_data->focusIndicator = focusIndicator; } @@ -192,165 +659,439 @@ void QwtPlotCanvas::setFocusIndicator(FocusIndicator focusIndicator) /*! \return Focus indicator - \sa FocusIndicator, setFocusIndicator + \sa FocusIndicator, setFocusIndicator() */ QwtPlotCanvas::FocusIndicator QwtPlotCanvas::focusIndicator() const { return d_data->focusIndicator; } -void QwtPlotCanvas::hideEvent(QHideEvent *e) +/*! + Set the radius for the corners of the border frame + + \param radius Radius of a rounded corner + \sa borderRadius() +*/ +void QwtPlotCanvas::setBorderRadius( double radius ) { - QFrame::hideEvent(e); + d_data->borderRadius = qMax( 0.0, radius ); +} + +/*! + \return Radius for the corners of the border frame + \sa setBorderRadius() +*/ +double QwtPlotCanvas::borderRadius() const +{ + return d_data->borderRadius; +} - if ( d_data->paintAttributes & PaintPacked ) { - // enable system background to avoid the "looking through - // the canvas" effect, for the next show +/*! + Qt event handler for QEvent::PolishRequest and QEvent::StyleChange - setSystemBackground(true); + \param event Qt Event + \return See QFrame::event() +*/ +bool QwtPlotCanvas::event( QEvent *event ) +{ + if ( event->type() == QEvent::PolishRequest ) + { + if ( testPaintAttribute( QwtPlotCanvas::Opaque ) ) + { + // Setting a style sheet changes the + // Qt::WA_OpaquePaintEvent attribute, but we insist + // on painting the background. + + setAttribute( Qt::WA_OpaquePaintEvent, true ); + } } + + if ( event->type() == QEvent::PolishRequest || + event->type() == QEvent::StyleChange ) + { + updateStyleSheetInfo(); + } + + return QFrame::event( event ); } -//! Paint event -void QwtPlotCanvas::paintEvent(QPaintEvent *event) +/*! + Paint event + \param event Paint event +*/ +void QwtPlotCanvas::paintEvent( QPaintEvent *event ) { -#if QT_VERSION >= 0x040000 - QPainter painter(this); + QPainter painter( this ); + painter.setClipRegion( event->region() ); + + if ( testPaintAttribute( QwtPlotCanvas::BackingStore ) && + d_data->backingStore != NULL ) + { + QPixmap &bs = *d_data->backingStore; + if ( bs.size() != size() ) + { + bs = QwtPainter::backingStore( this, size() ); - if ( !contentsRect().contains( event->rect() ) ) { - painter.save(); - painter.setClipRegion( event->region() & frameRect() ); - drawFrame( &painter ); - painter.restore(); + if ( testAttribute(Qt::WA_StyledBackground) ) + { + QPainter p( &bs ); + qwtFillBackground( &p, this ); + drawCanvas( &p, true ); + } + else + { + QPainter p; + if ( d_data->borderRadius <= 0.0 ) + { + QwtPainter::fillPixmap( this, bs ); + p.begin( &bs ); + drawCanvas( &p, false ); + } + else + { + p.begin( &bs ); + qwtFillBackground( &p, this ); + drawCanvas( &p, true ); + } + + if ( frameWidth() > 0 ) + drawBorder( &p ); + } + } + + painter.drawPixmap( 0, 0, *d_data->backingStore ); } + else + { + if ( testAttribute(Qt::WA_StyledBackground ) ) + { + if ( testAttribute( Qt::WA_OpaquePaintEvent ) ) + { + qwtFillBackground( &painter, this ); + drawCanvas( &painter, true ); + } + else + { + drawCanvas( &painter, false ); + } + } + else + { + if ( testAttribute( Qt::WA_OpaquePaintEvent ) ) + { + if ( autoFillBackground() ) + { + qwtFillBackground( &painter, this ); + qwtDrawBackground( &painter, this ); + } + } + else + { + if ( borderRadius() > 0.0 ) + { + QPainterPath clipPath; + clipPath.addRect( rect() ); + clipPath = clipPath.subtracted( borderPath( rect() ) ); + + painter.save(); + + painter.setClipPath( clipPath, Qt::IntersectClip ); + qwtFillBackground( &painter, this ); + qwtDrawBackground( &painter, this ); + + painter.restore(); + } + } - painter.setClipRegion(event->region() & contentsRect()); + drawCanvas( &painter, false ); - drawContents( &painter ); -#else // QT_VERSION < 0x040000 - QFrame::paintEvent(event); -#endif + if ( frameWidth() > 0 ) + drawBorder( &painter ); + } + } - if ( d_data->paintAttributes & PaintPacked ) - setSystemBackground(false); + if ( hasFocus() && focusIndicator() == CanvasFocusIndicator ) + drawFocusIndicator( &painter ); } -//! Redraw the canvas, and focus rect -void QwtPlotCanvas::drawContents(QPainter *painter) +void QwtPlotCanvas::drawCanvas( QPainter *painter, bool withBackground ) { - if ( d_data->paintAttributes & PaintCached && d_data->cache - && d_data->cache->size() == contentsRect().size() ) { - painter->drawPixmap(contentsRect().topLeft(), *d_data->cache); - } else { - QwtPlot *plot = ((QwtPlot *)parent()); - const bool doAutoReplot = plot->autoReplot(); - plot->setAutoReplot(false); + bool hackStyledBackground = false; + + if ( withBackground && testAttribute( Qt::WA_StyledBackground ) + && testPaintAttribute( HackStyledBackground ) ) + { + // Antialiasing rounded borders is done by + // inserting pixels with colors between the + // border color and the color on the canvas, + // When the border is painted before the plot items + // these colors are interpolated for the canvas + // and the plot items need to be clipped excluding + // the anialiased pixels. In situations, where + // the plot items fill the area at the rounded + // borders this is noticeable. + // The only way to avoid these annoying "artefacts" + // is to paint the border on top of the plot items. + + if ( d_data->styleSheet.hasBorder && + !d_data->styleSheet.borderPath.isEmpty() ) + { + // We have a border with at least one rounded corner + hackStyledBackground = true; + } + } - drawCanvas(painter); + if ( withBackground ) + { + painter->save(); - plot->setAutoReplot(doAutoReplot); + if ( testAttribute( Qt::WA_StyledBackground ) ) + { + if ( hackStyledBackground ) + { + // paint background without border + + painter->setPen( Qt::NoPen ); + painter->setBrush( d_data->styleSheet.background.brush ); + painter->setBrushOrigin( d_data->styleSheet.background.origin ); + painter->setClipPath( d_data->styleSheet.borderPath ); + painter->drawRect( contentsRect() ); + } + else + { + qwtDrawStyledBackground( this, painter ); + } + } + else if ( autoFillBackground() ) + { + painter->setPen( Qt::NoPen ); + painter->setBrush( palette().brush( backgroundRole() ) ); + + if ( d_data->borderRadius > 0.0 && ( rect() == frameRect() ) ) + { + if ( frameWidth() > 0 ) + { + painter->setClipPath( borderPath( rect() ) ); + painter->drawRect( rect() ); + } + else + { + painter->setRenderHint( QPainter::Antialiasing, true ); + painter->drawPath( borderPath( rect() ) ); + } + } + else + { + painter->drawRect( rect() ); + } + } + + painter->restore(); } - if ( hasFocus() && focusIndicator() == CanvasFocusIndicator ) - drawFocusIndicator(painter); + painter->save(); + + if ( !d_data->styleSheet.borderPath.isEmpty() ) + { + painter->setClipPath( + d_data->styleSheet.borderPath, Qt::IntersectClip ); + } + else + { + if ( d_data->borderRadius > 0.0 ) + painter->setClipPath( borderPath( frameRect() ), Qt::IntersectClip ); + else + painter->setClipRect( contentsRect(), Qt::IntersectClip ); + } + + plot()->drawCanvas( painter ); + + painter->restore(); + + if ( withBackground && hackStyledBackground ) + { + // Now paint the border on top + QStyleOptionFrame opt; + opt.initFrom(this); + style()->drawPrimitive( QStyle::PE_Frame, &opt, painter, this); + } } /*! - Draw the the canvas - - Paints all plot items to the contentsRect(), using QwtPlot::drawCanvas - and updates the paint cache. + Draw the border of the plot canvas - \sa QwtPlot::drawCanvas, setPaintAttributes(), testPaintAttributes() + \param painter Painter + \sa setBorderRadius() */ - -void QwtPlotCanvas::drawCanvas(QPainter *painter) +void QwtPlotCanvas::drawBorder( QPainter *painter ) { - if ( !contentsRect().isValid() ) - return; + if ( d_data->borderRadius > 0 ) + { + if ( frameWidth() > 0 ) + { + QwtPainter::drawRoundedFrame( painter, QRectF( frameRect() ), + d_data->borderRadius, d_data->borderRadius, + palette(), frameWidth(), frameStyle() ); + } + } + else + { +#if QT_VERSION >= 0x040500 + QStyleOptionFrameV3 opt; + opt.init(this); - QBrush bgBrush; -#if QT_VERSION >= 0x040000 - bgBrush = palette().brush(backgroundRole()); -#else - QColorGroup::ColorRole role = - QPalette::backgroundRoleFromMode( backgroundMode() ); - bgBrush = colorGroup().brush( role ); + int frameShape = frameStyle() & QFrame::Shape_Mask; + int frameShadow = frameStyle() & QFrame::Shadow_Mask; + + opt.frameShape = QFrame::Shape( int( opt.frameShape ) | frameShape ); +#if 0 + opt.rect = frameRect(); #endif - if ( d_data->paintAttributes & PaintCached && d_data->cache ) { - *d_data->cache = QPixmap(contentsRect().size()); + switch (frameShape) + { + case QFrame::Box: + case QFrame::HLine: + case QFrame::VLine: + case QFrame::StyledPanel: + case QFrame::Panel: + { + opt.lineWidth = lineWidth(); + opt.midLineWidth = midLineWidth(); + break; + } + default: + { + opt.lineWidth = frameWidth(); + break; + } + } + + if ( frameShadow == Sunken ) + opt.state |= QStyle::State_Sunken; + else if ( frameShadow == Raised ) + opt.state |= QStyle::State_Raised; -#ifdef Q_WS_X11 -#if QT_VERSION >= 0x040000 - if ( d_data->cache->x11Info().screen() != x11Info().screen() ) - d_data->cache->x11SetScreen(x11Info().screen()); + style()->drawControl(QStyle::CE_ShapedFrame, &opt, painter, this); #else - if ( d_data->cache->x11Screen() != x11Screen() ) - d_data->cache->x11SetScreen(x11Screen()); -#endif + drawFrame( painter ); #endif + } +} - if ( d_data->paintAttributes & PaintPacked ) { - QPainter bgPainter(d_data->cache); - bgPainter.setPen(Qt::NoPen); +/*! + Resize event + \param event Resize event +*/ +void QwtPlotCanvas::resizeEvent( QResizeEvent *event ) +{ + QFrame::resizeEvent( event ); + updateStyleSheetInfo(); +} - bgPainter.setBrush(bgBrush); - bgPainter.drawRect(d_data->cache->rect()); - } else - d_data->cache->fill(this, d_data->cache->rect().topLeft()); +/*! + Draw the focus indication + \param painter Painter +*/ +void QwtPlotCanvas::drawFocusIndicator( QPainter *painter ) +{ + const int margin = 1; - QPainter cachePainter(d_data->cache); - cachePainter.translate(-contentsRect().x(), - -contentsRect().y()); + QRect focusRect = contentsRect(); + focusRect.setRect( focusRect.x() + margin, focusRect.y() + margin, + focusRect.width() - 2 * margin, focusRect.height() - 2 * margin ); - ((QwtPlot *)parent())->drawCanvas(&cachePainter); + QwtPainter::drawFocusRect( painter, this, focusRect ); +} - cachePainter.end(); +/*! + Invalidate the paint cache and repaint the canvas + \sa invalidatePaintCache() +*/ +void QwtPlotCanvas::replot() +{ + invalidateBackingStore(); - painter->drawPixmap(contentsRect(), *d_data->cache); - } else { -#if QT_VERSION >= 0x040000 - if ( d_data->paintAttributes & PaintPacked ) -#endif - { - painter->save(); + if ( testPaintAttribute( QwtPlotCanvas::ImmediatePaint ) ) + repaint( contentsRect() ); + else + update( contentsRect() ); +} - painter->setPen(Qt::NoPen); - painter->setBrush(bgBrush); - painter->drawRect(contentsRect()); +//! Update the cached information about the current style sheet +void QwtPlotCanvas::updateStyleSheetInfo() +{ + if ( !testAttribute(Qt::WA_StyledBackground ) ) + return; - painter->restore(); + QwtStyleSheetRecorder recorder( size() ); + + QPainter painter( &recorder ); + + QStyleOption opt; + opt.initFrom(this); + style()->drawPrimitive( QStyle::PE_Widget, &opt, &painter, this); + + painter.end(); + + d_data->styleSheet.hasBorder = !recorder.border.rectList.isEmpty(); + d_data->styleSheet.cornerRects = recorder.clipRects; + + if ( recorder.background.path.isEmpty() ) + { + if ( !recorder.border.rectList.isEmpty() ) + { + d_data->styleSheet.borderPath = + qwtCombinePathList( rect(), recorder.border.pathList ); } - - ((QwtPlot *)parent())->drawCanvas(painter); + } + else + { + d_data->styleSheet.borderPath = recorder.background.path; + d_data->styleSheet.background.brush = recorder.background.brush; + d_data->styleSheet.background.origin = recorder.background.origin; } } -//! Draw the focus indication -void QwtPlotCanvas::drawFocusIndicator(QPainter *painter) +/*! + Calculate the painter path for a styled or rounded border + + When the canvas has no styled background or rounded borders + the painter path is empty. + + \param rect Bounding rectangle of the canvas + \return Painter path, that can be used for clipping +*/ +QPainterPath QwtPlotCanvas::borderPath( const QRect &rect ) const { - const int margin = 1; + if ( testAttribute(Qt::WA_StyledBackground ) ) + { + QwtStyleSheetRecorder recorder( rect.size() ); - QRect focusRect = contentsRect(); - focusRect.setRect(focusRect.x() + margin, focusRect.y() + margin, - focusRect.width() - 2 * margin, focusRect.height() - 2 * margin); + QPainter painter( &recorder ); - QwtPainter::drawFocusRect(painter, this, focusRect); -} + QStyleOption opt; + opt.initFrom(this); + opt.rect = rect; + style()->drawPrimitive( QStyle::PE_Widget, &opt, &painter, this); -void QwtPlotCanvas::setSystemBackground(bool on) -{ -#if QT_VERSION < 0x040000 - if ( backgroundMode() == Qt::NoBackground ) { - if ( on ) - setBackgroundMode(Qt::PaletteBackground); - } else { - if ( !on ) - setBackgroundMode(Qt::NoBackground); + painter.end(); + + if ( !recorder.background.path.isEmpty() ) + return recorder.background.path; + + if ( !recorder.border.rectList.isEmpty() ) + return qwtCombinePathList( rect, recorder.border.pathList ); } -#else - if ( testAttribute(Qt::WA_NoSystemBackground) == on ) - setAttribute(Qt::WA_NoSystemBackground, !on); -#endif + else if ( d_data->borderRadius > 0.0 ) + { + double fw2 = frameWidth() * 0.5; + QRectF r = QRectF(rect).adjusted( fw2, fw2, -fw2, -fw2 ); + + QPainterPath path; + path.addRoundedRect( r, d_data->borderRadius, d_data->borderRadius ); + return path; + } + + return QPainterPath(); } diff --git a/libs/qwt/qwt_plot_canvas.h b/libs/qwt/qwt_plot_canvas.h index f3ff0ffa2..daea8a086 100644 --- a/libs/qwt/qwt_plot_canvas.h +++ b/libs/qwt/qwt_plot_canvas.h @@ -7,109 +7,165 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ -// vim: expandtab - #ifndef QWT_PLOT_CANVAS_H #define QWT_PLOT_CANVAS_H -#include -#include #include "qwt_global.h" +#include +#include class QwtPlot; class QPixmap; /*! - Canvas of a QwtPlot. - \sa QwtPlot + \brief Canvas of a QwtPlot. + + Canvas is the widget where all plot items are displayed + + \sa QwtPlot::setCanvas(), QwtPlotGLCanvas */ class QWT_EXPORT QwtPlotCanvas : public QFrame { Q_OBJECT + Q_PROPERTY( double borderRadius READ borderRadius WRITE setBorderRadius ) + public: /*! \brief Paint attributes - - PaintCached\n - Paint double buffered and reuse the content of the pixmap buffer - for some spontaneous repaints that happen when a plot gets unhidden, - deiconified or changes the focus. - Disabling the cache will improve the performance for - incremental paints (using QwtPlotCurve::draw). + The default setting enables BackingStore and Opaque. - - PaintPacked\n - Suppress system background repaints and paint it together with - the canvas contents. - Painting packed might avoid flickering for expensive repaints, - when there is a notable gap between painting the background - and the plot contents. - - The default setting enables PaintCached and PaintPacked - - \sa setPaintAttribute(), testPaintAttribute(), paintCache() + \sa setPaintAttribute(), testPaintAttribute() */ - enum PaintAttribute { - PaintCached = 1, - PaintPacked = 2 + enum PaintAttribute + { + /*! + \brief Paint double buffered reusing the content + of the pixmap buffer when possible. + + Using a backing store might improve the performance + significantly, when working with widget overlays ( like rubber bands ). + Disabling the cache might improve the performance for + incremental paints (using QwtPlotDirectPainter ). + + \sa backingStore(), invalidateBackingStore() + */ + BackingStore = 1, + + /*! + \brief Try to fill the complete contents rectangle + of the plot canvas + + When using styled backgrounds Qt assumes, that the + canvas doesn't fill its area completely + ( f.e because of rounded borders ) and fills the area + below the canvas. When this is done with gradients it might + result in a serious performance bottleneck - depending on the size. + + When the Opaque attribute is enabled the canvas tries to + identify the gaps with some heuristics and to fill those only. + + \warning Will not work for semitransparent backgrounds + */ + Opaque = 2, + + /*! + \brief Try to improve painting of styled backgrounds + + QwtPlotCanvas supports the box model attributes for + customizing the layout with style sheets. Unfortunately + the design of Qt style sheets has no concept how to + handle backgrounds with rounded corners - beside of padding. + + When HackStyledBackground is enabled the plot canvas tries + to separate the background from the background border + by reverse engineering to paint the background before and + the border after the plot items. In this order the border + gets perfectly antialiased and you can avoid some pixel + artifacts in the corners. + */ + HackStyledBackground = 4, + + /*! + When ImmediatePaint is set replot() calls repaint() + instead of update(). + + \sa replot(), QWidget::repaint(), QWidget::update() + */ + ImmediatePaint = 8 }; + //! Paint attributes + typedef QFlags PaintAttributes; + /*! \brief Focus indicator - - - NoFocusIndicator\n - Don't paint a focus indicator - - - CanvasFocusIndicator\n - The focus is related to the complete canvas. - Paint the focus indicator using paintFocus() - - - ItemFocusIndicator\n - The focus is related to an item (curve, point, ...) on - the canvas. It is up to the application to display a - focus indication using f.e. highlighting. - + The default setting is NoFocusIndicator \sa setFocusIndicator(), focusIndicator(), paintFocus() */ - enum FocusIndicator { + enum FocusIndicator + { + //! Don't paint a focus indicator NoFocusIndicator, + + /*! + The focus is related to the complete canvas. + Paint the focus indicator using paintFocus() + */ CanvasFocusIndicator, + + /*! + The focus is related to an item (curve, point, ...) on + the canvas. It is up to the application to display a + focus indication using f.e. highlighting. + */ ItemFocusIndicator }; - explicit QwtPlotCanvas(QwtPlot *); + explicit QwtPlotCanvas( QwtPlot * = NULL ); virtual ~QwtPlotCanvas(); QwtPlot *plot(); const QwtPlot *plot() const; - void setFocusIndicator(FocusIndicator); + void setFocusIndicator( FocusIndicator ); FocusIndicator focusIndicator() const; - void setPaintAttribute(PaintAttribute, bool on = true); - bool testPaintAttribute(PaintAttribute) const; + void setBorderRadius( double ); + double borderRadius() const; - QPixmap *paintCache(); - const QPixmap *paintCache() const; - void invalidatePaintCache(); + void setPaintAttribute( PaintAttribute, bool on = true ); + bool testPaintAttribute( PaintAttribute ) const; -protected: - virtual void hideEvent(QHideEvent *); + const QPixmap *backingStore() const; + void invalidateBackingStore(); - virtual void paintEvent(QPaintEvent *); + virtual bool event( QEvent * ); - virtual void drawContents(QPainter *); - virtual void drawFocusIndicator(QPainter *); + Q_INVOKABLE QPainterPath borderPath( const QRect & ) const; - void drawCanvas(QPainter *painter = NULL); +public Q_SLOTS: + void replot(); + +protected: + virtual void paintEvent( QPaintEvent * ); + virtual void resizeEvent( QResizeEvent * ); + + virtual void drawFocusIndicator( QPainter * ); + virtual void drawBorder( QPainter * ); + + void updateStyleSheetInfo(); private: - void setSystemBackground(bool); + void drawCanvas( QPainter *, bool withBackground ); class PrivateData; PrivateData *d_data; }; +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotCanvas::PaintAttributes ) + #endif diff --git a/libs/qwt/qwt_plot_curve.cpp b/libs/qwt/qwt_plot_curve.cpp index fd744207c..140cc5996 100644 --- a/libs/qwt/qwt_plot_curve.cpp +++ b/libs/qwt/qwt_plot_curve.cpp @@ -7,260 +7,111 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ -#include -#include -#include -#include "qwt_global.h" -#include "qwt_legend.h" -#include "qwt_legend_item.h" -#include "qwt_data.h" -#include "qwt_scale_map.h" -#include "qwt_double_rect.h" +#include "qwt_plot_curve.h" +#include "qwt_point_data.h" #include "qwt_math.h" #include "qwt_clipper.h" #include "qwt_painter.h" +#include "qwt_scale_map.h" #include "qwt_plot.h" -#include "qwt_plot_canvas.h" #include "qwt_curve_fitter.h" #include "qwt_symbol.h" -#include "qwt_plot_curve.h" - -#define SCALE_PEN 0 - -#if QT_VERSION < 0x040000 -#include -#else -#include -#endif - -#if QT_VERSION >= 0x040000 - -#include -#include - -class QwtPlotCurvePaintHelper: public QObject -{ -public: - QwtPlotCurvePaintHelper(const QwtPlotCurve *curve, int from, int to): - _curve(curve), - _from(from), - _to(to) { - } - - virtual bool eventFilter(QObject *, QEvent *event) { - if ( event->type() == QEvent::Paint ) { - _curve->draw(_from, _to); - return true; - } - return false; - } -private: - const QwtPlotCurve *_curve; - int _from; - int _to; -}; - -#endif // QT_VERSION >= 0x040000 - -// Creating and initializing a QPainter is an -// expensive operation. So we keep an painter -// open for situations, where we paint outside -// of paint events. This improves the performance -// of incremental painting like in the realtime -// example a lot. -// But it is not possible to have more than -// one QPainter open at the same time. So we -// need to close it before regular paint events -// are processed. +#include "qwt_point_mapper.h" +#include +#include +#include +#include -class QwtGuardedPainter: public QObject +static void qwtUpdateLegendIconSize( QwtPlotCurve *curve ) { -public: - ~QwtGuardedPainter() { - end(); - } - - QPainter *begin(QwtPlotCanvas *canvas) { - _canvas = canvas; - - QMap::iterator it = _map.find(_canvas); - if ( it == _map.end() ) { - QPainter *painter = new QPainter(_canvas); - painter->setClipping(true); - painter->setClipRect(_canvas->contentsRect()); + if ( curve->symbol() && + curve->testLegendAttribute( QwtPlotCurve::LegendShowSymbol ) ) + { + QSize sz = curve->symbol()->boundingRect().size(); + sz += QSize( 2, 2 ); // margin - it = _map.insert(_canvas, painter); - _canvas->installEventFilter(this); - } -#if QT_VERSION < 0x040000 - return it.data(); -#else - return it.value(); -#endif - } + if ( curve->testLegendAttribute( QwtPlotCurve::LegendShowLine ) ) + { + // Avoid, that the line is completely covered by the symbol - void end() { - if ( _canvas ) { - QMap::iterator it = _map.find(_canvas); - if ( it != _map.end() ) { - _canvas->removeEventFilter(this); + int w = qCeil( 1.5 * sz.width() ); + if ( w % 2 ) + w++; -#if QT_VERSION < 0x040000 - delete it.data(); -#else - delete it.value(); -#endif - _map.erase(it); - } + sz.setWidth( qMax( 8, w ) ); } - } - virtual bool eventFilter(QObject *, QEvent *event) { - if ( event->type() == QEvent::Paint ) - end(); - - return false; + curve->setLegendIconSize( sz ); } - -private: -#if QT_VERSION < 0x040000 - QGuardedPtr _canvas; -#else - QPointer _canvas; -#endif - static QMap _map; -}; - -QMap QwtGuardedPainter::_map; - -static QwtPolygon clipPolygon(QPainter *painter, int attributes, - const QwtPolygon &polygon) -{ - bool doClipping = attributes & QwtPlotCurve::ClipPolygons; - QRect clipRect = painter->window(); - - if ( !doClipping ) { -#if QT_VERSION >= 0x040000 - const QPaintEngine *pe = painter->paintEngine(); - if ( pe && pe->type() == QPaintEngine::SVG ) -#else - if ( painter->device()->devType() == QInternal::Picture ) -#endif - { - // The SVG paint engine ignores any clipping, - // so we enable polygon clipping. - - doClipping = true; - if ( painter->hasClipping() ) - clipRect &= painter->clipRegion().boundingRect(); - } - } - - if ( doClipping ) - return QwtClipper::clipPolygon(clipRect, polygon); - - return polygon; } -static int verifyRange(int size, int &i1, int &i2) +static int qwtVerifyRange( int size, int &i1, int &i2 ) { - if (size < 1) + if ( size < 1 ) return 0; - i1 = qwtLim(i1, 0, size-1); - i2 = qwtLim(i2, 0, size-1); + i1 = qBound( 0, i1, size - 1 ); + i2 = qBound( 0, i2, size - 1 ); if ( i1 > i2 ) - qSwap(i1, i2); + qSwap( i1, i2 ); - return (i2 - i1 + 1); + return ( i2 - i1 + 1 ); } class QwtPlotCurve::PrivateData { public: - class PixelMatrix: private QBitArray - { - public: - PixelMatrix(const QRect& rect): - QBitArray(rect.width() * rect.height()), - _rect(rect) { - fill(false); - } - - inline bool testPixel(const QPoint& pos) { - if ( !_rect.contains(pos) ) - return false; - - const int idx = _rect.width() * (pos.y() - _rect.y()) + - (pos.x() - _rect.x()); - - const bool marked = testBit(idx); - if ( !marked ) - setBit(idx, true); - - return !marked; - } - - private: - QRect _rect; - }; - PrivateData(): - curveType(Yfx), - style(QwtPlotCurve::Lines), - reference(0.0), - attributes(0), - paintAttributes(0) { - symbol = new QwtSymbol(); - pen = QPen(Qt::black); + style( QwtPlotCurve::Lines ), + baseline( 0.0 ), + symbol( NULL ), + attributes( 0 ), + paintAttributes( + QwtPlotCurve::ClipPolygons | QwtPlotCurve::FilterPoints ), + legendAttributes( 0 ) + { + pen = QPen( Qt::black ); curveFitter = new QwtSplineCurveFitter; } - ~PrivateData() { + ~PrivateData() + { delete symbol; delete curveFitter; } - QwtPlotCurve::CurveType curveType; QwtPlotCurve::CurveStyle style; - double reference; + double baseline; - QwtSymbol *symbol; + const QwtSymbol *symbol; QwtCurveFitter *curveFitter; QPen pen; QBrush brush; - int attributes; - int paintAttributes; + QwtPlotCurve::CurveAttributes attributes; + QwtPlotCurve::PaintAttributes paintAttributes; - QwtGuardedPainter guardedPainter; + QwtPlotCurve::LegendAttributes legendAttributes; }; -//! Constructor -QwtPlotCurve::QwtPlotCurve(): - QwtPlotItem(QwtText()) -{ - init(); -} - /*! Constructor - \param title title of the curve + \param title Title of the curve */ -QwtPlotCurve::QwtPlotCurve(const QwtText &title): - QwtPlotItem(title) +QwtPlotCurve::QwtPlotCurve( const QwtText &title ): + QwtPlotSeriesItem( title ) { init(); } /*! Constructor - \param title title of the curve + \param title Title of the curve */ -QwtPlotCurve::QwtPlotCurve(const QString &title): - QwtPlotItem(QwtText(title)) +QwtPlotCurve::QwtPlotCurve( const QString &title ): + QwtPlotSeriesItem( QwtText( title ) ) { init(); } @@ -268,22 +119,19 @@ QwtPlotCurve::QwtPlotCurve(const QString &title): //! Destructor QwtPlotCurve::~QwtPlotCurve() { - delete d_xy; delete d_data; } -/*! - \brief Initialize data members -*/ +//! Initialize internal members void QwtPlotCurve::init() { - setItemAttribute(QwtPlotItem::Legend); - setItemAttribute(QwtPlotItem::AutoScale); + setItemAttribute( QwtPlotItem::Legend ); + setItemAttribute( QwtPlotItem::AutoScale ); d_data = new PrivateData; - d_xy = new QwtPolygonFData(QwtArray()); + setData( new QwtPointSeriesData() ); - setZ(20.0); + setZ( 20.0 ); } //! \return QwtPlotItem::Rtti_PlotCurve @@ -293,29 +141,13 @@ int QwtPlotCurve::rtti() const } /*! - \brief Specify an attribute how to draw the curve - - The attributes can be used to modify the drawing algorithm. - - The following attributes are defined:
-
PaintFiltered
-
Tries to reduce the data that has to be painted, by sorting out - duplicates, or paintings outside the visible area. Might have a - notable impact on curves with many close points. - Only a couple of very basic filtering algos are implemented.
-
ClipPolygons
-
Clip polygons before painting them. In situations, where points - are far outside the visible area this might be a great improvement - for the painting performance ( especially on Windows ). -
- - The default is, that no paint attributes are enabled. + Specify an attribute how to draw the curve \param attribute Paint attribute \param on On/Off - /sa testPaintAttribute() + \sa testPaintAttribute() */ -void QwtPlotCurve::setPaintAttribute(PaintAttribute attribute, bool on) +void QwtPlotCurve::setPaintAttribute( PaintAttribute attribute, bool on ) { if ( on ) d_data->paintAttributes |= attribute; @@ -324,366 +156,211 @@ void QwtPlotCurve::setPaintAttribute(PaintAttribute attribute, bool on) } /*! - \brief Return the current paint attributes - \sa setPaintAttribute + \return True, when attribute is enabled + \sa setPaintAttribute() */ -bool QwtPlotCurve::testPaintAttribute(PaintAttribute attribute) const +bool QwtPlotCurve::testPaintAttribute( PaintAttribute attribute ) const { - return (d_data->paintAttributes & attribute); + return ( d_data->paintAttributes & attribute ); } /*! - \brief Set the curve's drawing style - - Valid styles are: -
-
NoCurve
-
Don't draw a curve. Note: This doesn't affect the symbol.
-
Lines
-
Connect the points with straight lines. The lines might - be interpolated depending on the 'Fitted' option. Curve - fitting can be configured using setCurveFitter.
-
Sticks
-
Draw vertical sticks from a baseline which is defined by setBaseline().
-
Steps
-
Connect the points with a step function. The step function - is drawn from the left to the right or vice versa, - depending on the 'Inverted' option.
-
Dots
-
Draw dots at the locations of the data points. Note: - This is different from a dotted line (see setPen()).
-
UserCurve ...
-
Styles >= UserCurve are reserved for derived - classes of QwtPlotCurve that overload drawCurve() with - additional application specific curve types.
-
- \sa style() + Specify an attribute how to draw the legend icon + + \param attribute Attribute + \param on On/Off + /sa testLegendAttribute(). legendIcon() */ -void QwtPlotCurve::setStyle(CurveStyle style) +void QwtPlotCurve::setLegendAttribute( LegendAttribute attribute, bool on ) { - if ( style != d_data->style ) { - d_data->style = style; - itemChanged(); + if ( on != testLegendAttribute( attribute ) ) + { + if ( on ) + d_data->legendAttributes |= attribute; + else + d_data->legendAttributes &= ~attribute; + + qwtUpdateLegendIconSize( this ); + legendChanged(); } } /*! - \brief Return the current style - \sa setStyle() + \return True, when attribute is enabled + \sa setLegendAttribute() */ -QwtPlotCurve::CurveStyle QwtPlotCurve::style() const +bool QwtPlotCurve::testLegendAttribute( LegendAttribute attribute ) const { - return d_data->style; + return ( d_data->legendAttributes & attribute ); } /*! - \brief Assign a symbol - \param symbol Symbol - \sa symbol() -*/ -void QwtPlotCurve::setSymbol(const QwtSymbol &symbol ) -{ - delete d_data->symbol; - d_data->symbol = symbol.clone(); - itemChanged(); -} + Set the curve's drawing style -/*! - \brief Return the current symbol - \sa setSymbol() + \param style Curve style + \sa style() */ -const QwtSymbol &QwtPlotCurve::symbol() const +void QwtPlotCurve::setStyle( CurveStyle style ) { - return *d_data->symbol; -} + if ( style != d_data->style ) + { + d_data->style = style; -/*! - \brief Assign a pen - \param pen New pen - \sa pen(), brush() -*/ -void QwtPlotCurve::setPen(const QPen &pen) -{ - if ( pen != d_data->pen ) { - d_data->pen = pen; + legendChanged(); itemChanged(); } } /*! - \brief Return the pen used to draw the lines - \sa setPen(), brush() + \return Style of the curve + \sa setStyle() */ -const QPen& QwtPlotCurve::pen() const +QwtPlotCurve::CurveStyle QwtPlotCurve::style() const { - return d_data->pen; + return d_data->style; } /*! - \brief Assign a brush. - In case of brush.style() != QBrush::NoBrush - and style() != QwtPlotCurve::Sticks - the area between the curve and the baseline will be filled. - In case !brush.color().isValid() the area will be filled by - pen.color(). The fill algorithm simply connects the first and the - last curve point to the baseline. So the curve data has to be sorted - (ascending or descending). - \param brush New brush - \sa brush(), setBaseline(), baseline() + \brief Assign a symbol + + The curve will take the ownership of the symbol, hence the previously + set symbol will be delete by setting a new one. If \p symbol is + \c NULL no symbol will be drawn. + + \param symbol Symbol + \sa symbol() */ -void QwtPlotCurve::setBrush(const QBrush &brush) +void QwtPlotCurve::setSymbol( QwtSymbol *symbol ) { - if ( brush != d_data->brush ) { - d_data->brush = brush; + if ( symbol != d_data->symbol ) + { + delete d_data->symbol; + d_data->symbol = symbol; + + qwtUpdateLegendIconSize( this ); + + legendChanged(); itemChanged(); } } /*! - \brief Return the brush used to fill the area between lines and the baseline - \sa setBrush(), setBaseline(), baseline() + \return Current symbol or NULL, when no symbol has been assigned + \sa setSymbol() */ -const QBrush& QwtPlotCurve::brush() const +const QwtSymbol *QwtPlotCurve::symbol() const { - return d_data->brush; + return d_data->symbol; } - /*! - Set data by copying x- and y-values from specified memory blocks. - Contrary to setCurveRawData(), this function makes a 'deep copy' of - the data. - - \param xData pointer to x values - \param yData pointer to y values - \param size size of xData and yData - - \sa QwtCPointerData -*/ -void QwtPlotCurve::setData(const double *xData, const double *yData, int size) -{ - delete d_xy; - d_xy = new QwtArrayData(xData, yData, size); - itemChanged(); -} + Build and assign a pen -/*! - \brief Initialize data with x- and y-arrays (explicitly shared) + In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it + non cosmetic ( see QPen::isCosmetic() ). This method has been introduced + to hide this incompatibility. - \param xData x data - \param yData y data + \param color Pen color + \param width Pen width + \param style Pen style - \sa QwtArrayData -*/ -void QwtPlotCurve::setData(const QwtArray &xData, - const QwtArray &yData) + \sa pen(), brush() + */ +void QwtPlotCurve::setPen( const QColor &color, qreal width, Qt::PenStyle style ) { - delete d_xy; - d_xy = new QwtArrayData(xData, yData); - itemChanged(); + setPen( QPen( color, width, style ) ); } /*! - Initialize data with an array of points (explicitly shared). + Assign a pen - \param data Data - \sa QwtPolygonFData + \param pen New pen + \sa pen(), brush() */ -#if QT_VERSION < 0x040000 -void QwtPlotCurve::setData(const QwtArray &data) -#else -void QwtPlotCurve::setData(const QPolygonF &data) -#endif +void QwtPlotCurve::setPen( const QPen &pen ) { - delete d_xy; - d_xy = new QwtPolygonFData(data); - itemChanged(); + if ( pen != d_data->pen ) + { + d_data->pen = pen; + + legendChanged(); + itemChanged(); + } } /*! - Initialize data with a pointer to QwtData. - - \param data Data - \sa QwtData::copy() + \return Pen used to draw the lines + \sa setPen(), brush() */ -void QwtPlotCurve::setData(const QwtData &data) +const QPen& QwtPlotCurve::pen() const { - delete d_xy; - d_xy = data.copy(); - itemChanged(); + return d_data->pen; } /*! - \brief Initialize the data by pointing to memory blocks which are not managed - by QwtPlotCurve. + \brief Assign a brush. - setRawData is provided for efficiency. It is important to keep the pointers - during the lifetime of the underlying QwtCPointerData class. + In case of brush.style() != QBrush::NoBrush + and style() != QwtPlotCurve::Sticks + the area between the curve and the baseline will be filled. - \param xData pointer to x data - \param yData pointer to y data - \param size size of x and y + In case !brush.color().isValid() the area will be filled by + pen.color(). The fill algorithm simply connects the first and the + last curve point to the baseline. So the curve data has to be sorted + (ascending or descending). - \sa QwtCPointerData::setData. + \param brush New brush + \sa brush(), setBaseline(), baseline() */ -void QwtPlotCurve::setRawData(const double *xData, const double *yData, int size) +void QwtPlotCurve::setBrush( const QBrush &brush ) { - delete d_xy; - d_xy = new QwtCPointerData(xData, yData, size); - itemChanged(); + if ( brush != d_data->brush ) + { + d_data->brush = brush; + + legendChanged(); + itemChanged(); + } } /*! - Returns the bounding rectangle of the curve data. If there is - no bounding rect, like for empty data the rectangle is invalid. - \sa QwtData::boundingRect(), QwtDoubleRect::isValid() + \return Brush used to fill the area between lines and the baseline + \sa setBrush(), setBaseline(), baseline() */ - -QwtDoubleRect QwtPlotCurve::boundingRect() const +const QBrush& QwtPlotCurve::brush() const { - if ( d_xy == NULL ) - return QwtDoubleRect(1.0, 1.0, -2.0, -2.0); // invalid - - return d_xy->boundingRect(); + return d_data->brush; } /*! - \brief Draw the complete curve + Draw an interval of the curve \param painter Painter \param xMap Maps x-values into pixel coordinates. \param yMap Maps y-values into pixel coordinates. - - \sa drawCurve(), drawSymbols() -*/ -void QwtPlotCurve::draw(QPainter *painter, - const QwtScaleMap &xMap, const QwtScaleMap &yMap, - const QRect &) const -{ - draw(painter, xMap, yMap, 0, -1); -} - -/*! - \brief Draw a set of points of a curve. - - When observing an measurement while it is running, new points have to be - added to an existing curve. drawCurve can be used to display them avoiding - a complete redraw of the canvas. - - Setting plot()->canvas()->setAttribute(Qt::WA_PaintOutsidePaintEvent, true); - will result in faster painting, if the paint engine of the canvas widget - supports this feature. - + \param canvasRect Contents rectangle of the canvas \param from Index of the first point to be painted \param to Index of the last point to be painted. If to < 0 the curve will be painted to its last point. - \sa drawCurve(), drawSymbols() + \sa drawCurve(), drawSymbols(), */ -void QwtPlotCurve::draw(int from, int to) const +void QwtPlotCurve::drawSeries( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const { - if ( !plot() ) - return; + const size_t numSamples = dataSize(); - QwtPlotCanvas *canvas = plot()->canvas(); - -#if QT_VERSION >= 0x040000 -#if 0 - if ( canvas->paintEngine()->type() == QPaintEngine::OpenGL ) { - /* - OpenGL alway repaint the complete widget. - So for this operation OpenGL is one of the slowest - environments. - */ - canvas->repaint(); + if ( !painter || numSamples <= 0 ) return; - } -#endif - if ( !canvas->testAttribute(Qt::WA_WState_InPaintEvent) && - !canvas->testAttribute(Qt::WA_PaintOutsidePaintEvent) ) { - /* - We save curve and range in helper and call repaint. - The helper filters the Paint event, to repeat - the QwtPlotCurve::draw, but now from inside the paint - event. - */ - - QwtPlotCurvePaintHelper helper(this, from, to); - canvas->installEventFilter(&helper); - - const bool noSystemBackground = - canvas->testAttribute(Qt::WA_NoSystemBackground); - canvas->setAttribute(Qt::WA_NoSystemBackground, true); - canvas->repaint(); - canvas->setAttribute(Qt::WA_NoSystemBackground, noSystemBackground); + if ( to < 0 ) + to = numSamples - 1; - return; - } -#endif - - const QwtScaleMap xMap = plot()->canvasMap(xAxis()); - const QwtScaleMap yMap = plot()->canvasMap(yAxis()); - - if ( canvas->testPaintAttribute(QwtPlotCanvas::PaintCached) && - canvas->paintCache() && !canvas->paintCache()->isNull() ) { - QPainter cachePainter((QPixmap *)canvas->paintCache()); - cachePainter.translate(-canvas->contentsRect().x(), - -canvas->contentsRect().y()); - - draw(&cachePainter, xMap, yMap, from, to); - } - -#if QT_VERSION >= 0x040000 - if ( canvas->testAttribute(Qt::WA_WState_InPaintEvent) ) { - QPainter painter(canvas); - - painter.setClipping(true); - painter.setClipRect(canvas->contentsRect()); - - draw(&painter, xMap, yMap, from, to); - } else -#endif + if ( qwtVerifyRange( numSamples, from, to ) > 0 ) { - QPainter *painter = d_data->guardedPainter.begin(canvas); - draw(painter, xMap, yMap, from, to); - } -} - -/*! - \brief Draw an interval of the curve - \param painter Painter - \param xMap maps x-values into pixel coordinates. - \param yMap maps y-values into pixel coordinates. - \param from index of the first point to be painted - \param to index of the last point to be painted. If to < 0 the - curve will be painted to its last point. - - \sa drawCurve(), drawSymbols(), -*/ -void QwtPlotCurve::draw(QPainter *painter, - const QwtScaleMap &xMap, const QwtScaleMap &yMap, - int from, int to) const -{ - if ( !painter || dataSize() <= 0 ) - return; - - if (to < 0) - to = dataSize() - 1; - - if ( verifyRange(dataSize(), from, to) > 0 ) { painter->save(); - - QPen pen = d_data->pen; - -#if SCALE_PEN - if ( pen.width() > 0 ) { - const QwtMetricsMap &metricsMap = QwtPainter::metricsMap(); - pen.setWidth(metricsMap.screenToLayoutX(pen.width())); - } -#endif - - painter->setPen(pen); + painter->setPen( d_data->pen ); /* Qt 4.0.0 is slow when drawing lines, but it's even @@ -691,12 +368,15 @@ void QwtPlotCurve::draw(QPainter *painter, set the brush before we really need it. */ - drawCurve(painter, d_data->style, xMap, yMap, from, to); + drawCurve( painter, d_data->style, xMap, yMap, canvasRect, from, to ); painter->restore(); - if (d_data->symbol->style() != QwtSymbol::NoSymbol) { + if ( d_data->symbol && + ( d_data->symbol->style() != QwtSymbol::NoSymbol ) ) + { painter->save(); - drawSymbols(painter, *d_data->symbol, xMap, yMap, from, to); + drawSymbols( painter, *d_data->symbol, + xMap, yMap, canvasRect, from, to ); painter->restore(); } } @@ -708,37 +388,39 @@ void QwtPlotCurve::draw(QPainter *painter, \param style curve style, see QwtPlotCurve::CurveStyle \param xMap x map \param yMap y map + \param canvasRect Contents rectangle of the canvas \param from index of the first point to be painted \param to index of the last point to be painted \sa draw(), drawDots(), drawLines(), drawSteps(), drawSticks() */ - -void QwtPlotCurve::drawCurve(QPainter *painter, int style, - const QwtScaleMap &xMap, const QwtScaleMap &yMap, - int from, int to) const +void QwtPlotCurve::drawCurve( QPainter *painter, int style, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const { - switch (style) { - case Lines: - if ( testCurveAttribute(Fitted) ) { - // we always need the complete - // curve for fitting - from = 0; - to = dataSize() - 1; - } - drawLines(painter, xMap, yMap, from, to); - break; - case Sticks: - drawSticks(painter, xMap, yMap, from, to); - break; - case Steps: - drawSteps(painter, xMap, yMap, from, to); - break; - case Dots: - drawDots(painter, xMap, yMap, from, to); - break; - case NoCurve: - default: - break; + switch ( style ) + { + case Lines: + if ( testCurveAttribute( Fitted ) ) + { + // we always need the complete + // curve for fitting + from = 0; + to = dataSize() - 1; + } + drawLines( painter, xMap, yMap, canvasRect, from, to ); + break; + case Sticks: + drawSticks( painter, xMap, yMap, canvasRect, from, to ); + break; + case Steps: + drawSteps( painter, xMap, yMap, canvasRect, from, to ); + break; + case Dots: + drawDots( painter, xMap, yMap, canvasRect, from, to ); + break; + case NoCurve: + default: + break; } } @@ -751,108 +433,100 @@ void QwtPlotCurve::drawCurve(QPainter *painter, int style, \param painter Painter \param xMap x map \param yMap y map + \param canvasRect Contents rectangle of the canvas \param from index of the first point to be painted \param to index of the last point to be painted \sa setCurveAttribute(), setCurveFitter(), draw(), drawLines(), drawDots(), drawSteps(), drawSticks() */ -void QwtPlotCurve::drawLines(QPainter *painter, - const QwtScaleMap &xMap, const QwtScaleMap &yMap, - int from, int to) const +void QwtPlotCurve::drawLines( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const { - int size = to - from + 1; - if ( size <= 0 ) + if ( from > to ) return; - QwtPolygon polyline; - if ( ( d_data->attributes & Fitted ) && d_data->curveFitter ) { - // Transform x and y values to window coordinates - // to avoid a distinction between linear and - // logarithmic scales. + const bool doAlign = QwtPainter::roundingAlignment( painter ); + const bool doFit = ( d_data->attributes & Fitted ) && d_data->curveFitter; + const bool doFill = ( d_data->brush.style() != Qt::NoBrush ) + && ( d_data->brush.color().alpha() > 0 ); -#if QT_VERSION < 0x040000 - QwtArray points(size); -#else - QPolygonF points(size); -#endif - for (int i = from; i <= to; i++) { - QwtDoublePoint &p = points[i]; - p.setX( xMap.xTransform(x(i)) ); - p.setY( yMap.xTransform(y(i)) ); - } + QRectF clipRect; + if ( d_data->paintAttributes & ClipPolygons ) + { + qreal pw = qMax( qreal( 1.0 ), painter->pen().widthF()); + clipRect = canvasRect.adjusted(-pw, -pw, pw, pw); + } + + bool doIntegers = false; + +#if QT_VERSION < 0x040800 + + // For Qt <= 4.7 the raster paint engine is significantly faster + // for rendering QPolygon than for QPolygonF. So let's + // see if we can use it. - points = d_data->curveFitter->fitCurve(points); - size = points.size(); + if ( painter->paintEngine()->type() == QPaintEngine::Raster ) + { + // In case of filling or fitting performance doesn't count + // because both operations are much more expensive + // then drawing the polyline itself - if ( size == 0 ) - return; + if ( !doFit && !doFill ) + doIntegers = true; + } +#endif - // Round QwtDoublePoints to QPoints - // When Qwt support for Qt3 has been dropped (Qwt 6.x) - // we will use a doubles for painting and the following - // step will be obsolete. + const bool noDuplicates = d_data->paintAttributes & FilterPoints; - polyline.resize(size); + QwtPointMapper mapper; + mapper.setFlag( QwtPointMapper::RoundPoints, doAlign ); + mapper.setFlag( QwtPointMapper::WeedOutPoints, noDuplicates ); + mapper.setBoundingRect( canvasRect ); - const QwtDoublePoint *p = points.data(); - QPoint *pl = polyline.data(); - if ( d_data->paintAttributes & PaintFiltered ) { + if ( doIntegers ) + { + const QPolygon polyline = mapper.toPolygon( + xMap, yMap, data(), from, to ); - QPoint pp(qRound(p[0].x()), qRound(p[0].y())); - pl[0] = pp; + if ( d_data->paintAttributes & ClipPolygons ) + { + const QPolygon clipped = QwtClipper::clipPolygon( + clipRect.toAlignedRect(), polyline, false ); - int count = 1; - for (int i = 1; i < size; i++) { - const QPoint pi(qRound(p[i].x()), qRound(p[i].y())); - if ( pi != pp ) { - pl[count++] = pi; - pp = pi; - } - } - if ( count != size ) - polyline.resize(count); - } else { - for ( int i = 0; i < size; i++ ) { - pl[i].setX( qRound(p[i].x()) ); - pl[i].setY( qRound(p[i].y()) ); - } + QwtPainter::drawPolyline( painter, clipped ); } - } else { - polyline.resize(size); - - if ( d_data->paintAttributes & PaintFiltered ) { - QPoint pp( xMap.transform(x(from)), yMap.transform(y(from)) ); - polyline.setPoint(0, pp); - - int count = 1; - for (int i = from + 1; i <= to; i++) { - const QPoint pi(xMap.transform(x(i)), yMap.transform(y(i))); - if ( pi != pp ) { - polyline.setPoint(count, pi); - count++; - - pp = pi; - } - } - if ( count != size ) - polyline.resize(count); - } else { - for (int i = from; i <= to; i++) { - int xi = xMap.transform(x(i)); - int yi = yMap.transform(y(i)); - - polyline.setPoint(i - from, xi, yi); - } + else + { + QwtPainter::drawPolyline( painter, polyline ); } } + else + { + QPolygonF polyline = mapper.toPolygonF( xMap, yMap, + data(), from, to ); - polyline = ::clipPolygon(painter, d_data->paintAttributes, polyline); + if ( doFit ) + polyline = d_data->curveFitter->fitCurve( polyline ); - QwtPainter::drawPolyline(painter, polyline); + if ( d_data->paintAttributes & ClipPolygons ) + { + const QPolygonF clipped = QwtClipper::clipPolygonF( + clipRect, polyline, false ); - if ( d_data->brush.style() != Qt::NoBrush ) - fillCurve(painter, xMap, yMap, polyline); + QwtPainter::drawPolyline( painter, clipped ); + } + else + { + QwtPainter::drawPolyline( painter, polyline ); + } + + if ( doFill ) + { + fillCurve( painter, xMap, yMap, canvasRect, polyline ); + } + } } /*! @@ -861,27 +535,51 @@ void QwtPlotCurve::drawLines(QPainter *painter, \param painter Painter \param xMap x map \param yMap y map + \param canvasRect Contents rectangle of the canvas \param from index of the first point to be painted \param to index of the last point to be painted \sa draw(), drawCurve(), drawDots(), drawLines(), drawSteps() */ -void QwtPlotCurve::drawSticks(QPainter *painter, - const QwtScaleMap &xMap, const QwtScaleMap &yMap, - int from, int to) const +void QwtPlotCurve::drawSticks( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &, int from, int to ) const { - int x0 = xMap.transform(d_data->reference); - int y0 = yMap.transform(d_data->reference); + painter->save(); + painter->setRenderHint( QPainter::Antialiasing, false ); - for (int i = from; i <= to; i++) { - const int xi = xMap.transform(x(i)); - const int yi = yMap.transform(y(i)); + const bool doAlign = QwtPainter::roundingAlignment( painter ); + + double x0 = xMap.transform( d_data->baseline ); + double y0 = yMap.transform( d_data->baseline ); + if ( doAlign ) + { + x0 = qRound( x0 ); + y0 = qRound( y0 ); + } - if (d_data->curveType == Xfy) - QwtPainter::drawLine(painter, x0, yi, xi, yi); + const Qt::Orientation o = orientation(); + + const QwtSeriesData *series = data(); + + for ( int i = from; i <= to; i++ ) + { + const QPointF sample = series->sample( i ); + double xi = xMap.transform( sample.x() ); + double yi = yMap.transform( sample.y() ); + if ( doAlign ) + { + xi = qRound( xi ); + yi = qRound( yi ); + } + + if ( o == Qt::Horizontal ) + QwtPainter::drawLine( painter, x0, yi, xi, yi ); else - QwtPainter::drawLine(painter, xi, y0, xi, yi); + QwtPainter::drawLine( painter, xi, y0, xi, yi ); } + + painter->restore(); } /*! @@ -890,74 +588,95 @@ void QwtPlotCurve::drawSticks(QPainter *painter, \param painter Painter \param xMap x map \param yMap y map + \param canvasRect Contents rectangle of the canvas \param from index of the first point to be painted \param to index of the last point to be painted \sa draw(), drawCurve(), drawSticks(), drawLines(), drawSteps() */ -void QwtPlotCurve::drawDots(QPainter *painter, - const QwtScaleMap &xMap, const QwtScaleMap &yMap, - int from, int to) const +void QwtPlotCurve::drawDots( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const { - const QRect window = painter->window(); - if ( window.isEmpty() ) + const QColor color = painter->pen().color(); + + if ( painter->pen().style() == Qt::NoPen || color.alpha() == 0 ) + { return; + } - const bool doFill = d_data->brush.style() != Qt::NoBrush; + const bool doFill = ( d_data->brush.style() != Qt::NoBrush ) + && ( d_data->brush.color().alpha() > 0 ); + const bool doAlign = QwtPainter::roundingAlignment( painter ); - QwtPolygon polyline; - if ( doFill ) - polyline.resize(to - from + 1); + QwtPointMapper mapper; + mapper.setBoundingRect( canvasRect ); + mapper.setFlag( QwtPointMapper::RoundPoints, doAlign ); - if ( to > from && d_data->paintAttributes & PaintFiltered ) { - if ( doFill ) { - QPoint pp( xMap.transform(x(from)), yMap.transform(y(from)) ); + if ( d_data->paintAttributes & FilterPoints ) + { + if ( ( color.alpha() == 255 ) + && !( painter->renderHints() & QPainter::Antialiasing ) ) + { + mapper.setFlag( QwtPointMapper::WeedOutPoints, true ); + } + } - QwtPainter::drawPoint(painter, pp.x(), pp.y()); - polyline.setPoint(0, pp); + if ( doFill ) + { + mapper.setFlag( QwtPointMapper::WeedOutPoints, false ); - int count = 1; - for (int i = from + 1; i <= to; i++) { - const QPoint pi(xMap.transform(x(i)), yMap.transform(y(i))); - if ( pi != pp ) { - QwtPainter::drawPoint(painter, pi.x(), pi.y()); + QPolygonF points = mapper.toPointsF( + xMap, yMap, data(), from, to ); - polyline.setPoint(count, pi); - count++; + QwtPainter::drawPoints( painter, points ); + fillCurve( painter, xMap, yMap, canvasRect, points ); + } + else if ( d_data->paintAttributes & ImageBuffer ) + { + const QImage image = mapper.toImage( xMap, yMap, + data(), from, to, d_data->pen, + painter->testRenderHint( QPainter::Antialiasing ), + renderThreadCount() ); - pp = pi; - } - } - if ( int(polyline.size()) != count ) - polyline.resize(count); - } else { - // if we don't need to fill, we can sort out - // duplicates independent from the order + painter->drawImage( canvasRect.toAlignedRect(), image ); + } + else if ( d_data->paintAttributes & MinimizeMemory ) + { + const QwtSeriesData *series = data(); - PrivateData::PixelMatrix pixelMatrix(window); + for ( int i = from; i <= to; i++ ) + { + const QPointF sample = series->sample( i ); - for (int i = from; i <= to; i++) { - const QPoint p( xMap.transform(x(i)), - yMap.transform(y(i)) ); + double xi = xMap.transform( sample.x() ); + double yi = yMap.transform( sample.y() ); - if ( pixelMatrix.testPixel(p) ) - QwtPainter::drawPoint(painter, p.x(), p.y()); + if ( doAlign ) + { + xi = qRound( xi ); + yi = qRound( yi ); } - } - } else { - for (int i = from; i <= to; i++) { - const int xi = xMap.transform(x(i)); - const int yi = yMap.transform(y(i)); - QwtPainter::drawPoint(painter, xi, yi); - - if ( doFill ) - polyline.setPoint(i - from, xi, yi); + + QwtPainter::drawPoint( painter, QPointF( xi, yi ) ); } } + else + { + if ( doAlign ) + { + const QPolygon points = mapper.toPoints( + xMap, yMap, data(), from, to ); + + QwtPainter::drawPoints( painter, points ); + } + else + { + const QPolygonF points = mapper.toPointsF( + xMap, yMap, data(), from, to ); - if ( doFill ) { - polyline = ::clipPolygon(painter, d_data->paintAttributes, polyline); - fillCurve(painter, xMap, yMap, polyline); + QwtPainter::drawPoints( painter, points ); + } } } @@ -969,71 +688,89 @@ void QwtPlotCurve::drawDots(QPainter *painter, \param painter Painter \param xMap x map \param yMap y map + \param canvasRect Contents rectangle of the canvas \param from index of the first point to be painted \param to index of the last point to be painted \sa CurveAttribute, setCurveAttribute(), draw(), drawCurve(), drawDots(), drawLines(), drawSticks() */ -void QwtPlotCurve::drawSteps(QPainter *painter, - const QwtScaleMap &xMap, const QwtScaleMap &yMap, - int from, int to) const +void QwtPlotCurve::drawSteps( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const { - QwtPolygon polyline(2 * (to - from) + 1); + const bool doAlign = QwtPainter::roundingAlignment( painter ); - bool inverted = d_data->curveType == Yfx; + QPolygonF polygon( 2 * ( to - from ) + 1 ); + QPointF *points = polygon.data(); + + bool inverted = orientation() == Qt::Vertical; if ( d_data->attributes & Inverted ) inverted = !inverted; - int i,ip; - for (i = from, ip = 0; i <= to; i++, ip += 2) { - const int xi = xMap.transform(x(i)); - const int yi = yMap.transform(y(i)); + const QwtSeriesData *series = data(); + + int i, ip; + for ( i = from, ip = 0; i <= to; i++, ip += 2 ) + { + const QPointF sample = series->sample( i ); + double xi = xMap.transform( sample.x() ); + double yi = yMap.transform( sample.y() ); + if ( doAlign ) + { + xi = qRound( xi ); + yi = qRound( yi ); + } - if ( ip > 0 ) { - if (inverted) - polyline.setPoint(ip - 1, polyline[ip-2].x(), yi); + if ( ip > 0 ) + { + const QPointF &p0 = points[ip - 2]; + QPointF &p = points[ip - 1]; + + if ( inverted ) + { + p.rx() = p0.x(); + p.ry() = yi; + } else - polyline.setPoint(ip - 1, xi, polyline[ip-2].y()); + { + p.rx() = xi; + p.ry() = p0.y(); + } } - polyline.setPoint(ip, xi, yi); + points[ip].rx() = xi; + points[ip].ry() = yi; } - polyline = ::clipPolygon(painter, d_data->paintAttributes, polyline); + if ( d_data->paintAttributes & ClipPolygons ) + { + const QPolygonF clipped = QwtClipper::clipPolygonF( + canvasRect, polygon, false ); - QwtPainter::drawPolyline(painter, polyline); + QwtPainter::drawPolyline( painter, clipped ); + } + else + { + QwtPainter::drawPolyline( painter, polygon ); + } if ( d_data->brush.style() != Qt::NoBrush ) - fillCurve(painter, xMap, yMap, polyline); + fillCurve( painter, xMap, yMap, canvasRect, polygon ); } /*! - \brief Specify an attribute for drawing the curve - - The attributes can be used to modify the drawing style. - The following attributes are defined:
-
Fitted
-
For Lines only. A QwtCurveFitter tries to - interpolate/smooth the curve, before it is painted. - Note that curve fitting requires temorary memory - for calculating coefficients and additional points. - If painting in Fitted mode is slow it might be better - to fit the points, before they are passed to QwtPlotCurve. -
-
Inverted
-
For Steps only. Draws a step function - from the right to the left.
+ Specify an attribute for drawing the curve \param attribute Curve attribute \param on On/Off /sa testCurveAttribute(), setCurveFitter() */ -void QwtPlotCurve::setCurveAttribute(CurveAttribute attribute, bool on) +void QwtPlotCurve::setCurveAttribute( CurveAttribute attribute, bool on ) { - if ( bool(d_data->attributes & attribute) == on ) + if ( bool( d_data->attributes & attribute ) == on ) return; if ( on ) @@ -1048,45 +785,29 @@ void QwtPlotCurve::setCurveAttribute(CurveAttribute attribute, bool on) \return true, if attribute is enabled \sa setCurveAttribute() */ -bool QwtPlotCurve::testCurveAttribute(CurveAttribute attribute) const +bool QwtPlotCurve::testCurveAttribute( CurveAttribute attribute ) const { return d_data->attributes & attribute; } /*! - Assign the curve type + Assign a curve fitter -
QwtPlotCurve::Yfx -
Draws y as a function of x (the default). The - baseline is interpreted as a horizontal line - with y = baseline().
-
QwtPlotCurve::Xfy -
Draws x as a function of y. The baseline is - interpreted as a vertical line with x = baseline().
+ The curve fitter "smooths" the curve points, when the Fitted + CurveAttribute is set. setCurveFitter(NULL) also disables curve fitting. - The baseline is used for aligning the sticks, or - filling the curve with a brush. + The curve fitter operates on the translated points ( = widget coordinates) + to be functional for logarithmic scales. Obviously this is less performant + for fitting algorithms, that reduce the number of points. - \sa curveType() -*/ -void QwtPlotCurve::setCurveType(CurveType curveType) -{ - if ( d_data->curveType != curveType ) { - d_data->curveType = curveType; - itemChanged(); - } -} + For situations, where curve fitting is used to improve the performance + of painting huge series of points it might be better to execute the fitter + on the curve points once and to cache the result in the QwtSeriesData object. -/*! - Return the curve type - \sa setCurveType() + \param curveFitter() Curve fitter + \sa Fitted */ -QwtPlotCurve::CurveType QwtPlotCurve::curveType() const -{ - return d_data->curveType; -} - -void QwtPlotCurve::setCurveFitter(QwtCurveFitter *curveFitter) +void QwtPlotCurve::setCurveFitter( QwtCurveFitter *curveFitter ) { delete d_data->curveFitter; d_data->curveFitter = curveFitter; @@ -1094,6 +815,12 @@ void QwtPlotCurve::setCurveFitter(QwtCurveFitter *curveFitter) itemChanged(); } +/*! + Get the curve fitter. If curve fitting is disabled NULL is returned. + + \return Curve fitter + \sa setCurveFitter(), Fitted +*/ QwtCurveFitter *QwtPlotCurve::curveFitter() const { return d_data->curveFitter; @@ -1106,123 +833,120 @@ QwtCurveFitter *QwtPlotCurve::curveFitter() const \param painter Painter \param xMap x map \param yMap y map - \param pa Polygon + \param canvasRect Contents rectangle of the canvas + \param polygon Polygon - will be modified ! - \sa setBrush(), setBaseline(), setCurveType() + \sa setBrush(), setBaseline(), setStyle() */ - -void QwtPlotCurve::fillCurve(QPainter *painter, - const QwtScaleMap &xMap, const QwtScaleMap &yMap, - QwtPolygon &pa) const +void QwtPlotCurve::fillCurve( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, QPolygonF &polygon ) const { if ( d_data->brush.style() == Qt::NoBrush ) return; - closePolyline(xMap, yMap, pa); - if ( pa.count() <= 2 ) // a line can't be filled + closePolyline( painter, xMap, yMap, polygon ); + if ( polygon.count() <= 2 ) // a line can't be filled return; - QBrush b = d_data->brush; - if ( !b.color().isValid() ) - b.setColor(d_data->pen.color()); + QBrush brush = d_data->brush; + if ( !brush.color().isValid() ) + brush.setColor( d_data->pen.color() ); + + if ( d_data->paintAttributes & ClipPolygons ) + polygon = QwtClipper::clipPolygonF( canvasRect, polygon, true ); painter->save(); - painter->setPen(QPen(Qt::NoPen)); - painter->setBrush(b); + painter->setPen( Qt::NoPen ); + painter->setBrush( brush ); - QwtPainter::drawPolygon(painter, pa); + QwtPainter::drawPolygon( painter, polygon ); painter->restore(); } /*! - \brief Complete a polygon to be a closed polygon - including the area between the original polygon - and the baseline. + \brief Complete a polygon to be a closed polygon including the + area between the original polygon and the baseline. + + \param painter Painter \param xMap X map \param yMap Y map - \param pa Polygon to be completed + \param polygon Polygon to be completed */ - -void QwtPlotCurve::closePolyline( +void QwtPlotCurve::closePolyline( QPainter *painter, const QwtScaleMap &xMap, const QwtScaleMap &yMap, - QwtPolygon &pa) const + QPolygonF &polygon ) const { - const int sz = pa.size(); - if ( sz < 2 ) + if ( polygon.size() < 2 ) return; - pa.resize(sz + 2); - - if ( d_data->curveType == QwtPlotCurve::Xfy ) { - pa.setPoint(sz, - xMap.transform(d_data->reference), pa.point(sz - 1).y()); - pa.setPoint(sz + 1, - xMap.transform(d_data->reference), pa.point(0).y()); - } else { - pa.setPoint(sz, - pa.point(sz - 1).x(), yMap.transform(d_data->reference)); - pa.setPoint(pa.size() - 1, - pa.point(0).x(), yMap.transform(d_data->reference)); + const bool doAlign = QwtPainter::roundingAlignment( painter ); + + double baseline = d_data->baseline; + + if ( orientation() == Qt::Vertical ) + { + if ( yMap.transformation() ) + baseline = yMap.transformation()->bounded( baseline ); + + double refY = yMap.transform( baseline ); + if ( doAlign ) + refY = qRound( refY ); + + polygon += QPointF( polygon.last().x(), refY ); + polygon += QPointF( polygon.first().x(), refY ); + } + else + { + if ( xMap.transformation() ) + baseline = xMap.transformation()->bounded( baseline ); + + double refX = xMap.transform( baseline ); + if ( doAlign ) + refX = qRound( refX ); + + polygon += QPointF( refX, polygon.last().y() ); + polygon += QPointF( refX, polygon.first().y() ); } } /*! - \brief Draw symbols + Draw symbols + \param painter Painter \param symbol Curve symbol \param xMap x map \param yMap y map - \param from index of the first point to be painted - \param to index of the last point to be painted + \param canvasRect Contents rectangle of the canvas + \param from Index of the first point to be painted + \param to Index of the last point to be painted - \sa setSymbol(), draw(), drawCurve() + \sa setSymbol(), drawSeries(), drawCurve() */ -void QwtPlotCurve::drawSymbols(QPainter *painter, const QwtSymbol &symbol, - const QwtScaleMap &xMap, const QwtScaleMap &yMap, - int from, int to) const +void QwtPlotCurve::drawSymbols( QPainter *painter, const QwtSymbol &symbol, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const { - painter->setBrush(symbol.brush()); + QwtPointMapper mapper; + mapper.setFlag( QwtPointMapper::RoundPoints, + QwtPainter::roundingAlignment( painter ) ); + mapper.setFlag( QwtPointMapper::WeedOutPoints, + testPaintAttribute( QwtPlotCurve::FilterPoints ) ); + mapper.setBoundingRect( canvasRect ); - const QwtMetricsMap &metricsMap = QwtPainter::metricsMap(); + const int chunkSize = 500; - QPen pen = symbol.pen(); - -#if SCALE_PEN - if ( pen.width() > 0 ) - pen.setWidth(metricsMap.screenToLayoutX(pen.width())); -#endif - - painter->setPen(pen); - - QRect rect; - rect.setSize(metricsMap.screenToLayout(symbol.size())); - - if ( to > from && d_data->paintAttributes & PaintFiltered ) { - const QRect window = painter->window(); - if ( window.isEmpty() ) - return; - - PrivateData::PixelMatrix pixelMatrix(window); - - for (int i = from; i <= to; i++) { - const QPoint pi( xMap.transform(x(i)), - yMap.transform(y(i)) ); + for ( int i = from; i <= to; i += chunkSize ) + { + const int n = qMin( chunkSize, to - i + 1 ); - if ( pixelMatrix.testPixel(pi) ) { - rect.moveCenter(pi); - symbol.draw(painter, rect); - } - } - } else { - for (int i = from; i <= to; i++) { - const int xi = xMap.transform(x(i)); - const int yi = yMap.transform(y(i)); + const QPolygonF points = mapper.toPointsF( xMap, yMap, + data(), i, i + n - 1 ); - rect.moveCenter(QPoint(xi, yi)); - symbol.draw(painter, rect); - } + if ( points.size() > 0 ) + symbol.drawSymbols( painter, points ); } } @@ -1231,125 +955,237 @@ void QwtPlotCurve::drawSymbols(QPainter *painter, const QwtSymbol &symbol, The baseline is needed for filling the curve with a brush or the Sticks drawing style. - The default value is 0.0. The interpretation - of the baseline depends on the CurveType. With QwtPlotCurve::Yfx, - the baseline is interpreted as a horizontal line at y = baseline(), - with QwtPlotCurve::Yfy, it is interpreted as a vertical line at - x = baseline(). - \param reference baseline - \sa baseline(), setBrush(), setStyle(), setCurveType() + + The interpretation of the baseline depends on the orientation(). + With Qt::Horizontal, the baseline is interpreted as a horizontal line + at y = baseline(), with Qt::Vertical, it is interpreted as a vertical + line at x = baseline(). + + The default value is 0.0. + + \param value Value of the baseline + \sa baseline(), setBrush(), setStyle(), QwtPlotAbstractSeriesItem::orientation() */ -void QwtPlotCurve::setBaseline(double reference) +void QwtPlotCurve::setBaseline( double value ) { - if ( d_data->reference != reference ) { - d_data->reference = reference; + if ( d_data->baseline != value ) + { + d_data->baseline = value; itemChanged(); } } /*! - Return the value of the baseline - \sa setBaseline + \return Value of the baseline + \sa setBaseline() */ double QwtPlotCurve::baseline() const { - return d_data->reference; + return d_data->baseline; } /*! - Return the size of the data arrays - \sa setData() + Find the closest curve point for a specific position + + \param pos Position, where to look for the closest curve point + \param dist If dist != NULL, closestPoint() returns the distance between + the position and the closest curve point + \return Index of the closest curve point, or -1 if none can be found + ( f.e when the curve has no points ) + \note closestPoint() implements a dumb algorithm, that iterates + over all points */ -int QwtPlotCurve::dataSize() const +int QwtPlotCurve::closestPoint( const QPoint &pos, double *dist ) const { - return d_xy->size(); -} + const size_t numSamples = dataSize(); -int QwtPlotCurve::closestPoint(const QPoint &pos, double *dist) const -{ - if ( plot() == NULL || dataSize() <= 0 ) + if ( plot() == NULL || numSamples <= 0 ) return -1; - const QwtScaleMap xMap = plot()->canvasMap(xAxis()); - const QwtScaleMap yMap = plot()->canvasMap(yAxis()); + const QwtSeriesData *series = data(); + + const QwtScaleMap xMap = plot()->canvasMap( xAxis() ); + const QwtScaleMap yMap = plot()->canvasMap( yAxis() ); int index = -1; double dmin = 1.0e10; - for (int i=0; i < dataSize(); i++) { - const double cx = xMap.xTransform(x(i)) - pos.x(); - const double cy = yMap.xTransform(y(i)) - pos.y(); + for ( uint i = 0; i < numSamples; i++ ) + { + const QPointF sample = series->sample( i ); + + const double cx = xMap.transform( sample.x() ) - pos.x(); + const double cy = yMap.transform( sample.y() ) - pos.y(); - const double f = qwtSqr(cx) + qwtSqr(cy); - if (f < dmin) { + const double f = qwtSqr( cx ) + qwtSqr( cy ); + if ( f < dmin ) + { index = i; dmin = f; } } if ( dist ) - *dist = sqrt(dmin); + *dist = qSqrt( dmin ); return index; } -//! Update the widget that represents the curve on the legend -void QwtPlotCurve::updateLegend(QwtLegend *legend) const -{ - if ( !legend ) - return; +/*! + \return Icon representing the curve on the legend - QwtPlotItem::updateLegend(legend); + \param index Index of the legend entry + ( ignored as there is only one ) + \param size Icon size - QWidget *widget = legend->find(this); - if ( !widget || !widget->inherits("QwtLegendItem") ) - return; + \sa QwtPlotItem::setLegendIconSize(), QwtPlotItem::legendData() + */ +QwtGraphic QwtPlotCurve::legendIcon( int index, + const QSizeF &size ) const +{ + Q_UNUSED( index ); - QwtLegendItem *legendItem = (QwtLegendItem *)widget; + if ( size.isEmpty() ) + return QwtGraphic(); -#if QT_VERSION < 0x040000 - const bool doUpdate = legendItem->isUpdatesEnabled(); -#else - const bool doUpdate = legendItem->updatesEnabled(); -#endif - legendItem->setUpdatesEnabled(false); + QwtGraphic graphic; + graphic.setDefaultSize( size ); + graphic.setRenderHint( QwtGraphic::RenderPensUnscaled, true ); - const int policy = legend->displayPolicy(); + QPainter painter( &graphic ); + painter.setRenderHint( QPainter::Antialiasing, + testRenderHint( QwtPlotItem::RenderAntialiased ) ); - if (policy == QwtLegend::FixedIdentifier) { - int mode = legend->identifierMode(); + if ( d_data->legendAttributes == 0 || + d_data->legendAttributes & QwtPlotCurve::LegendShowBrush ) + { + QBrush brush = d_data->brush; - if (mode & QwtLegendItem::ShowLine) - legendItem->setCurvePen(pen()); + if ( brush.style() == Qt::NoBrush && + d_data->legendAttributes == 0 ) + { + if ( style() != QwtPlotCurve::NoCurve ) + { + brush = QBrush( pen().color() ); + } + else if ( d_data->symbol && + ( d_data->symbol->style() != QwtSymbol::NoSymbol ) ) + { + brush = QBrush( d_data->symbol->pen().color() ); + } + } - if (mode & QwtLegendItem::ShowSymbol) - legendItem->setSymbol(symbol()); + if ( brush.style() != Qt::NoBrush ) + { + QRectF r( 0, 0, size.width(), size.height() ); + painter.fillRect( r, brush ); + } + } - if (mode & QwtLegendItem::ShowText) - legendItem->setText(title()); - else - legendItem->setText(QwtText()); + if ( d_data->legendAttributes & QwtPlotCurve::LegendShowLine ) + { + if ( pen() != Qt::NoPen ) + { + QPen pn = pen(); + pn.setCapStyle( Qt::FlatCap ); - legendItem->setIdentifierMode(mode); - } else if (policy == QwtLegend::AutoIdentifier) { - int mode = 0; + painter.setPen( pn ); - if (QwtPlotCurve::NoCurve != style()) { - legendItem->setCurvePen(pen()); - mode |= QwtLegendItem::ShowLine; + const double y = 0.5 * size.height(); + QwtPainter::drawLine( &painter, 0.0, y, size.width(), y ); } - if (QwtSymbol::NoSymbol != symbol().style()) { - legendItem->setSymbol(symbol()); - mode |= QwtLegendItem::ShowSymbol; - } - if ( !title().isEmpty() ) { - legendItem->setText(title()); - mode |= QwtLegendItem::ShowText; - } else { - legendItem->setText(QwtText()); + } + + if ( d_data->legendAttributes & QwtPlotCurve::LegendShowSymbol ) + { + if ( d_data->symbol ) + { + QRectF r( 0, 0, size.width(), size.height() ); + d_data->symbol->drawSymbol( &painter, r ); } - legendItem->setIdentifierMode(mode); } - legendItem->setUpdatesEnabled(doUpdate); - legendItem->update(); + return graphic; +} + +/*! + Initialize data with an array of points. + + \param samples Vector of points + \note QVector is implicitly shared + \note QPolygonF is derived from QVector +*/ +void QwtPlotCurve::setSamples( const QVector &samples ) +{ + setData( new QwtPointSeriesData( samples ) ); +} + +/*! + Assign a series of points + + setSamples() is just a wrapper for setData() without any additional + value - beside that it is easier to find for the developer. + + \param data Data + \warning The item takes ownership of the data object, deleting + it when its not used anymore. +*/ +void QwtPlotCurve::setSamples( QwtSeriesData *data ) +{ + setData( data ); +} + +#ifndef QWT_NO_COMPAT + +/*! + \brief Initialize the data by pointing to memory blocks which + are not managed by QwtPlotCurve. + + setRawSamples is provided for efficiency. + It is important to keep the pointers + during the lifetime of the underlying QwtCPointerData class. + + \param xData pointer to x data + \param yData pointer to y data + \param size size of x and y + + \sa QwtCPointerData +*/ +void QwtPlotCurve::setRawSamples( + const double *xData, const double *yData, int size ) +{ + setData( new QwtCPointerData( xData, yData, size ) ); } + +/*! + Set data by copying x- and y-values from specified memory blocks. + Contrary to setRawSamples(), this function makes a 'deep copy' of + the data. + + \param xData pointer to x values + \param yData pointer to y values + \param size size of xData and yData + + \sa QwtPointArrayData +*/ +void QwtPlotCurve::setSamples( + const double *xData, const double *yData, int size ) +{ + setData( new QwtPointArrayData( xData, yData, size ) ); +} + +/*! + \brief Initialize data with x- and y-arrays (explicitly shared) + + \param xData x data + \param yData y data + + \sa QwtPointArrayData +*/ +void QwtPlotCurve::setSamples( const QVector &xData, + const QVector &yData ) +{ + setData( new QwtPointArrayData( xData, yData ) ); +} + +#endif // !QWT_NO_COMPAT + diff --git a/libs/qwt/qwt_plot_curve.h b/libs/qwt/qwt_plot_curve.h index 5e9219464..3421abf81 100644 --- a/libs/qwt/qwt_plot_curve.h +++ b/libs/qwt/qwt_plot_curve.h @@ -10,241 +10,328 @@ #ifndef QWT_PLOT_CURVE_H #define QWT_PLOT_CURVE_H -#include -#include #include "qwt_global.h" -#include "qwt_plot_item.h" +#include "qwt_plot_seriesitem.h" +#include "qwt_series_data.h" #include "qwt_text.h" -#include "qwt_polygon.h" -#include "qwt_data.h" +#include +#include class QPainter; +class QPolygonF; class QwtScaleMap; class QwtSymbol; class QwtCurveFitter; /*! - \brief A class which draws curves + \brief A plot item, that represents a series of points - This class can be used to display data as a curve in the x-y plane. - It supports different display styles, spline interpolation and symbols. + A curve is the representation of a series of points in the x-y plane. + It supports different display styles, interpolation ( f.e. spline ) + and symbols. \par Usage -
A. Assign curve properties
+
a) Assign curve properties
When a curve is created, it is configured to draw black solid lines - with QwtPlotCurve::Lines and no symbols. You can change this by calling + with in QwtPlotCurve::Lines style and no symbols. + You can change this by calling setPen(), setStyle() and setSymbol().
-
B. Assign or change data.
-
Data can be set in two ways:
    -
  • setData() is overloaded to initialize the x and y data by - copying from different data structures with different kind of copy semantics. -
  • setRawData() only stores the pointers and size information - and is provided for backwards compatibility. This function is less safe (you - must not delete the data while they are attached), but has been more - efficient, and has been more convenient for dynamically changing data. - Use of setData() in combination with a problem-specific subclass - of QwtData is always preferrable.
-
C. Draw
-
draw() maps the data into pixel coordinates and paints them. +
b) Connect/Assign data.
+
QwtPlotCurve gets its points using a QwtSeriesData object offering + a bridge to the real storage of the points ( like QAbstractItemModel ). + There are several convenience classes derived from QwtSeriesData, that also store + the points inside ( like QStandardItemModel ). QwtPlotCurve also offers + a couple of variations of setSamples(), that build QwtSeriesData objects from + arrays internally.
+
c) Attach the curve to a plot
+
See QwtPlotItem::attach()
\par Example: - see examples/curvdemo + see examples/bode - \sa QwtData, QwtSymbol, QwtScaleMap + \sa QwtPointSeriesData, QwtSymbol, QwtScaleMap */ -class QWT_EXPORT QwtPlotCurve: public QwtPlotItem +class QWT_EXPORT QwtPlotCurve: + public QwtPlotSeriesItem, public QwtSeriesStore { public: - enum CurveType { - Yfx, - Xfy - }; - /*! Curve styles. - \sa setStyle + \sa setStyle(), style() */ - enum CurveStyle { - NoCurve, - + enum CurveStyle + { + /*! + Don't draw a curve. Note: This doesn't affect the symbols. + */ + NoCurve = -1, + + /*! + Connect the points with straight lines. The lines might + be interpolated depending on the 'Fitted' attribute. Curve + fitting can be configured using setCurveFitter(). + */ Lines, + + /*! + Draw vertical or horizontal sticks ( depending on the + orientation() ) from a baseline which is defined by setBaseline(). + */ Sticks, + + /*! + Connect the points with a step function. The step function + is drawn from the left to the right or vice versa, + depending on the QwtPlotCurve::Inverted attribute. + */ Steps, + + /*! + Draw dots at the locations of the data points. Note: + This is different from a dotted line (see setPen()), and faster + as a curve in QwtPlotCurve::NoStyle style and a symbol + painting a point. + */ Dots, + /*! + Styles >= QwtPlotCurve::UserCurve are reserved for derived + classes of QwtPlotCurve that overload drawCurve() with + additional application specific curve types. + */ UserCurve = 100 }; /*! - Curve attributes. - \sa setCurveAttribute, testCurveAttribute + Attribute for drawing the curve + \sa setCurveAttribute(), testCurveAttribute(), curveFitter() */ - enum CurveAttribute { - Inverted = 1, - Fitted = 2 + enum CurveAttribute + { + /*! + For QwtPlotCurve::Steps only. + Draws a step function from the right to the left. + */ + Inverted = 0x01, + + /*! + Only in combination with QwtPlotCurve::Lines + A QwtCurveFitter tries to + interpolate/smooth the curve, before it is painted. + + \note Curve fitting requires temporary memory + for calculating coefficients and additional points. + If painting in QwtPlotCurve::Fitted mode is slow it might be better + to fit the points, before they are passed to QwtPlotCurve. + */ + Fitted = 0x02 }; + //! Curve attributes + typedef QFlags CurveAttributes; + /*! - Paint attributes - \sa setPaintAttribute, testPaintAttribute + Attributes how to represent the curve on the legend + + \sa setLegendAttribute(), testLegendAttribute(), + QwtPlotItem::legendData(), legendIcon() + */ + + enum LegendAttribute + { + /*! + QwtPlotCurve tries to find a color representing the curve + and paints a rectangle with it. + */ + LegendNoAttribute = 0x00, + + /*! + If the style() is not QwtPlotCurve::NoCurve a line + is painted with the curve pen(). + */ + LegendShowLine = 0x01, + + /*! + If the curve has a valid symbol it is painted. + */ + LegendShowSymbol = 0x02, + + /*! + If the curve has a brush a rectangle filled with the + curve brush() is painted. + */ + LegendShowBrush = 0x04 + }; + + //! Legend attributes + typedef QFlags LegendAttributes; + + /*! + Attributes to modify the drawing algorithm. + The default setting enables ClipPolygons | FilterPoints + + \sa setPaintAttribute(), testPaintAttribute() */ - enum PaintAttribute { - PaintFiltered = 1, - ClipPolygons = 2 + enum PaintAttribute + { + /*! + Clip polygons before painting them. In situations, where points + are far outside the visible area (f.e when zooming deep) this + might be a substantial improvement for the painting performance + */ + ClipPolygons = 0x01, + + /*! + Tries to reduce the data that has to be painted, by sorting out + duplicates, or paintings outside the visible area. Might have a + notable impact on curves with many close points. + Only a couple of very basic filtering algorithms are implemented. + */ + FilterPoints = 0x02, + + /*! + Minimize memory usage that is temporarily needed for the + translated points, before they get painted. + This might slow down the performance of painting + */ + MinimizeMemory = 0x04, + + /*! + Render the points to a temporary image and paint the image. + This is a very special optimization for Dots style, when + having a huge amount of points. + With a reasonable number of points QPainter::drawPoints() + will be faster. + */ + ImageBuffer = 0x08 }; - explicit QwtPlotCurve(); - explicit QwtPlotCurve(const QwtText &title); - explicit QwtPlotCurve(const QString &title); + //! Paint attributes + typedef QFlags PaintAttributes; + + explicit QwtPlotCurve( const QString &title = QString::null ); + explicit QwtPlotCurve( const QwtText &title ); virtual ~QwtPlotCurve(); virtual int rtti() const; - void setCurveType(CurveType); - CurveType curveType() const; + void setPaintAttribute( PaintAttribute, bool on = true ); + bool testPaintAttribute( PaintAttribute ) const; - void setPaintAttribute(PaintAttribute, bool on = true); - bool testPaintAttribute(PaintAttribute) const; + void setLegendAttribute( LegendAttribute, bool on = true ); + bool testLegendAttribute( LegendAttribute ) const; - void setRawData(const double *x, const double *y, int size); - void setData(const double *xData, const double *yData, int size); - void setData(const QwtArray &xData, const QwtArray &yData); -#if QT_VERSION < 0x040000 - void setData(const QwtArray &data); -#else - void setData(const QPolygonF &data); +#ifndef QWT_NO_COMPAT + void setRawSamples( const double *xData, const double *yData, int size ); + void setSamples( const double *xData, const double *yData, int size ); + void setSamples( const QVector &xData, const QVector &yData ); #endif - void setData(const QwtData &data); - - int closestPoint(const QPoint &pos, double *dist = NULL) const; - - QwtData &data(); - const QwtData &data() const; - - int dataSize() const; - inline double x(int i) const; - inline double y(int i) const; - - virtual QwtDoubleRect boundingRect() const; - - //! boundingRect().left() - inline double minXValue() const { - return boundingRect().left(); - } - //! boundingRect().right() - inline double maxXValue() const { - return boundingRect().right(); - } - //! boundingRect().top() - inline double minYValue() const { - return boundingRect().top(); - } - //! boundingRect().bottom() - inline double maxYValue() const { - return boundingRect().bottom(); - } - - void setCurveAttribute(CurveAttribute, bool on = true); - bool testCurveAttribute(CurveAttribute) const; - - void setPen(const QPen &); + void setSamples( const QVector & ); + void setSamples( QwtSeriesData * ); + + int closestPoint( const QPoint &pos, double *dist = NULL ) const; + + double minXValue() const; + double maxXValue() const; + double minYValue() const; + double maxYValue() const; + + void setCurveAttribute( CurveAttribute, bool on = true ); + bool testCurveAttribute( CurveAttribute ) const; + + void setPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine ); + void setPen( const QPen & ); const QPen &pen() const; - void setBrush(const QBrush &); + void setBrush( const QBrush & ); const QBrush &brush() const; - void setBaseline(double ref); + void setBaseline( double ); double baseline() const; - void setStyle(CurveStyle style); + void setStyle( CurveStyle style ); CurveStyle style() const; - void setSymbol(const QwtSymbol &s); - const QwtSymbol& symbol() const; + void setSymbol( QwtSymbol * ); + const QwtSymbol *symbol() const; - void setCurveFitter(QwtCurveFitter *); + void setCurveFitter( QwtCurveFitter * ); QwtCurveFitter *curveFitter() const; - virtual void draw(QPainter *p, - const QwtScaleMap &xMap, const QwtScaleMap &yMap, - const QRect &) const; - - virtual void draw(QPainter *p, - const QwtScaleMap &xMap, const QwtScaleMap &yMap, - int from, int to) const; - - void draw(int from, int to) const; + virtual void drawSeries( QPainter *, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; - virtual void updateLegend(QwtLegend *) const; + virtual QwtGraphic legendIcon( int index, const QSizeF & ) const; protected: void init(); - virtual void drawCurve(QPainter *p, int style, - const QwtScaleMap &xMap, const QwtScaleMap &yMap, - int from, int to) const; - - virtual void drawSymbols(QPainter *p, const QwtSymbol &, - const QwtScaleMap &xMap, const QwtScaleMap &yMap, - int from, int to) const; - - void drawLines(QPainter *p, - const QwtScaleMap &xMap, const QwtScaleMap &yMap, - int from, int to) const; - void drawSticks(QPainter *p, - const QwtScaleMap &xMap, const QwtScaleMap &yMap, - int from, int to) const; - void drawDots(QPainter *p, - const QwtScaleMap &xMap, const QwtScaleMap &yMap, - int from, int to) const; - void drawSteps(QPainter *p, - const QwtScaleMap &xMap, const QwtScaleMap &yMap, - int from, int to) const; - - void fillCurve(QPainter *, - const QwtScaleMap &, const QwtScaleMap &, - QwtPolygon &) const; - void closePolyline(const QwtScaleMap &, const QwtScaleMap &, - QwtPolygon &) const; + virtual void drawCurve( QPainter *p, int style, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; -private: - QwtData *d_xy; + virtual void drawSymbols( QPainter *p, const QwtSymbol &, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + + virtual void drawLines( QPainter *p, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + + virtual void drawSticks( QPainter *p, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + + virtual void drawDots( QPainter *p, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + virtual void drawSteps( QPainter *p, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + + virtual void fillCurve( QPainter *, + const QwtScaleMap &, const QwtScaleMap &, + const QRectF &canvasRect, QPolygonF & ) const; + + void closePolyline( QPainter *, + const QwtScaleMap &, const QwtScaleMap &, QPolygonF & ) const; + +private: class PrivateData; PrivateData *d_data; }; -//! \return the the curve data -inline QwtData &QwtPlotCurve::data() +//! boundingRect().left() +inline double QwtPlotCurve::minXValue() const { - return *d_xy; + return boundingRect().left(); } -//! \return the the curve data -inline const QwtData &QwtPlotCurve::data() const +//! boundingRect().right() +inline double QwtPlotCurve::maxXValue() const { - return *d_xy; + return boundingRect().right(); } -/*! - \param i index - \return x-value at position i -*/ -inline double QwtPlotCurve::x(int i) const +//! boundingRect().top() +inline double QwtPlotCurve::minYValue() const { - return d_xy->x(i); + return boundingRect().top(); } -/*! - \param i index - \return y-value at position i -*/ -inline double QwtPlotCurve::y(int i) const +//! boundingRect().bottom() +inline double QwtPlotCurve::maxYValue() const { - return d_xy->y(i); + return boundingRect().bottom(); } +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotCurve::PaintAttributes ) +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotCurve::LegendAttributes ) +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotCurve::CurveAttributes ) + #endif diff --git a/libs/qwt/qwt_plot_dict.cpp b/libs/qwt/qwt_plot_dict.cpp index 2de1b4826..17c61ed47 100644 --- a/libs/qwt/qwt_plot_dict.cpp +++ b/libs/qwt/qwt_plot_dict.cpp @@ -7,72 +7,52 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ -// vim: expandtab - #include "qwt_plot_dict.h" class QwtPlotDict::PrivateData { public: -#if QT_VERSION < 0x040000 - class ItemList: public QValueList -#else class ItemList: public QList -#endif { public: - void insertItem(QwtPlotItem *item) { + void insertItem( QwtPlotItem *item ) + { if ( item == NULL ) return; - // Unfortunately there is no inSort operation - // for lists in Qt4. The implementation below - // is slow, but there shouldn't be many plot items. - -#ifdef __GNUC__ -#endif - -#if QT_VERSION < 0x040000 - QValueListIterator it; -#else - QList::Iterator it; -#endif - for ( it = begin(); it != end(); ++it ) { - if ( *it == item ) - return; - - if ( (*it)->z() > item->z() ) { - insert(it, item); - return; - } - } - append(item); + QList::iterator it = + qUpperBound( begin(), end(), item, LessZThan() ); + insert( it, item ); } - void removeItem(QwtPlotItem *item) { + void removeItem( QwtPlotItem *item ) + { if ( item == NULL ) return; - int i = 0; - -#if QT_VERSION < 0x040000 - QValueListIterator it; -#else - QList::Iterator it; -#endif - for ( it = begin(); it != end(); ++it ) { - if ( item == *it ) { -#if QT_VERSION < 0x040000 - remove(it); -#else - removeAt(i); -#endif - return; + QList::iterator it = + qLowerBound( begin(), end(), item, LessZThan() ); + + for ( ; it != end(); ++it ) + { + if ( item == *it ) + { + erase( it ); + break; } - i++; } } + private: + class LessZThan + { + public: + inline bool operator()( const QwtPlotItem *item1, + const QwtPlotItem *item2 ) const + { + return item1->z() < item2->z(); + } + }; }; ItemList itemList; @@ -83,7 +63,7 @@ public: Constructor Auto deletion is enabled. - \sa setAutoDelete, attachItem + \sa setAutoDelete(), QwtPlotItem::attach() */ QwtPlotDict::QwtPlotDict() { @@ -94,12 +74,12 @@ QwtPlotDict::QwtPlotDict() /*! Destructor - If autoDelete is on, all attached items will be deleted - \sa setAutoDelete, autoDelete, attachItem + If autoDelete() is on, all attached items will be deleted + \sa setAutoDelete(), autoDelete(), QwtPlotItem::attach() */ QwtPlotDict::~QwtPlotDict() { - detachItems(QwtPlotItem::Rtti_PlotItem, d_data->autoDelete); + detachItems( QwtPlotItem::Rtti_PlotItem, d_data->autoDelete ); delete d_data; } @@ -109,16 +89,16 @@ QwtPlotDict::~QwtPlotDict() If Auto deletion is on all attached plot items will be deleted in the destructor of QwtPlotDict. The default value is on. - \sa autoDelete, attachItem + \sa autoDelete(), insertItem() */ -void QwtPlotDict::setAutoDelete(bool autoDelete) +void QwtPlotDict::setAutoDelete( bool autoDelete ) { d_data->autoDelete = autoDelete; } /*! \return true if auto deletion is enabled - \sa setAutoDelete, attachItem + \sa setAutoDelete(), insertItem() */ bool QwtPlotDict::autoDelete() const { @@ -126,23 +106,25 @@ bool QwtPlotDict::autoDelete() const } /*! - Attach/Detach a plot item + Insert a plot item - Attached items will be deleted in the destructor, - if auto deletion is enabled (default). Manually detached - items are not deleted. + \param item PlotItem + \sa removeItem() + */ +void QwtPlotDict::insertItem( QwtPlotItem *item ) +{ + d_data->itemList.insertItem( item ); +} - \param item Plot item to attach/detach - \ on If true attach, else detach the item +/*! + Remove a plot item - \sa setAutoDelete, ~QwtPlotDict -*/ -void QwtPlotDict::attachItem(QwtPlotItem *item, bool on) + \param item PlotItem + \sa insertItem() + */ +void QwtPlotDict::removeItem( QwtPlotItem *item ) { - if ( on ) - d_data->itemList.insertItem(item); - else - d_data->itemList.removeItem(item); + d_data->itemList.removeItem( item ); } /*! @@ -152,30 +134,58 @@ void QwtPlotDict::attachItem(QwtPlotItem *item, bool on) otherwise only those items of the type rtti. \param autoDelete If true, delete all detached items */ -void QwtPlotDict::detachItems(int rtti, bool autoDelete) +void QwtPlotDict::detachItems( int rtti, bool autoDelete ) { PrivateData::ItemList list = d_data->itemList; QwtPlotItemIterator it = list.begin(); - while ( it != list.end() ) { + while ( it != list.end() ) + { QwtPlotItem *item = *it; ++it; // increment before removing item from the list - if ( rtti == QwtPlotItem::Rtti_PlotItem || item->rtti() == rtti ) { - item->attach(NULL); + if ( rtti == QwtPlotItem::Rtti_PlotItem || item->rtti() == rtti ) + { + item->attach( NULL ); if ( autoDelete ) delete item; } } } -//! \brief A QwtPlotItemList of all attached plot items. -/// -/// Use caution when iterating these lists, as removing/detaching an item will -/// invalidate the iterator. Instead you can place pointers to objects to be -/// removed in a removal list, and traverse that list later. -//! \return List of all attached plot items. +/*! + \brief A QwtPlotItemList of all attached plot items. + + Use caution when iterating these lists, as removing/detaching an item will + invalidate the iterator. Instead you can place pointers to objects to be + removed in a removal list, and traverse that list later. + + \return List of all attached plot items. +*/ const QwtPlotItemList &QwtPlotDict::itemList() const { return d_data->itemList; } + +/*! + \return List of all attached plot items of a specific type. + \param rtti See QwtPlotItem::RttiValues + \sa QwtPlotItem::rtti() +*/ +QwtPlotItemList QwtPlotDict::itemList( int rtti ) const +{ + if ( rtti == QwtPlotItem::Rtti_PlotItem ) + return d_data->itemList; + + QwtPlotItemList items; + + PrivateData::ItemList list = d_data->itemList; + for ( QwtPlotItemIterator it = list.begin(); it != list.end(); ++it ) + { + QwtPlotItem *item = *it; + if ( item->rtti() == rtti ) + items += item; + } + + return items; +} diff --git a/libs/qwt/qwt_plot_dict.h b/libs/qwt/qwt_plot_dict.h index f5640ad6b..5d34f0c05 100644 --- a/libs/qwt/qwt_plot_dict.h +++ b/libs/qwt/qwt_plot_dict.h @@ -7,28 +7,18 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ -// vim: expandtab - /*! \file !*/ #ifndef QWT_PLOT_DICT #define QWT_PLOT_DICT #include "qwt_global.h" #include "qwt_plot_item.h" - -#if QT_VERSION < 0x040000 -#include -typedef QValueListConstIterator QwtPlotItemIterator; -/// \var typedef QValueList< QwtPlotItem *> QwtPlotItemList -/// \brief See QT 3.x assistant documentation for QValueList -typedef QValueList QwtPlotItemList; -#else #include -typedef QList::ConstIterator QwtPlotItemIterator; + /// \var typedef QList< QwtPlotItem *> QwtPlotItemList /// \brief See QT 4.x assistant documentation for QList typedef QList QwtPlotItemList; -#endif +typedef QList::ConstIterator QwtPlotItemIterator; /*! \brief A dictionary for plot items @@ -36,6 +26,8 @@ typedef QList QwtPlotItemList; QwtPlotDict organizes plot items in increasing z-order. If autoDelete() is enabled, all attached items will be deleted in the destructor of the dictionary. + QwtPlotDict can be used to get access to all QwtPlotItem items - or all + items of a specific type - that are currently on the plot. \sa QwtPlotItem::attach(), QwtPlotItem::detach(), QwtPlotItem::z() */ @@ -43,21 +35,22 @@ class QWT_EXPORT QwtPlotDict { public: explicit QwtPlotDict(); - ~QwtPlotDict(); + virtual ~QwtPlotDict(); - void setAutoDelete(bool); + void setAutoDelete( bool ); bool autoDelete() const; const QwtPlotItemList& itemList() const; + QwtPlotItemList itemList( int rtti ) const; - void detachItems(int rtti = QwtPlotItem::Rtti_PlotItem, - bool autoDelete = true); - -private: - friend class QwtPlotItem; + void detachItems( int rtti = QwtPlotItem::Rtti_PlotItem, + bool autoDelete = true ); - void attachItem(QwtPlotItem *, bool); +protected: + void insertItem( QwtPlotItem * ); + void removeItem( QwtPlotItem * ); +private: class PrivateData; PrivateData *d_data; }; diff --git a/libs/qwt/qwt_plot_directpainter.cpp b/libs/qwt/qwt_plot_directpainter.cpp new file mode 100644 index 000000000..d78352740 --- /dev/null +++ b/libs/qwt/qwt_plot_directpainter.cpp @@ -0,0 +1,317 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_plot_directpainter.h" +#include "qwt_scale_map.h" +#include "qwt_plot.h" +#include "qwt_plot_canvas.h" +#include "qwt_plot_seriesitem.h" +#include +#include +#include +#include + +static inline void qwtRenderItem( + QPainter *painter, const QRect &canvasRect, + QwtPlotSeriesItem *seriesItem, int from, int to ) +{ + // A minor performance improvement is possible + // with caching the maps. TODO ... + + QwtPlot *plot = seriesItem->plot(); + const QwtScaleMap xMap = plot->canvasMap( seriesItem->xAxis() ); + const QwtScaleMap yMap = plot->canvasMap( seriesItem->yAxis() ); + + painter->setRenderHint( QPainter::Antialiasing, + seriesItem->testRenderHint( QwtPlotItem::RenderAntialiased ) ); + seriesItem->drawSeries( painter, xMap, yMap, canvasRect, from, to ); +} + +static inline bool qwtHasBackingStore( const QwtPlotCanvas *canvas ) +{ + return canvas->testPaintAttribute( QwtPlotCanvas::BackingStore ) + && canvas->backingStore() && !canvas->backingStore()->isNull(); +} + +class QwtPlotDirectPainter::PrivateData +{ +public: + PrivateData(): + attributes( 0 ), + hasClipping(false), + seriesItem( NULL ) + { + } + + QwtPlotDirectPainter::Attributes attributes; + + bool hasClipping; + QRegion clipRegion; + + QPainter painter; + + QwtPlotSeriesItem *seriesItem; + int from; + int to; +}; + +//! Constructor +QwtPlotDirectPainter::QwtPlotDirectPainter( QObject *parent ): + QObject( parent ) +{ + d_data = new PrivateData; +} + +//! Destructor +QwtPlotDirectPainter::~QwtPlotDirectPainter() +{ + delete d_data; +} + +/*! + Change an attribute + + \param attribute Attribute to change + \param on On/Off + + \sa Attribute, testAttribute() +*/ +void QwtPlotDirectPainter::setAttribute( Attribute attribute, bool on ) +{ + if ( bool( d_data->attributes & attribute ) != on ) + { + if ( on ) + d_data->attributes |= attribute; + else + d_data->attributes &= ~attribute; + + if ( ( attribute == AtomicPainter ) && on ) + reset(); + } +} + +/*! + \return True, when attribute is enabled + \param attribute Attribute to be tested + \sa Attribute, setAttribute() +*/ +bool QwtPlotDirectPainter::testAttribute( Attribute attribute ) const +{ + return d_data->attributes & attribute; +} + +/*! + En/Disables clipping + + \param enable Enables clipping is true, disable it otherwise + \sa hasClipping(), clipRegion(), setClipRegion() +*/ +void QwtPlotDirectPainter::setClipping( bool enable ) +{ + d_data->hasClipping = enable; +} + +/*! + \return true, when clipping is enabled + \sa setClipping(), clipRegion(), setClipRegion() +*/ +bool QwtPlotDirectPainter::hasClipping() const +{ + return d_data->hasClipping; +} + +/*! + \brief Assign a clip region and enable clipping + + Depending on the environment setting a proper clip region might improve + the performance heavily. F.e. on Qt embedded only the clipped part of + the backing store will be copied to a ( maybe unaccelerated ) frame buffer + device. + + \param region Clip region + \sa clipRegion(), hasClipping(), setClipping() +*/ +void QwtPlotDirectPainter::setClipRegion( const QRegion ®ion ) +{ + d_data->clipRegion = region; + d_data->hasClipping = true; +} + +/*! + \return Currently set clip region. + \sa setClipRegion(), setClipping(), hasClipping() +*/ +QRegion QwtPlotDirectPainter::clipRegion() const +{ + return d_data->clipRegion; +} + +/*! + \brief Draw a set of points of a seriesItem. + + When observing an measurement while it is running, new points have to be + added to an existing seriesItem. drawSeries() can be used to display them avoiding + a complete redraw of the canvas. + + Setting plot()->canvas()->setAttribute(Qt::WA_PaintOutsidePaintEvent, true); + will result in faster painting, if the paint engine of the canvas widget + supports this feature. + + \param seriesItem Item to be painted + \param from Index of the first point to be painted + \param to Index of the last point to be painted. If to < 0 the + series will be painted to its last point. +*/ +void QwtPlotDirectPainter::drawSeries( + QwtPlotSeriesItem *seriesItem, int from, int to ) +{ + if ( seriesItem == NULL || seriesItem->plot() == NULL ) + return; + + QWidget *canvas = seriesItem->plot()->canvas(); + const QRect canvasRect = canvas->contentsRect(); + + QwtPlotCanvas *plotCanvas = qobject_cast( canvas ); + + if ( plotCanvas && qwtHasBackingStore( plotCanvas ) ) + { + QPainter painter( const_cast( plotCanvas->backingStore() ) ); + + if ( d_data->hasClipping ) + painter.setClipRegion( d_data->clipRegion ); + + qwtRenderItem( &painter, canvasRect, seriesItem, from, to ); + + if ( testAttribute( QwtPlotDirectPainter::FullRepaint ) ) + { + plotCanvas->repaint(); + return; + } + } + + bool immediatePaint = true; + if ( !canvas->testAttribute( Qt::WA_WState_InPaintEvent ) ) + { +#if QT_VERSION < 0x050000 + if ( !canvas->testAttribute( Qt::WA_PaintOutsidePaintEvent ) ) +#endif + immediatePaint = false; + } + + if ( immediatePaint ) + { + if ( !d_data->painter.isActive() ) + { + reset(); + + d_data->painter.begin( canvas ); + canvas->installEventFilter( this ); + } + + if ( d_data->hasClipping ) + { + d_data->painter.setClipRegion( + QRegion( canvasRect ) & d_data->clipRegion ); + } + else + { + if ( !d_data->painter.hasClipping() ) + d_data->painter.setClipRect( canvasRect ); + } + + qwtRenderItem( &d_data->painter, canvasRect, seriesItem, from, to ); + + if ( d_data->attributes & QwtPlotDirectPainter::AtomicPainter ) + { + reset(); + } + else + { + if ( d_data->hasClipping ) + d_data->painter.setClipping( false ); + } + } + else + { + reset(); + + d_data->seriesItem = seriesItem; + d_data->from = from; + d_data->to = to; + + QRegion clipRegion = canvasRect; + if ( d_data->hasClipping ) + clipRegion &= d_data->clipRegion; + + canvas->installEventFilter( this ); + canvas->repaint(clipRegion); + canvas->removeEventFilter( this ); + + d_data->seriesItem = NULL; + } +} + +//! Close the internal QPainter +void QwtPlotDirectPainter::reset() +{ + if ( d_data->painter.isActive() ) + { + QWidget *w = static_cast( d_data->painter.device() ); + if ( w ) + w->removeEventFilter( this ); + + d_data->painter.end(); + } +} + +//! Event filter +bool QwtPlotDirectPainter::eventFilter( QObject *, QEvent *event ) +{ + if ( event->type() == QEvent::Paint ) + { + reset(); + + if ( d_data->seriesItem ) + { + const QPaintEvent *pe = static_cast< QPaintEvent *>( event ); + + QWidget *canvas = d_data->seriesItem->plot()->canvas(); + + QPainter painter( canvas ); + painter.setClipRegion( pe->region() ); + + bool doCopyCache = testAttribute( CopyBackingStore ); + + if ( doCopyCache ) + { + QwtPlotCanvas *plotCanvas = + qobject_cast( canvas ); + if ( plotCanvas ) + { + doCopyCache = qwtHasBackingStore( plotCanvas ); + if ( doCopyCache ) + { + painter.drawPixmap( plotCanvas->contentsRect().topLeft(), + *plotCanvas->backingStore() ); + } + } + } + + if ( !doCopyCache ) + { + qwtRenderItem( &painter, canvas->contentsRect(), + d_data->seriesItem, d_data->from, d_data->to ); + } + + return true; // don't call QwtPlotCanvas::paintEvent() + } + } + + return false; +} diff --git a/libs/qwt/qwt_plot_directpainter.h b/libs/qwt/qwt_plot_directpainter.h new file mode 100644 index 000000000..b555c87c3 --- /dev/null +++ b/libs/qwt/qwt_plot_directpainter.h @@ -0,0 +1,100 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_PLOT_DIRECT_PAINTER_H +#define QWT_PLOT_DIRECT_PAINTER_H + +#include "qwt_global.h" +#include + +class QRegion; +class QwtPlotSeriesItem; + +/*! + \brief Painter object trying to paint incrementally + + Often applications want to display samples while they are + collected. When there are too many samples complete replots + will be expensive to be processed in a collection cycle. + + QwtPlotDirectPainter offers an API to paint + subsets ( f.e all additions points ) without erasing/repainting + the plot canvas. + + On certain environments it might be important to calculate a proper + clip region before painting. F.e. for Qt Embedded only the clipped part + of the backing store will be copied to a ( maybe unaccelerated ) + frame buffer. + + \warning Incremental painting will only help when no replot is triggered + by another operation ( like changing scales ) and nothing needs + to be erased. +*/ +class QWT_EXPORT QwtPlotDirectPainter: public QObject +{ +public: + /*! + \brief Paint attributes + \sa setAttribute(), testAttribute(), drawSeries() + */ + enum Attribute + { + /*! + Initializing a QPainter is an expensive operation. + When AtomicPainter is set each call of drawSeries() opens/closes + a temporary QPainter. Otherwise QwtPlotDirectPainter tries to + use the same QPainter as long as possible. + */ + AtomicPainter = 0x01, + + /*! + When FullRepaint is set the plot canvas is explicitly repainted + after the samples have been rendered. + */ + FullRepaint = 0x02, + + /*! + When QwtPlotCanvas::BackingStore is enabled the painter + has to paint to the backing store and the widget. In certain + situations/environments it might be faster to paint to + the backing store only and then copy the backing store to the canvas. + This flag can also be useful for settings, where Qt fills the + the clip region with the widget background. + */ + CopyBackingStore = 0x04 + }; + + //! Paint attributes + typedef QFlags Attributes; + + QwtPlotDirectPainter( QObject *parent = NULL ); + virtual ~QwtPlotDirectPainter(); + + void setAttribute( Attribute, bool on ); + bool testAttribute( Attribute ) const; + + void setClipping( bool ); + bool hasClipping() const; + + void setClipRegion( const QRegion & ); + QRegion clipRegion() const; + + void drawSeries( QwtPlotSeriesItem *, int from, int to ); + void reset(); + + virtual bool eventFilter( QObject *, QEvent * ); + +private: + class PrivateData; + PrivateData *d_data; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotDirectPainter::Attributes ) + +#endif diff --git a/libs/qwt/qwt_plot_glcanvas.cpp b/libs/qwt/qwt_plot_glcanvas.cpp new file mode 100644 index 000000000..87a4cfde7 --- /dev/null +++ b/libs/qwt/qwt_plot_glcanvas.cpp @@ -0,0 +1,357 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_plot_glcanvas.h" +#include "qwt_plot.h" +#include +#include +#include +#include +#include +#include "qwt_painter.h" + +static QWidget *qwtBGWidget( QWidget *widget ) +{ + QWidget *w = widget; + + for ( ; w->parentWidget() != NULL; w = w->parentWidget() ) + { + if ( w->autoFillBackground() || + w->testAttribute( Qt::WA_StyledBackground ) ) + { + return w; + } + } + + return w; +} + +static void qwtUpdateContentsRect( QwtPlotGLCanvas *canvas ) +{ + const int fw = canvas->frameWidth(); + canvas->setContentsMargins( fw, fw, fw, fw ); +} + +class QwtPlotGLCanvas::PrivateData +{ +public: + PrivateData(): + frameStyle( QFrame::Panel | QFrame::Sunken), + lineWidth( 2 ), + midLineWidth( 0 ) + { + } + + int frameStyle; + int lineWidth; + int midLineWidth; +}; + +/*! + \brief Constructor + + \param plot Parent plot widget + \sa QwtPlot::setCanvas() +*/ +QwtPlotGLCanvas::QwtPlotGLCanvas( QwtPlot *plot ): + QGLWidget( plot ) +{ + d_data = new PrivateData; + +#ifndef QT_NO_CURSOR + setCursor( Qt::CrossCursor ); +#endif + + setAutoFillBackground( true ); + qwtUpdateContentsRect( this ); +} + +//! Destructor +QwtPlotGLCanvas::~QwtPlotGLCanvas() +{ + delete d_data; +} + +/*! + Set the frame style + + \param style The bitwise OR between a shape and a shadow. + + \sa frameStyle(), QFrame::setFrameStyle(), + setFrameShadow(), setFrameShape() + */ +void QwtPlotGLCanvas::setFrameStyle( int style ) +{ + if ( style != d_data->frameStyle ) + { + d_data->frameStyle = style; + qwtUpdateContentsRect( this ); + + update(); + } +} + +/*! + \return The bitwise OR between a frameShape() and a frameShadow() + \sa setFrameStyle(), QFrame::frameStyle() + */ +int QwtPlotGLCanvas::frameStyle() const +{ + return d_data->frameStyle; +} + +/*! + Set the frame shadow + + \param shadow Frame shadow + \sa frameShadow(), setFrameShape(), QFrame::setFrameShadow() + */ +void QwtPlotGLCanvas::setFrameShadow( Shadow shadow ) +{ + setFrameStyle(( d_data->frameStyle & QFrame::Shape_Mask ) | shadow ); +} + +/*! + \return Frame shadow + \sa setFrameShadow(), QFrame::setFrameShadow() + */ +QwtPlotGLCanvas::Shadow QwtPlotGLCanvas::frameShadow() const +{ + return (Shadow) ( d_data->frameStyle & QFrame::Shadow_Mask ); +} + +/*! + Set the frame shape + + \param shape Frame shape + \sa frameShape(), setFrameShadow(), QFrame::frameShape() + */ +void QwtPlotGLCanvas::setFrameShape( Shape shape ) +{ + setFrameStyle( ( d_data->frameStyle & QFrame::Shadow_Mask ) | shape ); +} + +/*! + \return Frame shape + \sa setFrameShape(), QFrame::frameShape() + */ +QwtPlotGLCanvas::Shape QwtPlotGLCanvas::frameShape() const +{ + return (Shape) ( d_data->frameStyle & QFrame::Shape_Mask ); +} + +/*! + Set the frame line width + + The default line width is 2 pixels. + + \param width Line width of the frame + \sa lineWidth(), setMidLineWidth() +*/ +void QwtPlotGLCanvas::setLineWidth( int width ) +{ + width = qMax( width, 0 ); + if ( width != d_data->lineWidth ) + { + d_data->lineWidth = qMax( width, 0 ); + qwtUpdateContentsRect( this ); + update(); + } +} + +/*! + \return Line width of the frame + \sa setLineWidth(), midLineWidth() + */ +int QwtPlotGLCanvas::lineWidth() const +{ + return d_data->lineWidth; +} + +/*! + Set the frame mid line width + + The default midline width is 0 pixels. + + \param width Midline width of the frame + \sa midLineWidth(), setLineWidth() +*/ +void QwtPlotGLCanvas::setMidLineWidth( int width ) +{ + width = qMax( width, 0 ); + if ( width != d_data->midLineWidth ) + { + d_data->midLineWidth = width; + qwtUpdateContentsRect( this ); + update(); + } +} + +/*! + \return Midline width of the frame + \sa setMidLineWidth(), lineWidth() + */ +int QwtPlotGLCanvas::midLineWidth() const +{ + return d_data->midLineWidth; +} + +/*! + \return Frame width depending on the style, line width and midline width. + */ +int QwtPlotGLCanvas::frameWidth() const +{ + return ( frameStyle() != NoFrame ) ? d_data->lineWidth : 0; +} + +/*! + Paint event + + \param event Paint event + \sa QwtPlot::drawCanvas() +*/ +void QwtPlotGLCanvas::paintEvent( QPaintEvent *event ) +{ + Q_UNUSED( event ); + + QPainter painter( this ); + + drawBackground( &painter ); + drawItems( &painter ); + + if ( !testAttribute( Qt::WA_StyledBackground ) ) + { + if ( frameWidth() > 0 ) + drawBorder( &painter ); + } +} +/*! + Qt event handler for QEvent::PolishRequest and QEvent::StyleChange + \param event Qt Event + \return See QGLWidget::event() +*/ +bool QwtPlotGLCanvas::event( QEvent *event ) +{ + const bool ok = QGLWidget::event( event ); + + if ( event->type() == QEvent::PolishRequest || + event->type() == QEvent::StyleChange ) + { + // assuming, that we always have a styled background + // when we have a style sheet + + setAttribute( Qt::WA_StyledBackground, + testAttribute( Qt::WA_StyleSheet ) ); + } + + return ok; +} + +/*! + Draw the plot items + \param painter Painter + + \sa QwtPlot::drawCanvas() +*/ +void QwtPlotGLCanvas::drawItems( QPainter *painter ) +{ + painter->save(); + + painter->setClipRect( contentsRect(), Qt::IntersectClip ); + + QwtPlot *plot = qobject_cast< QwtPlot *>( parent() ); + if ( plot ) + plot->drawCanvas( painter ); + + painter->restore(); +} + +/*! + Draw the background of the canvas + \param painter Painter +*/ +void QwtPlotGLCanvas::drawBackground( QPainter *painter ) +{ + painter->save(); + + QWidget *w = qwtBGWidget( this ); + + const QPoint off = mapTo( w, QPoint() ); + painter->translate( -off ); + + const QRect fillRect = rect().translated( off ); + + if ( w->testAttribute( Qt::WA_StyledBackground ) ) + { + painter->setClipRect( fillRect ); + + QStyleOption opt; + opt.initFrom( w ); + w->style()->drawPrimitive( QStyle::PE_Widget, &opt, painter, w); + } + else + { + painter->fillRect( fillRect, + w->palette().brush( w->backgroundRole() ) ); + } + + painter->restore(); +} + +/*! + Draw the border of the canvas + \param painter Painter +*/ +void QwtPlotGLCanvas::drawBorder( QPainter *painter ) +{ + const int fw = frameWidth(); + if ( fw <= 0 ) + return; + + if ( frameShadow() == QwtPlotGLCanvas::Plain ) + { + qDrawPlainRect( painter, frameRect(), + palette().shadow().color(), lineWidth() ); + } + else + { + if ( frameShape() == QwtPlotGLCanvas::Box ) + { + qDrawShadeRect( painter, frameRect(), palette(), + frameShadow() == Sunken, lineWidth(), midLineWidth() ); + } + else + { + qDrawShadePanel( painter, frameRect(), palette(), + frameShadow() == Sunken, lineWidth() ); + } + } +} + +//! Calls repaint() +void QwtPlotGLCanvas::replot() +{ + repaint(); +} + +/*! + \return Empty path +*/ +QPainterPath QwtPlotGLCanvas::borderPath( const QRect &rect ) const +{ + Q_UNUSED( rect ); + return QPainterPath(); +} + +//! \return The rectangle where the frame is drawn in. +QRect QwtPlotGLCanvas::frameRect() const +{ + const int fw = frameWidth(); + return contentsRect().adjusted( -fw, -fw, fw, fw ); +} diff --git a/libs/qwt/qwt_plot_glcanvas.h b/libs/qwt/qwt_plot_glcanvas.h new file mode 100644 index 000000000..2ff1cf2e3 --- /dev/null +++ b/libs/qwt/qwt_plot_glcanvas.h @@ -0,0 +1,136 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_PLOT_GLCANVAS_H +#define QWT_PLOT_GLCANVAS_H + +#include "qwt_global.h" +#include +#include + +class QwtPlot; + +/*! + \brief An alternative canvas for a QwtPlot derived from QGLWidget + + QwtPlotGLCanvas implements the very basics to act as canvas + inside of a QwtPlot widget. It might be extended to a full + featured alternative to QwtPlotCanvas in a future version of Qwt. + + Even if QwtPlotGLCanvas is not derived from QFrame it imitates + its API. When using style sheets it supports the box model - beside + backgrounds with rounded borders. + + \sa QwtPlot::setCanvas(), QwtPlotCanvas + + \note You might want to use the QPaintEngine::OpenGL paint engine + ( see QGL::setPreferredPaintEngine() ). On a Linux test system + QPaintEngine::OpenGL2 shows very basic problems ( wrong + geometries of rectangles ) but also more advanced stuff + like antialiasing doesn't work. + + \note Another way to introduce OpenGL rendering to Qwt + is to use QGLPixelBuffer or QGLFramebufferObject. Both + type of buffers can be converted into a QImage and + used in combination with a regular QwtPlotCanvas. +*/ +class QWT_EXPORT QwtPlotGLCanvas: public QGLWidget +{ + Q_OBJECT + + Q_ENUMS( Shape Shadow ) + + Q_PROPERTY( Shadow frameShadow READ frameShadow WRITE setFrameShadow ) + Q_PROPERTY( Shape frameShape READ frameShape WRITE setFrameShape ) + Q_PROPERTY( int lineWidth READ lineWidth WRITE setLineWidth ) + Q_PROPERTY( int midLineWidth READ midLineWidth WRITE setMidLineWidth ) + Q_PROPERTY( int frameWidth READ frameWidth ) + Q_PROPERTY( QRect frameRect READ frameRect DESIGNABLE false ) + +public: + /*! + \brief Frame shadow + + Unfortunately it is not possible to use QFrame::Shadow + as a property of a widget that is not derived from QFrame. + The following enum is made for the designer only. It is safe + to use QFrame::Shadow instead. + */ + enum Shadow + { + //! QFrame::Plain + Plain = QFrame::Plain, + + //! QFrame::Raised + Raised = QFrame::Raised, + + //! QFrame::Sunken + Sunken = QFrame::Sunken + }; + + /*! + \brief Frame shape + + Unfortunately it is not possible to use QFrame::Shape + as a property of a widget that is not derived from QFrame. + The following enum is made for the designer only. It is safe + to use QFrame::Shadow instead. + + \note QFrame::StyledPanel and QFrame::WinPanel are unsuported + and will be displayed as QFrame::Panel. + */ + enum Shape + { + NoFrame = QFrame::NoFrame, + + Box = QFrame::Box, + Panel = QFrame::Panel + }; + + explicit QwtPlotGLCanvas( QwtPlot * = NULL ); + virtual ~QwtPlotGLCanvas(); + + void setFrameStyle( int style ); + int frameStyle() const; + + void setFrameShadow( Shadow ); + Shadow frameShadow() const; + + void setFrameShape( Shape ); + Shape frameShape() const; + + void setLineWidth( int ); + int lineWidth() const; + + void setMidLineWidth( int ); + int midLineWidth() const; + + int frameWidth() const; + QRect frameRect() const; + + Q_INVOKABLE QPainterPath borderPath( const QRect & ) const; + + virtual bool event( QEvent * ); + +public Q_SLOTS: + void replot(); + +protected: + virtual void paintEvent( QPaintEvent * ); + + virtual void drawBackground( QPainter * ); + virtual void drawBorder( QPainter * ); + virtual void drawItems( QPainter * ); + +private: + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/libs/qwt/qwt_plot_grid.cpp b/libs/qwt/qwt_plot_grid.cpp index 0d512b425..4375e53d7 100644 --- a/libs/qwt/qwt_plot_grid.cpp +++ b/libs/qwt/qwt_plot_grid.cpp @@ -7,22 +7,24 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ -#include -#include +#include "qwt_plot_grid.h" #include "qwt_painter.h" #include "qwt_text.h" #include "qwt_scale_map.h" #include "qwt_scale_div.h" -#include "qwt_plot_grid.h" +#include "qwt_math.h" +#include +#include class QwtPlotGrid::PrivateData { public: PrivateData(): - xEnabled(true), - yEnabled(true), - xMinEnabled(false), - yMinEnabled(false) { + xEnabled( true ), + yEnabled( true ), + xMinEnabled( false ), + yMinEnabled( false ) + { } bool xEnabled; @@ -33,16 +35,18 @@ public: QwtScaleDiv xScaleDiv; QwtScaleDiv yScaleDiv; - QPen majPen; - QPen minPen; + QPen majorPen; + QPen minorPen; }; //! Enables major grid, disables minor grid QwtPlotGrid::QwtPlotGrid(): - QwtPlotItem(QwtText("Grid")) + QwtPlotItem( QwtText( "Grid" ) ) { d_data = new PrivateData; - setZ(10.0); + + setItemInterest( QwtPlotItem::ScaleInterest, true ); + setZ( 10.0 ); } //! Destructor @@ -58,122 +62,201 @@ int QwtPlotGrid::rtti() const } /*! - \brief Enable or disable vertical gridlines - \param tf Enable (true) or disable + \brief Enable or disable vertical grid lines + \param on Enable (true) or disable - \sa Minor gridlines can be enabled or disabled with + \sa Minor grid lines can be enabled or disabled with enableXMin() */ -void QwtPlotGrid::enableX(bool tf) +void QwtPlotGrid::enableX( bool on ) { - if ( d_data->xEnabled != tf ) { - d_data->xEnabled = tf; + if ( d_data->xEnabled != on ) + { + d_data->xEnabled = on; + + legendChanged(); itemChanged(); } } /*! - \brief Enable or disable horizontal gridlines - \param tf Enable (true) or disable - \sa Minor gridlines can be enabled or disabled with enableYMin() + \brief Enable or disable horizontal grid lines + \param on Enable (true) or disable + \sa Minor grid lines can be enabled or disabled with enableYMin() */ -void QwtPlotGrid::enableY(bool tf) +void QwtPlotGrid::enableY( bool on ) { - if ( d_data->yEnabled != tf ) { - d_data->yEnabled = tf; + if ( d_data->yEnabled != on ) + { + d_data->yEnabled = on; + + legendChanged(); itemChanged(); } } /*! - \brief Enable or disable minor vertical gridlines. - \param tf Enable (true) or disable + \brief Enable or disable minor vertical grid lines. + \param on Enable (true) or disable \sa enableX() */ -void QwtPlotGrid::enableXMin(bool tf) +void QwtPlotGrid::enableXMin( bool on ) { - if ( d_data->xMinEnabled != tf ) { - d_data->xMinEnabled = tf; + if ( d_data->xMinEnabled != on ) + { + d_data->xMinEnabled = on; + + legendChanged(); itemChanged(); } } /*! - \brief Enable or disable minor horizontal gridlines - \param tf Enable (true) or disable + \brief Enable or disable minor horizontal grid lines + \param on Enable (true) or disable \sa enableY() */ -void QwtPlotGrid::enableYMin(bool tf) +void QwtPlotGrid::enableYMin( bool on ) { - if ( d_data->yMinEnabled != tf ) { - d_data->yMinEnabled = tf; + if ( d_data->yMinEnabled != on ) + { + d_data->yMinEnabled = on; + + legendChanged(); itemChanged(); } } /*! - \brief Assign an x axis scale division + Assign an x axis scale division + \param scaleDiv Scale division - \warning QwtPlotGrid uses implicit sharing (see Qt Manual) for - the scale divisions. */ -void QwtPlotGrid::setXDiv(const QwtScaleDiv &scaleDiv) +void QwtPlotGrid::setXDiv( const QwtScaleDiv &scaleDiv ) { - if ( d_data->xScaleDiv != scaleDiv ) { + if ( d_data->xScaleDiv != scaleDiv ) + { d_data->xScaleDiv = scaleDiv; itemChanged(); } } /*! - \brief Assign a y axis division - \param sy Scale division - \warning QwtPlotGrid uses implicit sharing (see Qt Manual) for - the scale divisions. + Assign a y axis division + + \param scaleDiv Scale division */ -void QwtPlotGrid::setYDiv(const QwtScaleDiv &sy) +void QwtPlotGrid::setYDiv( const QwtScaleDiv &scaleDiv ) { - if ( d_data->yScaleDiv != sy ) { - d_data->yScaleDiv = sy; + if ( d_data->yScaleDiv != scaleDiv ) + { + d_data->yScaleDiv = scaleDiv; itemChanged(); } } /*! - \brief Assign a pen for both major and minor gridlines - \param p Pen - \sa setMajPen(), setMinPen() + Build and assign a pen for both major and minor grid lines + + In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it + non cosmetic ( see QPen::isCosmetic() ). This method has been introduced + to hide this incompatibility. + + \param color Pen color + \param width Pen width + \param style Pen style + + \sa pen(), brush() + */ +void QwtPlotGrid::setPen( const QColor &color, qreal width, Qt::PenStyle style ) +{ + setPen( QPen( color, width, style ) ); +} + +/*! + Assign a pen for both major and minor grid lines + + \param pen Pen + \sa setMajorPen(), setMinorPen() */ -void QwtPlotGrid::setPen(const QPen &p) +void QwtPlotGrid::setPen( const QPen &pen ) { - if ( d_data->majPen != p || d_data->minPen != p ) { - d_data->majPen = p; - d_data->minPen = p; + if ( d_data->majorPen != pen || d_data->minorPen != pen ) + { + d_data->majorPen = pen; + d_data->minorPen = pen; + + legendChanged(); itemChanged(); } } /*! - \brief Assign a pen for the major gridlines - \param p Pen - \sa majPen(), setMinPen(), setPen() + Build and assign a pen for both major grid lines + + In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it + non cosmetic ( see QPen::isCosmetic() ). This method has been introduced + to hide this incompatibility. + + \param color Pen color + \param width Pen width + \param style Pen style + + \sa pen(), brush() + */ +void QwtPlotGrid::setMajorPen( const QColor &color, qreal width, Qt::PenStyle style ) +{ + setMajorPen( QPen( color, width, style ) ); +} + +/*! + Assign a pen for the major grid lines + + \param pen Pen + \sa majorPen(), setMinorPen(), setPen() */ -void QwtPlotGrid::setMajPen(const QPen &p) +void QwtPlotGrid::setMajorPen( const QPen &pen ) { - if ( d_data->majPen != p ) { - d_data->majPen = p; + if ( d_data->majorPen != pen ) + { + d_data->majorPen = pen; + + legendChanged(); itemChanged(); } } /*! - \brief Assign a pen for the minor gridlines - \param p Pen + Build and assign a pen for the minor grid lines + + In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it + non cosmetic ( see QPen::isCosmetic() ). This method has been introduced + to hide this incompatibility. + + \param color Pen color + \param width Pen width + \param style Pen style + + \sa pen(), brush() + */ +void QwtPlotGrid::setMinorPen( const QColor &color, qreal width, Qt::PenStyle style ) +{ + setMinorPen( QPen( color, width, style ) ); +} + +/*! + Assign a pen for the minor grid lines + + \param pen Pen + \sa minorPen(), setMajorPen(), setPen() */ -void QwtPlotGrid::setMinPen(const QPen &p) +void QwtPlotGrid::setMinorPen( const QPen &pen ) { - if ( d_data->minPen != p ) { - d_data->minPen = p; + if ( d_data->minorPen != pen ) + { + d_data->minorPen = pen; + + legendChanged(); itemChanged(); } } @@ -182,90 +265,116 @@ void QwtPlotGrid::setMinPen(const QPen &p) \brief Draw the grid The grid is drawn into the bounding rectangle such that - gridlines begin and end at the rectangle's borders. The X and Y + grid lines begin and end at the rectangle's borders. The X and Y maps are used to map the scale divisions into the drawing region screen. + \param painter Painter \param xMap X axis map \param yMap Y axis - \param canvasRect Contents rect of the plot canvas + \param canvasRect Contents rectangle of the plot canvas */ -void QwtPlotGrid::draw(QPainter *painter, - const QwtScaleMap &xMap, const QwtScaleMap &yMap, - const QRect &canvasRect) const +void QwtPlotGrid::draw( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect ) const { - // draw minor gridlines - painter->setPen(d_data->minPen); - - if (d_data->xEnabled && d_data->xMinEnabled) { - drawLines(painter, canvasRect, Qt::Vertical, xMap, - d_data->xScaleDiv.ticks(QwtScaleDiv::MinorTick)); - drawLines(painter, canvasRect, Qt::Vertical, xMap, - d_data->xScaleDiv.ticks(QwtScaleDiv::MediumTick)); + // draw minor grid lines + QPen minorPen = d_data->minorPen; + minorPen.setCapStyle( Qt::FlatCap ); + + painter->setPen( minorPen ); + + if ( d_data->xEnabled && d_data->xMinEnabled ) + { + drawLines( painter, canvasRect, Qt::Vertical, xMap, + d_data->xScaleDiv.ticks( QwtScaleDiv::MinorTick ) ); + drawLines( painter, canvasRect, Qt::Vertical, xMap, + d_data->xScaleDiv.ticks( QwtScaleDiv::MediumTick ) ); } - if (d_data->yEnabled && d_data->yMinEnabled) { - drawLines(painter, canvasRect, Qt::Horizontal, yMap, - d_data->yScaleDiv.ticks(QwtScaleDiv::MinorTick)); - drawLines(painter, canvasRect, Qt::Horizontal, yMap, - d_data->yScaleDiv.ticks(QwtScaleDiv::MediumTick)); + if ( d_data->yEnabled && d_data->yMinEnabled ) + { + drawLines( painter, canvasRect, Qt::Horizontal, yMap, + d_data->yScaleDiv.ticks( QwtScaleDiv::MinorTick ) ); + drawLines( painter, canvasRect, Qt::Horizontal, yMap, + d_data->yScaleDiv.ticks( QwtScaleDiv::MediumTick ) ); } - // draw major gridlines - painter->setPen(d_data->majPen); + // draw major grid lines + QPen majorPen = d_data->majorPen; + majorPen.setCapStyle( Qt::FlatCap ); - if (d_data->xEnabled) { - drawLines(painter, canvasRect, Qt::Vertical, xMap, - d_data->xScaleDiv.ticks(QwtScaleDiv::MajorTick)); + painter->setPen( majorPen ); + + if ( d_data->xEnabled ) + { + drawLines( painter, canvasRect, Qt::Vertical, xMap, + d_data->xScaleDiv.ticks( QwtScaleDiv::MajorTick ) ); } - if (d_data->yEnabled) { - drawLines(painter, canvasRect, Qt::Horizontal, yMap, - d_data->yScaleDiv.ticks(QwtScaleDiv::MajorTick)); + if ( d_data->yEnabled ) + { + drawLines( painter, canvasRect, Qt::Horizontal, yMap, + d_data->yScaleDiv.ticks( QwtScaleDiv::MajorTick ) ); } } -void QwtPlotGrid::drawLines(QPainter *painter, const QRect &canvasRect, - Qt::Orientation orientation, const QwtScaleMap &scaleMap, - const QwtValueList &values) const +void QwtPlotGrid::drawLines( QPainter *painter, const QRectF &canvasRect, + Qt::Orientation orientation, const QwtScaleMap &scaleMap, + const QList &values ) const { - const int x1 = canvasRect.left(); - const int x2 = canvasRect.right(); - const int y1 = canvasRect.top(); - const int y2 = canvasRect.bottom(); - - for (uint i = 0; i < (uint)values.count(); i++) { - const int value = scaleMap.transform(values[i]); - if ( orientation == Qt::Horizontal ) { - if ((value >= y1) && (value <= y2)) - QwtPainter::drawLine(painter, x1, value, x2, value); - } else { - if ((value >= x1) && (value <= x2)) - QwtPainter::drawLine(painter, value, y1, value, y2); + const double x1 = canvasRect.left(); + const double x2 = canvasRect.right() - 1.0; + const double y1 = canvasRect.top(); + const double y2 = canvasRect.bottom() - 1.0; + + const bool doAlign = QwtPainter::roundingAlignment( painter ); + + for ( int i = 0; i < values.count(); i++ ) + { + double value = scaleMap.transform( values[i] ); + if ( doAlign ) + value = qRound( value ); + + if ( orientation == Qt::Horizontal ) + { + if ( qwtFuzzyGreaterOrEqual( value, y1 ) && + qwtFuzzyLessOrEqual( value, y2 ) ) + { + QwtPainter::drawLine( painter, x1, value, x2, value ); + } + } + else + { + if ( qwtFuzzyGreaterOrEqual( value, x1 ) && + qwtFuzzyLessOrEqual( value, x2 ) ) + { + QwtPainter::drawLine( painter, value, y1, value, y2 ); + } } } } /*! - \return the pen for the major gridlines - \sa setMajPen(), setMinPen(), setPen() + \return the pen for the major grid lines + \sa setMajorPen(), setMinorPen(), setPen() */ -const QPen &QwtPlotGrid::majPen() const +const QPen &QwtPlotGrid::majorPen() const { - return d_data->majPen; + return d_data->majorPen; } /*! - \return the pen for the minor gridlines - \sa setMinPen(), setMajPen(), setPen() + \return the pen for the minor grid lines + \sa setMinorPen(), setMajorPen(), setPen() */ -const QPen &QwtPlotGrid::minPen() const +const QPen &QwtPlotGrid::minorPen() const { - return d_data->minPen; + return d_data->minorPen; } /*! - \return true if vertical gridlines are enabled + \return true if vertical grid lines are enabled \sa enableX() */ bool QwtPlotGrid::xEnabled() const @@ -274,7 +383,7 @@ bool QwtPlotGrid::xEnabled() const } /*! - \return true if minor vertical gridlines are enabled + \return true if minor vertical grid lines are enabled \sa enableXMin() */ bool QwtPlotGrid::xMinEnabled() const @@ -283,7 +392,7 @@ bool QwtPlotGrid::xMinEnabled() const } /*! - \return true if horizontal gridlines are enabled + \return true if horizontal grid lines are enabled \sa enableY() */ bool QwtPlotGrid::yEnabled() const @@ -292,7 +401,7 @@ bool QwtPlotGrid::yEnabled() const } /*! - \return true if minor horizontal gridlines are enabled + \return true if minor horizontal grid lines are enabled \sa enableYMin() */ bool QwtPlotGrid::yMinEnabled() const @@ -313,9 +422,17 @@ const QwtScaleDiv &QwtPlotGrid::yScaleDiv() const return d_data->yScaleDiv; } -void QwtPlotGrid::updateScaleDiv(const QwtScaleDiv& xDiv, - const QwtScaleDiv& yDiv) +/*! + Update the grid to changes of the axes scale division + + \param xScaleDiv Scale division of the x-axis + \param yScaleDiv Scale division of the y-axis + + \sa QwtPlot::updateAxes() +*/ +void QwtPlotGrid::updateScaleDiv( const QwtScaleDiv& xScaleDiv, + const QwtScaleDiv& yScaleDiv ) { - setXDiv(xDiv); - setYDiv(yDiv); + setXDiv( xScaleDiv ); + setYDiv( yScaleDiv ); } diff --git a/libs/qwt/qwt_plot_grid.h b/libs/qwt/qwt_plot_grid.h index 407035337..16d984c72 100644 --- a/libs/qwt/qwt_plot_grid.h +++ b/libs/qwt/qwt_plot_grid.h @@ -24,7 +24,7 @@ class QwtScaleDiv; The QwtPlotGrid class can be used to draw a coordinate grid. A coordinate grid consists of major and minor vertical - and horizontal gridlines. The locations of the gridlines + and horizontal grid lines. The locations of the grid lines are determined by the X and Y scale divisions which can be assigned with setXDiv() and setYDiv(). The draw() member draws the grid within a bounding @@ -39,43 +39,46 @@ public: virtual int rtti() const; - void enableX(bool tf); + void enableX( bool tf ); bool xEnabled() const; - void enableY(bool tf); + void enableY( bool tf ); bool yEnabled() const; - void enableXMin(bool tf); + void enableXMin( bool tf ); bool xMinEnabled() const; - void enableYMin(bool tf); + void enableYMin( bool tf ); bool yMinEnabled() const; - void setXDiv(const QwtScaleDiv &sx); + void setXDiv( const QwtScaleDiv &sx ); const QwtScaleDiv &xScaleDiv() const; - void setYDiv(const QwtScaleDiv &sy); + void setYDiv( const QwtScaleDiv &sy ); const QwtScaleDiv &yScaleDiv() const; - void setPen(const QPen &p); + void setPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine ); + void setPen( const QPen & ); - void setMajPen(const QPen &p); - const QPen& majPen() const; + void setMajorPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine ); + void setMajorPen( const QPen & ); + const QPen& majorPen() const; - void setMinPen(const QPen &p); - const QPen& minPen() const; + void setMinorPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine ); + void setMinorPen( const QPen &p ); + const QPen& minorPen() const; - virtual void draw(QPainter *p, - const QwtScaleMap &xMap, const QwtScaleMap &yMap, - const QRect &rect) const; + virtual void draw( QPainter *p, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &rect ) const; - virtual void updateScaleDiv(const QwtScaleDiv &xMap, - const QwtScaleDiv &yMap); + virtual void updateScaleDiv( + const QwtScaleDiv &xMap, const QwtScaleDiv &yMap ); private: - void drawLines(QPainter *painter, const QRect &, - Qt::Orientation orientation, const QwtScaleMap &, - const QwtValueList &) const; + void drawLines( QPainter *painter, const QRectF &, + Qt::Orientation orientation, const QwtScaleMap &, + const QList & ) const; class PrivateData; PrivateData *d_data; diff --git a/libs/qwt/qwt_plot_histogram.cpp b/libs/qwt/qwt_plot_histogram.cpp new file mode 100644 index 000000000..4464f03ff --- /dev/null +++ b/libs/qwt/qwt_plot_histogram.cpp @@ -0,0 +1,690 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_plot_histogram.h" +#include "qwt_plot.h" +#include "qwt_painter.h" +#include "qwt_column_symbol.h" +#include "qwt_scale_map.h" +#include +#include + +static inline bool qwtIsCombinable( const QwtInterval &d1, + const QwtInterval &d2 ) +{ + if ( d1.isValid() && d2.isValid() ) + { + if ( d1.maxValue() == d2.minValue() ) + { + if ( !( d1.borderFlags() & QwtInterval::ExcludeMaximum + && d2.borderFlags() & QwtInterval::ExcludeMinimum ) ) + { + return true; + } + } + } + + return false; +} + +class QwtPlotHistogram::PrivateData +{ +public: + PrivateData(): + baseline( 0.0 ), + style( Columns ), + symbol( NULL ) + { + } + + ~PrivateData() + { + delete symbol; + } + + double baseline; + + QPen pen; + QBrush brush; + QwtPlotHistogram::HistogramStyle style; + const QwtColumnSymbol *symbol; +}; + +/*! + Constructor + \param title Title of the histogram. +*/ +QwtPlotHistogram::QwtPlotHistogram( const QwtText &title ): + QwtPlotSeriesItem( title ) +{ + init(); +} + +/*! + Constructor + \param title Title of the histogram. +*/ +QwtPlotHistogram::QwtPlotHistogram( const QString &title ): + QwtPlotSeriesItem( title ) +{ + init(); +} + +//! Destructor +QwtPlotHistogram::~QwtPlotHistogram() +{ + delete d_data; +} + +//! Initialize data members +void QwtPlotHistogram::init() +{ + d_data = new PrivateData(); + setData( new QwtIntervalSeriesData() ); + + setItemAttribute( QwtPlotItem::AutoScale, true ); + setItemAttribute( QwtPlotItem::Legend, true ); + + setZ( 20.0 ); +} + +/*! + Set the histogram's drawing style + + \param style Histogram style + \sa HistogramStyle, style() +*/ +void QwtPlotHistogram::setStyle( HistogramStyle style ) +{ + if ( style != d_data->style ) + { + d_data->style = style; + + legendChanged(); + itemChanged(); + } +} + +/*! + \return Style of the histogram + \sa HistogramStyle, setStyle() +*/ +QwtPlotHistogram::HistogramStyle QwtPlotHistogram::style() const +{ + return d_data->style; +} + +/*! + Build and assign a pen + + In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it + non cosmetic ( see QPen::isCosmetic() ). This method has been introduced + to hide this incompatibility. + + \param color Pen color + \param width Pen width + \param style Pen style + + \sa pen(), brush() + */ +void QwtPlotHistogram::setPen( const QColor &color, qreal width, Qt::PenStyle style ) +{ + setPen( QPen( color, width, style ) ); +} + +/*! + Assign a pen, that is used in a style() depending way. + + \param pen New pen + \sa pen(), brush() +*/ +void QwtPlotHistogram::setPen( const QPen &pen ) +{ + if ( pen != d_data->pen ) + { + d_data->pen = pen; + + legendChanged(); + itemChanged(); + } +} + +/*! + \return Pen used in a style() depending way. + \sa setPen(), brush() +*/ +const QPen &QwtPlotHistogram::pen() const +{ + return d_data->pen; +} + +/*! + Assign a brush, that is used in a style() depending way. + + \param brush New brush + \sa pen(), brush() +*/ +void QwtPlotHistogram::setBrush( const QBrush &brush ) +{ + if ( brush != d_data->brush ) + { + d_data->brush = brush; + + legendChanged(); + itemChanged(); + } +} + +/*! + \return Brush used in a style() depending way. + \sa setPen(), brush() +*/ +const QBrush &QwtPlotHistogram::brush() const +{ + return d_data->brush; +} + +/*! + \brief Assign a symbol + + In Column style an optional symbol can be assigned, that is responsible + for displaying the rectangle that is defined by the interval and + the distance between baseline() and value. When no symbol has been + defined the area is displayed as plain rectangle using pen() and brush(). + + \sa style(), symbol(), drawColumn(), pen(), brush() + + \note In applications, where different intervals need to be displayed + in a different way ( f.e different colors or even using different symbols) + it is recommended to overload drawColumn(). +*/ +void QwtPlotHistogram::setSymbol( const QwtColumnSymbol *symbol ) +{ + if ( symbol != d_data->symbol ) + { + delete d_data->symbol; + d_data->symbol = symbol; + + legendChanged(); + itemChanged(); + } +} + +/*! + \return Current symbol or NULL, when no symbol has been assigned + \sa setSymbol() +*/ +const QwtColumnSymbol *QwtPlotHistogram::symbol() const +{ + return d_data->symbol; +} + +/*! + \brief Set the value of the baseline + + Each column representing an QwtIntervalSample is defined by its + interval and the interval between baseline and the value of the sample. + + The default value of the baseline is 0.0. + + \param value Value of the baseline + \sa baseline() +*/ +void QwtPlotHistogram::setBaseline( double value ) +{ + if ( d_data->baseline != value ) + { + d_data->baseline = value; + itemChanged(); + } +} + +/*! + \return Value of the baseline + \sa setBaseline() +*/ +double QwtPlotHistogram::baseline() const +{ + return d_data->baseline; +} + +/*! + \return Bounding rectangle of all samples. + For an empty series the rectangle is invalid. +*/ +QRectF QwtPlotHistogram::boundingRect() const +{ + QRectF rect = data()->boundingRect(); + if ( !rect.isValid() ) + return rect; + + if ( orientation() == Qt::Horizontal ) + { + rect = QRectF( rect.y(), rect.x(), + rect.height(), rect.width() ); + + if ( rect.left() > d_data->baseline ) + rect.setLeft( d_data->baseline ); + else if ( rect.right() < d_data->baseline ) + rect.setRight( d_data->baseline ); + } + else + { + if ( rect.bottom() < d_data->baseline ) + rect.setBottom( d_data->baseline ); + else if ( rect.top() > d_data->baseline ) + rect.setTop( d_data->baseline ); + } + + return rect; +} + +//! \return QwtPlotItem::Rtti_PlotHistogram +int QwtPlotHistogram::rtti() const +{ + return QwtPlotItem::Rtti_PlotHistogram; +} + +/*! + Initialize data with an array of samples. + \param samples Vector of points +*/ +void QwtPlotHistogram::setSamples( + const QVector &samples ) +{ + setData( new QwtIntervalSeriesData( samples ) ); +} + +/*! + Assign a series of samples + + setSamples() is just a wrapper for setData() without any additional + value - beside that it is easier to find for the developer. + + \param data Data + \warning The item takes ownership of the data object, deleting + it when its not used anymore. +*/ +void QwtPlotHistogram::setSamples( + QwtSeriesData *data ) +{ + setData( data ); +} + +/*! + Draw a subset of the histogram samples + + \param painter Painter + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param canvasRect Contents rectangle of the canvas + \param from Index of the first sample to be painted + \param to Index of the last sample to be painted. If to < 0 the + series will be painted to its last sample. + + \sa drawOutline(), drawLines(), drawColumns +*/ +void QwtPlotHistogram::drawSeries( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &, int from, int to ) const +{ + if ( !painter || dataSize() <= 0 ) + return; + + if ( to < 0 ) + to = dataSize() - 1; + + switch ( d_data->style ) + { + case Outline: + drawOutline( painter, xMap, yMap, from, to ); + break; + case Lines: + drawLines( painter, xMap, yMap, from, to ); + break; + case Columns: + drawColumns( painter, xMap, yMap, from, to ); + break; + default: + break; + } +} + +/*! + Draw a histogram in Outline style() + + \param painter Painter + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param from Index of the first sample to be painted + \param to Index of the last sample to be painted. If to < 0 the + histogram will be painted to its last point. + + \sa setStyle(), style() + \warning The outline style requires, that the intervals are in increasing + order and not overlapping. +*/ +void QwtPlotHistogram::drawOutline( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + int from, int to ) const +{ + const bool doAlign = QwtPainter::roundingAlignment( painter ); + + double v0 = ( orientation() == Qt::Horizontal ) ? + xMap.transform( baseline() ) : yMap.transform( baseline() ); + if ( doAlign ) + v0 = qRound( v0 ); + + QwtIntervalSample previous; + + QPolygonF polygon; + for ( int i = from; i <= to; i++ ) + { + const QwtIntervalSample sample = this->sample( i ); + + if ( !sample.interval.isValid() ) + { + flushPolygon( painter, v0, polygon ); + previous = sample; + continue; + } + + if ( previous.interval.isValid() ) + { + if ( !qwtIsCombinable( previous.interval, sample.interval ) ) + flushPolygon( painter, v0, polygon ); + } + + if ( orientation() == Qt::Vertical ) + { + double x1 = xMap.transform( sample.interval.minValue() ); + double x2 = xMap.transform( sample.interval.maxValue() ); + double y = yMap.transform( sample.value ); + if ( doAlign ) + { + x1 = qRound( x1 ); + x2 = qRound( x2 ); + y = qRound( y ); + } + + if ( polygon.size() == 0 ) + polygon += QPointF( x1, v0 ); + + polygon += QPointF( x1, y ); + polygon += QPointF( x2, y ); + } + else + { + double y1 = yMap.transform( sample.interval.minValue() ); + double y2 = yMap.transform( sample.interval.maxValue() ); + double x = xMap.transform( sample.value ); + if ( doAlign ) + { + y1 = qRound( y1 ); + y2 = qRound( y2 ); + x = qRound( x ); + } + + if ( polygon.size() == 0 ) + polygon += QPointF( v0, y1 ); + + polygon += QPointF( x, y1 ); + polygon += QPointF( x, y2 ); + } + previous = sample; + } + + flushPolygon( painter, v0, polygon ); +} + +/*! + Draw a histogram in Columns style() + + \param painter Painter + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param from Index of the first sample to be painted + \param to Index of the last sample to be painted. If to < 0 the + histogram will be painted to its last point. + + \sa setStyle(), style(), setSymbol(), drawColumn() +*/ +void QwtPlotHistogram::drawColumns( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + int from, int to ) const +{ + painter->setPen( d_data->pen ); + painter->setBrush( d_data->brush ); + + const QwtSeriesData *series = data(); + + for ( int i = from; i <= to; i++ ) + { + const QwtIntervalSample sample = series->sample( i ); + if ( !sample.interval.isNull() ) + { + const QwtColumnRect rect = columnRect( sample, xMap, yMap ); + drawColumn( painter, rect, sample ); + } + } +} + +/*! + Draw a histogram in Lines style() + + \param painter Painter + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param from Index of the first sample to be painted + \param to Index of the last sample to be painted. If to < 0 the + histogram will be painted to its last point. + + \sa setStyle(), style(), setPen() +*/ +void QwtPlotHistogram::drawLines( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + int from, int to ) const +{ + const bool doAlign = QwtPainter::roundingAlignment( painter ); + + painter->setPen( d_data->pen ); + painter->setBrush( Qt::NoBrush ); + + const QwtSeriesData *series = data(); + + for ( int i = from; i <= to; i++ ) + { + const QwtIntervalSample sample = series->sample( i ); + if ( !sample.interval.isNull() ) + { + const QwtColumnRect rect = columnRect( sample, xMap, yMap ); + + QRectF r = rect.toRect(); + if ( doAlign ) + { + r.setLeft( qRound( r.left() ) ); + r.setRight( qRound( r.right() ) ); + r.setTop( qRound( r.top() ) ); + r.setBottom( qRound( r.bottom() ) ); + } + + switch ( rect.direction ) + { + case QwtColumnRect::LeftToRight: + { + QwtPainter::drawLine( painter, + r.topRight(), r.bottomRight() ); + break; + } + case QwtColumnRect::RightToLeft: + { + QwtPainter::drawLine( painter, + r.topLeft(), r.bottomLeft() ); + break; + } + case QwtColumnRect::TopToBottom: + { + QwtPainter::drawLine( painter, + r.bottomRight(), r.bottomLeft() ); + break; + } + case QwtColumnRect::BottomToTop: + { + QwtPainter::drawLine( painter, + r.topRight(), r.topLeft() ); + break; + } + } + } + } +} + +//! Internal, used by the Outline style. +void QwtPlotHistogram::flushPolygon( QPainter *painter, + double baseLine, QPolygonF &polygon ) const +{ + if ( polygon.size() == 0 ) + return; + + if ( orientation() == Qt::Horizontal ) + polygon += QPointF( baseLine, polygon.last().y() ); + else + polygon += QPointF( polygon.last().x(), baseLine ); + + if ( d_data->brush.style() != Qt::NoBrush ) + { + painter->setPen( Qt::NoPen ); + painter->setBrush( d_data->brush ); + + if ( orientation() == Qt::Horizontal ) + { + polygon += QPointF( polygon.last().x(), baseLine ); + polygon += QPointF( polygon.first().x(), baseLine ); + } + else + { + polygon += QPointF( baseLine, polygon.last().y() ); + polygon += QPointF( baseLine, polygon.first().y() ); + } + + QwtPainter::drawPolygon( painter, polygon ); + + polygon.pop_back(); + polygon.pop_back(); + } + if ( d_data->pen.style() != Qt::NoPen ) + { + painter->setBrush( Qt::NoBrush ); + painter->setPen( d_data->pen ); + QwtPainter::drawPolyline( painter, polygon ); + } + polygon.clear(); +} + +/*! + Calculate the area that is covered by a sample + + \param sample Sample + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + + \return Rectangle, that is covered by a sample +*/ +QwtColumnRect QwtPlotHistogram::columnRect( const QwtIntervalSample &sample, + const QwtScaleMap &xMap, const QwtScaleMap &yMap ) const +{ + QwtColumnRect rect; + + const QwtInterval &iv = sample.interval; + if ( !iv.isValid() ) + return rect; + + if ( orientation() == Qt::Horizontal ) + { + const double x0 = xMap.transform( baseline() ); + const double x = xMap.transform( sample.value ); + const double y1 = yMap.transform( iv.minValue() ); + const double y2 = yMap.transform( iv.maxValue() ); + + rect.hInterval.setInterval( x0, x ); + rect.vInterval.setInterval( y1, y2, iv.borderFlags() ); + rect.direction = ( x < x0 ) ? QwtColumnRect::RightToLeft : + QwtColumnRect::LeftToRight; + } + else + { + const double x1 = xMap.transform( iv.minValue() ); + const double x2 = xMap.transform( iv.maxValue() ); + const double y0 = yMap.transform( baseline() ); + const double y = yMap.transform( sample.value ); + + rect.hInterval.setInterval( x1, x2, iv.borderFlags() ); + rect.vInterval.setInterval( y0, y ); + rect.direction = ( y < y0 ) ? QwtColumnRect::BottomToTop : + QwtColumnRect::TopToBottom; + } + + return rect; +} + +/*! + Draw a column for a sample in Columns style(). + + When a symbol() has been set the symbol is used otherwise the + column is displayed as plain rectangle using pen() and brush(). + + \param painter Painter + \param rect Rectangle where to paint the column in paint device coordinates + \param sample Sample to be displayed + + \note In applications, where different intervals need to be displayed + in a different way ( f.e different colors or even using different symbols) + it is recommended to overload drawColumn(). +*/ +void QwtPlotHistogram::drawColumn( QPainter *painter, + const QwtColumnRect &rect, const QwtIntervalSample &sample ) const +{ + Q_UNUSED( sample ); + + if ( d_data->symbol && + ( d_data->symbol->style() != QwtColumnSymbol::NoStyle ) ) + { + d_data->symbol->draw( painter, rect ); + } + else + { + QRectF r = rect.toRect(); + if ( QwtPainter::roundingAlignment( painter ) ) + { + r.setLeft( qRound( r.left() ) ); + r.setRight( qRound( r.right() ) ); + r.setTop( qRound( r.top() ) ); + r.setBottom( qRound( r.bottom() ) ); + } + + QwtPainter::drawRect( painter, r ); + } +} + +/*! + A plain rectangle without pen using the brush() + + \param index Index of the legend entry + ( ignored as there is only one ) + \param size Icon size + \return A graphic displaying the icon + + \sa QwtPlotItem::setLegendIconSize(), QwtPlotItem::legendData() +*/ +QwtGraphic QwtPlotHistogram::legendIcon( int index, + const QSizeF &size ) const +{ + Q_UNUSED( index ); + return defaultIcon( d_data->brush, size ); +} diff --git a/libs/qwt/qwt_plot_histogram.h b/libs/qwt/qwt_plot_histogram.h new file mode 100644 index 000000000..b96bdddc0 --- /dev/null +++ b/libs/qwt/qwt_plot_histogram.h @@ -0,0 +1,139 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_PLOT_HISTOGRAM_H +#define QWT_PLOT_HISTOGRAM_H + +#include "qwt_global.h" +#include "qwt_plot_seriesitem.h" +#include "qwt_column_symbol.h" +#include +#include + +class QwtIntervalData; +class QString; +class QPolygonF; + +/*! + \brief QwtPlotHistogram represents a series of samples, where an interval + is associated with a value ( \f$y = f([x1,x2])\f$ ). + + The representation depends on the style() and an optional symbol() + that is displayed for each interval. + + \note The term "histogram" is used in a different way in the areas of + digital image processing and statistics. Wikipedia introduces the + terms "image histogram" and "color histogram" to avoid confusions. + While "image histograms" can be displayed by a QwtPlotCurve there + is no applicable plot item for a "color histogram" yet. + + \sa QwtPlotBarChart, QwtPlotMultiBarChart +*/ + +class QWT_EXPORT QwtPlotHistogram: + public QwtPlotSeriesItem, public QwtSeriesStore +{ +public: + /*! + Histogram styles. + The default style is QwtPlotHistogram::Columns. + + \sa setStyle(), style(), setSymbol(), symbol(), setBaseline() + */ + enum HistogramStyle + { + /*! + Draw an outline around the area, that is build by all intervals + using the pen() and fill it with the brush(). The outline style + requires, that the intervals are in increasing order and + not overlapping. + */ + Outline, + + /*! + Draw a column for each interval. When a symbol() has been set + the symbol is used otherwise the column is displayed as + plain rectangle using pen() and brush(). + */ + Columns, + + /*! + Draw a simple line using the pen() for each interval. + */ + Lines, + + /*! + Styles >= UserStyle are reserved for derived + classes that overload drawSeries() with + additional application specific ways to display a histogram. + */ + UserStyle = 100 + }; + + explicit QwtPlotHistogram( const QString &title = QString::null ); + explicit QwtPlotHistogram( const QwtText &title ); + virtual ~QwtPlotHistogram(); + + virtual int rtti() const; + + void setPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine ); + void setPen( const QPen & ); + const QPen &pen() const; + + void setBrush( const QBrush & ); + const QBrush &brush() const; + + void setSamples( const QVector & ); + void setSamples( QwtSeriesData * ); + + void setBaseline( double reference ); + double baseline() const; + + void setStyle( HistogramStyle style ); + HistogramStyle style() const; + + void setSymbol( const QwtColumnSymbol * ); + const QwtColumnSymbol *symbol() const; + + virtual void drawSeries( QPainter *p, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + + virtual QRectF boundingRect() const; + + virtual QwtGraphic legendIcon( int index, const QSizeF & ) const; + +protected: + virtual QwtColumnRect columnRect( const QwtIntervalSample &, + const QwtScaleMap &, const QwtScaleMap & ) const; + + virtual void drawColumn( QPainter *, const QwtColumnRect &, + const QwtIntervalSample & ) const; + + void drawColumns( QPainter *, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + int from, int to ) const; + + void drawOutline( QPainter *, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + int from, int to ) const; + + void drawLines( QPainter *, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + int from, int to ) const; + +private: + void init(); + void flushPolygon( QPainter *, double baseLine, QPolygonF & ) const; + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/libs/qwt/qwt_plot_intervalcurve.cpp b/libs/qwt/qwt_plot_intervalcurve.cpp new file mode 100644 index 000000000..200ea39b5 --- /dev/null +++ b/libs/qwt/qwt_plot_intervalcurve.cpp @@ -0,0 +1,603 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_plot_intervalcurve.h" +#include "qwt_interval_symbol.h" +#include "qwt_scale_map.h" +#include "qwt_clipper.h" +#include "qwt_painter.h" +#include + +#include + +static inline bool qwtIsHSampleInside( const QwtIntervalSample &sample, + double xMin, double xMax, double yMin, double yMax ) +{ + const double y = sample.value; + const double x1 = sample.interval.minValue(); + const double x2 = sample.interval.maxValue(); + + const bool isOffScreen = ( y < yMin ) || ( y > yMax ) + || ( x1 < xMin && x2 < xMin ) || ( x1 > xMax && x2 > xMax ); + + return !isOffScreen; +} + +static inline bool qwtIsVSampleInside( const QwtIntervalSample &sample, + double xMin, double xMax, double yMin, double yMax ) +{ + const double x = sample.value; + const double y1 = sample.interval.minValue(); + const double y2 = sample.interval.maxValue(); + + const bool isOffScreen = ( x < xMin ) || ( x > xMax ) + || ( y1 < yMin && y2 < yMin ) || ( y1 > yMax && y2 > yMax ); + + return !isOffScreen; +} + +class QwtPlotIntervalCurve::PrivateData +{ +public: + PrivateData(): + style( QwtPlotIntervalCurve::Tube ), + symbol( NULL ), + pen( Qt::black ), + brush( Qt::white ) + { + paintAttributes = QwtPlotIntervalCurve::ClipPolygons; + paintAttributes |= QwtPlotIntervalCurve::ClipSymbol; + + pen.setCapStyle( Qt::FlatCap ); + } + + ~PrivateData() + { + delete symbol; + } + + QwtPlotIntervalCurve::CurveStyle style; + const QwtIntervalSymbol *symbol; + + QPen pen; + QBrush brush; + + QwtPlotIntervalCurve::PaintAttributes paintAttributes; +}; + +/*! + Constructor + \param title Title of the curve +*/ +QwtPlotIntervalCurve::QwtPlotIntervalCurve( const QwtText &title ): + QwtPlotSeriesItem( title ) +{ + init(); +} + +/*! + Constructor + \param title Title of the curve +*/ +QwtPlotIntervalCurve::QwtPlotIntervalCurve( const QString &title ): + QwtPlotSeriesItem( QwtText( title ) ) +{ + init(); +} + +//! Destructor +QwtPlotIntervalCurve::~QwtPlotIntervalCurve() +{ + delete d_data; +} + +//! Initialize internal members +void QwtPlotIntervalCurve::init() +{ + setItemAttribute( QwtPlotItem::Legend, true ); + setItemAttribute( QwtPlotItem::AutoScale, true ); + + d_data = new PrivateData; + setData( new QwtIntervalSeriesData() ); + + setZ( 19.0 ); +} + +//! \return QwtPlotItem::Rtti_PlotIntervalCurve +int QwtPlotIntervalCurve::rtti() const +{ + return QwtPlotIntervalCurve::Rtti_PlotIntervalCurve; +} + +/*! + Specify an attribute how to draw the curve + + \param attribute Paint attribute + \param on On/Off + \sa testPaintAttribute() +*/ +void QwtPlotIntervalCurve::setPaintAttribute( + PaintAttribute attribute, bool on ) +{ + if ( on ) + d_data->paintAttributes |= attribute; + else + d_data->paintAttributes &= ~attribute; +} + +/*! + \return True, when attribute is enabled + \sa PaintAttribute, setPaintAttribute() +*/ +bool QwtPlotIntervalCurve::testPaintAttribute( + PaintAttribute attribute ) const +{ + return ( d_data->paintAttributes & attribute ); +} + +/*! + Initialize data with an array of samples. + \param samples Vector of samples +*/ +void QwtPlotIntervalCurve::setSamples( + const QVector &samples ) +{ + setData( new QwtIntervalSeriesData( samples ) ); +} + +/*! + Assign a series of samples + + setSamples() is just a wrapper for setData() without any additional + value - beside that it is easier to find for the developer. + + \param data Data + \warning The item takes ownership of the data object, deleting + it when its not used anymore. +*/ +void QwtPlotIntervalCurve::setSamples( + QwtSeriesData *data ) +{ + setData( data ); +} + +/*! + Set the curve's drawing style + + \param style Curve style + \sa CurveStyle, style() +*/ +void QwtPlotIntervalCurve::setStyle( CurveStyle style ) +{ + if ( style != d_data->style ) + { + d_data->style = style; + + legendChanged(); + itemChanged(); + } +} + +/*! + \return Style of the curve + \sa setStyle() +*/ +QwtPlotIntervalCurve::CurveStyle QwtPlotIntervalCurve::style() const +{ + return d_data->style; +} + +/*! + Assign a symbol. + + \param symbol Symbol + \sa symbol() +*/ +void QwtPlotIntervalCurve::setSymbol( const QwtIntervalSymbol *symbol ) +{ + if ( symbol != d_data->symbol ) + { + delete d_data->symbol; + d_data->symbol = symbol; + + legendChanged(); + itemChanged(); + } +} + +/*! + \return Current symbol or NULL, when no symbol has been assigned + \sa setSymbol() +*/ +const QwtIntervalSymbol *QwtPlotIntervalCurve::symbol() const +{ + return d_data->symbol; +} + +/*! + Build and assign a pen + + In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it + non cosmetic ( see QPen::isCosmetic() ). This method has been introduced + to hide this incompatibility. + + \param color Pen color + \param width Pen width + \param style Pen style + + \sa pen(), brush() + */ +void QwtPlotIntervalCurve::setPen( const QColor &color, qreal width, Qt::PenStyle style ) +{ + setPen( QPen( color, width, style ) ); +} + +/*! + \brief Assign a pen + \param pen New pen + \sa pen(), brush() +*/ +void QwtPlotIntervalCurve::setPen( const QPen &pen ) +{ + if ( pen != d_data->pen ) + { + d_data->pen = pen; + + legendChanged(); + itemChanged(); + } +} + +/*! + \return Pen used to draw the lines + \sa setPen(), brush() +*/ +const QPen& QwtPlotIntervalCurve::pen() const +{ + return d_data->pen; +} + +/*! + Assign a brush. + + The brush is used to fill the area in Tube style(). + + \param brush Brush + \sa brush(), pen(), setStyle(), CurveStyle +*/ +void QwtPlotIntervalCurve::setBrush( const QBrush &brush ) +{ + if ( brush != d_data->brush ) + { + d_data->brush = brush; + + legendChanged(); + itemChanged(); + } +} + +/*! + \return Brush used to fill the area in Tube style() + \sa setBrush(), setStyle(), CurveStyle +*/ +const QBrush& QwtPlotIntervalCurve::brush() const +{ + return d_data->brush; +} + +/*! + \return Bounding rectangle of all samples. + For an empty series the rectangle is invalid. +*/ +QRectF QwtPlotIntervalCurve::boundingRect() const +{ + QRectF rect = QwtPlotSeriesItem::boundingRect(); + if ( rect.isValid() && orientation() == Qt::Vertical ) + rect.setRect( rect.y(), rect.x(), rect.height(), rect.width() ); + + return rect; +} + +/*! + Draw a subset of the samples + + \param painter Painter + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param canvasRect Contents rectangle of the canvas + \param from Index of the first sample to be painted + \param to Index of the last sample to be painted. If to < 0 the + series will be painted to its last sample. + + \sa drawTube(), drawSymbols() +*/ +void QwtPlotIntervalCurve::drawSeries( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const +{ + if ( to < 0 ) + to = dataSize() - 1; + + if ( from < 0 ) + from = 0; + + if ( from > to ) + return; + + switch ( d_data->style ) + { + case Tube: + drawTube( painter, xMap, yMap, canvasRect, from, to ); + break; + + case NoCurve: + default: + break; + } + + if ( d_data->symbol && + ( d_data->symbol->style() != QwtIntervalSymbol::NoSymbol ) ) + { + drawSymbols( painter, *d_data->symbol, + xMap, yMap, canvasRect, from, to ); + } +} + +/*! + Draw a tube + + Builds 2 curves from the upper and lower limits of the intervals + and draws them with the pen(). The area between the curves is + filled with the brush(). + + \param painter Painter + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param canvasRect Contents rectangle of the canvas + \param from Index of the first sample to be painted + \param to Index of the last sample to be painted. If to < 0 the + series will be painted to its last sample. + + \sa drawSeries(), drawSymbols() +*/ +void QwtPlotIntervalCurve::drawTube( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const +{ + const bool doAlign = QwtPainter::roundingAlignment( painter ); + + painter->save(); + + const size_t size = to - from + 1; + QPolygonF polygon( 2 * size ); + QPointF *points = polygon.data(); + + for ( uint i = 0; i < size; i++ ) + { + QPointF &minValue = points[i]; + QPointF &maxValue = points[2 * size - 1 - i]; + + const QwtIntervalSample intervalSample = sample( from + i ); + if ( orientation() == Qt::Vertical ) + { + double x = xMap.transform( intervalSample.value ); + double y1 = yMap.transform( intervalSample.interval.minValue() ); + double y2 = yMap.transform( intervalSample.interval.maxValue() ); + if ( doAlign ) + { + x = qRound( x ); + y1 = qRound( y1 ); + y2 = qRound( y2 ); + } + + minValue.rx() = x; + minValue.ry() = y1; + maxValue.rx() = x; + maxValue.ry() = y2; + } + else + { + double y = yMap.transform( intervalSample.value ); + double x1 = xMap.transform( intervalSample.interval.minValue() ); + double x2 = xMap.transform( intervalSample.interval.maxValue() ); + if ( doAlign ) + { + y = qRound( y ); + x1 = qRound( x1 ); + x2 = qRound( x2 ); + } + + minValue.rx() = x1; + minValue.ry() = y; + maxValue.rx() = x2; + maxValue.ry() = y; + } + } + + if ( d_data->brush.style() != Qt::NoBrush ) + { + painter->setPen( QPen( Qt::NoPen ) ); + painter->setBrush( d_data->brush ); + + if ( d_data->paintAttributes & ClipPolygons ) + { + const qreal m = 1.0; + const QPolygonF p = QwtClipper::clipPolygonF( + canvasRect.adjusted( -m, -m, m, m ), polygon, true ); + + QwtPainter::drawPolygon( painter, p ); + } + else + { + QwtPainter::drawPolygon( painter, polygon ); + } + } + + if ( d_data->pen.style() != Qt::NoPen ) + { + painter->setPen( d_data->pen ); + painter->setBrush( Qt::NoBrush ); + + if ( d_data->paintAttributes & ClipPolygons ) + { + qreal pw = qMax( qreal( 1.0 ), painter->pen().widthF() ); + const QRectF clipRect = canvasRect.adjusted( -pw, -pw, pw, pw ); + + QPolygonF p; + + p.resize( size ); + ::memcpy( p.data(), points, size * sizeof( QPointF ) ); + p = QwtClipper::clipPolygonF( clipRect, p ); + QwtPainter::drawPolyline( painter, p ); + + p.resize( size ); + ::memcpy( p.data(), points + size, size * sizeof( QPointF ) ); + p = QwtClipper::clipPolygonF( clipRect, p ); + QwtPainter::drawPolyline( painter, p ); + } + else + { + QwtPainter::drawPolyline( painter, points, size ); + QwtPainter::drawPolyline( painter, points + size, size ); + } + } + + painter->restore(); +} + +/*! + Draw symbols for a subset of the samples + + \param painter Painter + \param symbol Interval symbol + \param xMap x map + \param yMap y map + \param canvasRect Contents rectangle of the canvas + \param from Index of the first sample to be painted + \param to Index of the last sample to be painted + + \sa setSymbol(), drawSeries(), drawTube() +*/ +void QwtPlotIntervalCurve::drawSymbols( + QPainter *painter, const QwtIntervalSymbol &symbol, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const +{ + painter->save(); + + QPen pen = symbol.pen(); + pen.setCapStyle( Qt::FlatCap ); + + painter->setPen( pen ); + painter->setBrush( symbol.brush() ); + + const QRectF tr = QwtScaleMap::invTransform( xMap, yMap, canvasRect ); + + const double xMin = tr.left(); + const double xMax = tr.right(); + const double yMin = tr.top(); + const double yMax = tr.bottom(); + + const bool doClip = d_data->paintAttributes & ClipSymbol; + + for ( int i = from; i <= to; i++ ) + { + const QwtIntervalSample s = sample( i ); + + if ( orientation() == Qt::Vertical ) + { + if ( !doClip || qwtIsVSampleInside( s, xMin, xMax, yMin, yMax ) ) + { + const double x = xMap.transform( s.value ); + const double y1 = yMap.transform( s.interval.minValue() ); + const double y2 = yMap.transform( s.interval.maxValue() ); + + symbol.draw( painter, orientation(), + QPointF( x, y1 ), QPointF( x, y2 ) ); + } + } + else + { + if ( !doClip || qwtIsHSampleInside( s, xMin, xMax, yMin, yMax ) ) + { + const double y = yMap.transform( s.value ); + const double x1 = xMap.transform( s.interval.minValue() ); + const double x2 = xMap.transform( s.interval.maxValue() ); + + symbol.draw( painter, orientation(), + QPointF( x1, y ), QPointF( x2, y ) ); + } + } + } + + painter->restore(); +} + +/*! + \return Icon for the legend + + In case of Tube style() the icon is a plain rectangle filled with the brush(). + If a symbol is assigned it is scaled to size. + + \param index Index of the legend entry + ( ignored as there is only one ) + \param size Icon size + + \sa QwtPlotItem::setLegendIconSize(), QwtPlotItem::legendData() +*/ +QwtGraphic QwtPlotIntervalCurve::legendIcon( + int index, const QSizeF &size ) const +{ + Q_UNUSED( index ); + + if ( size.isEmpty() ) + return QwtGraphic(); + + QwtGraphic icon; + icon.setDefaultSize( size ); + icon.setRenderHint( QwtGraphic::RenderPensUnscaled, true ); + + QPainter painter( &icon ); + painter.setRenderHint( QPainter::Antialiasing, + testRenderHint( QwtPlotItem::RenderAntialiased ) ); + + if ( d_data->style == Tube ) + { + QRectF r( 0, 0, size.width(), size.height() ); + painter.fillRect( r, d_data->brush ); + } + + if ( d_data->symbol && + ( d_data->symbol->style() != QwtIntervalSymbol::NoSymbol ) ) + { + QPen pen = d_data->symbol->pen(); + pen.setWidthF( pen.widthF() ); + pen.setCapStyle( Qt::FlatCap ); + + painter.setPen( pen ); + painter.setBrush( d_data->symbol->brush() ); + + if ( orientation() == Qt::Vertical ) + { + const double x = 0.5 * size.width(); + + d_data->symbol->draw( &painter, orientation(), + QPointF( x, 0 ), QPointF( x, size.height() - 1.0 ) ); + } + else + { + const double y = 0.5 * size.height(); + + d_data->symbol->draw( &painter, orientation(), + QPointF( 0.0, y ), QPointF( size.width() - 1.0, y ) ); + } + } + + return icon; +} diff --git a/libs/qwt/qwt_plot_intervalcurve.h b/libs/qwt/qwt_plot_intervalcurve.h new file mode 100644 index 000000000..624d82f1b --- /dev/null +++ b/libs/qwt/qwt_plot_intervalcurve.h @@ -0,0 +1,132 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_PLOT_INTERVAL_CURVE_H +#define QWT_PLOT_INTERVAL_CURVE_H + +#include "qwt_global.h" +#include "qwt_plot_seriesitem.h" +#include "qwt_series_data.h" + +class QwtIntervalSymbol; + +/*! + \brief QwtPlotIntervalCurve represents a series of samples, where each value + is associated with an interval ( \f$[y1,y2] = f(x)\f$ ). + + The representation depends on the style() and an optional symbol() + that is displayed for each interval. QwtPlotIntervalCurve might be used + to display error bars or the area between 2 curves. +*/ +class QWT_EXPORT QwtPlotIntervalCurve: + public QwtPlotSeriesItem, public QwtSeriesStore +{ +public: + /*! + \brief Curve styles. + The default setting is QwtPlotIntervalCurve::Tube. + + \sa setStyle(), style() + */ + enum CurveStyle + { + /*! + Don't draw a curve. Note: This doesn't affect the symbols. + */ + NoCurve, + + /*! + Build 2 curves from the upper and lower limits of the intervals + and draw them with the pen(). The area between the curves is + filled with the brush(). + */ + Tube, + + /*! + Styles >= QwtPlotIntervalCurve::UserCurve are reserved for derived + classes that overload drawSeries() with + additional application specific curve types. + */ + UserCurve = 100 + }; + + /*! + Attributes to modify the drawing algorithm. + \sa setPaintAttribute(), testPaintAttribute() + */ + enum PaintAttribute + { + /*! + Clip polygons before painting them. In situations, where points + are far outside the visible area (f.e when zooming deep) this + might be a substantial improvement for the painting performance. + */ + ClipPolygons = 0x01, + + //! Check if a symbol is on the plot canvas before painting it. + ClipSymbol = 0x02 + }; + + //! Paint attributes + typedef QFlags PaintAttributes; + + explicit QwtPlotIntervalCurve( const QString &title = QString::null ); + explicit QwtPlotIntervalCurve( const QwtText &title ); + + virtual ~QwtPlotIntervalCurve(); + + virtual int rtti() const; + + void setPaintAttribute( PaintAttribute, bool on = true ); + bool testPaintAttribute( PaintAttribute ) const; + + void setSamples( const QVector & ); + void setSamples( QwtSeriesData * ); + + void setPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine ); + void setPen( const QPen & ); + const QPen &pen() const; + + void setBrush( const QBrush & ); + const QBrush &brush() const; + + void setStyle( CurveStyle style ); + CurveStyle style() const; + + void setSymbol( const QwtIntervalSymbol * ); + const QwtIntervalSymbol *symbol() const; + + virtual void drawSeries( QPainter *p, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + + virtual QRectF boundingRect() const; + + virtual QwtGraphic legendIcon( int index, const QSizeF & ) const; + +protected: + + void init(); + + virtual void drawTube( QPainter *, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + + virtual void drawSymbols( QPainter *, const QwtIntervalSymbol &, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + +private: + class PrivateData; + PrivateData *d_data; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotIntervalCurve::PaintAttributes ) + +#endif diff --git a/libs/qwt/qwt_plot_item.cpp b/libs/qwt/qwt_plot_item.cpp index 953449eb5..4cb03bbb2 100644 --- a/libs/qwt/qwt_plot_item.cpp +++ b/libs/qwt/qwt_plot_item.cpp @@ -7,44 +7,55 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ +#include "qwt_plot_item.h" #include "qwt_text.h" #include "qwt_plot.h" -#include "qwt_legend.h" -#include "qwt_legend_item.h" -#include "qwt_plot_item.h" +#include "qwt_legend_data.h" +#include "qwt_scale_div.h" +#include "qwt_graphic.h" +#include class QwtPlotItem::PrivateData { public: PrivateData(): - plot(NULL), - isVisible(true), - attributes(0), -#if QT_VERSION >= 0x040000 - renderHints(0), -#endif - z(0.0), - xAxis(QwtPlot::xBottom), - yAxis(QwtPlot::yLeft) { + plot( NULL ), + isVisible( true ), + attributes( 0 ), + interests( 0 ), + renderHints( 0 ), + renderThreadCount( 1 ), + z( 0.0 ), + xAxis( QwtPlot::xBottom ), + yAxis( QwtPlot::yLeft ), + legendIconSize( 8, 8 ) + { } mutable QwtPlot *plot; bool isVisible; - int attributes; -#if QT_VERSION >= 0x040000 - int renderHints; -#endif + + QwtPlotItem::ItemAttributes attributes; + QwtPlotItem::ItemInterests interests; + + QwtPlotItem::RenderHints renderHints; + uint renderThreadCount; + double z; int xAxis; int yAxis; QwtText title; + QSize legendIconSize; }; -//! Constructor -QwtPlotItem::QwtPlotItem(const QwtText &title) +/*! + Constructor + \param title Title of the item +*/ +QwtPlotItem::QwtPlotItem( const QwtText &title ) { d_data = new PrivateData; d_data->title = title; @@ -53,7 +64,7 @@ QwtPlotItem::QwtPlotItem(const QwtText &title) //! Destroy the QwtPlotItem QwtPlotItem::~QwtPlotItem() { - attach(NULL); + attach( NULL ); delete d_data; } @@ -65,36 +76,33 @@ QwtPlotItem::~QwtPlotItem() necessary). If a NULL argument is passed, it will detach from any QwtPlot it was attached to. - \sa QwtPlotItem::detach() + \param plot Plot widget + \sa detach() */ -void QwtPlotItem::attach(QwtPlot *plot) +void QwtPlotItem::attach( QwtPlot *plot ) { if ( plot == d_data->plot ) return; - // remove the item from the previous plot - - if ( d_data->plot ) { - if ( d_data->plot->legend() ) { - QWidget *legendItem = d_data->plot->legend()->find(this); - if ( legendItem ) - delete legendItem; - } - - d_data->plot->attachItem(this, false); - - if ( d_data->plot->autoReplot() ) - d_data->plot->update(); - } + if ( d_data->plot ) + d_data->plot->attachItem( this, false ); d_data->plot = plot; - if ( d_data->plot ) { - // insert the item into the current plot + if ( d_data->plot ) + d_data->plot->attachItem( this, true ); +} - d_data->plot->attachItem(this, true); - itemChanged(); - } +/*! + \brief This method detaches a QwtPlotItem from any + QwtPlot it has been associated with. + + detach() is equivalent to calling attach( NULL ) + \sa attach() +*/ +void QwtPlotItem::detach() +{ + attach( NULL ); } /*! @@ -138,15 +146,18 @@ double QwtPlotItem::z() const \param z Z-value \sa z(), QwtPlotDict::itemList() */ -void QwtPlotItem::setZ(double z) +void QwtPlotItem::setZ( double z ) { - if ( d_data->z != z ) { + if ( d_data->z != z ) + { + if ( d_data->plot ) // update the z order + d_data->plot->attachItem( this, false ); + d_data->z = z; - if ( d_data->plot ) { - // update the z order - d_data->plot->attachItem(this, false); - d_data->plot->attachItem(this, true); - } + + if ( d_data->plot ) + d_data->plot->attachItem( this, true ); + itemChanged(); } } @@ -157,9 +168,9 @@ void QwtPlotItem::setZ(double z) \param title Title \sa title() */ -void QwtPlotItem::setTitle(const QString &title) +void QwtPlotItem::setTitle( const QString &title ) { - setTitle(QwtText(title)); + setTitle( QwtText( title ) ); } /*! @@ -168,11 +179,16 @@ void QwtPlotItem::setTitle(const QString &title) \param title Title \sa title() */ -void QwtPlotItem::setTitle(const QwtText &title) +void QwtPlotItem::setTitle( const QwtText &title ) { - if ( d_data->title != title ) { + if ( d_data->title != title ) + { d_data->title = title; + + legendChanged(); +#if 0 itemChanged(); +#endif } } @@ -191,16 +207,20 @@ const QwtText &QwtPlotItem::title() const \param attribute Attribute type \param on true/false - \sa testItemAttribute(), ItemAttribute + \sa testItemAttribute(), ItemInterest */ -void QwtPlotItem::setItemAttribute(ItemAttribute attribute, bool on) +void QwtPlotItem::setItemAttribute( ItemAttribute attribute, bool on ) { - if ( bool(d_data->attributes & attribute) != on ) { + if ( d_data->attributes.testFlag( attribute ) != on ) + { if ( on ) d_data->attributes |= attribute; else d_data->attributes &= ~attribute; + if ( attribute == QwtPlotItem::Legend ) + legendChanged(); + itemChanged(); } } @@ -208,16 +228,47 @@ void QwtPlotItem::setItemAttribute(ItemAttribute attribute, bool on) /*! Test an item attribute - \param ItemAttribute Attribute type + \param attribute Attribute type \return true/false - \sa setItemAttribute(), ItemAttribute + \sa setItemAttribute(), ItemInterest */ -bool QwtPlotItem::testItemAttribute(ItemAttribute attribute) const +bool QwtPlotItem::testItemAttribute( ItemAttribute attribute ) const { - return d_data->attributes & attribute; + return d_data->attributes.testFlag( attribute ); } -#if QT_VERSION >= 0x040000 +/*! + Toggle an item interest + + \param interest Interest type + \param on true/false + + \sa testItemInterest(), ItemAttribute +*/ +void QwtPlotItem::setItemInterest( ItemInterest interest, bool on ) +{ + if ( d_data->interests.testFlag( interest ) != on ) + { + if ( on ) + d_data->interests |= interest; + else + d_data->interests &= ~interest; + + itemChanged(); + } +} + +/*! + Test an item interest + + \param interest Interest type + \return true/false + \sa setItemInterest(), ItemAttribute +*/ +bool QwtPlotItem::testItemInterest( ItemInterest interest ) const +{ + return d_data->interests.testFlag( interest ); +} /*! Toggle an render hint @@ -227,9 +278,10 @@ bool QwtPlotItem::testItemAttribute(ItemAttribute attribute) const \sa testRenderHint(), RenderHint */ -void QwtPlotItem::setRenderHint(RenderHint hint, bool on) +void QwtPlotItem::setRenderHint( RenderHint hint, bool on ) { - if ( ((d_data->renderHints & hint) != 0) != on ) { + if ( d_data->renderHints.testFlag( hint ) != on ) + { if ( on ) d_data->renderHints |= hint; else @@ -246,23 +298,123 @@ void QwtPlotItem::setRenderHint(RenderHint hint, bool on) \return true/false \sa setRenderHint(), RenderHint */ -bool QwtPlotItem::testRenderHint(RenderHint hint) const +bool QwtPlotItem::testRenderHint( RenderHint hint ) const { - return (d_data->renderHints & hint); + return d_data->renderHints.testFlag( hint ); } -#endif +/*! + On multi core systems rendering of certain plot item + ( f.e QwtPlotRasterItem ) can be done in parallel in + several threads. + + The default setting is set to 1. + + \param numThreads Number of threads to be used for rendering. + If numThreads is set to 0, the system specific + ideal thread count is used. + + The default thread count is 1 ( = no additional threads ) +*/ +void QwtPlotItem::setRenderThreadCount( uint numThreads ) +{ + d_data->renderThreadCount = numThreads; +} + +/*! + \return Number of threads to be used for rendering. + If numThreads() is set to 0, the system specific + ideal thread count is used. +*/ +uint QwtPlotItem::renderThreadCount() const +{ + return d_data->renderThreadCount; +} + +/*! + Set the size of the legend icon + + The default setting is 8x8 pixels + + \param size Size + \sa legendIconSize(), legendIcon() +*/ +void QwtPlotItem::setLegendIconSize( const QSize &size ) +{ + if ( d_data->legendIconSize != size ) + { + d_data->legendIconSize = size; + legendChanged(); + } +} + +/*! + \return Legend icon size + \sa setLegendIconSize(), legendIcon() +*/ +QSize QwtPlotItem::legendIconSize() const +{ + return d_data->legendIconSize; +} + +/*! + \return Icon representing the item on the legend + + The default implementation returns an invalid icon + + \param index Index of the legend entry + ( usually there is only one ) + \param size Icon size + + \sa setLegendIconSize(), legendData() + */ +QwtGraphic QwtPlotItem::legendIcon( + int index, const QSizeF &size ) const +{ + Q_UNUSED( index ) + Q_UNUSED( size ) + + return QwtGraphic(); +} + +/*! + \brief Return a default icon from a brush + + The default icon is a filled rectangle used + in several derived classes as legendIcon(). + + \param brush Fill brush + \param size Icon size + + \return A filled rectangle + */ +QwtGraphic QwtPlotItem::defaultIcon( + const QBrush &brush, const QSizeF &size ) const +{ + QwtGraphic icon; + if ( !size.isEmpty() ) + { + icon.setDefaultSize( size ); + + QRectF r( 0, 0, size.width(), size.height() ); + + QPainter painter( &icon ); + painter.fillRect( r, brush ); + } + + return icon; +} //! Show the item void QwtPlotItem::show() { - setVisible(true); + setVisible( true ); } //! Hide the item void QwtPlotItem::hide() { - setVisible(false); + setVisible( false ); } /*! @@ -271,9 +423,10 @@ void QwtPlotItem::hide() \param on Show if true, otherwise hide \sa isVisible(), show(), hide() */ -void QwtPlotItem::setVisible(bool on) +void QwtPlotItem::setVisible( bool on ) { - if ( on != d_data->isVisible ) { + if ( on != d_data->isVisible ) + { d_data->isVisible = on; itemChanged(); } @@ -289,37 +442,43 @@ bool QwtPlotItem::isVisible() const } /*! - Update the legend and call QwtPlot::autoRefresh for the + Update the legend and call QwtPlot::autoRefresh() for the parent plot. - \sa updateLegend() + \sa QwtPlot::legendChanged(), QwtPlot::autoRefresh() */ void QwtPlotItem::itemChanged() { - if ( d_data->plot ) { - if ( d_data->plot->legend() ) - updateLegend(d_data->plot->legend()); - + if ( d_data->plot ) d_data->plot->autoRefresh(); - } +} + +/*! + Update the legend of the parent plot. + \sa QwtPlot::updateLegend(), itemChanged() +*/ +void QwtPlotItem::legendChanged() +{ + if ( testItemAttribute( QwtPlotItem::Legend ) && d_data->plot ) + d_data->plot->updateLegend( this ); } /*! Set X and Y axis - The item will painted according to the coordinates its Axes. + The item will painted according to the coordinates of its Axes. - \param xAxis X Axis - \param yAxis Y Axis + \param xAxis X Axis ( QwtPlot::xBottom or QwtPlot::xTop ) + \param yAxis Y Axis ( QwtPlot::yLeft or QwtPlot::yRight ) - \sa setXAxis(), setYAxis(), xAxis(), yAxis() + \sa setXAxis(), setYAxis(), xAxis(), yAxis(), QwtPlot::Axis */ -void QwtPlotItem::setAxis(int xAxis, int yAxis) +void QwtPlotItem::setAxes( int xAxis, int yAxis ) { - if (xAxis == QwtPlot::xBottom || xAxis == QwtPlot::xTop ) + if ( xAxis == QwtPlot::xBottom || xAxis == QwtPlot::xTop ) d_data->xAxis = xAxis; - if (yAxis == QwtPlot::yLeft || yAxis == QwtPlot::yRight ) + if ( yAxis == QwtPlot::yLeft || yAxis == QwtPlot::yRight ) d_data->yAxis = yAxis; itemChanged(); @@ -330,12 +489,13 @@ void QwtPlotItem::setAxis(int xAxis, int yAxis) The item will painted according to the coordinates its Axes. - \param axis X Axis - \sa setAxis(), setYAxis(), xAxis() + \param axis X Axis ( QwtPlot::xBottom or QwtPlot::xTop ) + \sa setAxes(), setYAxis(), xAxis(), QwtPlot::Axis */ -void QwtPlotItem::setXAxis(int axis) +void QwtPlotItem::setXAxis( int axis ) { - if (axis == QwtPlot::xBottom || axis == QwtPlot::xTop ) { + if ( axis == QwtPlot::xBottom || axis == QwtPlot::xTop ) + { d_data->xAxis = axis; itemChanged(); } @@ -346,12 +506,13 @@ void QwtPlotItem::setXAxis(int axis) The item will painted according to the coordinates its Axes. - \param axis Y Axis - \sa setAxis(), setXAxis(), yAxis() + \param axis Y Axis ( QwtPlot::yLeft or QwtPlot::yRight ) + \sa setAxes(), setXAxis(), yAxis(), QwtPlot::Axis */ -void QwtPlotItem::setYAxis(int axis) +void QwtPlotItem::setYAxis( int axis ) { - if (axis == QwtPlot::yLeft || axis == QwtPlot::yRight ) { + if ( axis == QwtPlot::yLeft || axis == QwtPlot::yRight ) + { d_data->yAxis = axis; itemChanged(); } @@ -370,72 +531,90 @@ int QwtPlotItem::yAxis() const } /*! - \return An invalid bounding rect: QwtDoubleRect(1.0, 1.0, -2.0, -2.0) + \return An invalid bounding rect: QRectF(1.0, 1.0, -2.0, -2.0) + \note A width or height < 0.0 is ignored by the autoscaler */ -QwtDoubleRect QwtPlotItem::boundingRect() const +QRectF QwtPlotItem::boundingRect() const { - return QwtDoubleRect(1.0, 1.0, -2.0, -2.0); // invalid + return QRectF( 1.0, 1.0, -2.0, -2.0 ); // invalid } /*! - \brief Allocate the widget that represents the item on the legend + \brief Calculate a hint for the canvas margin - The default implementation is made for QwtPlotCurve and returns a - QwtLegendItem(), but an item could be represented by any type of widget, - by overloading legendItem() and updateLegend(). - - \return QwtLegendItem() - \sa updateLegend() QwtLegend() -*/ -QWidget *QwtPlotItem::legendItem() const -{ - return new QwtLegendItem; -} + When the QwtPlotItem::Margins flag is enabled the plot item + indicates, that it needs some margins at the borders of the canvas. + This is f.e. used by bar charts to reserve space for displaying + the bars. -/*! - \brief Update the widget that represents the item on the legend + The margins are in target device coordinates ( pixels on screen ) - updateLegend() is called from itemChanged() to adopt the widget - representing the item on the legend to its new configuration. + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param canvasRect Contents rectangle of the canvas in painter coordinates + \param left Returns the left margin + \param top Returns the top margin + \param right Returns the right margin + \param bottom Returns the bottom margin - The default implementation is made for QwtPlotCurve and updates a - QwtLegendItem(), but an item could be represented by any type of widget, - by overloading legendItem() and updateLegend(). + \return The default implementation returns 0 for all margins - \sa legendItem(), itemChanged(), QwtLegend() -*/ -void QwtPlotItem::updateLegend(QwtLegend *legend) const + \sa QwtPlot::getCanvasMarginsHint(), QwtPlot::updateCanvasMargins() + */ +void QwtPlotItem::getCanvasMarginHint( const QwtScaleMap &xMap, + const QwtScaleMap &yMap, const QRectF &canvasRect, + double &left, double &top, double &right, double &bottom ) const { - if ( !legend ) - return; + Q_UNUSED( xMap ); + Q_UNUSED( yMap ); + Q_UNUSED( canvasRect ); - QWidget *lgdItem = legend->find(this); - if ( testItemAttribute(QwtPlotItem::Legend) ) { - if ( lgdItem == NULL ) { - lgdItem = legendItem(); - if ( lgdItem ) { - if ( lgdItem->inherits("QwtLegendItem") ) { - QwtLegendItem *label = (QwtLegendItem *)lgdItem; - label->setItemMode(legend->itemMode()); - - if ( d_data->plot ) { - QObject::connect(label, SIGNAL(clicked()), - d_data->plot, SLOT(legendItemClicked())); - QObject::connect(label, SIGNAL(checked(bool)), - d_data->plot, SLOT(legendItemChecked(bool))); - } - } - legend->insert(this, lgdItem); - } - } - if ( lgdItem && lgdItem->inherits("QwtLegendItem") ) { - QwtLegendItem* label = (QwtLegendItem*)lgdItem; - if ( label ) - label->setText(d_data->title); - } - } else { - delete lgdItem; - } + // use QMargins, when we don't need to support Qt < 4.6 anymore + left = top = right = bottom = 0.0; +} + +/*! + \brief Return all information, that is needed to represent + the item on the legend + + Most items are represented by one entry on the legend + showing an icon and a text, but f.e. QwtPlotMultiBarChart + displays one entry for each bar. + + QwtLegendData is basically a list of QVariants that makes it + possible to overload and reimplement legendData() to + return almost any type of information, that is understood + by the receiver that acts as the legend. + + The default implementation returns one entry with + the title() of the item and the legendIcon(). + + \return Data, that is needed to represent the item on the legend + \sa title(), legendIcon(), QwtLegend, QwtPlotLegendItem + */ +QList QwtPlotItem::legendData() const +{ + QwtLegendData data; + + QwtText label = title(); + label.setRenderFlags( label.renderFlags() & Qt::AlignLeft ); + + QVariant titleValue; + qVariantSetValue( titleValue, label ); + data.setValue( QwtLegendData::TitleRole, titleValue ); + + const QwtGraphic graphic = legendIcon( 0, legendIconSize() ); + if ( !graphic.isNull() ) + { + QVariant iconValue; + qVariantSetValue( iconValue, graphic ); + data.setValue( QwtLegendData::IconRole, iconValue ); + } + + QList list; + list += data; + + return list; } /*! @@ -446,92 +625,74 @@ void QwtPlotItem::updateLegend(QwtLegend *legend) const on the scale division (like QwtPlotGrid()) have to reimplement updateScaleDiv() + updateScaleDiv() is only called when the ScaleInterest interest + is enabled. The default implementation does nothing. + \param xScaleDiv Scale division of the x-axis \param yScaleDiv Scale division of the y-axis - \sa QwtPlot::updateAxes() + \sa QwtPlot::updateAxes(), ScaleInterest */ -void QwtPlotItem::updateScaleDiv(const QwtScaleDiv &, - const QwtScaleDiv &) +void QwtPlotItem::updateScaleDiv( const QwtScaleDiv &xScaleDiv, + const QwtScaleDiv &yScaleDiv ) { + Q_UNUSED( xScaleDiv ); + Q_UNUSED( yScaleDiv ); } /*! - \brief Calculate the bounding scale rect of 2 maps + \brief Update the item to changes of the legend info - \param xMap X map - \param yMap X map + Plot items that want to display a legend ( not those, that want to + be displayed on a legend ! ) will have to implement updateLegend(). - \return Bounding rect of the scale maps -*/ -QwtDoubleRect QwtPlotItem::scaleRect(const QwtScaleMap &xMap, - const QwtScaleMap &yMap) const -{ - return QwtDoubleRect(xMap.s1(), yMap.s1(), - xMap.sDist(), yMap.sDist() ); -} + updateLegend() is only called when the LegendInterest interest + is enabled. The default implementation does nothing. -/*! - \brief Calculate the bounding paint rect of 2 maps + \param item Plot item to be displayed on a legend + \param data Attributes how to display item on the legend - \param xMap X map - \param yMap X map + \sa QwtPlotLegendItem - \return Bounding rect of the scale maps -*/ -QRect QwtPlotItem::paintRect(const QwtScaleMap &xMap, - const QwtScaleMap &yMap) const + \note Plot items, that want to be displayed on a legend + need to enable the QwtPlotItem::Legend flag and to implement + legendData() and legendIcon() + */ +void QwtPlotItem::updateLegend( const QwtPlotItem *item, + const QList &data ) { - const QRect rect( qRound(xMap.p1()), qRound(yMap.p1()), - qRound(xMap.pDist()), qRound(yMap.pDist()) ); - - return rect; + Q_UNUSED( item ); + Q_UNUSED( data ); } /*! - Transform a rectangle + \brief Calculate the bounding scale rectangle of 2 maps - \param xMap X map - \param yMap Y map - \param rect Rectangle in scale coordinates - \return Rectangle in paint coordinates + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. - \sa invTransform() + \return Bounding scale rect of the scale maps, not normalized */ -QRect QwtPlotItem::transform(const QwtScaleMap &xMap, - const QwtScaleMap &yMap, const QwtDoubleRect& rect) const +QRectF QwtPlotItem::scaleRect( const QwtScaleMap &xMap, + const QwtScaleMap &yMap ) const { - int x1 = qRound(xMap.transform(rect.left())); - int x2 = qRound(xMap.transform(rect.right())); - int y1 = qRound(yMap.transform(rect.top())); - int y2 = qRound(yMap.transform(rect.bottom())); - - if ( x2 < x1 ) - qSwap(x1, x2); - if ( y2 < y1 ) - qSwap(y1, y2); - - return QRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1); + return QRectF( xMap.s1(), yMap.s1(), + xMap.sDist(), yMap.sDist() ); } /*! - Transform a rectangle from paint to scale coordinates + \brief Calculate the bounding paint rectangle of 2 maps - \param xMap X map - \param yMap Y map - \param rect Rectangle in paint coordinates - \return Rectangle in scale coordinates - \sa transform() + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + + \return Bounding paint rectangle of the scale maps, not normalized */ -QwtDoubleRect QwtPlotItem::invTransform(const QwtScaleMap &xMap, - const QwtScaleMap &yMap, const QRect& rect) const +QRectF QwtPlotItem::paintRect( const QwtScaleMap &xMap, + const QwtScaleMap &yMap ) const { - const double x1 = xMap.invTransform(rect.left()); - const double x2 = xMap.invTransform(rect.right()); - const double y1 = yMap.invTransform(rect.top()); - const double y2 = yMap.invTransform(rect.bottom()); - - const QwtDoubleRect r(x1, y1, x2 - x1, y2 - y1); + const QRectF rect( xMap.p1(), yMap.p1(), + xMap.pDist(), yMap.pDist() ); - return r.normalized(); + return rect; } diff --git a/libs/qwt/qwt_plot_item.h b/libs/qwt/qwt_plot_item.h index 8d1397e23..c76634e74 100644 --- a/libs/qwt/qwt_plot_item.h +++ b/libs/qwt/qwt_plot_item.h @@ -11,100 +11,248 @@ #define QWT_PLOT_ITEM_H #include "qwt_global.h" -#include "qwt_legend_itemmanager.h" #include "qwt_text.h" -#include "qwt_double_rect.h" +#include "qwt_legend_data.h" +#include "qwt_graphic.h" +#include +#include +#include -class QString; -class QRect; class QPainter; -class QWidget; -class QwtPlot; -class QwtLegend; class QwtScaleMap; class QwtScaleDiv; +class QwtPlot; /*! \brief Base class for items on the plot canvas + + A plot item is "something", that can be painted on the plot canvas, + or only affects the scales of the plot widget. They can be categorized as: + + - Representator\n + A "Representator" is an item that represents some sort of data + on the plot canvas. The different representator classes are organized + according to the characteristics of the data: + - QwtPlotMarker + Represents a point or a horizontal/vertical coordinate + - QwtPlotCurve + Represents a series of points + - QwtPlotSpectrogram ( QwtPlotRasterItem ) + Represents raster data + - ... + + - Decorators\n + A "Decorator" is an item, that displays additional information, that + is not related to any data: + - QwtPlotGrid + - QwtPlotScaleItem + - QwtPlotSvgItem + - ... + + Depending on the QwtPlotItem::ItemAttribute flags, an item is included + into autoscaling or has an entry on the legend. + + Before misusing the existing item classes it might be better to + implement a new type of plot item + ( don't implement a watermark as spectrogram ). + Deriving a new type of QwtPlotItem primarily means to implement + the YourPlotItem::draw() method. + + \sa The cpuplot example shows the implementation of additional plot items. */ -class QWT_EXPORT QwtPlotItem: public QwtLegendItemManager +class QWT_EXPORT QwtPlotItem { public: - enum RttiValues { + /*! + \brief Runtime type information + + RttiValues is used to cast plot items, without + having to enable runtime type information of the compiler. + */ + enum RttiValues + { + //! Unspecific value, that can be used, when it doesn't matter Rtti_PlotItem = 0, + //! For QwtPlotGrid Rtti_PlotGrid, + + //! For QwtPlotScaleItem Rtti_PlotScale, + + //! For QwtPlotLegendItem + Rtti_PlotLegend, + + //! For QwtPlotMarker Rtti_PlotMarker, + + //! For QwtPlotCurve Rtti_PlotCurve, + + //! For QwtPlotSpectroCurve + Rtti_PlotSpectroCurve, + + //! For QwtPlotIntervalCurve + Rtti_PlotIntervalCurve, + + //! For QwtPlotHistogram Rtti_PlotHistogram, + + //! For QwtPlotSpectrogram Rtti_PlotSpectrogram, + + //! For QwtPlotSvgItem Rtti_PlotSVG, + //! For QwtPlotTradingCurve + Rtti_PlotTradingCurve, + + //! For QwtPlotBarChart + Rtti_PlotBarChart, + + //! For QwtPlotMultiBarChart + Rtti_PlotMultiBarChart, + + //! For QwtPlotShapeItem + Rtti_PlotShape, + + //! For QwtPlotTextLabel + Rtti_PlotTextLabel, + + //! For QwtPlotZoneItem + Rtti_PlotZone, + + /*! + Values >= Rtti_PlotUserItem are reserved for plot items + not implemented in the Qwt library. + */ Rtti_PlotUserItem = 1000 }; - enum ItemAttribute { - Legend = 1, - AutoScale = 2 + /*! + \brief Plot Item Attributes + + Various aspects of a plot widget depend on the attributes of + the attached plot items. If and how a single plot item + participates in these updates depends on its attributes. + + \sa setItemAttribute(), testItemAttribute(), ItemInterest + */ + enum ItemAttribute + { + //! The item is represented on the legend. + Legend = 0x01, + + /*! + The boundingRect() of the item is included in the + autoscaling calculation as long as its width or height + is >= 0.0. + */ + AutoScale = 0x02, + + /*! + The item needs extra space to display something outside + its bounding rectangle. + \sa getCanvasMarginHint() + */ + Margins = 0x04 }; -#if QT_VERSION >= 0x040000 - enum RenderHint { - RenderAntialiased = 1 + //! Plot Item Attributes + typedef QFlags ItemAttributes; + + /*! + \brief Plot Item Interests + + Plot items might depend on the situation of the corresponding + plot widget. By enabling an interest the plot item will be + notified, when the corresponding attribute of the plot widgets + has changed. + + \sa setItemAttribute(), testItemAttribute(), ItemInterest + */ + enum ItemInterest + { + /*! + The item is interested in updates of the scales + \sa updateScaleDiv() + */ + ScaleInterest = 0x01, + + /*! + The item is interested in updates of the legend ( of other items ) + This flag is intended for items, that want to implement a legend + for displaying entries of other plot item. + + \note If the plot item wants to be represented on a legend + enable QwtPlotItem::Legend instead. + + \sa updateLegend() + */ + LegendInterest = 0x02 }; -#endif - explicit QwtPlotItem(const QwtText &title = QwtText()); - virtual ~QwtPlotItem(); + //! Plot Item Interests + typedef QFlags ItemInterests; - void attach(QwtPlot *plot); + //! Render hints + enum RenderHint + { + //! Enable antialiasing + RenderAntialiased = 0x1 + }; - /*! - \brief This method detaches a QwtPlotItem from any QwtPlot it has been - associated with. + //! Render hints + typedef QFlags RenderHints; - detach() is equivalent to calling attach( NULL ) - \sa attach( QwtPlot* plot ) - */ - void detach() { - attach(NULL); - } + explicit QwtPlotItem( const QwtText &title = QwtText() ); + virtual ~QwtPlotItem(); + + void attach( QwtPlot *plot ); + void detach(); QwtPlot *plot() const; - void setTitle(const QString &title); - void setTitle(const QwtText &title); + void setTitle( const QString &title ); + void setTitle( const QwtText &title ); const QwtText &title() const; virtual int rtti() const; - void setItemAttribute(ItemAttribute, bool on = true); - bool testItemAttribute(ItemAttribute) const; + void setItemAttribute( ItemAttribute, bool on = true ); + bool testItemAttribute( ItemAttribute ) const; -#if QT_VERSION >= 0x040000 - void setRenderHint(RenderHint, bool on = true); - bool testRenderHint(RenderHint) const; -#endif + void setItemInterest( ItemInterest, bool on = true ); + bool testItemInterest( ItemInterest ) const; + + void setRenderHint( RenderHint, bool on = true ); + bool testRenderHint( RenderHint ) const; + + void setRenderThreadCount( uint numThreads ); + uint renderThreadCount() const; + + void setLegendIconSize( const QSize & ); + QSize legendIconSize() const; double z() const; - void setZ(double z); + void setZ( double z ); void show(); void hide(); - virtual void setVisible(bool); + virtual void setVisible( bool ); bool isVisible () const; - void setAxis(int xAxis, int yAxis); + void setAxes( int xAxis, int yAxis ); - void setXAxis(int axis); + void setXAxis( int axis ); int xAxis() const; - void setYAxis(int axis); + void setYAxis( int axis ); int yAxis() const; virtual void itemChanged(); + virtual void legendChanged(); /*! \brief Draw the item @@ -114,25 +262,32 @@ public: \param yMap Maps y-values into pixel coordinates. \param canvasRect Contents rect of the canvas in painter coordinates */ - virtual void draw(QPainter *painter, - const QwtScaleMap &xMap, const QwtScaleMap &yMap, - const QRect &canvasRect) const = 0; + virtual void draw( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect ) const = 0; + + virtual QRectF boundingRect() const; - virtual QwtDoubleRect boundingRect() const; + virtual void getCanvasMarginHint( + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasSize, + double &left, double &top, double &right, double &bottom) const; - virtual void updateLegend(QwtLegend *) const; - virtual void updateScaleDiv(const QwtScaleDiv&, - const QwtScaleDiv&); + virtual void updateScaleDiv( + const QwtScaleDiv&, const QwtScaleDiv& ); - virtual QWidget *legendItem() const; + virtual void updateLegend( const QwtPlotItem *, + const QList & ); - QwtDoubleRect scaleRect(const QwtScaleMap &, const QwtScaleMap &) const; - QRect paintRect(const QwtScaleMap &, const QwtScaleMap &) const; + QRectF scaleRect( const QwtScaleMap &, const QwtScaleMap & ) const; + QRectF paintRect( const QwtScaleMap &, const QwtScaleMap & ) const; - QRect transform(const QwtScaleMap &, const QwtScaleMap &, - const QwtDoubleRect&) const; - QwtDoubleRect invTransform(const QwtScaleMap &, const QwtScaleMap &, - const QRect&) const; + virtual QList legendData() const; + + virtual QwtGraphic legendIcon( int index, const QSizeF & ) const; + +protected: + QwtGraphic defaultIcon( const QBrush &, const QSizeF & ) const; private: // Disabled copy constructor and operator= @@ -143,4 +298,10 @@ private: PrivateData *d_data; }; +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotItem::ItemAttributes ) +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotItem::ItemInterests ) +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotItem::RenderHints ) + +Q_DECLARE_METATYPE( QwtPlotItem * ) + #endif diff --git a/libs/qwt/qwt_plot_layout.cpp b/libs/qwt/qwt_plot_layout.cpp index 364bd7e86..1c143e237 100644 --- a/libs/qwt/qwt_plot_layout.cpp +++ b/libs/qwt/qwt_plot_layout.cpp @@ -7,76 +7,84 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ -// vim: expandtab - -#include +#include "qwt_plot_layout.h" #include "qwt_text.h" #include "qwt_text_label.h" -#include "qwt_plot_canvas.h" #include "qwt_scale_widget.h" -#include "qwt_legend.h" -#include "qwt_plot_layout.h" +#include "qwt_abstract_legend.h" +#include +#include class QwtPlotLayout::LayoutData { public: - void init(const QwtPlot *, const QRect &rect); + void init( const QwtPlot *, const QRectF &rect ); - struct t_legendData { + struct t_legendData + { int frameWidth; - int vScrollBarWidth; - int hScrollBarHeight; + int hScrollExtent; + int vScrollExtent; QSize hint; } legend; - struct t_titleData { + struct t_titleData + { QwtText text; int frameWidth; } title; - struct t_scaleData { + struct t_footerData + { + QwtText text; + int frameWidth; + } footer; + + struct t_scaleData + { bool isEnabled; const QwtScaleWidget *scaleWidget; QFont scaleFont; int start; int end; int baseLineOffset; - int tickOffset; + double tickOffset; int dimWithoutTitle; } scale[QwtPlot::axisCnt]; - struct t_canvasData { - int frameWidth; + struct t_canvasData + { + int contentsMargins[ QwtPlot::axisCnt ]; + } canvas; }; /* Extract all layout relevant data from the plot components */ - -void QwtPlotLayout::LayoutData::init(const QwtPlot *plot, const QRect &rect) +void QwtPlotLayout::LayoutData::init( const QwtPlot *plot, const QRectF &rect ) { // legend - if ( plot->plotLayout()->legendPosition() != QwtPlot::ExternalLegend - && plot->legend() ) { + if ( plot->legend() ) + { legend.frameWidth = plot->legend()->frameWidth(); - legend.vScrollBarWidth = - plot->legend()->verticalScrollBar()->sizeHint().width(); - legend.hScrollBarHeight = - plot->legend()->horizontalScrollBar()->sizeHint().height(); + legend.hScrollExtent = + plot->legend()->scrollExtent( Qt::Horizontal ); + legend.vScrollExtent = + plot->legend()->scrollExtent( Qt::Vertical ); const QSize hint = plot->legend()->sizeHint(); - int w = qwtMin(hint.width(), rect.width()); - int h = plot->legend()->heightForWidth(w); - if ( h == 0 ) + int w = qMin( hint.width(), qFloor( rect.width() ) ); + int h = plot->legend()->heightForWidth( w ); + if ( h <= 0 ) h = hint.height(); if ( h > rect.height() ) - w += legend.vScrollBarWidth; + w += legend.hScrollExtent; - legend.hint = QSize(w, h); + legend.hint = QSize( w, h ); } // title @@ -84,20 +92,38 @@ void QwtPlotLayout::LayoutData::init(const QwtPlot *plot, const QRect &rect) title.frameWidth = 0; title.text = QwtText(); - if (plot->titleLabel() ) { + if ( plot->titleLabel() ) + { const QwtTextLabel *label = plot->titleLabel(); title.text = label->text(); - if ( !(title.text.testPaintAttribute(QwtText::PaintUsingTextFont)) ) - title.text.setFont(label->font()); + if ( !( title.text.testPaintAttribute( QwtText::PaintUsingTextFont ) ) ) + title.text.setFont( label->font() ); title.frameWidth = plot->titleLabel()->frameWidth(); } + // footer + + footer.frameWidth = 0; + footer.text = QwtText(); + + if ( plot->footerLabel() ) + { + const QwtTextLabel *label = plot->footerLabel(); + footer.text = label->text(); + if ( !( footer.text.testPaintAttribute( QwtText::PaintUsingTextFont ) ) ) + footer.text.setFont( label->font() ); + + footer.frameWidth = plot->footerLabel()->frameWidth(); + } + // scales - for (int axis = 0; axis < QwtPlot::axisCnt; axis++ ) { - if ( plot->axisEnabled(axis) ) { - const QwtScaleWidget *scaleWidget = plot->axisWidget(axis); + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + { + if ( plot->axisEnabled( axis ) ) + { + const QwtScaleWidget *scaleWidget = plot->axisWidget( axis ); scale[axis].isEnabled = true; @@ -111,55 +137,62 @@ void QwtPlotLayout::LayoutData::init(const QwtPlot *plot, const QRect &rect) scale[axis].baseLineOffset = scaleWidget->margin(); scale[axis].tickOffset = scaleWidget->margin(); if ( scaleWidget->scaleDraw()->hasComponent( - QwtAbstractScaleDraw::Ticks) ) { + QwtAbstractScaleDraw::Ticks ) ) + { scale[axis].tickOffset += - (int)scaleWidget->scaleDraw()->majTickLength(); + scaleWidget->scaleDraw()->maxTickLength(); } scale[axis].dimWithoutTitle = scaleWidget->dimForLength( - QWIDGETSIZE_MAX, scale[axis].scaleFont); + QWIDGETSIZE_MAX, scale[axis].scaleFont ); - if ( !scaleWidget->title().isEmpty() ) { + if ( !scaleWidget->title().isEmpty() ) + { scale[axis].dimWithoutTitle -= - scaleWidget->titleHeightForWidth(QWIDGETSIZE_MAX); + scaleWidget->titleHeightForWidth( QWIDGETSIZE_MAX ); } - } else { + } + else + { scale[axis].isEnabled = false; scale[axis].start = 0; scale[axis].end = 0; scale[axis].baseLineOffset = 0; - scale[axis].tickOffset = 0; + scale[axis].tickOffset = 0.0; scale[axis].dimWithoutTitle = 0; } } // canvas - canvas.frameWidth = plot->canvas()->frameWidth(); + plot->canvas()->getContentsMargins( + &canvas.contentsMargins[ QwtPlot::yLeft ], + &canvas.contentsMargins[ QwtPlot::xTop ], + &canvas.contentsMargins[ QwtPlot::yRight ], + &canvas.contentsMargins[ QwtPlot::xBottom ] ); } class QwtPlotLayout::PrivateData { public: PrivateData(): - margin(0), - spacing(5), - alignCanvasToScales(false) { + spacing( 5 ) + { } - QRect titleRect; - QRect legendRect; - QRect scaleRect[QwtPlot::axisCnt]; - QRect canvasRect; + QRectF titleRect; + QRectF footerRect; + QRectF legendRect; + QRectF scaleRect[QwtPlot::axisCnt]; + QRectF canvasRect; QwtPlotLayout::LayoutData layoutData; QwtPlot::LegendPosition legendPos; double legendRatio; - unsigned int margin; unsigned int spacing; unsigned int canvasMargin[QwtPlot::axisCnt]; - bool alignCanvasToScales; + bool alignCanvasToScales[QwtPlot::axisCnt]; }; /*! @@ -170,8 +203,9 @@ QwtPlotLayout::QwtPlotLayout() { d_data = new PrivateData; - setLegendPosition(QwtPlot::BottomLegend); - setCanvasMargin(4); + setLegendPosition( QwtPlot::BottomLegend ); + setCanvasMargin( 4 ); + setAlignCanvasToScales( false ); invalidate(); } @@ -182,30 +216,6 @@ QwtPlotLayout::~QwtPlotLayout() delete d_data; } -/*! - Change the margin of the plot. The margin is the space - around all components. - - \param margin new margin - \sa margin(), setSpacing(), - QwtPlot::setMargin() -*/ -void QwtPlotLayout::setMargin(int margin) -{ - if ( margin < 0 ) - margin = 0; - d_data->margin = margin; -} - -/*! - \return margin - \sa setMargin(), spacing(), QwtPlot::margin() -*/ -int QwtPlotLayout::margin() const -{ - return d_data->margin; -} - /*! Change a margin of the canvas. The margin is the space above/below the scale ticks. A negative margin will @@ -216,48 +226,69 @@ int QwtPlotLayout::margin() const -1 means margin at all borders. \sa canvasMargin() - \warning The margin will have no effect when alignCanvasToScales is true + \warning The margin will have no effect when alignCanvasToScale() is true */ -void QwtPlotLayout::setCanvasMargin(int margin, int axis) +void QwtPlotLayout::setCanvasMargin( int margin, int axis ) { if ( margin < -1 ) margin = -1; - if ( axis == -1 ) { - for (axis = 0; axis < QwtPlot::axisCnt; axis++) + if ( axis == -1 ) + { + for ( axis = 0; axis < QwtPlot::axisCnt; axis++ ) d_data->canvasMargin[axis] = margin; - } else if ( axis >= 0 && axis < QwtPlot::axisCnt ) + } + else if ( axis >= 0 && axis < QwtPlot::axisCnt ) d_data->canvasMargin[axis] = margin; } /*! + \param axisId Axis index \return Margin around the scale tick borders \sa setCanvasMargin() */ -int QwtPlotLayout::canvasMargin(int axis) const +int QwtPlotLayout::canvasMargin( int axisId ) const { - if ( axis < 0 || axis >= QwtPlot::axisCnt ) + if ( axisId < 0 || axisId >= QwtPlot::axisCnt ) return 0; - return d_data->canvasMargin[axis]; + return d_data->canvasMargin[axisId]; +} + +/*! + \brief Set the align-canvas-to-axis-scales flag for all axes + + \param on True/False + \sa setAlignCanvasToScale(), alignCanvasToScale() +*/ +void QwtPlotLayout::setAlignCanvasToScales( bool on ) +{ + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + d_data->alignCanvasToScales[axis] = on; } /*! Change the align-canvas-to-axis-scales setting. The canvas may: + - extend beyond the axis scale ends to maximize its size, - align with the axis scale ends to control its size. - \param alignCanvasToScales New align-canvas-to-axis-scales setting + The axisId parameter is somehow confusing as it identifies a border + of the plot and not the axes, that are aligned. F.e when QwtPlot::yLeft + is set, the left end of the the x-axes ( QwtPlot::xTop, QwtPlot::xBottom ) + is aligned. + + \param axisId Axis index + \param on New align-canvas-to-axis-scales setting - \sa setCanvasMargin() - \note In this context the term 'scale' means the backbone of a scale. - \warning In case of alignCanvasToScales == true canvasMargin will have - no effect + \sa setCanvasMargin(), alignCanvasToScale(), setAlignCanvasToScales() + \warning In case of on == true canvasMargin() will have no effect */ -void QwtPlotLayout::setAlignCanvasToScales(bool alignCanvasToScales) +void QwtPlotLayout::setAlignCanvasToScale( int axisId, bool on ) { - d_data->alignCanvasToScales = alignCanvasToScales; + if ( axisId >= 0 && axisId < QwtPlot::axisCnt ) + d_data->alignCanvasToScales[axisId] = on; } /*! @@ -265,29 +296,32 @@ void QwtPlotLayout::setAlignCanvasToScales(bool alignCanvasToScales) - extend beyond the axis scale ends to maximize its size - align with the axis scale ends to control its size. + \param axisId Axis index \return align-canvas-to-axis-scales setting - \sa setAlignCanvasToScales, setCanvasMargin() - \note In this context the term 'scale' means the backbone of a scale. + \sa setAlignCanvasToScale(), setAlignCanvasToScale(), setCanvasMargin() */ -bool QwtPlotLayout::alignCanvasToScales() const +bool QwtPlotLayout::alignCanvasToScale( int axisId ) const { - return d_data->alignCanvasToScales; + if ( axisId < 0 || axisId >= QwtPlot::axisCnt ) + return false; + + return d_data->alignCanvasToScales[ axisId ]; } /*! Change the spacing of the plot. The spacing is the distance between the plot components. - \param spacing new spacing - \sa setMargin(), spacing() + \param spacing New spacing + \sa setCanvasMargin(), spacing() */ -void QwtPlotLayout::setSpacing(int spacing) +void QwtPlotLayout::setSpacing( int spacing ) { - d_data->spacing = qwtMax(0, spacing); + d_data->spacing = qMax( 0, spacing ); } /*! - \return spacing + \return Spacing \sa margin(), setSpacing() */ int QwtPlotLayout::spacing() const @@ -298,8 +332,8 @@ int QwtPlotLayout::spacing() const /*! \brief Specify the position of the legend \param pos The legend's position. - \param ratio Ratio between legend and the bounding rect - of title, canvas and axes. The legend will be shrinked + \param ratio Ratio between legend and the bounding rectangle + of title, footer, canvas and axes. The legend will be shrunk if it would need more space than the given ratio. The ratio is limited to ]0.0 .. 1.0]. In case of <= 0.0 it will be reset to the default ratio. @@ -308,31 +342,29 @@ int QwtPlotLayout::spacing() const \sa QwtPlot::setLegendPosition() */ -void QwtPlotLayout::setLegendPosition(QwtPlot::LegendPosition pos, double ratio) +void QwtPlotLayout::setLegendPosition( QwtPlot::LegendPosition pos, double ratio ) { if ( ratio > 1.0 ) ratio = 1.0; - switch(pos) { - case QwtPlot::TopLegend: - case QwtPlot::BottomLegend: - if ( ratio <= 0.0 ) - ratio = 0.33; - d_data->legendRatio = ratio; - d_data->legendPos = pos; - break; - case QwtPlot::LeftLegend: - case QwtPlot::RightLegend: - if ( ratio <= 0.0 ) - ratio = 0.5; - d_data->legendRatio = ratio; - d_data->legendPos = pos; - break; - case QwtPlot::ExternalLegend: - d_data->legendRatio = ratio; // meaningless - d_data->legendPos = pos; - default: - break; + switch ( pos ) + { + case QwtPlot::TopLegend: + case QwtPlot::BottomLegend: + if ( ratio <= 0.0 ) + ratio = 0.33; + d_data->legendRatio = ratio; + d_data->legendPos = pos; + break; + case QwtPlot::LeftLegend: + case QwtPlot::RightLegend: + if ( ratio <= 0.0 ) + ratio = 0.5; + d_data->legendRatio = ratio; + d_data->legendPos = pos; + break; + default: + break; } } @@ -344,9 +376,9 @@ void QwtPlotLayout::setLegendPosition(QwtPlot::LegendPosition pos, double ratio) \sa QwtPlot::setLegendPosition() */ -void QwtPlotLayout::setLegendPosition(QwtPlot::LegendPosition pos) +void QwtPlotLayout::setLegendPosition( QwtPlot::LegendPosition pos ) { - setLegendPosition(pos, 0.0); + setLegendPosition( pos, 0.0 ); } /*! @@ -361,16 +393,16 @@ QwtPlot::LegendPosition QwtPlotLayout::legendPosition() const /*! Specify the relative size of the legend in the plot - \param ratio Ratio between legend and the bounding rect - of title, canvas and axes. The legend will be shrinked + \param ratio Ratio between legend and the bounding rectangle + of title, footer, canvas and axes. The legend will be shrunk if it would need more space than the given ratio. The ratio is limited to ]0.0 .. 1.0]. In case of <= 0.0 it will be reset to the default ratio. The default vertical/horizontal ratio is 0.33/0.5. */ -void QwtPlotLayout::setLegendRatio(double ratio) +void QwtPlotLayout::setLegendRatio( double ratio ) { - setLegendPosition(legendPosition(), ratio); + setLegendPosition( legendPosition(), ratio ); } /*! @@ -382,47 +414,124 @@ double QwtPlotLayout::legendRatio() const return d_data->legendRatio; } +/*! + \brief Set the geometry for the title + + This method is intended to be used from derived layouts + overloading activate() + + \sa titleRect(), activate() + */ +void QwtPlotLayout::setTitleRect( const QRectF &rect ) +{ + d_data->titleRect = rect; +} + /*! \return Geometry for the title \sa activate(), invalidate() */ - -const QRect &QwtPlotLayout::titleRect() const +QRectF QwtPlotLayout::titleRect() const { return d_data->titleRect; } /*! - \return Geometry for the legend + \brief Set the geometry for the footer + + This method is intended to be used from derived layouts + overloading activate() + + \sa footerRect(), activate() + */ +void QwtPlotLayout::setFooterRect( const QRectF &rect ) +{ + d_data->footerRect = rect; +} + +/*! + \return Geometry for the footer \sa activate(), invalidate() */ +QRectF QwtPlotLayout::footerRect() const +{ + return d_data->footerRect; +} + +/*! + \brief Set the geometry for the legend + + This method is intended to be used from derived layouts + overloading activate() + + \param rect Rectangle for the legend -const QRect &QwtPlotLayout::legendRect() const + \sa legendRect(), activate() + */ +void QwtPlotLayout::setLegendRect( const QRectF &rect ) +{ + d_data->legendRect = rect; +} + +/*! + \return Geometry for the legend + \sa activate(), invalidate() +*/ +QRectF QwtPlotLayout::legendRect() const { return d_data->legendRect; } +/*! + \brief Set the geometry for an axis + + This method is intended to be used from derived layouts + overloading activate() + + \param axis Axis index + \param rect Rectangle for the scale + + \sa scaleRect(), activate() + */ +void QwtPlotLayout::setScaleRect( int axis, const QRectF &rect ) +{ + if ( axis >= 0 && axis < QwtPlot::axisCnt ) + d_data->scaleRect[axis] = rect; +} + /*! \param axis Axis index \return Geometry for the scale \sa activate(), invalidate() */ - -const QRect &QwtPlotLayout::scaleRect(int axis) const +QRectF QwtPlotLayout::scaleRect( int axis ) const { - if ( axis < 0 || axis >= QwtPlot::axisCnt ) { - static QRect dummyRect; + if ( axis < 0 || axis >= QwtPlot::axisCnt ) + { + static QRectF dummyRect; return dummyRect; } return d_data->scaleRect[axis]; } +/*! + \brief Set the geometry for the canvas + + This method is intended to be used from derived layouts + overloading activate() + + \sa canvasRect(), activate() + */ +void QwtPlotLayout::setCanvasRect( const QRectF &rect ) +{ + d_data->canvasRect = rect; +} + /*! \return Geometry for the canvas \sa activate(), invalidate() */ - -const QRect &QwtPlotLayout::canvasRect() const +QRectF QwtPlotLayout::canvasRect() const { return d_data->canvasRect; } @@ -433,22 +542,27 @@ const QRect &QwtPlotLayout::canvasRect() const */ void QwtPlotLayout::invalidate() { - d_data->titleRect = d_data->legendRect = d_data->canvasRect = QRect(); - for (int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + d_data->titleRect = d_data->footerRect + = d_data->legendRect = d_data->canvasRect = QRect(); + + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) d_data->scaleRect[axis] = QRect(); } /*! - \brief Return a minimum size hint + \return Minimum size hint + \param plot Plot widget + \sa QwtPlot::minimumSizeHint() */ -QSize QwtPlotLayout::minimumSizeHint(const QwtPlot *plot) const +QSize QwtPlotLayout::minimumSizeHint( const QwtPlot *plot ) const { class ScaleData { public: - ScaleData() { + ScaleData() + { w = h = minLeft = minRight = tickOffset = 0; } @@ -461,40 +575,47 @@ QSize QwtPlotLayout::minimumSizeHint(const QwtPlot *plot) const int canvasBorder[QwtPlot::axisCnt]; + int fw; + plot->canvas()->getContentsMargins( &fw, NULL, NULL, NULL ); + int axis; - for ( axis = 0; axis < QwtPlot::axisCnt; axis++ ) { - if ( plot->axisEnabled(axis) ) { - const QwtScaleWidget *scl = plot->axisWidget(axis); + for ( axis = 0; axis < QwtPlot::axisCnt; axis++ ) + { + if ( plot->axisEnabled( axis ) ) + { + const QwtScaleWidget *scl = plot->axisWidget( axis ); ScaleData &sd = scaleData[axis]; const QSize hint = scl->minimumSizeHint(); sd.w = hint.width(); sd.h = hint.height(); - scl->getBorderDistHint(sd.minLeft, sd.minRight); + scl->getBorderDistHint( sd.minLeft, sd.minRight ); sd.tickOffset = scl->margin(); - if ( scl->scaleDraw()->hasComponent(QwtAbstractScaleDraw::Ticks) ) - sd.tickOffset += scl->scaleDraw()->majTickLength(); + if ( scl->scaleDraw()->hasComponent( QwtAbstractScaleDraw::Ticks ) ) + sd.tickOffset += qCeil( scl->scaleDraw()->maxTickLength() ); } - canvasBorder[axis] = plot->canvas()->frameWidth() + - d_data->canvasMargin[axis] + 1; - + canvasBorder[axis] = fw + d_data->canvasMargin[axis] + 1; } - for ( axis = 0; axis < QwtPlot::axisCnt; axis++ ) { + for ( axis = 0; axis < QwtPlot::axisCnt; axis++ ) + { ScaleData &sd = scaleData[axis]; - if ( sd.w && (axis == QwtPlot::xBottom || axis == QwtPlot::xTop) ) { - if ( (sd.minLeft > canvasBorder[QwtPlot::yLeft]) - && scaleData[QwtPlot::yLeft].w ) { + if ( sd.w && ( axis == QwtPlot::xBottom || axis == QwtPlot::xTop ) ) + { + if ( ( sd.minLeft > canvasBorder[QwtPlot::yLeft] ) + && scaleData[QwtPlot::yLeft].w ) + { int shiftLeft = sd.minLeft - canvasBorder[QwtPlot::yLeft]; if ( shiftLeft > scaleData[QwtPlot::yLeft].w ) shiftLeft = scaleData[QwtPlot::yLeft].w; sd.w -= shiftLeft; } - if ( (sd.minRight > canvasBorder[QwtPlot::yRight]) - && scaleData[QwtPlot::yRight].w ) { + if ( ( sd.minRight > canvasBorder[QwtPlot::yRight] ) + && scaleData[QwtPlot::yRight].w ) + { int shiftRight = sd.minRight - canvasBorder[QwtPlot::yRight]; if ( shiftRight > scaleData[QwtPlot::yRight].w ) shiftRight = scaleData[QwtPlot::yRight].w; @@ -503,17 +624,20 @@ QSize QwtPlotLayout::minimumSizeHint(const QwtPlot *plot) const } } - if ( sd.h && (axis == QwtPlot::yLeft || axis == QwtPlot::yRight) ) { - if ( (sd.minLeft > canvasBorder[QwtPlot::xBottom]) && - scaleData[QwtPlot::xBottom].h ) { + if ( sd.h && ( axis == QwtPlot::yLeft || axis == QwtPlot::yRight ) ) + { + if ( ( sd.minLeft > canvasBorder[QwtPlot::xBottom] ) && + scaleData[QwtPlot::xBottom].h ) + { int shiftBottom = sd.minLeft - canvasBorder[QwtPlot::xBottom]; if ( shiftBottom > scaleData[QwtPlot::xBottom].tickOffset ) shiftBottom = scaleData[QwtPlot::xBottom].tickOffset; sd.h -= shiftBottom; } - if ( (sd.minLeft > canvasBorder[QwtPlot::xTop]) && - scaleData[QwtPlot::xTop].h ) { + if ( ( sd.minLeft > canvasBorder[QwtPlot::xTop] ) && + scaleData[QwtPlot::xTop].h ) + { int shiftTop = sd.minRight - canvasBorder[QwtPlot::xTop]; if ( shiftTop > scaleData[QwtPlot::xTop].tickOffset ) shiftTop = scaleData[QwtPlot::xTop].tickOffset; @@ -523,136 +647,158 @@ QSize QwtPlotLayout::minimumSizeHint(const QwtPlot *plot) const } } - const QwtPlotCanvas *canvas = plot->canvas(); + const QWidget *canvas = plot->canvas(); + + int left, top, right, bottom; + canvas->getContentsMargins( &left, &top, &right, &bottom ); + const QSize minCanvasSize = canvas->minimumSize(); int w = scaleData[QwtPlot::yLeft].w + scaleData[QwtPlot::yRight].w; - int cw = qwtMax(scaleData[QwtPlot::xBottom].w, scaleData[QwtPlot::xTop].w) - + 2 * (canvas->frameWidth() + 1); - w += qwtMax(cw, minCanvasSize.width()); + int cw = qMax( scaleData[QwtPlot::xBottom].w, scaleData[QwtPlot::xTop].w ) + + left + 1 + right + 1; + w += qMax( cw, minCanvasSize.width() ); int h = scaleData[QwtPlot::xBottom].h + scaleData[QwtPlot::xTop].h; - int ch = qwtMax(scaleData[QwtPlot::yLeft].h, scaleData[QwtPlot::yRight].h) - + 2 * (canvas->frameWidth() + 1); - h += qwtMax(ch, minCanvasSize.height()); - - const QwtTextLabel *title = plot->titleLabel(); - if (title && !title->text().isEmpty()) { - // If only QwtPlot::yLeft or QwtPlot::yRight is showing, - // we center on the plot canvas. - const bool centerOnCanvas = !(plot->axisEnabled(QwtPlot::yLeft) - && plot->axisEnabled(QwtPlot::yRight)); - - int titleW = w; - if ( centerOnCanvas ) { - titleW -= scaleData[QwtPlot::yLeft].w - + scaleData[QwtPlot::yRight].w; - } + int ch = qMax( scaleData[QwtPlot::yLeft].h, scaleData[QwtPlot::yRight].h ) + + top + 1 + bottom + 1; + h += qMax( ch, minCanvasSize.height() ); + + const QwtTextLabel *labels[2]; + labels[0] = plot->titleLabel(); + labels[1] = plot->footerLabel(); - int titleH = title->heightForWidth(titleW); - if ( titleH > titleW ) { // Compensate for a long title - w = titleW = titleH; - if ( centerOnCanvas ) { - w += scaleData[QwtPlot::yLeft].w - + scaleData[QwtPlot::yRight].w; + for ( int i = 0; i < 2; i++ ) + { + const QwtTextLabel *label = labels[i]; + if ( label && !label->text().isEmpty() ) + { + // If only QwtPlot::yLeft or QwtPlot::yRight is showing, + // we center on the plot canvas. + const bool centerOnCanvas = !( plot->axisEnabled( QwtPlot::yLeft ) + && plot->axisEnabled( QwtPlot::yRight ) ); + + int labelW = w; + if ( centerOnCanvas ) + { + labelW -= scaleData[QwtPlot::yLeft].w + + scaleData[QwtPlot::yRight].w; } - titleH = title->heightForWidth(titleW); + int labelH = label->heightForWidth( labelW ); + if ( labelH > labelW ) // Compensate for a long title + { + w = labelW = labelH; + if ( centerOnCanvas ) + { + w += scaleData[QwtPlot::yLeft].w + + scaleData[QwtPlot::yRight].w; + } + + labelH = label->heightForWidth( labelW ); + } + h += labelH + d_data->spacing; } - h += titleH + d_data->spacing; } // Compute the legend contribution - const QwtLegend *legend = plot->legend(); - if ( d_data->legendPos != QwtPlot::ExternalLegend - && legend && !legend->isEmpty() ) { + const QwtAbstractLegend *legend = plot->legend(); + if ( legend && !legend->isEmpty() ) + { if ( d_data->legendPos == QwtPlot::LeftLegend - || d_data->legendPos == QwtPlot::RightLegend ) { + || d_data->legendPos == QwtPlot::RightLegend ) + { int legendW = legend->sizeHint().width(); - int legendH = legend->heightForWidth(legendW); + int legendH = legend->heightForWidth( legendW ); if ( legend->frameWidth() > 0 ) w += d_data->spacing; if ( legendH > h ) - legendW += legend->verticalScrollBar()->sizeHint().height(); + legendW += legend->scrollExtent( Qt::Horizontal ); if ( d_data->legendRatio < 1.0 ) - legendW = qwtMin(legendW, int(w / (1.0 - d_data->legendRatio))); + legendW = qMin( legendW, int( w / ( 1.0 - d_data->legendRatio ) ) ); - w += legendW; - } else { // QwtPlot::Top, QwtPlot::Bottom - int legendW = qwtMin(legend->sizeHint().width(), w); - int legendH = legend->heightForWidth(legendW); + w += legendW + d_data->spacing; + } + else // QwtPlot::Top, QwtPlot::Bottom + { + int legendW = qMin( legend->sizeHint().width(), w ); + int legendH = legend->heightForWidth( legendW ); if ( legend->frameWidth() > 0 ) h += d_data->spacing; if ( d_data->legendRatio < 1.0 ) - legendH = qwtMin(legendH, int(h / (1.0 - d_data->legendRatio))); + legendH = qMin( legendH, int( h / ( 1.0 - d_data->legendRatio ) ) ); - h += legendH; + h += legendH + d_data->spacing; } } - w += 2 * d_data->margin; - h += 2 * d_data->margin; - return QSize( w, h ); } /*! Find the geometry for the legend + \param options Options how to layout the legend \param rect Rectangle where to place the legend + \return Geometry for the legend + \sa Options */ -QRect QwtPlotLayout::layoutLegend(int options, - const QRect &rect) const +QRectF QwtPlotLayout::layoutLegend( Options options, + const QRectF &rect ) const { - const QSize hint(d_data->layoutData.legend.hint); + const QSize hint( d_data->layoutData.legend.hint ); int dim; if ( d_data->legendPos == QwtPlot::LeftLegend - || d_data->legendPos == QwtPlot::RightLegend ) { + || d_data->legendPos == QwtPlot::RightLegend ) + { // We don't allow vertical legends to take more than // half of the available space. - dim = qwtMin(hint.width(), int(rect.width() * d_data->legendRatio)); + dim = qMin( hint.width(), int( rect.width() * d_data->legendRatio ) ); - if ( !(options & IgnoreScrollbars) ) { - if ( hint.height() > rect.height() ) { + if ( !( options & IgnoreScrollbars ) ) + { + if ( hint.height() > rect.height() ) + { // The legend will need additional // space for the vertical scrollbar. - dim += d_data->layoutData.legend.vScrollBarWidth; + dim += d_data->layoutData.legend.hScrollExtent; } } - } else { - dim = qwtMin(hint.height(), int(rect.height() * d_data->legendRatio)); - dim = qwtMax(dim, d_data->layoutData.legend.hScrollBarHeight); + } + else + { + dim = qMin( hint.height(), int( rect.height() * d_data->legendRatio ) ); + dim = qMax( dim, d_data->layoutData.legend.vScrollExtent ); } - QRect legendRect = rect; - switch(d_data->legendPos) { - case QwtPlot::LeftLegend: - legendRect.setWidth(dim); - break; - case QwtPlot::RightLegend: - legendRect.setX(rect.right() - dim + 1); - legendRect.setWidth(dim); - break; - case QwtPlot::TopLegend: - legendRect.setHeight(dim); - break; - case QwtPlot::BottomLegend: - legendRect.setY(rect.bottom() - dim + 1); - legendRect.setHeight(dim); - break; - case QwtPlot::ExternalLegend: - break; + QRectF legendRect = rect; + switch ( d_data->legendPos ) + { + case QwtPlot::LeftLegend: + legendRect.setWidth( dim ); + break; + case QwtPlot::RightLegend: + legendRect.setX( rect.right() - dim ); + legendRect.setWidth( dim ); + break; + case QwtPlot::TopLegend: + legendRect.setHeight( dim ); + break; + case QwtPlot::BottomLegend: + legendRect.setY( rect.bottom() - dim ); + legendRect.setHeight( dim ); + break; } return legendRect; @@ -660,25 +806,32 @@ QRect QwtPlotLayout::layoutLegend(int options, /*! Align the legend to the canvas + \param canvasRect Geometry of the canvas \param legendRect Maximum geometry for the legend + \return Geometry for the aligned legend */ -QRect QwtPlotLayout::alignLegend(const QRect &canvasRect, - const QRect &legendRect) const +QRectF QwtPlotLayout::alignLegend( const QRectF &canvasRect, + const QRectF &legendRect ) const { - QRect alignedRect = legendRect; + QRectF alignedRect = legendRect; if ( d_data->legendPos == QwtPlot::BottomLegend - || d_data->legendPos == QwtPlot::TopLegend ) { - if ( d_data->layoutData.legend.hint.width() < canvasRect.width() ) { - alignedRect.setX(canvasRect.x()); - alignedRect.setWidth(canvasRect.width()); + || d_data->legendPos == QwtPlot::TopLegend ) + { + if ( d_data->layoutData.legend.hint.width() < canvasRect.width() ) + { + alignedRect.setX( canvasRect.x() ); + alignedRect.setWidth( canvasRect.width() ); } - } else { - if ( d_data->layoutData.legend.hint.height() < canvasRect.height() ) { - alignedRect.setY(canvasRect.y()); - alignedRect.setHeight(canvasRect.height()); + } + else + { + if ( d_data->layoutData.legend.hint.height() < canvasRect.height() ) + { + alignedRect.setY( canvasRect.y() ); + alignedRect.setHeight( canvasRect.height() ); } } @@ -690,28 +843,34 @@ QRect QwtPlotLayout::alignLegend(const QRect &canvasRect, of their widgets in orientation of the text. \param options Options how to layout the legend - \param rect Bounding rect for title, axes and canvas. + \param rect Bounding rectangle for title, footer, axes and canvas. \param dimTitle Expanded height of the title widget + \param dimFooter Expanded height of the footer widget \param dimAxis Expanded heights of the axis in axis orientation. + + \sa Options */ -void QwtPlotLayout::expandLineBreaks(int options, const QRect &rect, - int &dimTitle, int dimAxis[QwtPlot::axisCnt]) const +void QwtPlotLayout::expandLineBreaks( Options options, const QRectF &rect, + int &dimTitle, int &dimFooter, int dimAxis[QwtPlot::axisCnt] ) const { - dimTitle = 0; + dimTitle = dimFooter = 0; for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) dimAxis[axis] = 0; int backboneOffset[QwtPlot::axisCnt]; - for (int axis = 0; axis < QwtPlot::axisCnt; axis++ ) { + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + { backboneOffset[axis] = 0; - if ( !d_data->alignCanvasToScales ) + if ( !( options & IgnoreFrames ) ) + backboneOffset[axis] += d_data->layoutData.canvas.contentsMargins[ axis ]; + + if ( !d_data->alignCanvasToScales[axis] ) backboneOffset[axis] += d_data->canvasMargin[axis]; - if ( !(options & IgnoreFrames) ) - backboneOffset[axis] += d_data->layoutData.canvas.frameWidth; } bool done = false; - while (!done) { + while ( !done ) + { done = true; // the size for the 4 axis depend on each other. Expanding @@ -722,46 +881,78 @@ void QwtPlotLayout::expandLineBreaks(int options, const QRect &rect, // axis what might result in a line break of a horizontal // axis ... . So we loop as long until no size changes. - if ( !d_data->layoutData.title.text.isEmpty() ) { - int w = rect.width(); + if ( !( ( options & IgnoreTitle ) || + d_data->layoutData.title.text.isEmpty() ) ) + { + double w = rect.width(); if ( d_data->layoutData.scale[QwtPlot::yLeft].isEnabled - != d_data->layoutData.scale[QwtPlot::yRight].isEnabled ) { + != d_data->layoutData.scale[QwtPlot::yRight].isEnabled ) + { // center to the canvas w -= dimAxis[QwtPlot::yLeft] + dimAxis[QwtPlot::yRight]; } - int d = d_data->layoutData.title.text.heightForWidth(w); - if ( !(options & IgnoreFrames) ) + int d = qCeil( d_data->layoutData.title.text.heightForWidth( w ) ); + if ( !( options & IgnoreFrames ) ) d += 2 * d_data->layoutData.title.frameWidth; - if ( d > dimTitle ) { + if ( d > dimTitle ) + { dimTitle = d; done = false; } } - for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) { + if ( !( ( options & IgnoreFooter ) || + d_data->layoutData.footer.text.isEmpty() ) ) + { + double w = rect.width(); + + if ( d_data->layoutData.scale[QwtPlot::yLeft].isEnabled + != d_data->layoutData.scale[QwtPlot::yRight].isEnabled ) + { + // center to the canvas + w -= dimAxis[QwtPlot::yLeft] + dimAxis[QwtPlot::yRight]; + } + + int d = qCeil( d_data->layoutData.footer.text.heightForWidth( w ) ); + if ( !( options & IgnoreFrames ) ) + d += 2 * d_data->layoutData.footer.frameWidth; + + if ( d > dimFooter ) + { + dimFooter = d; + done = false; + } + } + + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + { const struct LayoutData::t_scaleData &scaleData = - d_data->layoutData.scale[axis]; + d_data->layoutData.scale[axis]; - if (scaleData.isEnabled) { - int length; - if ( axis == QwtPlot::xTop || axis == QwtPlot::xBottom ) { + if ( scaleData.isEnabled ) + { + double length; + if ( axis == QwtPlot::xTop || axis == QwtPlot::xBottom ) + { length = rect.width() - dimAxis[QwtPlot::yLeft] - - dimAxis[QwtPlot::yRight]; + - dimAxis[QwtPlot::yRight]; length -= scaleData.start + scaleData.end; if ( dimAxis[QwtPlot::yRight] > 0 ) length -= 1; - length += qwtMin(dimAxis[QwtPlot::yLeft], - scaleData.start - backboneOffset[QwtPlot::yLeft]); - length += qwtMin(dimAxis[QwtPlot::yRight], - scaleData.end - backboneOffset[QwtPlot::yRight]); - } else { // QwtPlot::yLeft, QwtPlot::yRight + length += qMin( dimAxis[QwtPlot::yLeft], + scaleData.start - backboneOffset[QwtPlot::yLeft] ); + length += qMin( dimAxis[QwtPlot::yRight], + scaleData.end - backboneOffset[QwtPlot::yRight] ); + } + else // QwtPlot::yLeft, QwtPlot::yRight + { length = rect.height() - dimAxis[QwtPlot::xTop] - - dimAxis[QwtPlot::xBottom]; + - dimAxis[QwtPlot::xBottom]; length -= scaleData.start + scaleData.end; length -= 1; @@ -770,15 +961,17 @@ void QwtPlotLayout::expandLineBreaks(int options, const QRect &rect, if ( dimAxis[QwtPlot::xTop] <= 0 ) length -= 1; - if ( dimAxis[QwtPlot::xBottom] > 0 ) { - length += qwtMin( - d_data->layoutData.scale[QwtPlot::xBottom].tickOffset, - scaleData.start - backboneOffset[QwtPlot::xBottom]); + if ( dimAxis[QwtPlot::xBottom] > 0 ) + { + length += qMin( + d_data->layoutData.scale[QwtPlot::xBottom].tickOffset, + double( scaleData.start - backboneOffset[QwtPlot::xBottom] ) ); } - if ( dimAxis[QwtPlot::xTop] > 0 ) { - length += qwtMin( - d_data->layoutData.scale[QwtPlot::xTop].tickOffset, - scaleData.end - backboneOffset[QwtPlot::xTop]); + if ( dimAxis[QwtPlot::xTop] > 0 ) + { + length += qMin( + d_data->layoutData.scale[QwtPlot::xTop].tickOffset, + double( scaleData.end - backboneOffset[QwtPlot::xTop] ) ); } if ( dimTitle > 0 ) @@ -786,12 +979,14 @@ void QwtPlotLayout::expandLineBreaks(int options, const QRect &rect, } int d = scaleData.dimWithoutTitle; - if ( !scaleData.scaleWidget->title().isEmpty() ) { - d += scaleData.scaleWidget->titleHeightForWidth(length); + if ( !scaleData.scaleWidget->title().isEmpty() ) + { + d += scaleData.scaleWidget->titleHeightForWidth( qFloor( length ) ); } - if ( d > dimAxis[axis] ) { + if ( d > dimAxis[axis] ) + { dimAxis[axis] = d; done = false; } @@ -803,132 +998,262 @@ void QwtPlotLayout::expandLineBreaks(int options, const QRect &rect, /*! Align the ticks of the axis to the canvas borders using the empty corners. + + \param options Layout options + \param canvasRect Geometry of the canvas ( IN/OUT ) + \param scaleRect Geometries of the scales ( IN/OUT ) + + \sa Options */ -void QwtPlotLayout::alignScales(int options, - QRect &canvasRect, QRect scaleRect[QwtPlot::axisCnt]) const +void QwtPlotLayout::alignScales( Options options, + QRectF &canvasRect, QRectF scaleRect[QwtPlot::axisCnt] ) const { - int axis; - int backboneOffset[QwtPlot::axisCnt]; - for (axis = 0; axis < QwtPlot::axisCnt; axis++ ) { + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + { backboneOffset[axis] = 0; - if ( !d_data->alignCanvasToScales ) + + if ( !d_data->alignCanvasToScales[axis] ) + { backboneOffset[axis] += d_data->canvasMargin[axis]; - if ( !(options & IgnoreFrames) ) - backboneOffset[axis] += d_data->layoutData.canvas.frameWidth; + } + + if ( !( options & IgnoreFrames ) ) + { + backboneOffset[axis] += + d_data->layoutData.canvas.contentsMargins[axis]; + } } - for (axis = 0; axis < QwtPlot::axisCnt; axis++ ) { + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + { if ( !scaleRect[axis].isValid() ) continue; const int startDist = d_data->layoutData.scale[axis].start; const int endDist = d_data->layoutData.scale[axis].end; - QRect &axisRect = scaleRect[axis]; + QRectF &axisRect = scaleRect[axis]; - if ( axis == QwtPlot::xTop || axis == QwtPlot::xBottom ) { + if ( axis == QwtPlot::xTop || axis == QwtPlot::xBottom ) + { + const QRectF &leftScaleRect = scaleRect[QwtPlot::yLeft]; const int leftOffset = backboneOffset[QwtPlot::yLeft] - startDist; - if ( scaleRect[QwtPlot::yLeft].isValid() ) { - int minLeft = scaleRect[QwtPlot::yLeft].left(); - int left = axisRect.left() + leftOffset; - axisRect.setLeft(qwtMax(left, minLeft)); - } else { - if ( d_data->alignCanvasToScales && leftOffset < 0 ) { - canvasRect.setLeft(qwtMax(canvasRect.left(), - axisRect.left() - leftOffset)); - } else { + if ( leftScaleRect.isValid() ) + { + const double dx = leftOffset + leftScaleRect.width(); + if ( d_data->alignCanvasToScales[QwtPlot::yLeft] && dx < 0.0 ) + { + /* + The axis needs more space than the width + of the left scale. + */ + const double cLeft = canvasRect.left(); // qreal -> double + canvasRect.setLeft( qMax( cLeft, axisRect.left() - dx ) ); + } + else + { + const double minLeft = leftScaleRect.left(); + const double left = axisRect.left() + leftOffset; + axisRect.setLeft( qMax( left, minLeft ) ); + } + } + else + { + if ( d_data->alignCanvasToScales[QwtPlot::yLeft] && leftOffset < 0 ) + { + canvasRect.setLeft( qMax( canvasRect.left(), + axisRect.left() - leftOffset ) ); + } + else + { if ( leftOffset > 0 ) - axisRect.setLeft(axisRect.left() + leftOffset); + axisRect.setLeft( axisRect.left() + leftOffset ); } } + const QRectF &rightScaleRect = scaleRect[QwtPlot::yRight]; const int rightOffset = backboneOffset[QwtPlot::yRight] - endDist + 1; - if ( scaleRect[QwtPlot::yRight].isValid() ) { - int maxRight = scaleRect[QwtPlot::yRight].right(); - int right = axisRect.right() - rightOffset; - axisRect.setRight(qwtMin(right, maxRight)); - } else { - if ( d_data->alignCanvasToScales && rightOffset < 0 ) { - canvasRect.setRight( qwtMin(canvasRect.right(), - axisRect.right() + rightOffset) ); - } else { + if ( rightScaleRect.isValid() ) + { + const double dx = rightOffset + rightScaleRect.width(); + if ( d_data->alignCanvasToScales[QwtPlot::yRight] && dx < 0 ) + { + /* + The axis needs more space than the width + of the right scale. + */ + const double cRight = canvasRect.right(); // qreal -> double + canvasRect.setRight( qMin( cRight, axisRect.right() + dx ) ); + } + + const double maxRight = rightScaleRect.right(); + const double right = axisRect.right() - rightOffset; + axisRect.setRight( qMin( right, maxRight ) ); + } + else + { + if ( d_data->alignCanvasToScales[QwtPlot::yRight] && rightOffset < 0 ) + { + canvasRect.setRight( qMin( canvasRect.right(), + axisRect.right() + rightOffset ) ); + } + else + { if ( rightOffset > 0 ) - axisRect.setRight(axisRect.right() - rightOffset); + axisRect.setRight( axisRect.right() - rightOffset ); } } - } else { // QwtPlot::yLeft, QwtPlot::yRight + } + else // QwtPlot::yLeft, QwtPlot::yRight + { + const QRectF &bottomScaleRect = scaleRect[QwtPlot::xBottom]; const int bottomOffset = backboneOffset[QwtPlot::xBottom] - endDist + 1; - if ( scaleRect[QwtPlot::xBottom].isValid() ) { - int maxBottom = scaleRect[QwtPlot::xBottom].top() + - d_data->layoutData.scale[QwtPlot::xBottom].tickOffset; - - int bottom = axisRect.bottom() - bottomOffset; - axisRect.setBottom(qwtMin(bottom, maxBottom)); - } else { - if ( d_data->alignCanvasToScales && bottomOffset < 0 ) { - canvasRect.setBottom(qwtMin(canvasRect.bottom(), - axisRect.bottom() + bottomOffset)); - } else { + if ( bottomScaleRect.isValid() ) + { + const double dy = bottomOffset + bottomScaleRect.height(); + if ( d_data->alignCanvasToScales[QwtPlot::xBottom] && dy < 0 ) + { + /* + The axis needs more space than the height + of the bottom scale. + */ + const double cBottom = canvasRect.bottom(); // qreal -> double + canvasRect.setBottom( qMin( cBottom, axisRect.bottom() + dy ) ); + } + else + { + const double maxBottom = bottomScaleRect.top() + + d_data->layoutData.scale[QwtPlot::xBottom].tickOffset; + const double bottom = axisRect.bottom() - bottomOffset; + axisRect.setBottom( qMin( bottom, maxBottom ) ); + } + } + else + { + if ( d_data->alignCanvasToScales[QwtPlot::xBottom] && bottomOffset < 0 ) + { + canvasRect.setBottom( qMin( canvasRect.bottom(), + axisRect.bottom() + bottomOffset ) ); + } + else + { if ( bottomOffset > 0 ) - axisRect.setBottom(axisRect.bottom() - bottomOffset); + axisRect.setBottom( axisRect.bottom() - bottomOffset ); } } + const QRectF &topScaleRect = scaleRect[QwtPlot::xTop]; const int topOffset = backboneOffset[QwtPlot::xTop] - startDist; - if ( scaleRect[QwtPlot::xTop].isValid() ) { - int minTop = scaleRect[QwtPlot::xTop].bottom() - - d_data->layoutData.scale[QwtPlot::xTop].tickOffset; - - int top = axisRect.top() + topOffset; - axisRect.setTop(qwtMax(top, minTop)); - } else { - if ( d_data->alignCanvasToScales && topOffset < 0 ) { - canvasRect.setTop(qwtMax(canvasRect.top(), - axisRect.top() - topOffset)); - } else { + if ( topScaleRect.isValid() ) + { + const double dy = topOffset + topScaleRect.height(); + if ( d_data->alignCanvasToScales[QwtPlot::xTop] && dy < 0 ) + { + /* + The axis needs more space than the height + of the top scale. + */ + const double cTop = canvasRect.top(); // qreal -> double + canvasRect.setTop( qMax( cTop, axisRect.top() - dy ) ); + } + else + { + const double minTop = topScaleRect.bottom() - + d_data->layoutData.scale[QwtPlot::xTop].tickOffset; + const double top = axisRect.top() + topOffset; + axisRect.setTop( qMax( top, minTop ) ); + } + } + else + { + if ( d_data->alignCanvasToScales[QwtPlot::xTop] && topOffset < 0 ) + { + canvasRect.setTop( qMax( canvasRect.top(), + axisRect.top() - topOffset ) ); + } + else + { if ( topOffset > 0 ) - axisRect.setTop(axisRect.top() + topOffset); + axisRect.setTop( axisRect.top() + topOffset ); } } } } - if ( d_data->alignCanvasToScales ) { - /* - The canvas has been aligned to the scale with largest - border distances. Now we have to realign the other scale. - */ - - int fw = 0; - if ( !(options & IgnoreFrames) ) - fw = d_data->layoutData.canvas.frameWidth; - - if ( scaleRect[QwtPlot::xBottom].isValid() && - scaleRect[QwtPlot::xTop].isValid() ) { - for ( int axis = QwtPlot::xBottom; axis <= QwtPlot::xTop; axis++ ) { - scaleRect[axis].setLeft(canvasRect.left() + fw - - d_data->layoutData.scale[axis].start); - scaleRect[axis].setRight(canvasRect.right() - fw - 1 - + d_data->layoutData.scale[axis].end); + /* + The canvas has been aligned to the scale with largest + border distances. Now we have to realign the other scale. + */ + + + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + { + QRectF &sRect = scaleRect[axis]; + + if ( !sRect.isValid() ) + continue; + + if ( axis == QwtPlot::xBottom || axis == QwtPlot::xTop ) + { + if ( d_data->alignCanvasToScales[QwtPlot::yLeft] ) + { + double y = canvasRect.left() - d_data->layoutData.scale[axis].start; + if ( !( options & IgnoreFrames ) ) + y += d_data->layoutData.canvas.contentsMargins[ QwtPlot::yLeft ]; + + sRect.setLeft( y ); + } + if ( d_data->alignCanvasToScales[QwtPlot::yRight] ) + { + double y = canvasRect.right() - 1 + d_data->layoutData.scale[axis].end; + if ( !( options & IgnoreFrames ) ) + y -= d_data->layoutData.canvas.contentsMargins[ QwtPlot::yRight ]; + + sRect.setRight( y ); + } + + if ( d_data->alignCanvasToScales[ axis ] ) + { + if ( axis == QwtPlot::xTop ) + sRect.setBottom( canvasRect.top() ); + else + sRect.setTop( canvasRect.bottom() ); } } + else + { + if ( d_data->alignCanvasToScales[QwtPlot::xTop] ) + { + double x = canvasRect.top() - d_data->layoutData.scale[axis].start; + if ( !( options & IgnoreFrames ) ) + x += d_data->layoutData.canvas.contentsMargins[ QwtPlot::xTop ]; + + sRect.setTop( x ); + } + if ( d_data->alignCanvasToScales[QwtPlot::xBottom] ) + { + double x = canvasRect.bottom() - 1 + d_data->layoutData.scale[axis].end; + if ( !( options & IgnoreFrames ) ) + x -= d_data->layoutData.canvas.contentsMargins[ QwtPlot::xBottom ]; + + sRect.setBottom( x ); + } - if ( scaleRect[QwtPlot::yLeft].isValid() && - scaleRect[QwtPlot::yRight].isValid() ) { - for ( int axis = QwtPlot::yLeft; axis <= QwtPlot::yRight; axis++ ) { - scaleRect[axis].setTop(canvasRect.top() + fw - - d_data->layoutData.scale[axis].start); - scaleRect[axis].setBottom(canvasRect.bottom() - fw - 1 - + d_data->layoutData.scale[axis].end); + if ( d_data->alignCanvasToScales[ axis ] ) + { + if ( axis == QwtPlot::yLeft ) + sRect.setRight( canvasRect.left() ); + else + sRect.setLeft( canvasRect.right() ); } } } @@ -938,72 +1263,51 @@ void QwtPlotLayout::alignScales(int options, \brief Recalculate the geometry of all components. \param plot Plot to be layout - \param plotRect Rect where to place the components - \param options Options + \param plotRect Rectangle where to place the components + \param options Layout options - \sa invalidate(), titleRect(), + \sa invalidate(), titleRect(), footerRect() legendRect(), scaleRect(), canvasRect() */ -void QwtPlotLayout::activate(const QwtPlot *plot, - const QRect &plotRect, int options) +void QwtPlotLayout::activate( const QwtPlot *plot, + const QRectF &plotRect, Options options ) { invalidate(); - QRect rect(plotRect); // undistributed rest of the plot rect + QRectF rect( plotRect ); // undistributed rest of the plot rect - if ( !(options & IgnoreMargin) ) { - // subtract the margin + // We extract all layout relevant parameters from the widgets, + // and save them to d_data->layoutData. - rect.setRect( - rect.x() + d_data->margin, - rect.y() + d_data->margin, - rect.width() - 2 * d_data->margin, - rect.height() - 2 * d_data->margin - ); - } + d_data->layoutData.init( plot, rect ); - // We extract all layout relevant data from the widgets, - // filter them through pfilter and save them to d_data->layoutData. - - d_data->layoutData.init(plot, rect); - - if (!(options & IgnoreLegend) - && d_data->legendPos != QwtPlot::ExternalLegend - && plot->legend() && !plot->legend()->isEmpty() ) { - d_data->legendRect = layoutLegend(options, rect); + if ( !( options & IgnoreLegend ) + && plot->legend() && !plot->legend()->isEmpty() ) + { + d_data->legendRect = layoutLegend( options, rect ); // subtract d_data->legendRect from rect - const QRegion region(rect); - rect = region.subtract(d_data->legendRect).boundingRect(); + const QRegion region( rect.toRect() ); + rect = region.subtracted( d_data->legendRect.toRect() ).boundingRect(); - if ( d_data->layoutData.legend.frameWidth && - !(options & IgnoreFrames ) ) { - // In case of a frame we have to insert a spacing. - // Otherwise the leading of the font separates - // legend and scale/canvas - - switch(d_data->legendPos) { + switch ( d_data->legendPos ) + { case QwtPlot::LeftLegend: - rect.setLeft(rect.left() + d_data->spacing); + rect.setLeft( rect.left() + d_data->spacing ); break; case QwtPlot::RightLegend: - rect.setRight(rect.right() - d_data->spacing); + rect.setRight( rect.right() - d_data->spacing ); break; case QwtPlot::TopLegend: - rect.setTop(rect.top() + d_data->spacing); + rect.setTop( rect.top() + d_data->spacing ); break; case QwtPlot::BottomLegend: - rect.setBottom(rect.bottom() - d_data->spacing); + rect.setBottom( rect.bottom() - d_data->spacing ); break; - case QwtPlot::ExternalLegend: - break; // suppress compiler warning - } } } -#ifdef __GNUC__ -#endif /* +---+-----------+---+ | Title | @@ -1017,73 +1321,94 @@ void QwtPlotLayout::activate(const QwtPlot *plot, +---+-----------+---+ | | Axis | | +---+-----------+---+ + | Footer | + +---+-----------+---+ */ - // axes and title include text labels. The height of each + // title, footer and axes include text labels. The height of each // label depends on its line breaks, that depend on the width // for the label. A line break in a horizontal text will reduce // the available width for vertical texts and vice versa. - // expandLineBreaks finds the height/width for title and axes + // expandLineBreaks finds the height/width for title, footer and axes // including all line breaks. - int dimTitle, dimAxes[QwtPlot::axisCnt]; - expandLineBreaks(options, rect, dimTitle, dimAxes); + int dimTitle, dimFooter, dimAxes[QwtPlot::axisCnt]; + expandLineBreaks( options, rect, dimTitle, dimFooter, dimAxes ); - if (dimTitle > 0 ) { - d_data->titleRect = QRect(rect.x(), rect.y(), - rect.width(), dimTitle); + if ( dimTitle > 0 ) + { + d_data->titleRect.setRect( + rect.left(), rect.top(), rect.width(), dimTitle ); + + rect.setTop( d_data->titleRect.bottom() + d_data->spacing ); if ( d_data->layoutData.scale[QwtPlot::yLeft].isEnabled != - d_data->layoutData.scale[QwtPlot::yRight].isEnabled ) { + d_data->layoutData.scale[QwtPlot::yRight].isEnabled ) + { // if only one of the y axes is missing we align // the title centered to the canvas - d_data->titleRect.setX(rect.x() + dimAxes[QwtPlot::yLeft]); - d_data->titleRect.setWidth(rect.width() - - dimAxes[QwtPlot::yLeft] - dimAxes[QwtPlot::yRight]); + d_data->titleRect.setX( rect.left() + dimAxes[QwtPlot::yLeft] ); + d_data->titleRect.setWidth( rect.width() + - dimAxes[QwtPlot::yLeft] - dimAxes[QwtPlot::yRight] ); } + } - // subtract title - rect.setTop(rect.top() + dimTitle + d_data->spacing); + if ( dimFooter > 0 ) + { + d_data->footerRect.setRect( + rect.left(), rect.bottom() - dimFooter, rect.width(), dimFooter ); + + rect.setBottom( d_data->footerRect.top() - d_data->spacing ); + + if ( d_data->layoutData.scale[QwtPlot::yLeft].isEnabled != + d_data->layoutData.scale[QwtPlot::yRight].isEnabled ) + { + // if only one of the y axes is missing we align + // the footer centered to the canvas + + d_data->footerRect.setX( rect.left() + dimAxes[QwtPlot::yLeft] ); + d_data->footerRect.setWidth( rect.width() + - dimAxes[QwtPlot::yLeft] - dimAxes[QwtPlot::yRight] ); + } } d_data->canvasRect.setRect( rect.x() + dimAxes[QwtPlot::yLeft], rect.y() + dimAxes[QwtPlot::xTop], rect.width() - dimAxes[QwtPlot::yRight] - dimAxes[QwtPlot::yLeft], - rect.height() - dimAxes[QwtPlot::xBottom] - dimAxes[QwtPlot::xTop]); + rect.height() - dimAxes[QwtPlot::xBottom] - dimAxes[QwtPlot::xTop] ); - for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) { + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + { // set the rects for the axes - if ( dimAxes[axis] ) { + if ( dimAxes[axis] ) + { int dim = dimAxes[axis]; - QRect &scaleRect = d_data->scaleRect[axis]; + QRectF &scaleRect = d_data->scaleRect[axis]; scaleRect = d_data->canvasRect; - switch(axis) { - case QwtPlot::yLeft: - scaleRect.setX(d_data->canvasRect.left() - dim); - scaleRect.setWidth(dim); - break; - case QwtPlot::yRight: - scaleRect.setX(d_data->canvasRect.right() + 1); - scaleRect.setWidth(dim); - break; - case QwtPlot::xBottom: - scaleRect.setY(d_data->canvasRect.bottom() + 1); - scaleRect.setHeight(dim); - break; - case QwtPlot::xTop: - scaleRect.setY(d_data->canvasRect.top() - dim); - scaleRect.setHeight(dim); - break; + switch ( axis ) + { + case QwtPlot::yLeft: + scaleRect.setX( d_data->canvasRect.left() - dim ); + scaleRect.setWidth( dim ); + break; + case QwtPlot::yRight: + scaleRect.setX( d_data->canvasRect.right() ); + scaleRect.setWidth( dim ); + break; + case QwtPlot::xBottom: + scaleRect.setY( d_data->canvasRect.bottom() ); + scaleRect.setHeight( dim ); + break; + case QwtPlot::xTop: + scaleRect.setY( d_data->canvasRect.top() - dim ); + scaleRect.setHeight( dim ); + break; } -#if QT_VERSION < 0x040000 - scaleRect = scaleRect.normalize(); -#else scaleRect = scaleRect.normalized(); -#endif } } @@ -1107,12 +1432,13 @@ void QwtPlotLayout::activate(const QwtPlot *plot, // corners to extend the axes, so that the label texts // left/right of the min/max ticks are moved into them. - alignScales(options, d_data->canvasRect, d_data->scaleRect); + alignScales( options, d_data->canvasRect, d_data->scaleRect ); - if (!d_data->legendRect.isEmpty() ) { + if ( !d_data->legendRect.isEmpty() ) + { // We prefer to align the legend to the canvas - not to // the complete plot - if possible. - d_data->legendRect = alignLegend(d_data->canvasRect, d_data->legendRect); + d_data->legendRect = alignLegend( d_data->canvasRect, d_data->legendRect ); } } diff --git a/libs/qwt/qwt_plot_layout.h b/libs/qwt/qwt_plot_layout.h index 0ade70c5e..c72c04fc3 100644 --- a/libs/qwt/qwt_plot_layout.h +++ b/libs/qwt/qwt_plot_layout.h @@ -14,69 +14,102 @@ #include "qwt_plot.h" /*! - \brief Layout class for QwtPlot. + \brief Layout engine for QwtPlot. - Organizes the geometry for the different QwtPlot components. + It is used by the QwtPlot widget to organize its internal widgets + or by QwtPlot::print() to render its content to a QPaintDevice like + a QPrinter, QPixmap/QImage or QSvgRenderer. + + \sa QwtPlot::setPlotLayout() */ class QWT_EXPORT QwtPlotLayout { public: - enum Options { - AlignScales = 1, - IgnoreScrollbars = 2, - IgnoreFrames = 4, - IgnoreMargin = 8, - IgnoreLegend = 16 + /*! + Options to configure the plot layout engine + \sa activate(), QwtPlotRenderer + */ + enum Option + { + //! Unused + AlignScales = 0x01, + + /*! + Ignore the dimension of the scrollbars. There are no + scrollbars, when the plot is not rendered to widgets. + */ + IgnoreScrollbars = 0x02, + + //! Ignore all frames. + IgnoreFrames = 0x04, + + //! Ignore the legend. + IgnoreLegend = 0x08, + + //! Ignore the title. + IgnoreTitle = 0x10, + + //! Ignore the footer. + IgnoreFooter = 0x20 }; + //! Layout options + typedef QFlags