Google Tag Manager

2016/12/27

Change the shape of JToolTip to balloon

Code

class BalloonToolTip extends JToolTip {
  private HierarchyListener listener;
  @Override public void updateUI() {
    removeHierarchyListener(listener);
    super.updateUI();
    listener = e -> {
      Component c = e.getComponent();
      boolean b = (e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0;
      if (b && c.isShowing()) {
        Window w = SwingUtilities.getWindowAncestor(c);
        if (w != null && w.getType() == Window.Type.POPUP) {
          // Popup$HeavyWeightWindow
          w.setBackground(new Color(0x0, true));
        }
      }
    };
    addHierarchyListener(listener);
    setOpaque(false);
    setBorder(BorderFactory.createEmptyBorder(8, 5, 0, 5));
  }

  @Override public Dimension getPreferredSize() {
    Dimension d = super.getPreferredSize();
    d.height = 28;
    return d;
  }

  @Override public void paintComponent(Graphics g) {
    Shape s = makeBalloonShape();
    Graphics2D g2 = (Graphics2D) g.create();
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                        RenderingHints.VALUE_ANTIALIAS_ON);
    g2.setColor(getBackground());
    g2.fill(s);
    g2.setColor(getForeground());
    g2.draw(s);
    g2.dispose();
    super.paintComponent(g);
  }

  private Shape makeBalloonShape() {
    Insets i = getInsets();
    int w = getWidth() - 1;
    int h = getHeight() - 1;
    int v = i.top / 2;
    Polygon triangle = new Polygon();
    triangle.addPoint(i.left + v + v, 0);
    triangle.addPoint(i.left + v, v);
    triangle.addPoint(i.left + v + v + v, v);
    Area area = new Area(new RoundRectangle2D.Float(
        0, v, w, h - i.bottom - v, i.top, i.top));
    area.add(new Area(triangle));
    return area;
  }
}

//...
JList<String> list = new JList<String>(model) {
  @Override public JToolTip createToolTip() {
    JToolTip tip = new BalloonToolTip();
    tip.setComponent(this);
    return tip;
  }
};

References

2016/11/24

Filtering JList items by Regex

Code

DefaultListModel model = new DefaultListModel<>();
JList list = new JList(model) {
  @Override public void updateUI() {
    setSelectionForeground(null);
    setSelectionBackground(null);
    setCellRenderer(null);
    super.updateUI();
    setLayoutOrientation(JList.HORIZONTAL_WRAP);
    setVisibleRowCount(0);
    setFixedCellWidth(82);
    setFixedCellHeight(64);
    setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10));
    setCellRenderer(new ListItemListCellRenderer());
    getSelectionModel().setSelectionMode(
        ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
  }
};
private Optional getPattern() {
  try {
    return Optional.ofNullable(field.getText())
                   .filter(s -> !s.isEmpty())
                   .map(Pattern::compile);
  } catch (PatternSyntaxException ex) {
    return Optional.empty();
  }
}
private void filter() {
  getPattern().ifPresent(pattern -> {
    List selected = list.getSelectedValuesList();
    model.clear();
    Stream.of(defaultModel)
          .filter(item -> pattern.matcher(item.title).find())
          .forEach(model::addElement);
    for (ListItem item : selected) {
      int i = model.indexOf(item);
      list.addSelectionInterval(i, i);
    }
  });
}

References

2016/10/25

Undo redo the selected state of the checkboxes

Code

private BigInteger status = new BigInteger("111000111", 2);
private static final int BIT_LENGTH = 50;
//...
for (int i = 0; i < BIT_LENGTH; i++) {
  BigInteger l = BigInteger.ONE.shiftLeft(i);
  JCheckBox c = new JCheckBox(Integer.toString(i + 1));
  c.addActionListener(e -> {
    JCheckBox cb = (JCheckBox) e.getSource();
    BigInteger newValue = cb.isSelected() ? status.or(l) : status.xor(l);
    undoSupport.postEdit(new StatusEdit(status, newValue));
    status = newValue;
    label.setText(print(status));
  });
  c.setSelected(!status.and(l).equals(BigInteger.ZERO));
  p.add(c);
}

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/08/26

