Google Tag Manager

2023/10/31

Sort JTable rows with multiple conditions

Code

RowSorter<? extends TableModel> sorter = table.getRowSorter();
if (sorter instanceof TableRowSorter) {
  TableRowSorter<? extends TableModel> rs
      = (TableRowSorter<? extends TableModel>) sorter;
  rs.setComparator(0, Comparator.comparing(RowData::getPosition));
  rs.setComparator(1, Comparator.comparing(RowData::getTeam));
  rs.setComparator(2, Comparator.comparing(RowData::getMatches));
  rs.setComparator(3, Comparator.comparing(RowData::getWins));
  rs.setComparator(4, Comparator.comparing(RowData::getDraws));
  rs.setComparator(5, Comparator.comparing(RowData::getLosses));
  rs.setComparator(6, Comparator.comparing(RowData::getGoalsFor));
  rs.setComparator(7, Comparator.comparing(RowData::getGoalsAgainst));
  rs.setComparator(8, Comparator.comparing(RowData::getGoalDifference));
  rs.setComparator(9, Comparator.comparing(RowData::getPoints)
      .thenComparing(RowData::getGoalDifference));
}
add(new JScrollPane(table));

References

2023/09/30

Move the selected item in JList up or down by clicking on the JButton placed on the JToolBar

Since DefaultListModel does not have a move method like DefaultTableModel#moveRow(int start, int end, int to), the DefaultListModel#get(int index), DefaultListModel#remove(int index), and DefaultListModel#add(int index, E element) methods are used in combination to move selected items up and down the JList.

Code

JButton up = new JButton("▲");
up.setFocusable(false);
up.addActionListener(e -> {
  int[] pos = list.getSelectedIndices();
  if (pos.length == 0) {
    return;
  }
  boolean isShiftDown = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
  int index0 = isShiftDown ? 0 : Math.max(0, pos[0] - 1);
  int idx = index0;
  for (int i : pos) {
    model.add(idx, model.remove(i));
    list.addSelectionInterval(idx, idx);
    idx++;
  }
  // scroll
  Rectangle r = list.getCellBounds(index0, index0 + pos.length);
  list.scrollRectToVisible(r);
});

JButton down = new JButton("▼");
down.setFocusable(false);
down.addActionListener(e -> {
  int[] pos = list.getSelectedIndices();
  if (pos.length == 0) {
    return;
  }
  boolean isShiftDown = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
  int max = model.getSize();
  int index = isShiftDown ? max : Math.min(max, pos[pos.length - 1] + 1);
  int index0 = index;
  // copy
  for (int i : pos) {
    int idx = Math.min(model.getSize(), ++index);
    model.add(idx, model.get(i));
    list.addSelectionInterval(idx, idx);
  }
  // clean
  for (int i = pos.length - 1; i >= 0; i--) {
    model.remove(pos[i]);
  }
  // scroll
  Rectangle r = list.getCellBounds(index0 - pos.length, index0);
  list.scrollRectToVisible(r);
});

References

2023/08/31

Hide the check icon on the JRadioButtonMenuItem and reduce the text offset

Code

UIManager.put(PRE + "minimumTextOffset", 10);
UIManager.put(PRE + "afterCheckIconGap", 0);
UIManager.put(PRE + "checkIconOffset", 0);
Icon checkIcon = getCheckIcon();
int height = checkIcon == null ? 22 : checkIcon.getIconHeight();
UIManager.put(PRE + "checkIcon", new EmptyIcon());
Dimension d = new Dimension(100, height);
JPopupMenu popup = new JPopupMenu();
ButtonGroup bg = new ButtonGroup();
Arrays.asList(
    makeMenuItem("0.5 pt", .5f, d),
    makeMenuItem("0.75 pt", .75f, d),
    makeMenuItem("1 pt", 1f, d),
    makeMenuItem("1.5 pt", 1.5f, d),
    makeMenuItem("2.25 pt", 2.25f, d),
    makeMenuItem("3 pt", 3f, d)
).forEach(m -> {
  popup.add(m);
  bg.add(m);
});
// ...
private static JMenuItem makeMenuItem(String txt, float width, Dimension d) {
  float px = width * Toolkit.getDefaultToolkit().getScreenResolution() / 72f;
  return new JRadioButtonMenuItem(txt, new LineIcon(new BasicStroke(px), d)) {
    @Override protected void init(String text, Icon icon) {
      super.init(text, icon);
      setHorizontalTextPosition(LEADING);
      setHorizontalAlignment(TRAILING);
    }

    @Override protected void paintComponent(Graphics g) {
      if (isSelected()) {
        Graphics2D g2 = (Graphics2D) g.create();
        g2.setPaint(new Color(0xAA_64_AA_FF, true));
        g2.fillRect(0, 0, getWidth(), getHeight());
        g2.dispose();
      }
      super.paintComponent(g);
    }
  };
}

References

2023/07/31

Add item check boxes to JList cells

Code

@Override public void setSelectionInterval(int anchor, int lead) {
  if (checkedIndex < 0 && isDragging()) {
    super.setSelectionInterval(anchor, lead);
  } else {
    EventQueue.invokeLater(() -> {
      if (checkedIndex >= 0 && lead == anchor && checkedIndex == anchor) {
        super.addSelectionInterval(checkedIndex, checkedIndex);
      } else {
        super.setSelectionInterval(anchor, lead);
      }
    });
  }
}

protected boolean isDragging() {
  Rectangle r = getRubberBand().getBounds();
  return r.width != 0 || r.height != 0;
}

