Google Tag Manager

2017/08/22

Apply sort order cycle with ascending, descending, and unsorted in TableRowSorter with multi-key sorting

Summary

A three-state sorter(ascending, descending, none) example. Primary sort column is highlighted and the next ones have their sort-order number in brackets as shown on the screenshot below.
  • Left click: Change sort order in ascending, descending, unsorted
  • Right click: Unsorted

Code

/**
 * @author ssr
 */
jTable1.getTableHeader().setDefaultRenderer(new TestTableCellRenderer(jTable1));
TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(jTable1.getModel()) {
  @Override
  public void toggleSortOrder(int column) {
    int keyIndex;
    if (column >= 0 && column < getModelWrapper().getColumnCount() && isSortable(column)) {
      List&lt;RowSorter.SortKey> keys = new ArrayList&lt;>(getSortKeys());
      boolean found = false;
      for (keyIndex = 0; keyIndex < keys.size(); keyIndex++) {
        RowSorter.SortKey sortKey = keys.get(keyIndex);
        if (sortKey.getColumn() == column) {
          found = true;
          break;
        }
      }
      if (isSortable(column)) {
        if (!found) {
          // Key doesn't exist
          RowSorter.SortKey sortKey = new RowSorter.SortKey(column, SortOrder.ASCENDING);
          keys.add(sortKey);
          System.out.println("ADDED: ");
          keys.stream().forEach(k -> System.out.println(k.getColumn() + ":" + k.getSortOrder()));
        } else {
          RowSorter.SortKey sortKey = keys.get(keyIndex);
          if (sortKey.getSortOrder() == SortOrder.DESCENDING) {
            keys.remove(keyIndex);
            System.out.println("REMOVED: ");
            keys.stream().forEach(k -> System.out.println(k.getColumn() + ":" + k.getSortOrder()));
          } else {
            keys.set(keyIndex, new RowSorter.SortKey(
                column, sortKey.getSortOrder() == SortOrder.ASCENDING
                  ? SortOrder.DESCENDING
                  : SortOrder.ASCENDING));
            System.out.println("MODIFIED: ");
            keys.stream().forEach(k -> System.out.println(k.getColumn() + ":" + k.getSortOrder()));
          }
        }
        if (keys.size() > getMaxSortKeys()) {
          keys = keys.subList(0, getMaxSortKeys());
        }
        setSortKeys(keys);
      }
    }
  }
};
sorter.setMaxSortKeys(4);
jTable1.setRowSorter(sorter);

References

2017/07/31

Detects that it has reached the bottom of JScrollPane

Code

JScrollPane scroll = new JScrollPane(c);
scroll.getVerticalScrollBar().getModel().addChangeListener(e -> {
  BoundedRangeModel m = (BoundedRangeModel) e.getSource();
  int extent = m.getExtent();
  int maximum = m.getMaximum();
  int value = m.getValue();
  if (value + extent >= maximum) {
    check.setEnabled(true);
  }
});

References

2017/06/29

Automatically adjust the height of JTable's row

Code

class TextAreaCellRenderer extends JTextArea implements TableCellRenderer {
  private final List<List<Integer>> cellHeights = new ArrayList<>();

  @Override public void updateUI() {
    super.updateUI();
    setLineWrap(true);
    setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
    setName("Table.cellRenderer");
  }
  @Override public Component getTableCellRendererComponent(
      JTable table, Object value, boolean isSelected, boolean hasFocus,
      int row, int column) {
    setFont(table.getFont());
    setText(Objects.toString(value, ""));
    adjustRowHeight(table, row, column);
    return this;
  }

