Google Tag Manager

2014/01/30

Use JTree as the table of contents

Code

class TableOfContentsTreeCellRenderer extends DefaultTreeCellRenderer {
  private static BasicStroke READER = new BasicStroke(
    1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER,
    1f, new float[] { 1f }, 0f);
  private String pn;
  private Point pnPt = new Point();
  private int rxs, rxe, ry;
  private boolean isSynth = false;
  private final JPanel p = new JPanel(new BorderLayout()) {
    @Override protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      if (pn != null) {
        Graphics2D g2 = (Graphics2D) g.create();
        g2.setColor(isSynth ? getForeground() : getTextNonSelectionColor());
        g2.drawString(pn, pnPt.x - getX(), pnPt.y);
        g2.setStroke(READER);
        g2.drawLine(rxs, pnPt.y, rxe - getX(), pnPt.y);
        g2.dispose();
      }
    }

    @Override public Dimension getPreferredSize() {
      Dimension d = super.getPreferredSize();
      d.width = Short.MAX_VALUE;
      return d;
    }
  };

  public TableOfContentsTreeCellRenderer() {
    super();
    p.setOpaque(false);
  }

  @Override public void updateUI() {
    super.updateUI();
    isSynth = getUI().getClass().getName().contains("Synth");
    if (isSynth) {
      //System.out.println("XXX: FocusBorder bug?, JDK 1.7.0, Nimbus start LnF");
      setBackgroundSelectionColor(new Color(0x0, true));
    }
  }

  @Override public Component getTreeCellRendererComponent(
      JTree tree, Object value, boolean selected, boolean expanded,
      boolean leaf, int row, boolean hasFocus) {
    JLabel l = (JLabel) super.getTreeCellRendererComponent(
        tree, value, selected, expanded, leaf, row, hasFocus);
    if (value instanceof DefaultMutableTreeNode) {
      DefaultMutableTreeNode n = (DefaultMutableTreeNode) value;
      Object o = n.getUserObject();
      if (o instanceof TableOfContents) {
        TableOfContents toc = (TableOfContents) o;
        FontMetrics metrics = l.getFontMetrics(l.getFont());
        int gap = l.getIconTextGap();
        int h = l.getPreferredSize().height;
        Insets ins = tree.getInsets();

        p.removeAll();
        p.add(l, BorderLayout.WEST);
        if (isSynth) p.setForeground(l.getForeground());

        pn = String.format("%3d", toc.page);
        pnPt.x = tree.getWidth() - metrics.stringWidth(pn) - gap;
        pnPt.y = (h + metrics.getAscent()) / 2;

        rxs = l.getPreferredSize().width + gap;
        rxe = tree.getWidth() - ins.right - metrics.stringWidth("000") - gap;
        ry  = h / 2;

        return p;
      }
    }
    pn = null;
    return l;
  }
}

References