Use an editable JComboBox as a TableCellEditor

Code

class ComboCellEditor extends AbstractCellEditor implements TableCellEditor {
  private final JComboBox combo = new JComboBox<>();
  protected ComboCellEditor() {
    super();
    combo.setEditable(true);
    combo.addActionListener(e -> {
      fireEditingStopped();
    });
  }
  @Override public Component getTableCellEditorComponent(
      JTable table, Object value, boolean isSelected, int row, int column) {
    if (value instanceof ComboBoxModel) {
      @SuppressWarnings("unchecked")
      ComboBoxModel m = (ComboBoxModel) value;
      combo.setModel(m);
    }
    return combo;
  }
  @Override public Object getCellEditorValue() {
    @SuppressWarnings("unchecked")
    DefaultComboBoxModel m = (DefaultComboBoxModel) combo.getModel();
    if (combo.isEditable()) {
      String str = Objects.toString(combo.getEditor().getItem(), "");
      if (!str.isEmpty() && m.getIndexOf(str) < 0) {
        m.insertElementAt(str, 0);
        combo.setSelectedIndex(0);
      }
    }
    return m;
  }
}

References

2016/07/26

Select multiple JCheckBox in JComboBox

Code

class CheckedComboBox<E extends CheckableItem> extends JComboBox<E> {
  private boolean keepOpen;
  private transient ActionListener listener;

  protected CheckedComboBox() {
    super();
  }
  protected CheckedComboBox(ComboBoxModel<E> aModel) {
    super(aModel);
  }
  protected CheckedComboBox(E[] m) {
    super(m);
  }
  @Override public Dimension getPreferredSize() {
    return new Dimension(200, 20);
  }
  @Override public void updateUI() {
    setRenderer(null);
    removeActionListener(listener);
    super.updateUI();
    listener = e -> {
      if ((e.getModifiers() & InputEvent.MOUSE_EVENT_MASK) != 0) {
        updateItem(getSelectedIndex());
        keepOpen = true;
      }
    };
    setRenderer(new CheckBoxCellRenderer<CheckableItem>());
    addActionListener(listener);
    getInputMap(JComponent.WHEN_FOCUSED).put(
        KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "checkbox-select");
    getActionMap().put("checkbox-select", new AbstractAction() {
      @Override public void actionPerformed(ActionEvent e) {
        Accessible a = getAccessibleContext().getAccessibleChild(0);
        if (a instanceof BasicComboPopup) {
          BasicComboPopup pop = (BasicComboPopup) a;
          updateItem(pop.getList().getSelectedIndex());
        }
      }
    });
  }
  private void updateItem(int index) {
    if (isPopupVisible()) {
      E item = getItemAt(index);
      item.selected ^= true;
      removeItemAt(index);
      insertItemAt(item, index);
      setSelectedItem(item);
    }
  }
  @Override public void setPopupVisible(boolean v) {
    if (keepOpen) {
      keepOpen = false;
    } else {
      super.setPopupVisible(v);
    }
  }
}

References

2016/06/27

Change the color of the label represents the current value of a JSlider

Code

slider.getModel().addChangeListener(new ChangeListener() {
  private int prev = -1;
  private void resetForeground(Object o, Color c) {
    if (o instanceof Component) {
      ((Component) o).setForeground(c);
    }
  }
  @Override public void stateChanged(ChangeEvent e) {
    BoundedRangeModel m = (BoundedRangeModel) e.getSource();
    int i = m.getValue();
    if ((slider.getMajorTickSpacing() == 0 ||
         i % slider.getMajorTickSpacing() == 0) && i != prev) {
      Dictionary dictionary = slider.getLabelTable();
      resetForeground(dictionary.get(i), Color.RED);
      resetForeground(dictionary.get(prev), Color.BLACK);
      slider.repaint();
      prev = i;
    }
  }
});

