mirror of
https://gitlab.com/biskuteri-cafe/JKomasto2.git
synced 2024-11-20 06:44:51 +01:00
e6fea4c061
(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.)
423 lines
9.7 KiB
Java
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);
|
|
}
|
|
|
|
}
|