Google Tag Manager

Showing posts with label JTableHeader. Show all posts
Showing posts with label JTableHeader. Show all posts

2025/02/28

Display the insert cursor for a new column on the boundary of a JTable column

Code

class ColumnInsertLayerUI extends LayerUI<JScrollPane> {
  private static final Color LINE_COLOR = new Color(0x00_78_D7);
  private static final int LINE_WIDTH = 4;
  private final Rectangle2D line = new Rectangle2D.Double();
  private final Ellipse2D plus = new Ellipse2D.Double(0d, 0d, 10d, 10d);

  @Override public void paint(Graphics g, JComponent c) {
    super.paint(g, c);
    if (c instanceof JLayer && !line.isEmpty()) {
      JScrollPane scroll = (JScrollPane) ((JLayer<?>) c).getView();
      JTableHeader header =
          ((JTable) scroll.getViewport().getView()).getTableHeader();
      Graphics2D g2 = (Graphics2D) g.create();
      g2.setRenderingHint(
          RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
      Point pt0 = line.getBounds().getLocation();
      Point pt1 = SwingUtilities.convertPoint(header, pt0, c);
      g2.translate(pt1.getX() - pt0.getX(), pt1.getY() - pt0.getY());
      // paint Insert Line
      g2.setPaint(LINE_COLOR);
      g2.fill(line);
      // paint Plus Icon
      g2.setPaint(Color.WHITE);
      g2.fill(plus);
      g2.setPaint(LINE_COLOR);
      double cx = plus.getCenterX();
      double cy = plus.getCenterY();
      double w2 = plus.getWidth() / 2d;
      double h2 = plus.getHeight() / 2d;
      g2.draw(new Line2D.Double(cx - w2, cy, cx + w2, cy));
      g2.draw(new Line2D.Double(cx, cy - h2, cx, cy + h2));
      g2.draw(plus);
      g2.dispose();
    }
  }

  private void updateLineLocation(JScrollPane scroll, Point loc) {
    JTable table = (JTable) scroll.getViewport().getView();
    JTableHeader header = table.getTableHeader();
    Rectangle rect = scroll.getVisibleRect();
    JScrollBar bar = scroll.getHorizontalScrollBar();
    int scrollHeight = bar.isVisible() ? bar.getHeight() : 0;
    Dimension d = new Dimension(
        LINE_WIDTH, rect.height - scrollHeight);
    for (int i = 0; i < table.getColumnCount(); i++) {
      if (canInsert(header, loc, i, d)) {
        return;
      }
    }
  }

  private boolean canInsert(
        JTableHeader header, Point loc, int i, Dimension d) {
    Rectangle r = header.getHeaderRect(i);
    Rectangle r1 = getWestRect(r, i);
    Rectangle r2 = getEastRect(r);
    boolean hit = false;
    if (r1.contains(loc)) {
      updateInsertLineLocation(r1, loc, d, header);
      hit = true;
    } else if (r2.contains(loc)) {
      updateInsertLineLocation(r2, loc, d, header);
      hit = true;
    } else if (r.contains(loc)) {
      line.setFrame(0d, 0d, 0d, 0d);
      header.setCursor(Cursor.getDefaultCursor());
      hit = true;
    }
    return hit;
  }

  private Rectangle getWestRect(Rectangle r, int i) {
    Rectangle rect = r.getBounds();
    Rectangle bounds = plus.getBounds();
    if (i != 0) {
      rect.x -= bounds.width / 2;
    }
    rect.setSize(bounds.getSize());
    return rect;
  }

  private Rectangle getEastRect(Rectangle r) {
    Rectangle rect = r.getBounds();
    Rectangle bounds = plus.getBounds();
    rect.x += rect.width - bounds.width / 2;
    rect.setSize(bounds.getSize());
    return rect;
  }

  private void updateInsertLineLocation(
        Rectangle r, Point loc, Dimension d, Component c) {
    if (r.contains(loc)) {
      double cx = r.getCenterX();
      double cy = r.getCenterY();
      line.setFrame(
          cx - d.getWidth() / 2d, r.getY(), d.width, d.height);
      double pw = plus.getWidth() / 2d;
      double ph = plus.getHeight() / 2d;
      plus.setFrameFromCenter(cx, cy, cx - pw, cy - ph);
      c.setCursor(Cursor.getDefaultCursor());
    } else {
      line.setFrame(0d, 0d, 0d, 0d);
      c.setCursor(Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR));
    }
  }