References

2016/05/30

Keep visible the JPopupMenu while clicking on CheckBox

Code

JMenuItem mi = new JMenuItem(" ");
mi.setLayout(new BorderLayout());
mi.add(new JCheckBox(title) {
  private transient MouseAdapter handler;
  @Override public void updateUI() {
    removeMouseListener(handler);
    removeMouseMotionListener(handler);
    super.updateUI();
    handler = new DispatchParentHandler();
    addMouseListener(handler);
    addMouseMotionListener(handler);
    setFocusable(false);
    setOpaque(false);
  }
});
popup.add(mi);

popup.add(new JCheckBoxMenuItem("keeping open #2") {
  @Override public void updateUI() {
    super.updateUI();
    setUI(new BasicCheckBoxMenuItemUI() {
      @Override protected void doClick(MenuSelectionManager msm) {
        //super.doClick(msm);
        System.out.println("MenuSelectionManager: doClick");
        menuItem.doClick(0);
      }
    });
  }
});
// https://bugs.openjdk.java.net/browse/JDK-8165234
// Provide a way to not close toggle menu items on mouse click on component level
// JDK 9
JMenuItem menuItem = new JMenuItem("JMenuItem");
menuItem.putClientProperty("CheckBoxMenuItem.doNotCloseOnMouseClick", true);

References

2016/05/10

The JRadioButton that is currently selected in the ButtonGroup set as the default focus component

Code

buttons.setFocusTraversalPolicyProvider(true);
buttons.setFocusTraversalPolicy(new LayoutFocusTraversalPolicy() {
  @Override public Component getDefaultComponent(Container focusCycleRoot) {
    ButtonModel selection = bg.getSelection();
    for (Component c: focusCycleRoot.getComponents()) {
      JRadioButton r = (JRadioButton) c;
      if (r.getModel().equals(selection)) {
        return r;
      }
    }
    return super.getDefaultComponent(focusCycleRoot);
  }
});

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/03/03

Validating input on an editable JComboBox with InputVerifier and JLayer

Code

JComboBox comboBox = new JComboBox(model) {
  @Override public void updateUI() {
    getActionMap().put(ENTER_PRESSED, null);
    super.updateUI();
    final JComboBox cb = this;
    final Action defalutEnterPressedAction = getActionMap().get(ENTER_PRESSED);
    getActionMap().put(ENTER_PRESSED, new AbstractAction() {
      @Override public void actionPerformed(ActionEvent e) {
        boolean isPopupVisible = isPopupVisible();
        setPopupVisible(false);
        DefaultComboBoxModel m = (DefaultComboBoxModel) getModel();
        String str = Objects.toString(getEditor().getItem(), "");
        if (m.getIndexOf(str) < 0 && getInputVerifier().verify(cb)) {
          m.removeElement(str);
          m.insertElementAt(str, 0);
          if (m.getSize() > 10) {
            m.removeElementAt(10);
          }
          setSelectedIndex(0);
          setPopupVisible(isPopupVisible);
        } else {
          defalutEnterPressedAction.actionPerformed(e);
        }
      }
    });
  }
};
comboBox.setEditable(true);
comboBox.setInputVerifier(new LengthInputVerifier());
comboBox.setEditor(new BasicComboBoxEditor() {
  private Component editorComponent;
  @Override public Component getEditorComponent() {
    if (editorComponent == null) {
      JTextComponent tc = (JTextComponent) super.getEditorComponent();
      editorComponent = new JLayer(tc, new ValidationLayerUI());
    }
    return editorComponent;
  }
});

class LengthInputVerifier extends InputVerifier {
  private static final int MAX_LEN = 6;
  @Override public boolean verify(JComponent c) {
    if (c instanceof JComboBox) {
      JComboBox cb = (JComboBox) c;
      String str = Objects.toString(cb.getEditor().getItem(), "");
      return MAX_LEN - str.length() >= 0;
    }
    return false;
  }
}

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