Google Tag Manager

2020/05/02

Disable the JOptionPane OK button until text is entered in the JTextField

Code

JPanel panel2 = new JPanel(new GridLayout(2, 1));
JTextField field2 = new JTextField();
Border enabledBorder = field2.getBorder();
Insets i = enabledBorder.getBorderInsets(field2);
Border disabledBorder = BorderFactory.createCompoundBorder(
    BorderFactory.createLineBorder(Color.RED),
    BorderFactory.createEmptyBorder(i.top - 1, i.left - 1, i.bottom - 1, i.right - 1));
String disabledMessage = "Text is required to create ...";
JLabel label2 = new JLabel(" ");
label2.setForeground(Color.RED);
panel2.add(field2);
panel2.add(label2);
if (field2.getText().isEmpty()) {
  field2.setBorder(disabledBorder);
  label2.setText(disabledMessage);
}
field2.addHierarchyListener(e -> {
  Component c = e.getComponent();
  if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0 && c.isShowing()) {
    EventQueue.invokeLater(c::requestFocusInWindow);
  }
});
field2.getDocument().addDocumentListener(new DocumentListener() {
  private void update() {
    boolean verified = !field2.getText().isEmpty();
    JButton b = field2.getRootPane().getDefaultButton();
    if (verified) {
      b.setEnabled(true);
      field2.setBorder(enabledBorder);
      label2.setText(" ");
    } else {
      b.setEnabled(false);
      field2.setBorder(disabledBorder);
      label2.setText(disabledMessage);
    }
  }

  @Override public void insertUpdate(DocumentEvent e) {
    update();
  }

  @Override public void removeUpdate(DocumentEvent e) {
    update();
  }

  @Override public void changedUpdate(DocumentEvent e) {
    update();
  }
});
JButton button2 = new JButton("show");
button2.addActionListener(e -> {
  Component p2 = log.getRootPane();
  EventQueue.invokeLater(() -> {
    JButton b = field2.getRootPane().getDefaultButton();
    if (b != null && field2.getText().isEmpty()) {
      b.setEnabled(false);
    }
  });
  int ret = JOptionPane.showConfirmDialog(
      p2, panel2, "Input text", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);
  if (ret == JOptionPane.OK_OPTION) {
    log.setText(field2.getText());
  }
});

References

2020/03/31

Rotate the tab title of JTabbedPane vertically by 90 degrees

Code

private Icon makeVerticalTabIcon(String title, Icon icon, boolean clockwise) {
  JLabel label = new JLabel(title, icon, SwingConstants.LEADING);
  label.setBorder(BorderFactory.createEmptyBorder(0, 2, 0, 2));
  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 = clockwise
      ? AffineTransform.getTranslateInstance(w, 0)
      : AffineTransform.getTranslateInstance(0, h);
  at.quadrantRotate(clockwise ? 1 : -1);
  g2.setTransform(at);
  SwingUtilities.paintComponent(g2, label, this, 0, 0, d.width, d.height);
  g2.dispose();
  return new ImageIcon(bi);
}

References

2020/02/29

Display the Unicode code point of the character at the Caret position in the JTextArea

Code

String u1F60x = "😀😁😂😃😄😅😆😇😈😉😊😋😌😍😎😏";
String u1F61x = "😐😑😒😓😔😕😖😗😘😙😚😛😜😝😞😟";
String u1F62x = "😠😡😢😣😤😥😦😧😨😩😪😫😬😭😮😯";
String u1F63x = "😰😱😲😳😴😵😶😷😸😹😺😻😼😽😾😿";
String u1F64x = "🙀🙁🙂  🙅🙆🙇🙈🙉🙊🙋🙌🙍🙎🙏";

JTextField label = new JTextField();
label.setEditable(false);
label.setFont(label.getFont().deriveFont(32f));

List<String> l = Arrays.asList(u1F60x, u1F61x, u1F62x, u1F63x, u1F64x);
JTextArea textArea = new JTextArea(String.join("\n", l));
textArea.addCaretListener(e -> {
  try {
    int dot = e.getDot();
    int mark = e.getMark();
    if (dot - mark == 0) {
      Document doc = textArea.getDocument();
      String txt = doc.getText(dot, 1);
      int code = txt.codePointAt(0);
      if (Character.isHighSurrogate((char) code)) {
        txt = doc.getText(dot, 2);
        code = txt.codePointAt(0);
      }
      label.setText(String.format("%s: U+%04X", txt, code));
    } else {
      label.setText("");
    }
  } catch (BadLocationException ex) {
    ex.printStackTrace();
  }
});

References

2020/01/30

Automatically update JTree node selection based on scroll position to indicate which link is currently active in the viewport of JEditorPane

Code