  @Override public void installUI(JComponent c) {
    super.installUI(c);
    if (c instanceof JLayer) {
      ((JLayer<?>) c).setLayerEventMask(
          AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
    }
  }

  @Override public void uninstallUI(JComponent c) {
    if (c instanceof JLayer) {
      ((JLayer<?>) c).setLayerEventMask(0);
    }
    super.uninstallUI(c);
  }

  @Override protected void processMouseEvent(
        MouseEvent e, JLayer<? extends JScrollPane> l) {
    super.processMouseEvent(e, l);
    if (e.getID() == MouseEvent.MOUSE_CLICKED) {
      JScrollPane scroll = l.getView();
      Point pt = e.getPoint();
      if (plus.contains(pt) && !line.isEmpty()) {
        JTable table = (JTable) scroll.getViewport().getView();
        TableModel model = table.getModel();
        int columnCount = table.getColumnCount();
        int maxColumn = model.getColumnCount();
        if (columnCount < maxColumn) {
          int idx = table.columnAtPoint(
              line.getBounds().getLocation());
          TableColumn column = new TableColumn(columnCount);
          column.setHeaderValue("Column" + columnCount);
          table.addColumn(column);
          table.moveColumn(columnCount, idx + 1);
          updateLineLocation(scroll, pt);
        }
      }
      l.repaint(scroll.getBounds());
    }
  }

  @Override protected void processMouseMotionEvent(
        MouseEvent e, JLayer<? extends JScrollPane> l) {
    super.processMouseMotionEvent(e, l);
    Component c = e.getComponent();
    int id = e.getID();
    JScrollPane scroll = l.getView();
    if (id == MouseEvent.MOUSE_MOVED && c instanceof JTableHeader) {
      updateLineLocation(scroll, e.getPoint());
    } else {
      line.setFrame(0d, 0d, 0d, 0d);
    }
    l.repaint(scroll.getBounds());
  }
}

References

2025/01/31

Limit the area where TableColumns can be reordered by dragging

Limit the area where JTableHeader column reordering drags can be initiated to the top half of the TableColumn and perform mouse cursor changes and draw drag handle icons on the JLayer.

Code

class ColumnDragLayerUI extends LayerUI<JScrollPane> {
  private final Rectangle draggableRect = new Rectangle();

  @Override public void installUI(JComponent c) {
    super.installUI(c);
    if (c instanceof JLayer) {
      ((JLayer<?>) c).setLayerEventMask(
          AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
    }
  }

  @Override public void uninstallUI(JComponent c) {
    if (c instanceof JLayer) {
      ((JLayer<?>) c).setLayerEventMask(0);
    }
    super.uninstallUI(c);
  }

  @Override public void paint(Graphics g, JComponent c) {
    super.paint(g, c);
    if (!draggableRect.isEmpty()) {
      Graphics2D g2 = (Graphics2D) g.create();
      // g2.fill(draggableRect);
      Icon icon = new DragAreaIcon();
      int x = (int) (draggableRect.getCenterX() - icon.getIconWidth() / 2d);
      int y = draggableRect.y + 1;
      icon.paintIcon(c, g2, x, y);
      g2.dispose();
    }
  }

  @Override protected void processMouseEvent(
      MouseEvent e, JLayer<? extends JScrollPane> l) {
    super.processMouseEvent(e, l);
    Component c = e.getComponent();
    if (c instanceof JTableHeader) {
      JTableHeader header = (JTableHeader) c;
      if (e.getID() == MouseEvent.MOUSE_PRESSED) {
        Point pt = e.getPoint();
        updateIconAndCursor(header, pt, l);
      } else if (e.getID() == MouseEvent.MOUSE_RELEASED) {
        header.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
        draggableRect.setSize(0, 0);
      }
    }
  }

  @Override protected void processMouseMotionEvent(
      MouseEvent e, JLayer<? extends JScrollPane> l) {
    Component c = e.getComponent();
    if (c instanceof JTableHeader) {
      JTableHeader header = (JTableHeader) c;
      if (e.getID() == MouseEvent.MOUSE_DRAGGED) {
        TableColumn draggedColumn = header.getDraggedColumn();
        if (!draggableRect.isEmpty() && draggedColumn != null) {
          EventQueue.invokeLater(() -> {
            int modelIndex = draggedColumn.getModelIndex();
            int viewIndex = header.getTable().convertColumnIndexToView(modelIndex);
            Rectangle rect = header.getHeaderRect(viewIndex);
            rect.x += header.getDraggedDistance();
            draggableRect.setRect(SwingUtilities.convertRectangle(header, rect, l));
            header.repaint(rect);
          });
        } else {
          e.consume(); // Refuse to start drag
        }
      } else if (e.getID() == MouseEvent.MOUSE_MOVED) {
        Point pt = e.getPoint();
        updateIconAndCursor(header, pt, l);
        header.repaint();
      }
    } else {
      c.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
      draggableRect.setSize(0, 0);
    }
  }

  private void updateIconAndCursor(JTableHeader header, Point pt, JLayer<?> l) {
    Rectangle r = header.getHeaderRect(header.columnAtPoint(pt));
    r.height /= 2;
    if (r.contains(pt)) {
      header.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
      draggableRect.setRect(SwingUtilities.convertRectangle(header, r, l));
    } else {
      header.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
      draggableRect.setSize(0, 0);
    }
  }
}

References

2024/08/31

Set JButton as cell renderer for JTableHeader

Code

class ButtonHeaderRenderer extends JButton implements TableCellRenderer {
  private int pushedColumn = -1;
  private int rolloverColumn = -1;