@Override public void removeSelectionInterval(int index0, int index1) {
  if (checkedIndex < 0) {
    super.removeSelectionInterval(index0, index1);
  } else {
    EventQueue.invokeLater(() -> super.removeSelectionInterval(index0, index1));
  }
}

private static <E> Optional<AbstractButton> getItemCheckBox(
    JList<E> list, MouseEvent e, int index) {
  if (e.isShiftDown() || e.isControlDown() || e.isAltDown()) {
    return Optional.empty();
  }
  E proto = list.getPrototypeCellValue();
  ListCellRenderer<? super E> cr = list.getCellRenderer();
  Component c = cr.getListCellRendererComponent(
      list, proto, index, false, false);
  Rectangle r = list.getCellBounds(index, index);
  c.setBounds(r);
  Point pt = e.getPoint();
  pt.translate(-r.x, -r.y);
  return Optional
    .ofNullable(SwingUtilities.getDeepestComponentAt(c, pt.x, pt.y))
    .filter(AbstractButton.class::isInstance)
    .map(AbstractButton.class::cast);
}

private final class ItemCheckBoxesListener extends MouseAdapter {
  private final Point srcPoint = new Point();

  @Override public void mouseDragged(MouseEvent e) {
    checkedIndex = -1;
    JList<?> l = (JList<?>) e.getComponent();
    l.setFocusable(true);
    Point destPoint = e.getPoint();
    Path2D rb = getRubberBand();
    rb.reset();
    rb.moveTo(srcPoint.x, srcPoint.y);
    rb.lineTo(destPoint.x, srcPoint.y);
    rb.lineTo(destPoint.x, destPoint.y);
    rb.lineTo(srcPoint.x, destPoint.y);
    rb.closePath();

    int[] indices = IntStream.range(0, l.getModel().getSize())
            .filter(i -> rb.intersects(l.getCellBounds(i, i))).toArray();
    l.setSelectedIndices(indices);
    l.repaint();
  }

  @Override public void mouseExited(MouseEvent e) {
    rollOverIndex = -1;
    e.getComponent().repaint();
  }

  @Override public void mouseMoved(MouseEvent e) {
    Point pt = e.getPoint();
    int idx = locationToIndex(pt);
    if (!getCellBounds(idx, idx).contains(pt)) {
      idx = -1;
    }
    Rectangle rect = new Rectangle();
    if (idx > 0) {
      rect.add(getCellBounds(idx, idx));
      if (rollOverIndex >= 0 && idx != rollOverIndex) {
        rect.add(getCellBounds(rollOverIndex, rollOverIndex));
      }
      rollOverIndex = idx;
    } else {
      if (rollOverIndex >= 0) {
        rect.add(getCellBounds(rollOverIndex, rollOverIndex));
      }
      rollOverIndex = -1;
    }
    ((JComponent) e.getComponent()).repaint(rect);
  }

  @Override public void mouseReleased(MouseEvent e) {
    getRubberBand().reset();
    Component c = e.getComponent();
    c.setFocusable(true);
    c.repaint();
  }

  @Override public void mousePressed(MouseEvent e) {
    JList<?> l = (JList<?>) e.getComponent();
    int index = l.locationToIndex(e.getPoint());
    if (l.getCellBounds(index, index).contains(e.getPoint())) {
      l.setFocusable(true);
      cellPressed(l, e, index);
    } else {
      l.setFocusable(false);
      l.clearSelection();
      l.getSelectionModel().setAnchorSelectionIndex(-1);
      l.getSelectionModel().setLeadSelectionIndex(-1);
    }
    srcPoint.setLocation(e.getPoint());
    l.repaint();
  }

  private void cellPressed(JList<?> l, MouseEvent e, int index) {
    if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() > 1) {
      ListItem item = getModel().getElementAt(index);
      JOptionPane.showMessageDialog(l.getRootPane(), item.title);
    } else {
      checkedIndex = -1;
      getItemCheckBox(l, e.getPoint(), index).ifPresent(rb -> {
        checkedIndex = index;
        if (l.isSelectedIndex(index)) {
          l.setFocusable(false);
          removeSelectionInterval(index, index);
        } else {
          setSelectionInterval(index, index);
        }
      });
    }
  }
}

References

2023/07/01

Click on the JCheckBox placed in the JTable cell to expand and collapss the row height

Code

int defaultHeight = 20;
JTable table = new JTable(model) {
  @Override public void updateUI() {
    super.updateUI();
    setAutoCreateRowSorter(true);
    setSurrendersFocusOnKeystroke(true);
    setRowHeight(defaultHeight);
    setDefaultRenderer(RowHeader.class, new RowHeaderRenderer());
    setDefaultEditor(RowHeader.class, new RowHeaderEditor());
    TableColumn column = getColumnModel().getColumn(1);
    column.setCellRenderer(new TextAreaCellRenderer());
    column.setPreferredWidth(160);
  }
};
table.getModel().addTableModelListener(e -> {
  int mc = e.getColumn();
  int mr = e.getFirstRow();
  int vc = table.convertColumnIndexToView(mc);
  int vr = table.convertRowIndexToView(mr);
  Object o = table.getValueAt(vr, vc);
  if (mc == 0 && o instanceof RowHeader) {
    RowHeader rh = (RowHeader) o;
    int vc1 = table.convertColumnIndexToView(1);
    TableCellRenderer r = table.getColumnModel().getColumn(vc1)
                               .getCellRenderer();
    Object v = table.getValueAt(vr, vc1);
    Component c = r.getTableCellRendererComponent(
        table, v, true, true, vr, vc1);
    int h = rh.isSelected() ? c.getPreferredSize().height
                            : defaultHeight;
    table.setRowHeight(vr, h);
  }
});

References