JScrollPane scroll = new JScrollPane(editor);
scroll.getVerticalScrollBar().getModel().addChangeListener(e -> {
  HTMLDocument.Iterator itr = doc.getIterator(HTML.Tag.A);
  for (; itr.isValid(); itr.next()) {
    try {
      Rectangle r = editor.modelToView(itr.getStartOffset());
      if (r != null && editor.getVisibleRect().contains(r.getLocation())) {
        searchTreeNode(tree, itr.getAttributes().getAttribute(HTML.Attribute.NAME));
        break;
      }
    } catch (BadLocationException ex) {
      UIManager.getLookAndFeel().provideErrorFeedback(editor);
    }
  }
});
// ...
tree.addTreeSelectionListener(e -> {
  if (!tree.isEnabled()) { // Ignore node selection from JEditorPane side
    return;
  }
  Object o = e.getNewLeadSelectionPath().getLastPathComponent();
  if (o instanceof DefaultMutableTreeNode) {
    DefaultMutableTreeNode node = (DefaultMutableTreeNode) o;
    String ref = Objects.toString(node.getUserObject());
    editor.scrollToReference(ref);
  }
});
// ...
private static void searchTreeNode(JTree tree, Object name) {
  TreeModel model = tree.getModel();
  DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
  Collections.list((Enumeration<?>) root.preorderEnumeration()).stream()
      .filter(DefaultMutableTreeNode.class::isInstance)
      .map(DefaultMutableTreeNode.class::cast)
      .filter(node -> Objects.equals(name, Objects.toString(node.getUserObject())))
      .findFirst()
      .ifPresent(node -> {
        tree.setEnabled(false); // Disable TreeSelectionListener in JTree
        TreePath path = new TreePath(node.getPath());
        tree.setSelectionPath(path);
        tree.scrollPathToVisible(path);
        tree.setEnabled(true); // Restores TreeSelectionListener in JTree
      });
}

References

2019/12/30

Create a speedometer by changing the shape of JProgressBar to a donut-shaped semicircle

Code

class SolidGaugeUI extends BasicProgressBarUI {
  private final int[] pallet;
  private final double extent;

  protected SolidGaugeUI(int range, double extent) {
    super();
    this.pallet = makeGradientPallet(range);
    this.extent = extent;
  }

  @Override public void paint(Graphics g, JComponent c) {
    Rectangle rect = SwingUtilities.calculateInnerArea(progressBar, null);
    if (rect.isEmpty()) {
      return;
    }
    Graphics2D g2 = (Graphics2D) g.create();
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                        RenderingHints.VALUE_ANTIALIAS_ON);

    // double extent = -150d;
    double start = 90d + extent * .5;
    double degree = extent * progressBar.getPercentComplete();
    double or = Math.min(rect.width, rect.height);
    double cx = rect.getCenterX();
    double cy = rect.getMaxY();
    double sz = or * 2d;
    double ir = or * .6;
    Shape inner = new Arc2D.Double(
        cx - ir, cy - ir, ir * 2d, ir * 2d, start, -extent, Arc2D.PIE);
    Shape outer = new Arc2D.Double(
        cx - or, cy - or, sz, sz, start, -extent, Arc2D.PIE);
    Shape sector = new Arc2D.Double(
        cx - or, cy - or, sz, sz, start, -degree, Arc2D.PIE);

    Area foreground = new Area(sector);
    Area background = new Area(outer);
    Area hole = new Area(inner);

    foreground.subtract(hole);
    background.subtract(hole);

    // Draw the track
    g2.setPaint(new Color(0xDD_DD_DD));
    g2.fill(background);

    // Draw the circular sector
    g2.setPaint(getColorFromPallet(
        pallet, progressBar.getPercentComplete()));
    g2.fill(foreground);

    // Draw ...
    Font font = progressBar.getFont();
    float fsz = font.getSize2D();
    float min = (float) (cx - or - fsz);
    float max = (float) (cx + or + 4d);
    g2.setPaint(progressBar.getForeground());
    g2.drawString(
        Objects.toString(progressBar.getMinimum()), min, (float) cy);
    g2.drawString(Objects.toString(
        progressBar.getMaximum()), max, (float) cy);

    // Deal with possible text painting
    if (progressBar.isStringPainted()) {
      float h = (float) cy - fsz;
      String str = String.format("%d", progressBar.getValue());
      float vx = (float) cx - g2.getFontMetrics().stringWidth(str) * .5f;
      g2.drawString(str, vx, h);
      float ksz = fsz * .66f;
      g2.setFont(font.deriveFont(ksz));
      String kmh = "㎞/h";
      float tx = (float) cx - g2.getFontMetrics().stringWidth(kmh) * .5f;
      g2.drawString(kmh, tx, h + ksz);
    }
    g2.dispose();
  }

  private static int[] makeGradientPallet(int range) {
    BufferedImage image = new BufferedImage(
        range, 1, BufferedImage.TYPE_INT_RGB);
    Graphics2D g2 = image.createGraphics();
    Point2D start = new Point2D.Float();
    Point2D end = new Point2D.Float(range - 1f, 0f);
    float[] dist = {0f, .8f, .9f, 1f};
    Color[] colors = {Color.GREEN, Color.YELLOW, Color.ORANGE, Color.RED};
    g2.setPaint(new LinearGradientPaint(start, end, dist, colors));
    g2.fillRect(0, 0, range, 1);
    g2.dispose();

    int width = image.getWidth(null);
    int[] pallet = new int[width];
    PixelGrabber pg = new PixelGrabber(image, 0, 0, width, 1, pallet, 0, width);
    try {
      pg.grabPixels();
    } catch (InterruptedException ex) {
      ex.printStackTrace();
      Toolkit.getDefaultToolkit().beep();
      Thread.currentThread().interrupt();
    }
    return pallet;
  }

  private static Color getColorFromPallet(int[] pallet, double pos) {
    if (pos < 0d || pos > 1d) {
      throw new IllegalArgumentException("Parameter outside of expected range");
    }
    int i = (int) (pallet.length * pos);
    int max = pallet.length - 1;
    int index = Math.min(Math.max(i, 0), max);
    return new Color(pallet[index] & 0x00_FF_FF_FF);
  }
}

References