void startBlock(BddtHistoryItem hi) {
   if (start_time == 0) start_time = hi.getTime();
   else start_time = Math.min(start_time, hi.getTime());
   GraphBlock gb = new GraphBlock(hi);
   all_blocks.add(gb);
   in_blocks.put(hi.getThread(), gb);
 }
 void finish(BddtHistoryItem hi) {
   GraphBlock gb = in_blocks.get(hi.getThread());
   if (gb != null) {
     gb.finish(hi.getTime());
     in_blocks.remove(hi.getThread());
   }
 }
 GraphBlock(BddtHistoryItem hi) {
   start_time = hi.getTime();
   last_time = start_time;
   end_time = 0;
   for_items = new ArrayList<BddtHistoryItem>();
   for_items.add(hi);
 }
 @Override
 public String getToolTipText(MouseEvent e) {
   BddtHistoryItem itm = getItemAtPoint(e.getX(), e.getY());
   if (itm != null) {
     StringBuffer buf = new StringBuffer();
     BumpThreadStack stk = itm.getStack();
     BumpStackFrame frm = stk.getFrame(0);
     if (frm == null) return null;
     buf.append(frm.getMethod() + " at " + frm.getLineNumber());
     return buf.toString();
   }
   GraphObject go = getObjectAtPoint(e.getX(), e.getY());
   if (go != null) {
     return go.getName();
   }
   return null;
 }
    void addThreadItems(Iterable<BddtHistoryItem> itms, long since) {
      GraphObject lastobj = null;
      BddtHistoryItem lastitem = null;
      for (BddtHistoryItem hi : itms) {
        GraphObject go = getObject(hi);
        if (since == 0 || hi.getTime() > since) {
          last_time = Math.max(last_time, hi.getTime());
          if (start_time == 0) start_time = last_time;
          else start_time = Math.min(start_time, hi.getTime());

          if (go == null) continue;
          if (lastobj == null) { // first time
            go.startBlock(hi);
          } else if (lastobj == go) { // step inside the same object
            go.extendBlock(hi);
          } else if (lastitem != null && hi.isInside(lastitem)) { // step/call into a new object
            go.startBlock(hi);
            lastobj.addLink(go, LinkType.ENTER, hi);
          } else if (lastitem != null && lastitem.isInside(hi)) { // return to prior object
            go.extendBlock(hi);
            // end prior block??
            lastobj.addLink(go, LinkType.RETURN, hi);
          } else {
            lastobj.finish(hi);
            go.startBlock(hi);
            lastobj.addLink(go, LinkType.NEXT, hi);
          }
        }
        lastobj = go;
        lastitem = hi;
      }
    }
 GraphObject getObject(BddtHistoryItem hi) {
   BumpRunValue rv = hi.getThisValue();
   GraphObject go = null;
   if (rv != null) {
     String key = rv.getValue();
     go = object_map.get(key);
     if (go == null) {
       go = new GraphObject(rv);
       graph_objects.add(go);
       object_map.put(key, go);
     }
   } else {
     String cnm = hi.getClassName();
     go = object_map.get(cnm);
     if (go == null) {
       go = new GraphObject(cnm);
       graph_objects.add(go);
       object_map.put(cnm, go);
     }
   }
   return go;
 }
    @Override
    public void actionPerformed(ActionEvent e) {
      BumpThreadStack stk = for_item.getStack();
      BumpStackFrame frame = stk.getFrame(0);
      BudaBubble bb = null;
      if (for_control.frameFileExists(frame)) {
        String mid = frame.getMethod() + frame.getSignature();
        bb = BaleFactory.getFactory().createMethodBubble(for_control.getProject(), mid);
      } else {
        bb = new BddtLibraryBubble(frame);
      }

      if (bb != null) {
        BoardMetrics.noteCommand("BDDT", "HistorySource");
        BudaBubbleArea bba = BudaRoot.findBudaBubbleArea(BddtHistoryBubble.this);
        if (bba != null) {
          bba.addBubble(
              bb,
              BddtHistoryBubble.this,
              null,
              PLACEMENT_RIGHT | PLACEMENT_GROUPED | PLACEMENT_LOGICAL | PLACEMENT_MOVETO);
        }
      }
    }
  /** ***************************************************************************** */
  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;
  }
 BumpThread getThread() {
   return for_item.getThread();
 }
 long getTime() {
   return for_item.getTime();
 }
 void addItem(BddtHistoryItem hi) {
   last_time = hi.getTime();
   for_items.add(hi);
 }
 BumpThread getThread() {
   if (for_items.isEmpty()) return null;
   BddtHistoryItem bi = for_items.get(0);
   return bi.getThread();
 }
 void extendBlock(BddtHistoryItem hi) {
   GraphBlock gb = in_blocks.get(hi.getThread());
   if (gb == null) startBlock(hi);
   else gb.addItem(hi);
 }
 GotoStackAction(BddtHistoryItem itm) {
   super("Show stack");
   for_stack = itm.getStack();
 }