diff --git a/common/utils/T/tracer/Makefile.remote b/common/utils/T/tracer/Makefile.remote
index 8dd9d9a7e2a6a88c288e6bba04e8e5d37f7c4e22..08b79f2aa1645206977accfaff1920f2ad0b185d 100644
--- a/common/utils/T/tracer/Makefile.remote
+++ b/common/utils/T/tracer/Makefile.remote
@@ -6,7 +6,7 @@ CFLAGS=-Wall -g -pthread -DT_TRACER -I.
 LIBS=-lX11 -lm
 
 PROG=tracer_remote
-OBJS=remote_old.o plot.o database.o gui.o
+OBJS=remote_old.o plot.o database.o gui.o utils.o
 
 $(PROG): gui/gui.a $(OBJS)
 	$(CC) $(CFLAGS) -o $(PROG) $(OBJS) gui/gui.a $(LIBS)
diff --git a/common/utils/T/tracer/database.c b/common/utils/T/tracer/database.c
index c0224af5d06d098b38190afb4b949c7cadc1b19a..62796149b3af38c4105077fe023ad0d0d4097e9f 100644
--- a/common/utils/T/tracer/database.c
+++ b/common/utils/T/tracer/database.c
@@ -1,4 +1,5 @@
 #include "database.h"
+#include "utils.h"
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
@@ -453,3 +454,48 @@ int number_of_ids(void *_d)
   database *d = _d;
   return d->isize;
 }
+
+int database_get_ids(void *_d, char ***ids)
+{
+  database *d = _d;
+  int i;
+  *ids = malloc(d->isize * sizeof(char **));
+  for (i = 0; i < d->isize; i++) (*ids)[i] = d->i[i].name;
+  return d->isize;
+}
+
+int database_get_groups(void *_d, char ***groups)
+{
+  database *d = _d;
+  int i;
+  *groups = malloc(d->gsize * sizeof(char **));
+  for (i = 0; i < d->gsize; i++) (*groups)[i] = d->g[i].name;
+  return d->gsize;
+}
+
+int database_pos_to_id(void *_d, int pos)
+{
+  database *d = _d;
+  return d->i[pos].id;
+}
+
+void database_get_generic_description(void *_d, int id,
+    char **name, char **desc)
+{
+  database *d = _d;
+  OBUF o;
+  int i;
+  *name = strdup(d->i[id].name);
+  o.osize = o.omaxsize = 0;
+  o.obuf = NULL;
+  PUTS(&o, *name);
+  for (i = 0; i < d->i[id].asize; i++) {
+    PUTC(&o, ' ');
+    PUTS(&o, d->i[id].arg_name[i]);
+    PUTS(&o, " [");
+    PUTS(&o, d->i[id].arg_name[i]);
+    PUTS(&o, "]");
+  }
+  PUTC(&o, 0);
+  *desc = o.obuf;
+}
diff --git a/common/utils/T/tracer/database.h b/common/utils/T/tracer/database.h
index e2f8aee8b7924365eef6b857140bd16d73bee2fa..c5cab05d9f7be0da0cc8d50fcf3fda243aec556a 100644
--- a/common/utils/T/tracer/database.h
+++ b/common/utils/T/tracer/database.h
@@ -10,6 +10,11 @@ void on_off(void *d, char *item, int *a, int onoff);
 char *event_name_from_id(void *database, int id);
 int event_id_from_name(void *database, char *name);
 int number_of_ids(void *database);
+int database_get_ids(void *database, char ***ids);
+int database_get_groups(void *database, char ***groups);
+int database_pos_to_id(void *database, int pos);
+void database_get_generic_description(void *database, int id,
+    char **name, char **desc);
 
 /****************************************************************************/
 /* get format of an event                                                   */
diff --git a/common/utils/T/tracer/event_selector.c b/common/utils/T/tracer/event_selector.c
index f632efb8daef9c9817978f1a64b00ac3099a5b44..650a6af59a4038f4fceeb070b477a816d731d1b0 100644
--- a/common/utils/T/tracer/event_selector.c
+++ b/common/utils/T/tracer/event_selector.c
@@ -1,13 +1,106 @@
 #include "event_selector.h"
 #include "gui/gui.h"
+#include "database.h"
+#include "utils.h"
+#include <stdlib.h>
+#include <string.h>
 