  @Override public void updateUI() {
    super.updateUI();
    setHorizontalTextPosition(LEFT);
  }

  @Override public Component getTableCellRendererComponent(
      JTable table, Object value,
      boolean isSelected, boolean hasFocus,
      int row, int column) {
    setText(Objects.toString(value, ""));
    int modelColumn = table.convertColumnIndexToModel(column);
    JTableHeader header = table.getTableHeader();
    if (header != null) {
      // setColor(header, hasFocus);
      boolean isPressed = modelColumn == pressedColumn;
      getModel().setPressed(isPressed);
      getModel().setArmed(isPressed);
      getModel().setRollover(modelColumn == rolloverColumn);
      setFont(header.getFont());
    }

    Icon sortIcon = null;
    if (table.getRowSorter() != null) {
      List<? extends RowSorter.SortKey> sortKeys =
          table.getRowSorter().getSortKeys();
      if (!sortKeys.isEmpty() &&
          sortKeys.get(0).getColumn() == modelColumn) {
        SortOrder sortOrder = sortKeys.get(0).getSortOrder();
        switch (sortOrder) {
          case ASCENDING:
            sortIcon = UIManager.getIcon("Table.ascendingSortIcon");
            break;
          case DESCENDING:
            sortIcon = UIManager.getIcon("Table.descendingSortIcon");
            break;
          // case UNSORTED:
          //   sortIcon = UIManager.getIcon("Table.naturalSortIcon");
          //   break;
          default:
            sortIcon = UIManager.getIcon("Table.naturalSortIcon");
        }
      }
    }
    setIcon(sortIcon);
    return this;
  }

  public void setPressedColumn(int column) {
    pushedColumn = column;
  }

  public void setRolloverColumn(int column) {
    rolloverColumn = column;
  }
}

class HeaderMouseListener extends MouseAdapter {
  @Override public void mousePressed(MouseEvent e) {
    JTableHeader header = (JTableHeader) e.getComponent();
    JTable table = header.getTable();
    TableCellRenderer renderer = header.getDefaultRenderer();
    int viewColumn = table.columnAtPoint(e.getPoint());
    if (viewColumn >= 0 && renderer instanceof ButtonHeaderRenderer) {
      int column = table.convertColumnIndexToModel(viewColumn);
      ((ButtonHeaderRenderer) renderer).setPressedColumn(column);
    }
  }

  @Override public void mouseReleased(MouseEvent e) {
    JTableHeader header = (JTableHeader) e.getComponent();
    TableCellRenderer renderer = header.getDefaultRenderer();
    if (renderer instanceof ButtonHeaderRenderer) {
      ((ButtonHeaderRenderer) renderer).setPressedColumn(-1);
    }
  }

  @Override public void mouseMoved(MouseEvent e) {
    JTableHeader header = (JTableHeader) e.getComponent();
    JTable table = header.getTable();
    TableCellRenderer renderer = header.getDefaultRenderer();
    int viewColumn = table.columnAtPoint(e.getPoint());
    if (viewColumn >= 0 && renderer instanceof ButtonHeaderRenderer) {
      int column = table.convertColumnIndexToModel(viewColumn);
      ((ButtonHeaderRenderer) renderer).setRolloverColumn(column);
    }
  }

