#include "ovpCTimeRuler.h" #include #include namespace OpenViBE { namespace Plugins { namespace SimpleVisualization { #define CONVERT_TIME(i) (double((i)>>32) + double(double((i)&0xFFFFFFFF) / double((uint64_t)1<<32))) //CALLBACKS //! Callback to redraw the bottom ruler gboolean TimeRulerExposeEventCB(GtkWidget* /*widget*/, GdkEventExpose* /*event*/, gpointer data) { //redraw the ruler auto* timeRuler = reinterpret_cast(data); timeRuler->draw(); //don't propagate this signal to the children if any return TRUE; } //! Called when the widget whose width is associated with the ruler is resized. gboolean TimeRulerResizeCB(GtkWidget* /*widget*/, GtkAllocation* allocation, gpointer data) { auto* timeRuler = reinterpret_cast(data); timeRuler->onResizeEventCB(allocation->width, allocation->height); return FALSE; } CTimeRuler::CTimeRuler(IStreamDatabase& streamDatabase, const int width, const int height) : m_stream(streamDatabase), m_height(height) { m_widget = gtk_drawing_area_new(); gtk_widget_set_size_request(m_widget, width, height); g_signal_connect_after(G_OBJECT(m_widget), "expose_event", G_CALLBACK(TimeRulerExposeEventCB), this); } void CTimeRuler::draw() { //if the widget is invisible, no need to redraw it if (!GTK_WIDGET_VISIBLE(m_widget)) { return; } //return if time between two consecutive buffers hasn't been computed yet if (!m_stream.isBufferTimeStepComputed()) { return; } //get widget size gint bottomRulerW; gdk_drawable_get_size(m_widget->window, &bottomRulerW, nullptr); const double startTime = CONVERT_TIME(m_stream.getStartTime(0)); const double endTime = CONVERT_TIME(m_stream.getStartTime(0) + m_stream.getMaxBufferCount() * m_stream.getBufferTimeStep()); const double intervalW = endTime - startTime; //compute step between two values displayed on the ruler const auto nearestSmallerPowerOf10 = double(pow(10, floor(log10(intervalW)))); const auto maxNLabels = uint64_t(bottomRulerW / m_pixelsPerLabel); double valueStep = nearestSmallerPowerOf10; if (uint64_t(floor(intervalW / nearestSmallerPowerOf10)) > maxNLabels) { valueStep = 2 * nearestSmallerPowerOf10; } else if (uint64_t(floor(intervalW / nearestSmallerPowerOf10)) < maxNLabels / 2) { valueStep = nearestSmallerPowerOf10 / 2; } //recompute step base value const double baseValue = valueStep * floor(startTime / valueStep); //X position of the first label const double bufferW = double(bottomRulerW) / double(m_stream.getMaxBufferCount()); auto baseX = int64_t(floor(bottomRulerW - (m_stream.getCurrentBufferCount() * bufferW))); if (baseX < 0) { baseX = 0; } //draw ruler base (horizontal line) gdk_draw_line(m_widget->window, m_widget->style->fg_gc[GTK_WIDGET_STATE(m_widget)], gint(baseX), 0, bottomRulerW, 0); //draw labels std::stringstream timeLabel; for (double i = baseValue; i < double(0.5 + endTime); i += valueStep) { //clear stringstream timeLabel.str(""); //compute label position const gint textX = gint(baseX + ((i - startTime) * ((double(bottomRulerW)) / intervalW))); if (textX >= bottomRulerW) { break; } timeLabel << i; PangoLayout* text = gtk_widget_create_pango_layout(m_widget, timeLabel.str().c_str()); int textW; pango_layout_get_pixel_size(text, &textW, nullptr); //if the width allocated per label becomes too small compared to the effective width of the label if (uint64_t(textW) >= m_pixelsPerLabel - 20) { //increases the allocated width per label m_pixelsPerLabel = textW + 30; } //display it gdk_draw_layout(m_widget->window, m_widget->style->fg_gc[GTK_WIDGET_STATE(m_widget)], textX, 4, text); //draw a small line above it gdk_draw_line(m_widget->window, m_widget->style->fg_gc[GTK_WIDGET_STATE(m_widget)], textX, 0, textX, 3); } } void CTimeRuler::linkWidthToWidget(GtkWidget* widget) { //add a callback to the widget for the size-allocate signal g_signal_connect(G_OBJECT(widget), "size-allocate", G_CALLBACK(TimeRulerResizeCB), this); } } // namespace SimpleVisualization } // namespace Plugins } // namespace OpenViBE