From 944dad4caa5a96965d99de1f442429a12bb92cbb Mon Sep 17 00:00:00 2001
From: Cedric Roux <cedric.roux@eurecom.fr>
Date: Thu, 12 May 2016 17:03:07 +0200
Subject: [PATCH] GUI timeline widget

---
 common/utils/T/tracer/gui/Makefile   |   3 +-
 common/utils/T/tracer/gui/gui.h      |  10 ++
 common/utils/T/tracer/gui/gui_defs.h |  19 +++-
 common/utils/T/tracer/gui/timeline.c | 150 +++++++++++++++++++++++++++
 common/utils/T/tracer/gui/widget.c   |   3 +-
 5 files changed, 182 insertions(+), 3 deletions(-)
 create mode 100644 common/utils/T/tracer/gui/timeline.c

diff --git a/common/utils/T/tracer/gui/Makefile b/common/utils/T/tracer/gui/Makefile
index b64af3bd18..7129d390b9 100644
--- a/common/utils/T/tracer/gui/Makefile
+++ b/common/utils/T/tracer/gui/Makefile
@@ -2,7 +2,8 @@ CC=gcc
 CFLAGS=-Wall -g -pthread
 
 OBJS=init.o loop.o toplevel_window.o x.o container.o widget.o \
-     gui.o label.o event.o xy_plot.o textlist.o notify.o positioner.o
+     gui.o label.o event.o xy_plot.o textlist.o notify.o positioner.o \
+     timeline.o
 
 gui.a: $(OBJS)
 	ar cr gui.a $(OBJS)
diff --git a/common/utils/T/tracer/gui/gui.h b/common/utils/T/tracer/gui/gui.h
index ab4bf503f4..5902ea2831 100644
--- a/common/utils/T/tracer/gui/gui.h
+++ b/common/utils/T/tracer/gui/gui.h
@@ -26,6 +26,8 @@ widget *new_label(gui *gui, const char *text);
 widget *new_xy_plot(gui *gui, int width, int height, char *label,
     int vruler_width);
 widget *new_textlist(gui *gui, int width, int nlines, int background_color);
+widget *new_timeline(gui *gui, int width, int number_of_sublines,
+    int subline_height);
 
 void label_set_clickable(gui *gui, widget *label, int clickable);
 
@@ -52,6 +54,12 @@ void textlist_get_line(gui *gui, widget *this, int line,
     char **text, int *color);
 void textlist_set_color(gui *gui, widget *this, int line, int color);
 
+void timeline_clear(gui *gui, widget *this);
+void timeline_add_points(gui *gui, widget *this, int subline, int color,
+    int *x, int len);
+void timeline_set_subline_background_color(gui *gui, widget *this,
+    int subline, int color);
+
 void gui_loop(gui *gui);
 
 void glock(gui *gui);
@@ -67,6 +75,8 @@ int new_color(gui *gui, char *color);
  *      - click      { int [2]: line, button }
  * - label:
  *      - click      { int: button } (if enabled)
+ * - timeline
+ *      - resize     { int: width }
  */
 
 /* same type as in gui_defs.h */
diff --git a/common/utils/T/tracer/gui/gui_defs.h b/common/utils/T/tracer/gui/gui_defs.h
index dd76922179..8f15747490 100644
--- a/common/utils/T/tracer/gui/gui_defs.h
+++ b/common/utils/T/tracer/gui/gui_defs.h
@@ -30,7 +30,8 @@ extern int volatile gui_logd;
 /*************************************************************************/
 
 enum widget_type {
-  TOPLEVEL_WINDOW, CONTAINER, POSITIONER, TEXT_LIST, XY_PLOT, BUTTON, LABEL
+  TOPLEVEL_WINDOW, CONTAINER, POSITIONER, TEXT_LIST, XY_PLOT, BUTTON, LABEL,
+  TIMELINE
 };
 
 struct widget_list;
@@ -117,6 +118,22 @@ struct xy_plot_widget {
   int nplots;
 };
 
+struct timeline_subline {
+  int *color;                  /* length = width of timeline widget
+                                * value = -1 if no color
+                                */
+  int width;
+  int background;              /* background color of the subline */
+};
+
+struct timeline_widget {
+  struct widget common;
+  int n;                         /* number of sublines */
+  struct timeline_subline *s;
+  int subline_height;
+  int wanted_width;
+};
+
 struct button_widget {
   struct widget common;
 };