  @Override public void mouseExited(MouseEvent e) {
    JTableHeader header = (JTableHeader) e.getComponent();
    TableCellRenderer renderer = header.getDefaultRenderer();
    if (renderer instanceof ButtonHeaderRenderer) {
      ((ButtonHeaderRenderer) renderer).setRolloverColumn(-1);
    }
  }
}

References

2023/11/30

Split TableColumn of JTableHeader with diagonal line Border

Code

int size = 32;
JTable table = new JTable(model) {
  @Override public void updateUI() {
    super.updateUI();
    setRowHeight(size);
    TableCellRenderer hr = new VerticalTableHeaderRenderer();
    TableColumnModel cm = getColumnModel();
    cm.getColumn(0).setHeaderRenderer(
        new DiagonallySplitHeaderRenderer());
    cm.getColumn(0).setPreferredWidth(size * 5);
    for (int i = 1; i < cm.getColumnCount(); i++) {
      TableColumn tc = cm.getColumn(i);
      tc.setHeaderRenderer(hr);
      tc.setPreferredWidth(size);
    }
  }
};
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);

JScrollPane scroll = new JScrollPane(table);
scroll.setColumnHeader(new JViewport() {
  @Override public Dimension getPreferredSize() {
    Dimension d = super.getPreferredSize();
    d.height = size * 2;
    return d;
  }
});
// ...

class DiagonallySplitBorder extends MatteBorder {
  protected DiagonallySplitBorder(
      int top, int left, int bottom, int right, Color matteColor) {
    super(top, left, bottom, right, matteColor);
  }

  @Override public void paintBorder(
      Component c, Graphics g, int x, int y, int width, int height) {
    super.paintBorder(c, g, x, y, width, height);
    Graphics2D g2 = (Graphics2D) g.create();
    g2.translate(x, y);
    g2.setRenderingHint(
        RenderingHints.KEY_ANTIALIASING,
        RenderingHints.VALUE_ANTIALIAS_ON);
    g2.setPaint(getMatteColor());
    g2.drawLine(0, 0, c.getWidth() - 1, c.getHeight() - 1);
    g2.dispose();
  }
}

References

2022/11/30

Rotate JTableHeader column title string to display vertically

Code

class VerticalTableHeaderRenderer implements TableCellRenderer {
  private static final String ASCENDING = "Table.ascendingSortIcon";
  private static final String DESCENDING = "Table.descendingSortIcon";
  private final Icon ascendingIcon;
  private final Icon descendingIcon;
  private final EmptyIcon emptyIcon = new EmptyIcon();
  private final JPanel intermediate = new JPanel();
  private final JLabel label = new JLabel("", null, SwingConstants.LEADING);

  protected VerticalTableHeaderRenderer() {
    ascendingIcon = UIManager.getLookAndFeelDefaults().getIcon(ASCENDING);
    descendingIcon = UIManager.getLookAndFeelDefaults().getIcon(DESCENDING);
    emptyIcon.width = ascendingIcon.getIconWidth();
    emptyIcon.height = ascendingIcon.getIconHeight();
  }

  @Override public Component getTableCellRendererComponent(
      JTable table, Object value, boolean isSelected, boolean hasFocus,
      int row, int column) {
    UIManager.put(ASCENDING, emptyIcon);
    UIManager.put(DESCENDING, emptyIcon);
    TableCellRenderer r = table.getTableHeader().getDefaultRenderer();
    Component c = r.getTableCellRendererComponent(
      table, value, isSelected, hasFocus, row, column);
    UIManager.put(ASCENDING, ascendingIcon);
    UIManager.put(DESCENDING, descendingIcon);
    if (c instanceof JLabel) {
      JLabel l = (JLabel) c;
      l.setHorizontalAlignment(SwingConstants.CENTER);
      SortOrder sortOrder = getColumnSortOrder(table, column);
      Icon sortIcon;
      switch (sortOrder) {
        case ASCENDING:
          sortIcon = ascendingIcon;
          break;
        case DESCENDING:
          sortIcon = descendingIcon;
          break;
        default: // case UNSORTED:
          sortIcon = emptyIcon;
          break;
      }
      label.setText(l.getText());
      label.setIcon(new RotateIcon(sortIcon, 90));
      label.setHorizontalTextPosition(SwingConstants.LEFT);
      label.setBorder(BorderFactory.createEmptyBorder(0, 2, 0, 2));
      // l.setIcon(new RotateIcon(new ComponentIcon(label), -90));
      l.setIcon(makeVerticalHeaderIcon(label));
      l.setText(null);
    }
    return c;
  }

