118 lines
4.0 KiB
C++
118 lines
4.0 KiB
C++
|
#include "ovpCTimeRuler.h"
|
||
|
#include <cmath>
|
||
|
#include <sstream>
|
||
|
|
||
|
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<CTimeRuler*>(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<CTimeRuler*>(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
|