diff --git a/common/utils/T/tracer/gui/timeline.c b/common/utils/T/tracer/gui/timeline.c
new file mode 100644
index 0000000000..9e794dda4c
--- /dev/null
+++ b/common/utils/T/tracer/gui/timeline.c
@@ -0,0 +1,150 @@
+#include "gui.h"
+#include "gui_defs.h"
+#include "x.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+static void paint(gui *_gui, widget *_this)
+{
+  struct gui *g = _gui;
+  struct timeline_widget *this = _this;
+  int i;
+  int j;
+
+  for (i = 0; i < this->n; i++) {
+    x_fill_rectangle(g->x, g->xwin, this->s[i].background,
+        this->common.x, this->common.y + i * this->subline_height,
+        this->common.width, this->subline_height);
+    for (j = 0; j < this->s[i].width; j++)
+      if (this->s[i].color[j] != -1)
+        x_draw_line(g->x, g->xwin, this->s[i].color[j],
+            this->common.x + j, this->common.y + i * this->subline_height,
+            this->common.x + j, this->common.y + this->subline_height -1
+                + i * this->subline_height);
+  }
+
+  LOGD("PAINT timeline xywh %d %d %d %d\n", this->common.x, this->common.y, this->common.width, this->common.height);
+}
+
+static void hints(gui *_gui, widget *_w, int *width, int *height)
+{
+  struct timeline_widget *w = _w;
+  *width = w->wanted_width;
+  *height = w->n * w->subline_height;
+  LOGD("HINTS timeline wh %d %d\n", *width, *height);
+}
+
+static void allocate(gui *_gui, widget *_this,
+    int x, int y, int width, int height)
+{
+  struct timeline_widget *this = _this;
+  int i;
+  int j;
+  this->common.x = x;
+  this->common.y = y;
+  this->common.width = width;
+  this->common.height = height;
+  LOGD("ALLOCATE timeline %p xywh %d %d %d %d\n", this, x, y, width, height);
+  for (i = 0; i < this->n; i++) {
+    this->s[i].width = width;
+    this->s[i].color = realloc(this->s[i].color, width * sizeof(int));
+    if (this->s[i].color == NULL) abort();
+    for (j = 0; j < width; j++) this->s[i].color[j] = -1;
+  }
+  gui_notify(_gui, "resize", _this, &width);
+}
+
+/*************************************************************************/
+/*                           creation function                           */
+/*************************************************************************/
+
+widget *new_timeline(gui *_gui, int width, int number_of_sublines,
+    int subline_height)
+{
+  struct gui *g = _gui;
+  struct timeline_widget *w;
+  int i;
+  int j;
+
+  glock(g);
+
+  w = new_widget(g, TIMELINE, sizeof(struct timeline_widget));
+
+  w->wanted_width = width;
+  w->n = number_of_sublines;
+  w->s = calloc(w->n, sizeof(struct timeline_subline)); if (w->s == NULL) OOM;
+  w->subline_height = subline_height;
+
+  /* initialize colors */
+  for (i = 0; i < w->n; i++) {
+    w->s[i].width = width;
+    w->s[i].color = calloc(width, sizeof(int));
+    if (w->s[i].color == NULL) abort();
+    for (j = 0; j < width; j++) w->s[i].color[j] = -1;
+    w->s[i].background = BACKGROUND_COLOR;
+  }
+
+  w->common.paint = paint;
+  w->common.hints = hints;
+  w->common.allocate = allocate;
+
+  gunlock(g);
+
+  return w;
+}
+
+/*************************************************************************/
+/*                           public functions                            */
+/*************************************************************************/
+
+void timeline_clear(gui *_gui, widget *_this)
+{
+  struct gui *g = _gui;
+  struct timeline_widget *this = _this;
+  int i;
+  int j;
+
+  glock(g);
+
+  for (i = 0; i < this->n; i++)
+    for (j = 0; j < this->s[i].width; j++)
+      this->s[i].color[j] = -1;
+
+  send_event(g, DIRTY, this->common.id);
+
+  gunlock(g);
+}
+
+void timeline_add_points(gui *_gui, widget *_this, int subline, int color,
+    int *x, int len)
+{
+  struct gui *g = _gui;
+  struct timeline_widget *this = _this;
+  int i;
+
+  glock(g);
+
+  for (i = 0; i < len; i++) {
+    if (x[i] >= this->s[subline].width) { WARN("out of bounds\n"); continue; }
+    this->s[subline].color[x[i]] = color;
+  }
+
+  send_event(g, DIRTY, this->common.id);
+
+  gunlock(g);
+}
+
+void timeline_set_subline_background_color(gui *_gui, widget *_this,
+    int subline, int color)
+{
+  struct gui *g = _gui;
+  struct timeline_widget *this = _this;
+
+  glock(g);
+
+  this->s[subline].background = color;
+
+  send_event(g, DIRTY, this->common.id);
+
+  gunlock(g);
+}
diff --git a/common/utils/T/tracer/gui/widget.c b/common/utils/T/tracer/gui/widget.c
index db8a754a70..944d27548f 100644
--- a/common/utils/T/tracer/gui/widget.c
+++ b/common/utils/T/tracer/gui/widget.c
@@ -265,7 +265,7 @@ void widget_dirty(gui *_gui, widget *_this)
 
 static const char *names[] = {
   "TOPLEVEL_WINDOW", "CONTAINER", "POSITIONER", "TEXT_LIST",
-  "XY_PLOT", "BUTTON", "LABEL"
+  "XY_PLOT", "BUTTON", "LABEL", "TIMELINE"
 };
 const char *widget_name(enum widget_type type)
 {
@@ -278,6 +278,7 @@ const char *widget_name(enum widget_type type)
   case XY_PLOT:
   case BUTTON:
   case LABEL:
+  case TIMELINE:
     return names[type];
   }
   return "UNKNOWN (error)";
-- 
2.26.2