  public static SortOrder getColumnSortOrder(JTable table, int column) {
    SortOrder rv = SortOrder.UNSORTED;
    if (table != null && table.getRowSorter() != null) {
      List<? extends RowSorter.SortKey> sortKeys =
        table.getRowSorter().getSortKeys();
      int mi = table.convertColumnIndexToModel(column);
      if (!sortKeys.isEmpty() && sortKeys.get(0).getColumn() == mi) {
        rv = sortKeys.get(0).getSortOrder();
      }
    }
    return rv;
  }

  private Icon makeVerticalHeaderIcon(Component label) {
    Dimension d = label.getPreferredSize();
    int w = d.height;
    int h = d.width;
    BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
    Graphics2D g2 = (Graphics2D) bi.getGraphics();
    AffineTransform at = AffineTransform.getTranslateInstance(0, h);
    at.quadrantRotate(-1);
    g2.setTransform(at);
    SwingUtilities.paintComponent(g2, label, intermediate, 0, 0, h, w);
    g2.dispose();
    return new ImageIcon(bi);
  }
}

References

2021/06/30

Round the corners of JTableHeader

Code

class RoundedHeaderRenderer extends DefaultTableCellRenderer {
  private final JLabel firstLabel = new JLabel() {
    @Override public void paintComponent(Graphics g) {
      Graphics2D g2 = (Graphics2D) g.create();
      g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
      g2.setPaint(getBackground());
      float r = 8f;
      float x = 0f;
      float y = 0f;
      float w = getWidth();
      float h = getHeight();
      Path2D p = new Path2D.Float();
      p.moveTo(x, y + r);
      p.quadTo(x, y, x + r, y);
      p.lineTo(x + w, y);
      p.lineTo(x + w, y + h);
      p.lineTo(x + r, y + h);
      p.quadTo(x, y + h, x, y + h - r);
      p.closePath();
      g2.fill(p);
      g2.dispose();
      super.paintComponent(g);
    }
  };
  private final JLabel lastLabel = new JLabel() {
    @Override public void paintComponent(Graphics g) {
      Graphics2D g2 = (Graphics2D) g.create();
      g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
      g2.setPaint(getBackground());
      float r = 8f;
      float x = 0f;
      float y = 0f;
      float w = getWidth();
      float h = getHeight();
      Path2D p = new Path2D.Float();
      p.moveTo(x, y);
      p.lineTo(x + w - r, y);
      p.quadTo(x + w, y, x + w, y + r);
      p.lineTo(x + w, y + h - r);
      p.quadTo(x + w, y + h, x + w - r, y + h);
      p.lineTo(x, y + h);
      p.closePath();
      g2.fill(p);
      g2.dispose();
      super.paintComponent(g);
    }
  };

  public RoundedHeaderRenderer() {
    super();
    firstLabel.setOpaque(false);
    lastLabel.setOpaque(false);
    setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
    firstLabel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));
    lastLabel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));
  }

  @Override public Component getTableCellRendererComponent(
        JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
    JLabel l = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
    if (column == 0) {
      l = firstLabel;
    } else if (column == table.getColumnCount() - 1) {
      l = lastLabel;
    }
    l.setFont(table.getFont());
    l.setText(value.toString());
    l.setForeground(table.getTableHeader().getForeground());
    l.setBackground(table.getTableHeader().getBackground());
    l.setHorizontalAlignment(SwingConstants.CENTER);
    return l;
  }
}

References

2020/10/31

Show or hide each TableColumn added to the JTableHeader

Code

class TableHeaderPopupMenu extends JPopupMenu {
  protected TableHeaderPopupMenu(JTable table) {
    super();
    TableColumnModel columnModel = table.getColumnModel();
    List>TableColumn> list = Collections.list(columnModel.getColumns());
    list.forEach(tableColumn -> {
      String name = Objects.toString(tableColumn.getHeaderValue());
      // System.out.format("%s - %s%n", name, tableColumn.getIdentifier());
      JCheckBoxMenuItem item = new JCheckBoxMenuItem(name, true);
      item.addItemListener(e -> {
        if (((AbstractButton) e.getItemSelectable()).isSelected()) {
          columnModel.addColumn(tableColumn);
        } else {
          columnModel.removeColumn(tableColumn);
        }
        updateMenuItems(columnModel);
      });
      add(item);
    });
  }