-void setup_event_selector(gui *g, void *database, int socket, int *is_on)
+struct event_selector {
+  int *is_on;
+  int red;
+  int green;
+  gui *g;
+  widget *events;
+  widget *groups;
+  void *database;
+  int nevents;
+  int ngroups;
+  int socket;
+};
+
+static void scroll(void *private, gui *g,
+    char *notification, widget *w, void *notification_data)
+{
+  int visible_lines;
+  int start_line;
+  int number_of_lines;
+  int new_line;
+  int inc;
+
+  text_list_state(g, w, &visible_lines, &start_line, &number_of_lines);
+  inc = 10;
+  if (inc > visible_lines - 2) inc = visible_lines - 2;
+  if (inc < 1) inc = 1;
+  if (!strcmp(notification, "scrollup")) inc = -inc;
+
+  new_line = start_line + inc;
+  if (new_line > number_of_lines - visible_lines)
+    new_line = number_of_lines - visible_lines;
+  if (new_line < 0) new_line = 0;
+
+  text_list_set_start_line(g, w, new_line);
+}
+
+static void click(void *private, gui *g,
+    char *notification, widget *w, void *notification_data)
 {
+  int *d = notification_data;
+  struct event_selector *this = private;
+  int set_on;
+  int line = d[0];
+  int button = d[1];
+  char *text;
+  int color;
+  int i;
+  char t;
+
+  if (button != 1 && button != 3) return;
+
+  if (button == 1) set_on = 1; else set_on = 0;
+
+  if (w == this->events)
+    text_list_get_line(this->g, this->events, line, &text, &color);
+  else
+    text_list_get_line(this->g, this->groups, line, &text, &color);
+
+  on_off(this->database, text, this->is_on, set_on);
+
+  for (i = 0; i < this->nevents; i++)
+    text_list_set_color(this->g, this->events, i,
+        this->is_on[database_pos_to_id(this->database, i)] ?
+            this->green : this->red);
+
+  for (i = 0; i < this->ngroups; i++)
+    text_list_set_color(this->g, this->groups, i, FOREGROUND_COLOR);
+  if (w == this->groups)
+    text_list_set_color(this->g, this->groups, line,
+        set_on ? this->green : this->red);
+
+  t = 1;
+  socket_send(this->socket, &t, 1);
+  socket_send(this->socket, &this->nevents, sizeof(int));
+  socket_send(this->socket, this->is_on, this->nevents * sizeof(int));
+}
+
+event_selector *setup_event_selector(gui *g, void *database, int socket,
+    int *is_on)
+{
+  struct event_selector *ret;
   widget *win;
   widget *main_container;
   widget *container;
   widget *left, *right;
   widget *events, *groups;
+  char **ids;
+  char **gps;
+  int n;
+  int i;
+  int red, green;
+
+  ret = calloc(1, sizeof(struct event_selector)); if (ret == NULL) abort();
+
+  red = new_color(g, "#944");
+  green = new_color(g, "#272");
 
   win = new_toplevel_window(g, 610, 800, "event selector");
   main_container = new_container(g, VERTICAL);
@@ -38,4 +131,40 @@ void setup_event_selector(gui *g, void *database, int socket, int *is_on)
   widget_add_child(g, right, groups, -1);
   container_set_child_growable(g, left, events, 1);
   container_set_child_growable(g, right, groups, 1);
+
+  n = database_get_ids(database, &ids);
+  for (i = 0; i < n; i++) {
+    text_list_add(g, events, ids[i], -1,
+        is_on[database_pos_to_id(database, i)] ? green : red);
+  }
+  free(ids);
+
+  ret->nevents = n;
+
+  n = database_get_groups(database, &gps);
+  for (i = 0; i < n; i++) {
+    text_list_add(g, groups, gps[i], -1, FOREGROUND_COLOR);
+  }
+  free(gps);
+
+  ret->ngroups = n;
+
+  ret->g = g;
+  ret->is_on = is_on;
+  ret->red = red;
+  ret->green = green;
+  ret->events = events;
+  ret->groups = groups;
+  ret->database = database;
+  ret->socket = socket;
+
+  register_notifier(g, "scrollup", events, scroll, ret);
+  register_notifier(g, "scrolldown", events, scroll, ret);
+  register_notifier(g, "click", events, click, ret);
+
+  register_notifier(g, "scrollup", groups, scroll, ret);
+  register_notifier(g, "scrolldown", groups, scroll, ret);
+  register_notifier(g, "click", groups, click, ret);
+
+  return ret;
 }
diff --git a/common/utils/T/tracer/event_selector.h b/common/utils/T/tracer/event_selector.h
index 11e977b4f641772e3f9380fd9baecdd04d517059..60da0a07e5c33d1ef3427fb2cfe2d752143d1786 100644
--- a/common/utils/T/tracer/event_selector.h
+++ b/common/utils/T/tracer/event_selector.h
@@ -3,6 +3,9 @@
 
 #include "gui/gui.h"
 
-void setup_event_selector(gui *g, void *database, int socket, int *is_on);
+typedef void event_selector;
+
+event_selector *setup_event_selector(gui *g, void *database, int socket,
+    int *is_on);
 
 #endif /* _EVENT_SELECTOR_H_ */
