biskuteri-cafe-JKomasto2/TwoToggleButton.java
Snowyfox e6fea4c061 Fixed bug when redraft makes no changes
(Before this, JKomasto and sometimes the Mastodon web client would get '411 Record Not Found' when submitting the same text after deleting and redrafting. Presumably the Mastodon server caches both whether an idempotency key was fulfilled and which post it leads to, and for some reason it looks up the second and fails.)
2022-05-31 03:39:56 -04:00

423 lines
9.7 KiB
Java

import javax.swing.AbstractButton;
import javax.swing.DefaultButtonModel;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import java.awt.Image;
import java.awt.Graphics;
import java.awt.Dimension;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;
import java.awt.event.KeyListener;
import java.awt.event.KeyEvent;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.FocusListener;
import java.awt.event.FocusEvent;
import java.net.URL;
import javax.swing.JFrame;
class
TwoToggleButton extends AbstractButton
implements KeyListener, MouseListener, FocusListener {
private String
primaryName,
secondaryName;
// - -%- -
private boolean
primaryToggled = false,
secondaryToggled = false;
private Image
primaryToggledIcon,
secondaryToggledIcon,
primaryUntoggledIcon,
secondaryUntoggledIcon;
private int
nextEventID = ActionEvent.ACTION_FIRST;
// - -%- -
private static Image
button,
selectedOverlay,
disabledOverlay;
// ---%-@-%---
public void
togglePrimary()
{
setPrimaryToggled(!primaryToggled);
}
public void
toggleSecondary()
{
setSecondaryToggled(!secondaryToggled);
}
public void
setPrimaryToggled(boolean toggled)
{
primaryToggled = toggled;
repaint();
announce(primaryName, toggled);
}
public void
setSecondaryToggled(boolean toggled)
{
secondaryToggled = toggled;
repaint();
announce(secondaryName, toggled);
}
public boolean
getPrimaryToggled()
{
return primaryToggled;
}
public boolean
getSecondaryToggled()
{
return secondaryToggled;
}
// - -%- -
private void
announce(String name, boolean toggled)
{
ActionEvent eA = new ActionEvent(
this, nextEventID++,
name + (toggled ? "On" : "Off")
);
for (ActionListener listener: getActionListeners())
listener.actionPerformed(eA);
}
protected void
paintComponent(Graphics g)
{
g.drawImage(button, 0, 0, this);
if (!isEnabled())
g.drawImage(disabledOverlay, 0, 0, this);
if (isFocusOwner())
g.drawImage(selectedOverlay, 0, 0, this);
if (secondaryToggled)
g.drawImage(secondaryToggledIcon, 0, 0, this);
else
g.drawImage(secondaryUntoggledIcon, 0, 0, this);
if (primaryToggled)
g.drawImage(primaryToggledIcon, 0, 0, this);
else
g.drawImage(primaryUntoggledIcon, 0, 0, this);
}
public void
mousePressed(MouseEvent eM)
{
boolean shift = eM.isShiftDown();
boolean prim = eM.getButton() == MouseEvent.BUTTON1;
boolean secon = eM.getButton() == MouseEvent.BUTTON3;
secon |= shift && prim;
if (secon) toggleSecondary();
else if (prim) togglePrimary();
requestFocusInWindow();
}
public void
keyPressed(KeyEvent eK)
{
boolean shift = eK.isShiftDown();
boolean prim = eK.getKeyCode() == KeyEvent.VK_SPACE;
boolean secon = eK.getKeyCode() == KeyEvent.VK_ENTER;
secon |= shift && prim;
if (secon) toggleSecondary();
else if (prim) togglePrimary();
requestFocusInWindow();
}
public void
focusGained(FocusEvent eF) { repaint(); }
public void
focusLost(FocusEvent eF) { repaint(); }
public void
mouseClicked(MouseEvent eM) { }
public void
mouseReleased(MouseEvent eM) { }
public void
mouseEntered(MouseEvent eM) { }
public void
mouseExited(MouseEvent eM) { }
public void
keyReleased(KeyEvent eK) { }
public void
keyTyped(KeyEvent eK) { }
// (悪) If we don't have the 'armed' behaviour of JButton,
// we really need to rectify that, it's important for this
// use case.
// ---%-@-%---
TwoToggleButton(String primaryName, String secondaryName)
{
if (button == null) loadCommonImages();
this.primaryName = primaryName;
this.secondaryName = secondaryName;
setModel(new DefaultButtonModel());
setFocusable(true);
setOpaque(false);
int w = button.getWidth(null);
int h = button.getHeight(null);
setPreferredSize(new Dimension(w, h));
loadSpecificImages();
this.addKeyListener(this);
this.addMouseListener(this);
this.addFocusListener(this);
}
private void
loadSpecificImages()
{
String p1 = "graphics/" + primaryName + "Toggled.png";
String p2 = "graphics/" + secondaryName + "Toggled.png";
String p3 = "graphics/" + primaryName + "Untoggled.png";
String p4 = "graphics/" + secondaryName + "Untoggled.png";
URL u1 = getClass().getResource(p1);
URL u2 = getClass().getResource(p2);
URL u3 = getClass().getResource(p3);
URL u4 = getClass().getResource(p4);
if (u1 == null) primaryToggledIcon = null;
else primaryToggledIcon = new ImageIcon(u1).getImage();
if (u2 == null) secondaryToggledIcon = null;
else secondaryToggledIcon = new ImageIcon(u2).getImage();
if (u3 == null) primaryUntoggledIcon = null;
else primaryUntoggledIcon = new ImageIcon(u3).getImage();
if (u4 == null) secondaryUntoggledIcon = null;
else secondaryUntoggledIcon = new ImageIcon(u4).getImage();
}
// - -%- -
private static void
loadCommonImages()
{
Class c = TwoToggleButton.class;
URL u1 = c.getResource("graphics/button.png");
URL u2 = c.getResource("graphics/disabledOverlay.png");
URL u3 = c.getResource("graphics/selectedOverlay.png");
assert u1 != null && u2 != null && u3 != null;
button = new ImageIcon(u1).getImage();
disabledOverlay = new ImageIcon(u2).getImage();
selectedOverlay = new ImageIcon(u3).getImage();
}
}
class
RoundButton extends AbstractButton
implements KeyListener, MouseListener, FocusListener {
private Image
image;
// - -%- -
private int
nextEventID = ActionEvent.ACTION_FIRST;
// - -%- -
private static Image
button,
selectedOverlay,
disabledOverlay;
// ---%-@-%---
public void
setImage(Image n) { image = n; }
// - -%- -
protected void
paintComponent(Graphics g)
{
g.drawImage(button, 0, 0, this);
if (!isEnabled())
g.drawImage(disabledOverlay, 0, 0, this);
if (isFocusOwner())
g.drawImage(selectedOverlay, 0, 0, this);
if (image == null) return;
int w1 = button.getWidth(this);
int h1 = button.getHeight(this);
int w2 = image.getWidth(this);
int h2 = image.getHeight(this);
if (w2 == -1) w2 = w1;
if (h2 == -1) h2 = h1;
if (h2 > w2) {
h2 = h2 * w1 / w2;
w2 = w1;
}
else {
w2 = w2 * h1 / h2;
h2 = h1;
}
Shape defaultClip, roundClip;
defaultClip = g.getClip();
roundClip = new Ellipse2D.Float(6, 6, w1 - 12, h1 - 12);
g.setClip(roundClip);
g.drawImage(image, 0, 0, w2, h2, getParent());
// I don't know why, but when we repaint ourselves, our
// parent doesn't repaint, so nothing seems to happen.
g.setClip(defaultClip);
}
private void
announce()
{
ActionEvent eA = new ActionEvent(this, nextEventID++, null);
for (ActionListener listener: getActionListeners())
listener.actionPerformed(eA);
}
public void
keyPressed(KeyEvent eK)
{
if (eK.getKeyCode() != KeyEvent.VK_SPACE) return;
doClick();
}
public void
mouseClicked(MouseEvent eM) { announce(); }
public void
focusGained(FocusEvent eF) { repaint(); }
public void
focusLost(FocusEvent eF) { repaint(); }
public void
mousePressed(MouseEvent eM) { }
public void
mouseReleased(MouseEvent eM) { }
public void
mouseEntered(MouseEvent eM) { }
public void
mouseExited(MouseEvent eM) { }
public void
keyReleased(KeyEvent eK) { }
public void
keyTyped(KeyEvent eK) { }
// (悪) Again, armed?
// ---%-@-%---
RoundButton()
{
if (button == null) loadCommonImages();
setModel(new DefaultButtonModel());
setFocusable(true);
setOpaque(false);
int w = button.getWidth(this);
int h = button.getHeight(this);
setPreferredSize(new Dimension(w, h));
this.addKeyListener(this);
this.addMouseListener(this);
this.addFocusListener(this);
}
// - -%- -
private static void
loadCommonImages()
{
Class c = TwoToggleButton.class;
URL u1 = c.getResource("graphics/button.png");
URL u2 = c.getResource("graphics/disabledOverlay.png");
URL u3 = c.getResource("graphics/selectedOverlay.png");
assert u1 != null && u2 != null && u3 != null;
button = new ImageIcon(u1).getImage();
disabledOverlay = new ImageIcon(u2).getImage();
selectedOverlay = new ImageIcon(u3).getImage();
}
}
class
TwoToggleButtonTest {
public static void
main(String... args)
{
if (args.length != 2)
{
String err = "Please give two toggle names.";
System.err.println(err);
System.exit(1);
}
String p = args[0], s = args[1];
TwoToggleButton t1 = new TwoToggleButton(p, s);
TwoToggleButton t2 = new TwoToggleButton(p, s);
TwoToggleButton t3 = new TwoToggleButton(p, s);
JFrame mainframe = new JFrame("Two-toggle button test");
mainframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainframe.setLocationByPlatform(true);
mainframe.add(t1, java.awt.BorderLayout.WEST);
mainframe.add(t2);
mainframe.add(t3, java.awt.BorderLayout.EAST);
mainframe.pack();
t2.setEnabled(false);
t3.requestFocusInWindow();
mainframe.setVisible(true);
}
}