  @Override public void show(Component c, int x, int y) {
    if (c instanceof JTableHeader) {
      JTableHeader header = (JTableHeader) c;
      JTable table = header.getTable();
      header.setDraggedColumn(null);
      header.repaint();
      table.repaint();
      updateMenuItems(header.getColumnModel());
      super.show(c, x, y);
    }
  }

  private void updateMenuItems(TableColumnModel columnModel) {
    boolean isOnlyOneMenu = columnModel.getColumnCount() == 1;
    if (isOnlyOneMenu) {
      stream(this).map(MenuElement::getComponent).forEach(mi ->
          mi.setEnabled(!(mi instanceof AbstractButton)
                        || !((AbstractButton) mi).isSelected()));
    } else {
      stream(this).forEach(me -> me.getComponent().setEnabled(true));
    }
  }

  private static Stream>MenuElement> stream(MenuElement me) {
    return Stream.of(me.getSubElements())
      .flatMap(m -> Stream.concat(Stream.of(m), stream(m)));
  }
}

Explanation

  • Initially shows all TableColumns generated from a TableModel
    • All JCheckBoxMenuItems are also selected
  • TableColumn hidden with TableColumnModel#removeColumn(TableColumn) method when deselected with JCheckBoxMenuItem
    • The column is removed from TableColumnModel and hidden from JTableHeader, but the column remains in TableModel
    • Check the number of TableColumn columns to enable/disable the JCheckBoxMenuItem, e.g. when opening a JPopupMenu, so that all TableColumns are not hidden
  • Show TableColumn with TableColumnModel#addColumn(TableColumn) method when selected and set with JCheckBoxMenuItem
    • Columns are added to TableColumnModel and appear in JTableHeader, but TableModel is unchanged from its initial state

  • UIManager.put("CheckBoxMenuItem.doNotCloseOnMouseClick", true) in Java 9 or higher; If set to and the currently selected TableColumn is hidden with JCheckBoxMenuItem while JPopupMenu is open, an ArrayIndexOutOfBoundsException will occur
    • Add PopupMenuListener to JPopupMenu or override the JPopupMenu#show(...) method to JTableHeader.setDraggedColumn(null) can be avoided by clearing the selection state in the scene view

References

2016/09/28

Changing JTable header text alignment of specific columns

Code

JTable table2 = makeTable();
table2.getColumnModel().getColumn(0).setHeaderRenderer(
    new HorizontalAlignmentHeaderRenderer(SwingConstants.LEFT));
table2.getColumnModel().getColumn(1).setHeaderRenderer(
    new HorizontalAlignmentHeaderRenderer(SwingConstants.CENTER));
table2.getColumnModel().getColumn(2).setHeaderRenderer(
    new HorizontalAlignmentHeaderRenderer(SwingConstants.RIGHT));

//...
class HorizontalAlignmentHeaderRenderer implements TableCellRenderer {
  private int horizontalAlignment = SwingConstants.LEFT;
  public HorizontalAlignmentHeaderRenderer(int horizontalAlignment) {
    this.horizontalAlignment = horizontalAlignment;
  }
  @Override public Component getTableCellRendererComponent(
      JTable table, Object value, boolean isSelected,
      boolean hasFocus, int row, int column) {
    TableCellRenderer r = table.getTableHeader().getDefaultRenderer();
    JLabel l = (JLabel) r.getTableCellRendererComponent(
        table, value, isSelected, hasFocus, row, column);
    l.setHorizontalAlignment(horizontalAlignment);
    return l;
  }
}

References

2016/03/31

How to hide the JTableHeader

Code

final JScrollPane scrollPane = new JScrollPane(table);
JCheckBox check = new JCheckBox("JTableHeader visible: ", true);
check.addActionListener(new ActionListener() {
  @Override public void actionPerformed(ActionEvent e) {
    JCheckBox cb = (JCheckBox) e.getSource();
    // table.getTableHeader().setVisible(cb.isSelected());
    scrollPane.getColumnHeader().setVisible(cb.isSelected());
    scrollPane.revalidate();
  }
});

References

2016/01/28

Save and load the state of a JTable and TableColumn(name, width, position and sortKey)

Code