diff --git a/common/utils/T/tracer/remote.c b/common/utils/T/tracer/remote.c
index 9d1d2b0968bc88a9686289cdbff726b8aa2b6554..e99bcf2c36d72a4dc9e70054c22b5a16df5578b2 100644
--- a/common/utils/T/tracer/remote.c
+++ b/common/utils/T/tracer/remote.c
@@ -118,6 +118,7 @@ int main(int n, char **v)
   textlog *textlog;
   gui *g;
   int gui_mode = 0;
+  view *out;
 
   on_off_name = malloc(n * sizeof(char *)); if (on_off_name == NULL) abort();
   on_off_action = malloc(n * sizeof(int)); if (on_off_action == NULL) abort();
@@ -153,26 +154,30 @@ int main(int n, char **v)
 
   h = new_handler(database);
 
-  textlog = new_textlog(h, database,
-      "ENB_UL_CHANNEL_ESTIMATE",
-      "ev: {} eNB_id [eNB_ID] frame [frame] subframe [subframe]");
-
   g = gui_init();
   new_thread(gui_thread, g);
 
   if (gui_mode) {
-    view *tout;
     widget *w, *win;
 //    w = new_text_list(g, 600, 20, 0);
     w = new_text_list(g, 600, 20, new_color(g, "#ffabab"));
     win = new_toplevel_window(g, 600, 20*12, "textlog");
     widget_add_child(g, win, w, -1);
-    tout = new_textlist(1000, 10, g, w);
+    out = new_textlist(1000, 10, g, w);
     //tout = new_textlist(7, 4, g, w);
-    textlog_add_view(textlog, tout);
   } else {
-    view *sout = new_stdout();
-    textlog_add_view(textlog, sout);
+    out = new_stdout();
+  }
+
+  for (i = 0; i < number_of_events; i++) {
+    char *name, *desc;
+    database_get_generic_description(database, i, &name, &desc);
+    textlog = new_textlog(h, database, name, desc);
+//        "ENB_UL_CHANNEL_ESTIMATE",
+//        "ev: {} eNB_id [eNB_ID] frame [frame] subframe [subframe]");
+    textlog_add_view(textlog, out);
+    free(name);
+    free(desc);
   }
 
   for (i = 0; i < on_off_n; i++)
diff --git a/common/utils/T/tracer/remote_old.c b/common/utils/T/tracer/remote_old.c
index 4988bd336ebfa01cce0f4296037bdd405789bf34..5c3a751bafc50e5011312c9cfbe06676798d079d 100644
--- a/common/utils/T/tracer/remote_old.c
+++ b/common/utils/T/tracer/remote_old.c
@@ -327,23 +327,6 @@ void get_message(int s)
   }
 }
 
