@Override
  public void ioReceived(IOInstance inst, int ret, Log... out) {
    if (inst == robot) {

      // Deal with streaming...
      LogSearching.Criteria criteria =
          LogSearching.criteriaAt(display.streamComboBox.getSelectedIndex());

      if (criteria != null) {
        Log pass = null;

        for (Log l : out) {
          if (criteria.fits(l.getFullDescription())) pass = l;
        }

        if (pass != null) displayLog(pass);
      }

      // Deal with adding to groups (i.e. keeping)...
      assert (lastGroup != null);
      for (Log l : out) {
        if (shouldKeep(l.jvm_unique_id)) lastGroup.add(LogReference.referenceFromLog(l));
      }

      Events.GLogsFound.generate(this, out);
    } else {
      Debug.error("%s got %d surprising logs from %s", this, out.length, inst);
    }
  }
  private void controlLoadAction() {
    if (robot == null) {
      Path selected = (Path) display.pathBox.getSelectedItem();
      if (selected == null) {
        Debug.error("null path");
        ToolMessage.display("load action: null path", Color.RED);
        return;
      }

      if (FileIO.isValidLogFolder(selected)) {
        updateComboBoxAndSettings(display.pathBox, UserSettings.loadPathes, selected);

        lastGroup = Group.groupFromPath(selected);
        LogReference[] added;
        try {
          added = FileIO.readAllRefsFromPath(selected, true);
        } catch (Throwable e) {
          ToolMessage.displayError(
              "error {%s} (see below) reading Log refs from %s", e.getMessage(), selected);
          e.printStackTrace();

          return;
        }
        lastGroup.add(added);

        // Log[] addedLogs = new Log[added.length];
        // for (int i = 0; i < added.length; ++i)
        // addedLogs[i] = added[i].get();

        ToolMessage.displayInfo("loaded %d logs into %s", added.length, lastGroup);

        Events.GGroupAdded.generate(this, lastGroup);
        Events.GLogRefsFound.generate(this, added);
        // Events.GLogsFound.generate(this, addedLogs);

        display.leftSideTabs.setSelectedComponent(display.logTab);
      } else {
        Debug.error("invalid Log folder: " + selected.toString());
        ToolMessage.display("invalid Log folder: " + selected.toString(), Color.RED);
        return;
      }

    } else {
      Debug.error("cannot load session while streaming (%s)", robot);
      ToolMessage.display("cannot load session while streaming", Color.ORANGE);
    }
  }
    @Override
    public void valueChanged(TreeSelectionEvent e) {
      if (!e.isAddedPath()) {
        // path unselected, ignore
        return;
      }

      TreePath[] selected = display.logTree.getSelectionPaths();
      switch (selected[0].getPathCount()) {
        case 0:
          Debug.error("null selection!");
          break;
        case 1:
          Debug.error("root selected!");
          break;
        case 2:
          {
            Group group = (Group) selected[0].getLastPathComponent();
            displayGroup(group);
            Events.GGroupSelected.generate(this, group);
          }
          return;
        case 3:
          {
            List<Log> all = new LinkedList<>();
            for (TreePath path : selected) {
              if (path.getPathCount() != 3) continue; // it isn't a Log selection

              LogReference ref = (LogReference) path.getLastPathComponent();
              all.add(ref.get());
            }

            Log first = all.remove(0);
            displayLog(first, all);
            Events.GLogSelected.generate(outerClassThis, first, all);
          }
          return;
        default:
          Debug.error(
              "selection count %d %s",
              selected[0].getPathCount(), selected[0].getLastPathComponent());
      }
    }
 @Override
 public int getIndexOfChild(Object parent, Object child) {
   if (parent == this) {
     return AllGroups.allGroups.indexOf(child);
   } else if (parent instanceof Group) {
     return currentlyDisplayedFrom((Group) parent).indexOf(child);
   } else {
     Debug.error("parent %s not container!", parent);
     return -1;
   }
 }
 @Override
 public void ioFinished(IOInstance instance) {
   if (instance == robot) {
     ToolMessage.displayWarn("robot %s disconnected!", instance);
     display.rpcScrollPane.setViewportView(new JLabel("no connection"));
     display.connectButton.setText("connect");
     display.loadButton.setEnabled(true);
     robot = null;
   } else {
     Debug.error("informed of dead connection %s but robot=%s", instance, robot);
   }
 }
 @Override
 public void valueForPathChanged(TreePath path, Object newValue) {
   Debug.error("logTree should not be editable...");
 }