class DefaultTableModelPersistenceDelegate extends DefaultPersistenceDelegate {
  @Override protected void initialize(
        Class<?> type, Object oldInstance, Object newInstance, Encoder encoder) {
    super.initialize(type, oldInstance,  newInstance, encoder);
    DefaultTableModel m = (DefaultTableModel) oldInstance;
    for (int row = 0; row < m.getRowCount(); row++) {
      for (int col = 0; col < m.getColumnCount(); col++) {
        Object[] o = new Object[] {m.getValueAt(row, col), row, col};
        encoder.writeStatement(new Statement(oldInstance, "setValueAt", o));
      }
    }
  }
}

File file = File.createTempFile("output", ".xml");
try (XMLEncoder xe = new XMLEncoder(new BufferedOutputStream(new FileOutputStream(file)))) {
  xe.setPersistenceDelegate(
    RowSorter.SortKey.class,
    new DefaultPersistenceDelegate(new String[] {"column", "sortOrder"}));
  xe.writeObject(table.getRowSorter().getSortKeys());
//...

class TableColumnModelPersistenceDelegate extends DefaultPersistenceDelegate {
  @Override protected void initialize(
      Class<?> type, Object oldInstance, Object newInstance, Encoder encoder) {
    super.initialize(type, oldInstance, newInstance, encoder);
    DefaultTableColumnModel m = (DefaultTableColumnModel) oldInstance;
    for (int col = 0; col < m.getColumnCount(); col++) {
      Object[] o = {m.getColumn(col)};
      encoder.writeStatement(new Statement(oldInstance, "addColumn", o));
    }
  }
}

References

2012/06/21

Height of JTableHeader

Code

JScrollPane scroll = new JScrollPane(table);
scroll.setColumnHeader(new JViewport() {
  @Override public Dimension getPreferredSize() {
    Dimension d = super.getPreferredSize();
    d.height = 32;
    return d;
  }
});
//or
table.setTableHeader(new JTableHeader(table.getColumnModel()) {
  @Override public Dimension getPreferredSize() {
    Dimension d = super.getPreferredSize();
    d.height = 32;
    return d;
  }
});

References

2011/12/22

Dropdown menu button in the JTableHeader

Code

@Override public void mouseClicked(MouseEvent e) {
  JTableHeader header = (JTableHeader) e.getSource();
  JTable table = header.getTable();
  TableColumnModel columnModel = table.getColumnModel();
  int vci = columnModel.getColumnIndexAtX(e.getX());
  int mci = table.convertColumnIndexToModel(vci);
  TableColumn column = table.getColumnModel().getColumn(mci);
  Rectangle r = header.getHeaderRect(vci);
  Container c = (Container) getTableCellRendererComponent(table, "", true, true, -1, vci);
  // if (!isNimbus) {
  //   Insets i = c.getInsets();
  //   r.translate(r.width - i.right, 0);
  // } else {
  r.translate(r.width - BUTTON_WIDTH, 0);
  r.setSize(BUTTON_WIDTH, r.height);
  Point pt = e.getPoint();
  if (c.getComponentCount() > 0 && r.contains(pt) && pop != null) {
    pop.show(header, r.x, r.height);
    JButton b = (JButton) c.getComponent(0);
    b.doClick();
    e.consume();
  }
}

@Override public void mouseExited(MouseEvent e) {
  rolloverIndex = -1;
}

@Override public void mouseMoved(MouseEvent e) {
  JTableHeader header = (JTableHeader) e.getSource();
  JTable table = header.getTable();
  TableColumnModel columnModel = table.getColumnModel();
  int vci = columnModel.getColumnIndexAtX(e.getX());
  int mci = table.convertColumnIndexToModel(vci);
  rolloverIndex = mci;
}

References

2011/04/19

Transparent JTableHeader

Code

class TransparentHeader extends JLabel implements TableCellRenderer {
  private final Border b = BorderFactory.createCompoundBorder(
      BorderFactory.createMatteBorder(0, 0, 1, 0, Color.BLACK),
      BorderFactory.createEmptyBorder(2, 2, 1, 2));
  private final Color alphaZero = new Color(0x0, true);
  @Override public Component getTableCellRendererComponent(
      JTable table, Object value, boolean isSelected, boolean hasFocus,
      int row, int column) {
    this.setText(Objects.toString(value, ""));
    this.setHorizontalAlignment(SwingConstants.CENTER);
    this.setOpaque(false);
    this.setBackground(alphaZero);
    this.setForeground(Color.BLACK);
    this.setBorder(b);
    return this;
  }
}

References

2009/02/16

JTableHeader CheckBox

Code

enum Status { SELECTED, DESELECTED, INDETERMINATE }

class HeaderRenderer extends JCheckBox implements TableCellRenderer {
  private final JLabel label = new JLabel("Check All");
  private final int targetColumnIndex;

  public HeaderRenderer(JTableHeader header, int index) {
    super((String) null);
    this.targetColumnIndex = index;
    setOpaque(false);
    setFont(header.getFont());
    header.addMouseListener(new MouseAdapter() {
      @Override public void mouseClicked(MouseEvent e) {
        JTableHeader header = (JTableHeader) e.getComponent();
        JTable table = header.getTable();
        TableColumnModel columnModel = table.getColumnModel();
        int vci = columnModel.getColumnIndexAtX(e.getX());
        int mci = table.convertColumnIndexToModel(vci);
        if (mci == targetColumnIndex) {
          TableColumn column = columnModel.getColumn(vci);
          boolean b = column.getHeaderValue() == Status.DESELECTED;
          TableModel m = table.getModel();
          for (int i = 0; i < m.getRowCount(); i++) {
            m.setValueAt(b, i, mci);
          }
          column.setHeaderValue(b ? Status.SELECTED : Status.DESELECTED);
          // header.repaint();
        }
      }
    });
  }

  @Override public Component getTableCellRendererComponent(
        JTable tbl, Object val, boolean isS, boolean hasF, int row, int col) {
    TableCellRenderer r = tbl.getTableHeader().getDefaultRenderer();
    JLabel l = (JLabel) r.getTableCellRendererComponent(tbl, val, isS, hasF, row, col);
    if (targetColumnIndex == tbl.convertColumnIndexToModel(col)) {
      if (val instanceof Status) {
        switch ((Status) val) {
        case SELECTED:
          setSelected(true);
          setEnabled(true);
          break;
        case DESELECTED:
          setSelected(false);
          setEnabled(true);
          break;
        case INDETERMINATE:
          setSelected(true);
          setEnabled(false);
          break;
        default:
          throw new AssertionError("Unknown Status");
        }
      } else {
        setSelected(true);
        setEnabled(false);
      }
      label.setIcon(new ComponentIcon(this));
      l.setIcon(new ComponentIcon(label));
      l.setText(null);
    }
    return l;
  }
}

class ComponentIcon implements Icon {
  private final JComponent cmp;

  public ComponentIcon(JComponent cmp) {
    this.cmp = cmp;
  }

  @Override public int getIconWidth() {
    return cmp.getPreferredSize().width;
  }

  @Override public int getIconHeight() {
    return cmp.getPreferredSize().height;
  }

  @Override public void paintIcon(Component c, Graphics g, int x, int y) {
    SwingUtilities.paintComponent(
        g, cmp, c.getParent(), x, y, getIconWidth(), getIconHeight());
  }
}

References

2008/11/11

create JTabbedPane like component using CardLayout and JRadioButton(or JTableHeader)

Code

class CardLayoutTabbedPane extends JPanel {
  protected final CardLayout cardLayout = new CardLayout();
  protected final JPanel tabPanel = new JPanel(new GridLayout(1, 0, 0, 0));
  protected final JPanel wrapPanel = new JPanel(new BorderLayout(0, 0));
  protected final JPanel contentsPanel = new JPanel(cardLayout);
  protected final ButtonGroup bg = new ButtonGroup();

  public CardLayoutTabbedPane() {
    super(new BorderLayout());
    int left  = 1;
    int right = 3;
    tabPanel.setBorder(BorderFactory.createEmptyBorder(1, left, 0, right));
    contentsPanel.setBorder(BorderFactory.createEmptyBorder(4, left, 2, right));
    wrapPanel.add(tabPanel);
    wrapPanel.add(new JLabel("test:"), BorderLayout.WEST);
    add(wrapPanel, BorderLayout.NORTH);
    add(contentsPanel);
  }

  public void addTab(final String title, final Component comp) {
    JRadioButton b = new TabButton(new AbstractAction(title) {
      public void actionPerformed(ActionEvent e) {
        cardLayout.show(contentsPanel, title);
      }
    });
    tabPanel.add(b);
    bg.add(b);
    b.setSelected(true);
    contentsPanel.add(comp, title);
    cardLayout.show(contentsPanel, title);
  }
}

References