  /**
   * Calculate the new preferred height for a given row,
   * and sets the height on the table.
   * http://blog.botunge.dk/post/2009/10/09/JTable-multiline-cell-renderer.aspx
   */
  private void adjustRowHeight(JTable table, int row, int column) {
  // The trick to get this to work properly is to set the width of the column to
  // the textarea. The reason for this is that getPreferredSize(), without a width
  // tries to place all the text in one line.
  // By setting the size with the width of the column,
  // getPreferredSize() returnes the proper height which the row should have in
  // order to make room for the text.
  // int cWidth = table.getTableHeader().getColumnModel().getColumn(column).getWidth();
  // int cWidth = table.getCellRect(row, column, false).width; // IntercellSpacingを無視
  // setSize(new Dimension(cWidth, 1000));

    setBounds(table.getCellRect(row, column, false)); // setSizeではなくsetBoundsでも可
    // doLayout(); // 必要なさそう

    int preferredHeight = getPreferredSize().height;
    while (cellHeights.size() <= row) {
      cellHeights.add(new ArrayList<>(column));
    }
    List<Integer> list = cellHeights.get(row);
    while (list.size() <= column) {
      list.add(0);
    }
    list.set(column, preferredHeight);
    int max = list.stream().max(Integer::compare).get();
    if (table.getRowHeight(row) != max) {
      table.setRowHeight(row, max);
    }
  }
}

References

2017/05/30

Use JComboBox as JTree's node cell editor

Code

class PluginCellEditor extends DefaultCellEditor {
  private final PluginPanel panel;
  private transient Node node;

  public PluginCellEditor(JComboBox<String> comboBox) {
    super(comboBox);
    panel = new PluginPanel(comboBox);
  }

  @Override public Component getTreeCellEditorComponent(
      JTree tree, Object value, boolean selected, boolean expanded,
      boolean leaf, int row) {
    Node node = panel.extractNode(value);
    panel.setContents(node);
    this.node = node;
    return panel;
  }

  @Override public Object getCellEditorValue() {
    Object o = super.getCellEditorValue();
    if (node == null) {
      return o;
    }
    DefaultComboBoxModel<String> m =
        (DefaultComboBoxModel<String>) panel.comboBox.getModel();
    Node n = new Node(panel.pluginName.getText(), node.plugins);
    n.setSelectedPluginIndex(m.getIndexOf(o));
    return n;
  }

  @Override public boolean isCellEditable(EventObject e) {
    Object source = e.getSource();
    if (!(source instanceof JTree) || !(e instanceof MouseEvent)) {
      return false;
    }
    JTree tree = (JTree) source;
    MouseEvent me = (MouseEvent) e;
    TreePath path = tree.getPathForLocation(me.getX(), me.getY());
    if (path == null) {
      return false;
    }
    Object node = path.getLastPathComponent();
    if (!(node instanceof DefaultMutableTreeNode)) {
      return false;
    }
    Rectangle r = tree.getPathBounds(path);
    if (r == null) {
      return false;
    }
    Dimension d = panel.getPreferredSize();
    r.setSize(new Dimension(d.width, r.height));
    if (r.contains(me.getX(), me.getY())) {
      showComboPopup(tree, me);
      return true;
    }
    return delegate.isCellEditable(e);
  }

  private void showComboPopup(final JTree tree, final MouseEvent me) {
    EventQueue.invokeLater(new Runnable() {
      @Override public void run() {
        Point pt = SwingUtilities.convertPoint(tree, me.getPoint(), panel);
        Component o = SwingUtilities.getDeepestComponentAt(panel, pt.x, pt.y);
        if (o instanceof JComboBox) {
          panel.comboBox.showPopup();
        } else if (o != null) {
          Container c = SwingUtilities.getAncestorOfClass(
              JComboBox.class, (Component) o);
          if (c instanceof JComboBox) {
            panel.comboBox.showPopup();
          }
        }
      }
    });
  }
}

References

2017/04/28

Disable any items in JList

Code

JList<String> list = new JList<>(model);
list.setCellRenderer(new DefaultListCellRenderer() {
  @Override public Component getListCellRendererComponent(
      JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
    Component c;
    if (disableIndexSet.contains(index)) {
      c = super.getListCellRendererComponent(list, value, index, false, false);
      c.setEnabled(false);
    } else {
      c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
    }
    return c;
  }
});

ActionMap am = list.getActionMap();
am.put("selectNextRow", new AbstractAction() {
  @Override public void actionPerformed(ActionEvent ae) {
    for (int i = list.getSelectedIndex() + 1; i < list.getModel().getSize(); i++) {
      if (!disableIndexSet.contains(Integer.valueOf(i))) {
        list.setSelectedIndex(i);
        break;
      }
    }
  }
});

References