-void new_thread(void *(*f)(void *), void *data)
-{
-  pthread_t t;
-  pthread_attr_t att;
-
-  if (pthread_attr_init(&att))
-    { fprintf(stderr, "pthread_attr_init err\n"); exit(1); }
-  if (pthread_attr_setdetachstate(&att, PTHREAD_CREATE_DETACHED))
-    { fprintf(stderr, "pthread_attr_setdetachstate err\n"); exit(1); }
-  if (pthread_attr_setstacksize(&att, 10000000))
-    { fprintf(stderr, "pthread_attr_setstacksize err\n"); exit(1); }
-  if (pthread_create(&t, &att, f, data))
-    { fprintf(stderr, "pthread_create err\n"); exit(1); }
-  if (pthread_attr_destroy(&att))
-    { fprintf(stderr, "pthread_attr_destroy err\n"); exit(1); }
-}
-
 void usage(void)
 {
   printf(
diff --git a/common/utils/T/tracer/textlog.c b/common/utils/T/tracer/textlog.c
index 0cb5bd855a7c6f82cdb677929ae0eaa66c893317..2b02fb144107528eb815d38fe2e89776d24c0986 100644
--- a/common/utils/T/tracer/textlog.c
+++ b/common/utils/T/tracer/textlog.c
@@ -2,6 +2,7 @@
 #include "handler.h"
 #include "database.h"
 #include "view/view.h"
+#include "utils.h"
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
@@ -32,55 +33,30 @@ struct textlog {
   view **v;
   int vsize;
   /* local output buffer */
-  int osize;
-  int omaxsize;
-  char *obuf;
+  OBUF o;
 };
 
-static void PUTC(struct textlog *l, char c)
-{
-  if (l->osize == l->omaxsize) {
-    l->omaxsize += 512;
-    l->obuf = realloc(l->obuf, l->omaxsize);
-    if (l->obuf == NULL) abort();
-  }
-  l->obuf[l->osize] = c;
-  l->osize++;
-}
-
-static void PUTS(struct textlog *l, char *s)
-{
-  while (*s) PUTC(l, *s++);
-}
-
-static void PUTI(struct textlog *l, int i)
-{
-  char s[64];
-  sprintf(s, "%d", i);
-  PUTS(l, s);
-}
-
 static void _event(void *p, event e)
 {
   struct textlog *l = p;
   int i;
 
-  l->osize = 0;
+  l->o.osize = 0;
 
   for (i = 0; i < l->fsize; i++)
   switch(l->f[i].type) {
-  case INSTRING: PUTS(l, l->f[i].s); break;
-  case INT:      PUTI(l, e.e[l->f[i].event_arg].i); break;
-  case STRING:   PUTS(l, e.e[l->f[i].event_arg].s); break;
+  case INSTRING: PUTS(&l->o, l->f[i].s); break;
+  case INT:      PUTI(&l->o, e.e[l->f[i].event_arg].i); break;
+  case STRING:   PUTS(&l->o, e.e[l->f[i].event_arg].s); break;
   case BUFFER:
-    PUTS(l, "{buffer size:");
-    PUTI(l, e.e[l->f[i].event_arg].bsize);
-    PUTS(l, "}");
+    PUTS(&l->o, "{buffer size:");
+    PUTI(&l->o, e.e[l->f[i].event_arg].bsize);
+    PUTS(&l->o, "}");
     break;
   }
-  PUTC(l, 0);
+  PUTC(&l->o, 0);
 
-  for (i = 0; i < l->vsize; i++) l->v[i]->append(l->v[i], l->obuf);
+  for (i = 0; i < l->vsize; i++) l->v[i]->append(l->v[i], l->o.obuf);
 }
 
 enum chunk_type { C_ERROR, C_STRING, C_ARG_NAME, C_EVENT_NAME };
diff --git a/common/utils/T/tracer/utils.c b/common/utils/T/tracer/utils.c
index 2e18ccb60e3f35cf0f5dd38544aaccebe53dfd02..f00097160ffc386965fe0e6802a3d8d2aa240fd7 100644
--- a/common/utils/T/tracer/utils.c
+++ b/common/utils/T/tracer/utils.c
@@ -3,6 +3,7 @@
 #include <stdlib.h>
 #include <pthread.h>
 #include <time.h>
+#include <unistd.h>
 
 void new_thread(void *(*f)(void *), void *data)
 {
@@ -59,3 +60,46 @@ list *list_append(list *l, void *data)
   l->last = new;
   return l;
 }
+
+/****************************************************************************/
+/* socket                                                                   */
+/****************************************************************************/
+
+void socket_send(int socket, void *buffer, int size)
+{
+  char *x = buffer;
+  int ret;
+  while (size) {
+    ret = write(socket, x, size);
+    if (ret <= 0) abort();
+    size -= ret;
+    x += ret;
+  }
+}
+
+/****************************************************************************/
+/* buffer                                                                   */
+/****************************************************************************/
+
+void PUTC(OBUF *o, char c)
+{
+  if (o->osize == o->omaxsize) {
+    o->omaxsize += 512;
+    o->obuf = realloc(o->obuf, o->omaxsize);
+    if (o->obuf == NULL) abort();
+  }
+  o->obuf[o->osize] = c;
+  o->osize++;
+}
+
+void PUTS(OBUF *o, char *s)
+{
+  while (*s) PUTC(o, *s++);
+}
+
+void PUTI(OBUF *o, int i)
+{
+  char s[64];
+  sprintf(s, "%d", i);
+  PUTS(o, s);
+}
diff --git a/common/utils/T/tracer/utils.h b/common/utils/T/tracer/utils.h
index 5a37aa34c3485b40f260e39685091ca4fe094a38..eb9aa15506547bea4f018962f2407e9251aebc94 100644
--- a/common/utils/T/tracer/utils.h
+++ b/common/utils/T/tracer/utils.h
@@ -16,4 +16,24 @@ typedef struct list {
 list *list_remove_head(list *l);
 list *list_append(list *l, void *data);
 
+/****************************************************************************/
+/* socket                                                                   */
+/****************************************************************************/
+
+void socket_send(int socket, void *buffer, int size);
+
+/****************************************************************************/
+/* buffer                                                                   */
+/****************************************************************************/
+
+typedef struct {
+  int osize;
+  int omaxsize;
+  char *obuf;
+} OBUF;
+
+void PUTC(OBUF *o, char c);
+void PUTS(OBUF *o, char *s);
+void PUTI(OBUF *o, int i);
+
 #endif /* _UTILS_H_ */