You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ovpCBottomTimeRuler.cpp 6.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. #include "ovpCBottomTimeRuler.h"
  2. #include <cmath>
  3. namespace OpenViBE {
  4. namespace Plugins {
  5. namespace SimpleVisualization {
  6. #define CONVERT_TIME(i) (double((i)>>32) + double(double((i)&0xFFFFFFFF) / double((uint64_t)1<<32)))
  7. gboolean BottomRulerExposeEventCB(GtkWidget* /*widget*/, GdkEventExpose* /*event*/, gpointer data)
  8. {
  9. //redraw the ruler
  10. auto* bottomRuler = reinterpret_cast<CBottomTimeRuler*>(data);
  11. bottomRuler->draw();
  12. //don't propagate this signal to the children if any
  13. return TRUE;
  14. }
  15. gboolean ResizeBottomRulerCB(GtkWidget* /*widget*/, GtkAllocation* allocation, gpointer data)
  16. {
  17. auto* bottomRuler = reinterpret_cast<CBottomTimeRuler*>(data);
  18. bottomRuler->onResizeEventCB(allocation->width, allocation->height);
  19. return FALSE;
  20. }
  21. CBottomTimeRuler::CBottomTimeRuler(CBufferDatabase& database, const int width, const int height)
  22. : m_database(&database), m_height(height)
  23. {
  24. //creates the main drawing area
  25. m_bottomRuler = gtk_drawing_area_new();
  26. gtk_widget_set_size_request(m_bottomRuler, width, height);
  27. g_signal_connect_after(G_OBJECT(m_bottomRuler), "expose_event", G_CALLBACK(BottomRulerExposeEventCB), this);
  28. }
  29. void CBottomTimeRuler::draw()
  30. {
  31. //if the widget is invisible, no need to redraw it
  32. if (!GTK_WIDGET_VISIBLE(m_bottomRuler)) { return; }
  33. //gets the number of buffers to display
  34. const uint64_t nBufferToDisplay = m_database->m_NBufferToDisplay;
  35. if (m_database->m_DimSizes[1] == 1 && nBufferToDisplay != 1) { /* nBufferToDisplay--;*/ }
  36. //gets the widget's size
  37. gint bottomRulerWidth;
  38. gint bottomRulerHeight;
  39. gdk_drawable_get_size(m_bottomRuler->window, &bottomRulerWidth, &bottomRulerHeight);
  40. //in ms
  41. const double intervalWidth = CONVERT_TIME(nBufferToDisplay * m_database->m_BufferDuration);
  42. //if(m_Database->areEpochsContiguous() == true){intervalWidth = CONVERT_TIME(nBufferToDisplay * m_Database->m_BufferDuration);}
  43. //else { intervalWidth = CONVERT_TIME(nBufferToDisplay * m_Database->m_BufferDuration); }
  44. //available width per buffer
  45. const double widthPerBuffer = double(bottomRulerWidth) / double(nBufferToDisplay);
  46. //computes the step of the values displayed on the ruler
  47. const auto nearestSmallerPowerOf10 = double(pow(10, floor(log10(intervalWidth))));
  48. const auto maxNumberOfLabels = uint64_t(bottomRulerWidth / m_pixelsPerLabel);
  49. double valueStep = nearestSmallerPowerOf10;
  50. if (uint64_t(floor(intervalWidth / nearestSmallerPowerOf10)) > maxNumberOfLabels) { valueStep = 2 * nearestSmallerPowerOf10; }
  51. else if (uint64_t(floor(intervalWidth / nearestSmallerPowerOf10)) < maxNumberOfLabels / 2) { valueStep = nearestSmallerPowerOf10 / 2; }
  52. if (m_database->getDisplayMode() == Scroll)
  53. {
  54. //compute start, end time and base value of the step
  55. double startTime = 0;
  56. if (!m_database->m_StartTime.empty()) { startTime = CONVERT_TIME(m_database->m_StartTime[0]); }
  57. const double endTime = startTime + intervalWidth;
  58. const double baseValue = valueStep * floor(startTime / valueStep);
  59. //X position of the first label (if there are less buffers than needed)
  60. auto baseX = int64_t(floor(bottomRulerWidth - (m_database->m_SampleBuffers.size() * widthPerBuffer)));
  61. if (baseX < 0) { baseX = 0; }
  62. //draw ruler base (horizontal line)
  63. gdk_draw_line(m_bottomRuler->window, m_bottomRuler->style->fg_gc[GTK_WIDGET_STATE(m_bottomRuler)], gint(baseX), 0, gint(bottomRulerWidth), 0);
  64. const int clipLeft = 0;
  65. const int clipRight = bottomRulerWidth - 1;
  66. drawRuler(baseX, bottomRulerWidth, startTime, endTime, intervalWidth, baseValue, valueStep, clipLeft, clipRight);
  67. }
  68. else //scan mode
  69. {
  70. //draw ruler base (horizontal line)
  71. gdk_draw_line(m_bottomRuler->window, m_bottomRuler->style->fg_gc[GTK_WIDGET_STATE(m_bottomRuler)], 0, 0, gint(bottomRulerWidth), 0);
  72. //left part of the ruler (recent data)
  73. size_t leftmostBufferToDisplay = 0;
  74. m_database->getIndexOfBufferStartingAtTime(m_leftmostDisplayedTime, leftmostBufferToDisplay);
  75. double startTime = 0;
  76. if (!m_database->m_StartTime.empty()) { startTime = CONVERT_TIME(m_leftmostDisplayedTime); }
  77. double endTime = startTime + intervalWidth;
  78. double baseValue = valueStep * floor(startTime / valueStep);
  79. int clipLeft = 0;
  80. int clipRight = int(double(m_database->m_NBufferToDisplay - leftmostBufferToDisplay) * widthPerBuffer);
  81. drawRuler(0, bottomRulerWidth, startTime, endTime, intervalWidth, baseValue, valueStep, clipLeft, clipRight);
  82. //right part (older data)
  83. startTime -= intervalWidth;
  84. endTime = startTime + intervalWidth;
  85. baseValue = valueStep * floor(startTime / valueStep);
  86. clipLeft = clipRight + 1;
  87. clipRight = bottomRulerWidth - 1;
  88. drawRuler(0, bottomRulerWidth, startTime, endTime, intervalWidth, baseValue, valueStep, clipLeft, clipRight);
  89. }
  90. }
  91. void CBottomTimeRuler::onResizeEventCB(const gint width, gint /*height*/) const { gtk_widget_set_size_request(m_bottomRuler, width, m_height); }
  92. void CBottomTimeRuler::drawRuler(const int64_t baseX, const int rulerWidth, const double startTime, const double endTime, const double length,
  93. const double baseValue, const double valueStep, const int clipLeft, const int clipRight)
  94. {
  95. for (double i = baseValue; i < double(0.5 + endTime); i += valueStep)
  96. {
  97. //compute the position of the label
  98. const gint textX = gint(baseX + ((i - startTime) * ((double(rulerWidth)) / length)));
  99. //is text clipped?
  100. if (textX < clipLeft) { continue; }
  101. std::string timeLabel = std::to_string(i);
  102. PangoLayout* text = gtk_widget_create_pango_layout(m_bottomRuler, timeLabel.c_str());
  103. int textWidth;
  104. pango_layout_get_pixel_size(text, &textWidth, nullptr);
  105. //is text beyond visible range?
  106. if (textX + textWidth > clipRight)
  107. {
  108. g_object_unref(text);
  109. break;
  110. }
  111. //if the width allocated per label becomes too small compared to the effective width of the label
  112. if (uint64_t(textWidth) >= m_pixelsPerLabel - 20)
  113. {
  114. //increases the allocated width per label
  115. m_pixelsPerLabel = textWidth + 30;
  116. }
  117. //display it
  118. gdk_draw_layout(m_bottomRuler->window, m_bottomRuler->style->fg_gc[GTK_WIDGET_STATE(m_bottomRuler)], textX, 4, text);
  119. //draw a small line above it
  120. gdk_draw_line(m_bottomRuler->window, m_bottomRuler->style->fg_gc[GTK_WIDGET_STATE(m_bottomRuler)], textX, 0, textX, 3);
  121. g_object_unref(text);
  122. }
  123. }
  124. void CBottomTimeRuler::linkWidthToWidget(GtkWidget* widget)
  125. {
  126. //adds a callback to the widget for the size-allocate signal
  127. g_signal_connect(G_OBJECT(widget), "size-allocate", G_CALLBACK(ResizeBottomRulerCB), this);
  128. }
  129. } // namespace SimpleVisualization
  130. } // namespace Plugins
  131. } // namespace OpenViBE