private double getTimeY(long t0) {
   double y0 = t0 - history_graph.getStartTime();
   y0 =
       y0
               / (history_graph.getEndTime() - history_graph.getStartTime() + 1)
               * (time_end - time_start)
           + time_start;
   return y0;
 }
  private GraphObject getObjectAtPoint(int x, int y) {
    if (object_width == 0) return null;

    GraphObject gobj = null;

    int idx = (int) ((x - left_right_margin) / (object_width + object_hspacing));
    if (idx >= 0 && idx < history_graph.getNumObjects()) {
      double xoff = x - (left_right_margin + idx * (object_width + object_hspacing));
      xoff -= object_width / 2;
      if (Math.abs(xoff) <= active_width) gobj = history_graph.getObject(idx);
    }

    return gobj;
  }
  /** ***************************************************************************** */
  private void updateGraph() {
    synchronized (this) {
      update_needed = false;
      if (restart_needed || history_graph == null) {
        history_graph = new HistoryGraph();
        last_update = 0;
        restart_needed = false;
      }
    }

    synchronized (history_graph) {
      for (BumpThread bt : history_data.getThreads()) {
        history_graph.addThreadItems(history_data.getItems(bt), last_update);
      }
      last_update = history_graph.finishBuild();
    }
  }
  private void handlePaint(Graphics2D g) {
    checkSizes();
    Dimension csz = draw_area.getSize();

    synchronized (this) {
      if (update_needed) updateGraph();
    }

    synchronized (history_graph) {
      int nobj = history_graph.getNumObjects();
      double twid =
          nobj * GRAPH_OBJECT_WIDTH
              + 2 * GRAPH_LEFT_RIGHT_MARGIN
              + (nobj - 1) * GRAPH_OBJECT_H_SPACE;
      double sx = csz.width / twid;
      left_right_margin = GRAPH_LEFT_RIGHT_MARGIN * sx;
      object_width = GRAPH_OBJECT_WIDTH * sx;
      object_hspacing = GRAPH_OBJECT_H_SPACE * sx;
      active_width = GRAPH_ACTIVE_WIDTH * sx;
      arrow_size = 3 / y_scale;

      double tht = 2 * GRAPH_TOP_BOTTOM_MARGIN + GRAPH_OBJECT_V_SPACE + GRAPH_TIME_SPACE;
      double sy = csz.height / tht;
      top_bottom_margin = GRAPH_TOP_BOTTOM_MARGIN * sy;
      object_height = GRAPH_OBJECT_HEIGHT * sy;
      time_start = top_bottom_margin + object_height + GRAPH_OBJECT_V_SPACE * sy;
      time_end = csz.height - top_bottom_margin;

      graph_locations = new HashMap<GraphObject, Double>();

      for (int i = 0; i < nobj; ++i) {
        GraphObject go = history_graph.getObject(i);
        drawObject(g, i, go);
      }

      for (int i = 0; i < nobj; ++i) {
        GraphObject go = history_graph.getObject(i);
        drawLinks(g, go);
      }
    }
  }
  /** ***************************************************************************** */
  private BddtHistoryItem getItemAtPoint(int x, int y) {
    if (object_width == 0) return null;

    double t =
        history_graph.getStartTime()
            + (y - time_start)
                / (time_end - time_start)
                * (history_graph.getEndTime() - history_graph.getStartTime() + 1);
    if (t < history_graph.getStartTime() || t > history_graph.getEndTime()) return null;

    GraphObject gobj = getObjectAtPoint(x, y);

    // correlate with blocks
    if (gobj != null) {
      GraphBlock gb0 = null;
      double dtime = 0;
      for (GraphBlock gb : gobj.getBlocks()) {
        double dt = gb.getEndTime() - gb.getStartTime();
        if (t >= gb.getStartTime() && t <= gb.getEndTime()) {
          if (gb0 == null || dt < dtime) {
            gb0 = gb;
            dtime = dt;
          }
        }
      }
      if (gb0 != null) {
        BddtHistoryItem bi0 = null;
        dtime = 0;
        for (BddtHistoryItem bhi : gb0.getItems()) {
          double dt = t - bhi.getTime();
          if (dt >= 0) {
            if (bi0 == null || dt < dtime) {
              bi0 = bhi;
              dtime = dt;
            }
          }
        }
        if (bi0 != null) return bi0;
      }
    }

    // correlate with links
    double delta =
        (history_graph.getEndTime() - history_graph.getStartTime()) / (time_end - time_start) * 3;

    double dtime = 0;
    GraphLink gl0 = null;
    for (int i = 0; i < history_graph.getNumObjects(); ++i) {
      GraphObject go = history_graph.getObject(i);
      double x0 = graph_locations.get(go);
      for (GraphLink gl : go.getLinks()) {
        double x1 = graph_locations.get(gl.getToObject());
        if (x > x0 && x < x1) {
          double dt = Math.abs(t - gl.getTime());
          if (gl0 == null || dt < dtime) {
            gl0 = gl;
            dtime = dt;
          }
        }
      }
    }
    if (gl0 != null && dtime <= delta) {
      return gl0.getItem();
    }

    return null;
  }