Added image attachment display. Added round buttons.
0
ComposeWindow.java
Normal file → Executable file
312
ImageWindow.java
Normal file
@ -0,0 +1,312 @@
|
||||
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.ImageIcon;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Image;
|
||||
import java.awt.FontMetrics;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.awt.event.MouseMotionListener;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseWheelListener;
|
||||
import java.awt.event.MouseWheelEvent;
|
||||
import java.awt.Image;
|
||||
import java.net.URL;
|
||||
import java.net.MalformedURLException;
|
||||
|
||||
class
|
||||
ImageWindow extends JFrame {
|
||||
|
||||
private Attachment[]
|
||||
attachments;
|
||||
|
||||
private int
|
||||
offset;
|
||||
|
||||
// - -%- -
|
||||
|
||||
private ImageComponent
|
||||
display;
|
||||
|
||||
// ---%-@-%---
|
||||
|
||||
public void
|
||||
showAttachments(Attachment[] attachments)
|
||||
{
|
||||
this.attachments = attachments;
|
||||
|
||||
if (attachments.length == 0) {
|
||||
display.setImage(null);
|
||||
display.setNext(null);
|
||||
display.setPrev(null);
|
||||
display.repaint();
|
||||
return;
|
||||
}
|
||||
|
||||
toImage(offset = 0);
|
||||
}
|
||||
|
||||
public void
|
||||
toNextImage()
|
||||
{
|
||||
if (attachments.length == 0) return;
|
||||
assert offset < attachments.length - 1;
|
||||
toImage(++offset);
|
||||
}
|
||||
|
||||
public void
|
||||
toPrevImage()
|
||||
{
|
||||
if (attachments.length == 0) return;
|
||||
assert offset > 0;
|
||||
toImage(--offset);
|
||||
}
|
||||
|
||||
// - -%- -
|
||||
|
||||
private void
|
||||
toImage(int offset)
|
||||
{
|
||||
int last = attachments.length - 1;
|
||||
assert offset < attachments.length;
|
||||
|
||||
Attachment prev, curr, next;
|
||||
curr = attachments[offset];
|
||||
prev = offset < last ? attachments[offset + 1] : null;
|
||||
next = offset > 0 ? attachments[offset - 1] : null;
|
||||
|
||||
display.setImage(curr.image);
|
||||
display.setNext(next != null ? next.image : null);
|
||||
display.setPrev(prev != null ? prev.image : null);
|
||||
|
||||
if (!curr.type.equals("image"))
|
||||
display.setToolTipText(
|
||||
display.getToolTipText()
|
||||
+ "\n(Media is of type '" + curr.type + "')"
|
||||
);
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
// ---%-@-%---
|
||||
|
||||
ImageWindow()
|
||||
{
|
||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||
setSize(400, 400);
|
||||
|
||||
display = new ImageComponent(this);
|
||||
showAttachments(new Attachment[0]);
|
||||
setContentPane(display);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class
|
||||
ImageComponent extends JPanel
|
||||
implements
|
||||
ActionListener,
|
||||
MouseListener, MouseMotionListener, MouseWheelListener {
|
||||
|
||||
private ImageWindow
|
||||
primaire;
|
||||
|
||||
// - -%- -
|
||||
|
||||
private Image
|
||||
image, prevImage, nextImage;
|
||||
|
||||
private JPanel
|
||||
buttonArea;
|
||||
|
||||
private JButton
|
||||
prev, next, toggle;
|
||||
|
||||
private boolean
|
||||
scaleImage;
|
||||
|
||||
private int
|
||||
xOffset, yOffset, zoomLevel;
|
||||
|
||||
private int
|
||||
dragX, dragY, xPOffset, yPOffset;
|
||||
|
||||
// ---%-@-%---
|
||||
|
||||
public void
|
||||
setImage(Image image)
|
||||
{
|
||||
this.image = image;
|
||||
if (image != null) {
|
||||
Object p = image.getProperty("comment", this);
|
||||
String desc = p instanceof String ? (String)p : null;
|
||||
setToolTipText(desc);
|
||||
}
|
||||
xOffset = yOffset = xPOffset = yPOffset = 0;
|
||||
zoomLevel = 100;
|
||||
}
|
||||
|
||||
public void
|
||||
setPrev(Image image)
|
||||
{
|
||||
prev.setEnabled(image != null);
|
||||
prev.setIcon(toIcon(image));
|
||||
}
|
||||
|
||||
public void
|
||||
setNext(Image image)
|
||||
{
|
||||
next.setEnabled(image != null);
|
||||
next.setIcon(toIcon(image));
|
||||
}
|
||||
|
||||
// - -%- -
|
||||
|
||||
public void
|
||||
actionPerformed(ActionEvent eA)
|
||||
{
|
||||
if (eA.getSource() == prev) primaire.toPrevImage();
|
||||
if (eA.getSource() == next) primaire.toNextImage();
|
||||
if (eA.getSource() == toggle) {
|
||||
scaleImage = !scaleImage;
|
||||
if (scaleImage) toggle.setText("Show unscaled");
|
||||
else toggle.setText("Show scaled to window");
|
||||
setImage(this.image);
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void
|
||||
mousePressed(MouseEvent eM)
|
||||
{
|
||||
dragX = eM.getX();
|
||||
dragY = eM.getY();
|
||||
}
|
||||
|
||||
public void
|
||||
mouseDragged(MouseEvent eM)
|
||||
{
|
||||
int dx = eM.getX() - dragX;
|
||||
int dy = eM.getY() - dragY;
|
||||
xPOffset = dx;
|
||||
yPOffset = dy;
|
||||
repaint();
|
||||
}
|
||||
|
||||
public void
|
||||
mouseReleased(MouseEvent eM)
|
||||
{
|
||||
xOffset += xPOffset;
|
||||
yOffset += yPOffset;
|
||||
xPOffset = yPOffset = 0;
|
||||
}
|
||||
|
||||
public void
|
||||
mouseWheelMoved(MouseWheelEvent eMw)
|
||||
{
|
||||
zoomLevel += 10 * -eMw.getUnitsToScroll();
|
||||
if (zoomLevel < 50) zoomLevel = 50;
|
||||
if (zoomLevel > 400) zoomLevel = 400;
|
||||
repaint();
|
||||
}
|
||||
|
||||
|
||||
public void
|
||||
mouseEntered(MouseEvent eM) { }
|
||||
|
||||
public void
|
||||
mouseExited(MouseEvent eM) { }
|
||||
|
||||
public void
|
||||
mouseClicked(MouseEvent eM) { }
|
||||
|
||||
public void
|
||||
mouseMoved(MouseEvent eM) { }
|
||||
|
||||
// - -%- -
|
||||
|
||||
private static ImageIcon
|
||||
toIcon(Image image)
|
||||
{
|
||||
if (image == null) return null;
|
||||
return new ImageIcon(image);
|
||||
}
|
||||
|
||||
// ---%-@-%---
|
||||
|
||||
private class
|
||||
Painter extends JPanel {
|
||||
|
||||
protected void
|
||||
paintComponent(Graphics g)
|
||||
{
|
||||
if (image == null)
|
||||
{
|
||||
String str = "(There are no images being displayed.)";
|
||||
FontMetrics fm = g.getFontMetrics();
|
||||
int x = (getWidth() - fm.stringWidth(str)) / 2;
|
||||
int y = (getHeight() + fm.getHeight()) / 2;
|
||||
g.drawString(str, x, y);
|
||||
return;
|
||||
}
|
||||
int wo = image.getWidth(this);
|
||||
int ho = image.getHeight(this);
|
||||
int wn, hn;
|
||||
if (wo > ho) {
|
||||
wn = scaleImage ? getWidth() : wo;
|
||||
hn = ho * wn / wo;
|
||||
}
|
||||
else {
|
||||
hn = scaleImage ? getHeight() : ho;
|
||||
wn = wo * hn / ho;
|
||||
}
|
||||
wn = wn * zoomLevel / 100;
|
||||
hn = hn * zoomLevel / 100;
|
||||
int x = (getWidth() - wn) / 2;
|
||||
int y = (getHeight() - hn) / 2;
|
||||
x += xOffset + xPOffset;
|
||||
y += yOffset + yPOffset;
|
||||
g.drawImage(image, x, y, wn, hn, this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ---%-@-%---
|
||||
|
||||
ImageComponent(ImageWindow primaire)
|
||||
{
|
||||
this.primaire = primaire;
|
||||
|
||||
setOpaque(false);
|
||||
scaleImage = true;
|
||||
zoomLevel = 100;
|
||||
|
||||
prev = new JButton("<");
|
||||
toggle = new JButton("Show unscaled");
|
||||
next = new JButton(">");
|
||||
prev.addActionListener(this);
|
||||
toggle.addActionListener(this);
|
||||
next.addActionListener(this);
|
||||
|
||||
buttonArea = new JPanel();
|
||||
buttonArea.setOpaque(false);
|
||||
buttonArea.add(prev);
|
||||
buttonArea.add(toggle);
|
||||
buttonArea.add(next);
|
||||
|
||||
setLayout(new BorderLayout());
|
||||
add(buttonArea, BorderLayout.SOUTH);
|
||||
add(new Painter(), BorderLayout.CENTER);
|
||||
|
||||
addMouseListener(this);
|
||||
addMouseMotionListener(this);
|
||||
addMouseWheelListener(this);
|
||||
}
|
||||
|
||||
}
|
32
JKomasto.java
Normal file → Executable file
@ -5,6 +5,7 @@ import javax.swing.JComponent;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Cursor;
|
||||
import java.awt.Image;
|
||||
import java.util.List;
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
@ -24,6 +25,9 @@ JKomasto {
|
||||
private LoginWindow
|
||||
loginWindow;
|
||||
|
||||
private ImageWindow
|
||||
mediaWindow;
|
||||
|
||||
private MastodonApi
|
||||
api;
|
||||
|
||||
@ -38,7 +42,7 @@ JKomasto {
|
||||
autoViewWindow.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||
timelineWindow.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||
|
||||
timelineWindow.showLatestPage();
|
||||
timelineWindow.showLatestPage();
|
||||
timelineWindow.setLocationByPlatform(true);
|
||||
timelineWindow.setVisible(true);
|
||||
|
||||
@ -56,6 +60,9 @@ JKomasto {
|
||||
public ComposeWindow
|
||||
getComposeWindow() { return composeWindow; }
|
||||
|
||||
public ImageWindow
|
||||
getMediaWindow() { return mediaWindow; }
|
||||
|
||||
// ---%-@-%---
|
||||
|
||||
public static void
|
||||
@ -72,10 +79,12 @@ JKomasto {
|
||||
composeWindow = new ComposeWindow(this);
|
||||
autoViewWindow = new PostWindow(this);
|
||||
loginWindow = new LoginWindow(this);
|
||||
mediaWindow = new ImageWindow();
|
||||
|
||||
composeWindow.dispose();
|
||||
autoViewWindow.dispose();
|
||||
timelineWindow.dispose();
|
||||
mediaWindow.dispose();
|
||||
loginWindow.setLocationByPlatform(true);
|
||||
loginWindow.setVisible(true);
|
||||
}
|
||||
@ -131,6 +140,9 @@ Post {
|
||||
public String
|
||||
authorId, authorName;
|
||||
|
||||
public Image
|
||||
authorAvatar;
|
||||
|
||||
public ZonedDateTime
|
||||
date;
|
||||
|
||||
@ -143,6 +155,24 @@ Post {
|
||||
public boolean
|
||||
boosted, favourited;
|
||||
|
||||
public Attachment[]
|
||||
attachments;
|
||||
|
||||
}
|
||||
|
||||
|
||||
class
|
||||
Attachment {
|
||||
|
||||
public String
|
||||
type;
|
||||
|
||||
public String
|
||||
url;
|
||||
|
||||
public Image
|
||||
image;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
0
LoginWindow.java
Normal file → Executable file
0
MastodonApi.java
Executable file → Normal file
82
PostWindow.java
Normal file → Executable file
@ -11,6 +11,7 @@ import javax.swing.BoxLayout;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.ImageIcon;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Font;
|
||||
import java.awt.FontMetrics;
|
||||
@ -19,6 +20,7 @@ import java.awt.Dimension;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.Cursor;
|
||||
import java.awt.Image;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.util.List;
|
||||
@ -90,12 +92,19 @@ implements ActionListener {
|
||||
|
||||
postDisplay.setAuthorName(post.authorName);
|
||||
postDisplay.setAuthorId(post.authorId);
|
||||
postDisplay.setAuthorAvatar(post.authorAvatar);
|
||||
postDisplay.setDate(DATE_FORMAT.format(post.date));
|
||||
postDisplay.setTime(TIME_FORMAT.format(post.date));
|
||||
postDisplay.setText(post.text);
|
||||
postDisplay.setFavourited(post.favourited);
|
||||
postDisplay.setBoosted(post.boosted);
|
||||
postDisplay.setMediaPreview(
|
||||
post.attachments.length == 0
|
||||
? null
|
||||
: post.attachments[0].image
|
||||
);
|
||||
repliesDisplay.setReplies(replies);
|
||||
postDisplay.resetFocus();
|
||||
repaint();
|
||||
}
|
||||
|
||||
@ -203,7 +212,14 @@ implements ActionListener {
|
||||
public void
|
||||
openMedia()
|
||||
{
|
||||
|
||||
ImageWindow w = primaire.getMediaWindow();
|
||||
w.showAttachments(post.attachments);
|
||||
int l = Math.min(40, post.text.length());
|
||||
w.setTitle(post.text.substring(0, l));
|
||||
if (!w.isVisible()) {
|
||||
w.setLocation(getX(), getY() + 100);
|
||||
w.setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
// - -%- -
|
||||
@ -258,6 +274,7 @@ implements ActionListener {
|
||||
samplePost.postId = "000000000";
|
||||
samplePost.boosted = false;
|
||||
samplePost.favourited = true;
|
||||
samplePost.attachments = new Attachment[0];
|
||||
showPost(samplePost);
|
||||
|
||||
setContentPane(postDisplay);
|
||||
@ -284,46 +301,29 @@ implements ActionListener {
|
||||
replyMisc,
|
||||
nextPrev;
|
||||
|
||||
private JButton
|
||||
private RoundButton
|
||||
profile,
|
||||
media;
|
||||
|
||||
// ---%-@-%---
|
||||
|
||||
public void
|
||||
setAuthorName(String authorName)
|
||||
{
|
||||
assert authorName != null;
|
||||
this.authorName = authorName;
|
||||
}
|
||||
setAuthorName(String n) { authorName = n; }
|
||||
|
||||
public void
|
||||
setAuthorId(String authorId)
|
||||
{
|
||||
assert authorId != null;
|
||||
this.authorId = authorId;
|
||||
}
|
||||
setAuthorId(String n) { authorId = n; }
|
||||
|
||||
public void
|
||||
setAuthorAvatar(Image n) { profile.setImage(n); }
|
||||
|
||||
public void
|
||||
setDate(String date)
|
||||
{
|
||||
assert date != null;
|
||||
this.date = date;
|
||||
}
|
||||
setDate(String n) { date = n; }
|
||||
|
||||
public void
|
||||
setTime(String time)
|
||||
{
|
||||
assert time != null;
|
||||
this.time = time;
|
||||
}
|
||||
setTime(String n) { time = n; }
|
||||
|
||||
public void
|
||||
setText(String text)
|
||||
{
|
||||
assert text != null;
|
||||
this.text = text;
|
||||
}
|
||||
setText(String n) { text = n; }
|
||||
|
||||
public void
|
||||
setFavourited(boolean a)
|
||||
@ -347,6 +347,12 @@ implements ActionListener {
|
||||
favouriteBoost.setEnabled(a);
|
||||
}
|
||||
|
||||
public void
|
||||
setMediaPreview(Image n) { media.setImage(n); }
|
||||
|
||||
public void
|
||||
resetFocus() { media.requestFocusInWindow(); }
|
||||
|
||||
// - -%- -
|
||||
|
||||
public void
|
||||
@ -405,6 +411,11 @@ implements ActionListener {
|
||||
{
|
||||
g.clearRect(0, 0, getWidth(), getHeight());
|
||||
|
||||
((java.awt.Graphics2D)g).setRenderingHint(
|
||||
java.awt.RenderingHints.KEY_TEXT_ANTIALIASING,
|
||||
java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON
|
||||
);
|
||||
|
||||
Font f1 = new Font("IPAGothic", Font.PLAIN, 16);
|
||||
Font f2 = new Font("IPAGothic", Font.PLAIN, 14);
|
||||
FontMetrics fm1 = g.getFontMetrics(f1);
|
||||
@ -443,6 +454,13 @@ implements ActionListener {
|
||||
|
||||
// - -%- -
|
||||
|
||||
private static ImageIcon
|
||||
toIcon(Image image)
|
||||
{
|
||||
if (image == null) return null;
|
||||
return new ImageIcon(image);
|
||||
}
|
||||
|
||||
private static List<String>
|
||||
split(String string, int lineLength)
|
||||
{
|
||||
@ -487,9 +505,8 @@ implements ActionListener {
|
||||
|
||||
Dimension buttonSize = new Dimension(20, 40);
|
||||
|
||||
profile = new JButton("P");
|
||||
profile.setPreferredSize(buttonSize);
|
||||
profile.setMargin(null);
|
||||
profile = new RoundButton();
|
||||
//profile.setPreferredSize(buttonSize);
|
||||
profile.addActionListener(this);
|
||||
|
||||
favouriteBoost = new TwoToggleButton("favourite", "boost");
|
||||
@ -501,9 +518,8 @@ implements ActionListener {
|
||||
nextPrev = new TwoToggleButton("next", "prev");
|
||||
nextPrev.addActionListener(this);
|
||||
|
||||
media = new JButton("media");
|
||||
media.setPreferredSize(buttonSize);
|
||||
media.setMargin(null);
|
||||
media = new RoundButton();
|
||||
//media.setPreferredSize(buttonSize);
|
||||
media.addActionListener(this);
|
||||
|
||||
Box ibuttons = Box.createVerticalBox();
|
||||
|
0
RequestListener.java
Executable file → Normal file
0
RudimentaryHTMLParser.java
Executable file → Normal file
49
TimelineWindow.java
Normal file → Executable file
@ -9,6 +9,7 @@ import javax.swing.JMenuItem;
|
||||
import javax.swing.JMenuBar;
|
||||
import javax.swing.JSeparator;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.BorderFactory;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.GridBagLayout;
|
||||
@ -22,6 +23,8 @@ import java.awt.Color;
|
||||
import java.awt.Graphics;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.net.URL;
|
||||
import java.net.MalformedURLException;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
@ -257,12 +260,13 @@ implements ActionListener {
|
||||
private static List<Post>
|
||||
toPosts(Tree<String> json)
|
||||
{
|
||||
List<Post> posts = new ArrayList<>();
|
||||
List<Post> posts = new ArrayList<>();
|
||||
for (Tree<String> post: json.children)
|
||||
{
|
||||
Tree<String> account = post.get("account");
|
||||
Post addee = new Post();
|
||||
|
||||
addee.postId = post.get("id").value;
|
||||
|
||||
try {
|
||||
String s = post.get("created_at").value;
|
||||
addee.date = ZonedDateTime.parse(s);
|
||||
@ -273,6 +277,7 @@ implements ActionListener {
|
||||
assert false;
|
||||
addee.date = ZonedDateTime.now();
|
||||
}
|
||||
|
||||
try {
|
||||
StringBuilder b = new StringBuilder();
|
||||
Tree<String> nodes =
|
||||
@ -299,17 +304,55 @@ implements ActionListener {
|
||||
eIo.printStackTrace();
|
||||
assert false;
|
||||
}
|
||||
|
||||
String s = post.get("spoiler_text").value;
|
||||
if (!s.isEmpty()) addee.contentWarning = s;
|
||||
else addee.contentWarning = null;
|
||||
|
||||
Tree<String> account = post.get("account");
|
||||
addee.authorId = account.get("acct").value;
|
||||
addee.authorName = account.get("username").value;
|
||||
String s2 = account.get("display_name").value;
|
||||
if (!s2.isEmpty()) addee.authorName = s2;
|
||||
try {
|
||||
String av = account.get("avatar").value;
|
||||
ImageIcon icon = new ImageIcon(new URL(av));
|
||||
addee.authorAvatar = icon.getImage();
|
||||
}
|
||||
catch (MalformedURLException eMu) {
|
||||
// Weird bug on their part.. We should
|
||||
// probably react by using a default avatar.
|
||||
}
|
||||
|
||||
String f = post.get("favourited").value;
|
||||
String b = post.get("reblogged").value;
|
||||
addee.favourited = f.equals("true");
|
||||
addee.boosted = b.equals("true");
|
||||
|
||||
Tree<String> a1 = post.get("media_attachments");
|
||||
Attachment[] a2 = new Attachment[a1.size()];
|
||||
for (int o = 0; o < a2.length; ++o)
|
||||
{
|
||||
a2[o] = new Attachment();
|
||||
a2[o].type = a1.get(o).get("type").value;
|
||||
|
||||
a2[o].url = a1.get(o).get("remote_url").value;
|
||||
if (a2[o].url == null)
|
||||
a2[o].url = a1.get(o).get("text_url").value;
|
||||
|
||||
a2[o].image = null;
|
||||
if (a2[o].type.equals("image")) try
|
||||
{
|
||||
URL url = new URL(a2[o].url);
|
||||
ImageIcon icon = new ImageIcon(url);
|
||||
String desc = a1.get(o).get("description").value;
|
||||
icon.setDescription(desc);
|
||||
a2[o].image = icon.getImage();
|
||||
}
|
||||
catch (MalformedURLException eMu) { }
|
||||
}
|
||||
addee.attachments = a2;
|
||||
|
||||
posts.add(addee);
|
||||
}
|
||||
return posts;
|
||||
@ -375,7 +418,7 @@ implements ActionListener {
|
||||
|
||||
page = new TimelinePage();
|
||||
page.posts = new ArrayList<>();
|
||||
setTimelineType(TimelineType.FEDERATED);
|
||||
setTimelineType(TimelineType.HOME);
|
||||
|
||||
display = new TimelineComponent(this);
|
||||
display.setNextPageAvailable(false);
|
||||
|
145
TwoToggleButton.java
Normal file → Executable file
@ -6,6 +6,8 @@ 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;
|
||||
@ -92,7 +94,7 @@ implements KeyListener, MouseListener, FocusListener {
|
||||
|
||||
// - -%- -
|
||||
|
||||
private void
|
||||
private void
|
||||
announce(String name, boolean toggled)
|
||||
{
|
||||
ActionEvent eA = new ActionEvent(
|
||||
@ -232,6 +234,147 @@ implements KeyListener, MouseListener, FocusListener {
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
((java.awt.Graphics2D)g).setRenderingHint(
|
||||
java.awt.RenderingHints.KEY_TEXT_ANTIALIASING,
|
||||
java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON
|
||||
);
|
||||
|
||||
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);
|
||||
Shape defaultClip = g.getClip();
|
||||
Shape roundClip = new Ellipse2D.Float(6, 6, w1 - 12, h1 - 12);
|
||||
int w2 = image.getWidth(this);
|
||||
int h2 = image.getHeight(this);
|
||||
if (h2 > w2) {
|
||||
h2 = h2 * w1 / w2;
|
||||
w2 = w1;
|
||||
}
|
||||
else {
|
||||
w2 = w2 * h1 / h2;
|
||||
h2 = h1;
|
||||
}
|
||||
g.setClip(roundClip);
|
||||
g.drawImage(image, 0, 0, w2, h2, this);
|
||||
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(null);
|
||||
int h = button.getHeight(null);
|
||||
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 {
|
||||
|
||||
|
0
graphics/Flags.xcf
Normal file → Executable file
0
graphics/Hourglass.xcf
Normal file → Executable file
0
graphics/boostToggled.png
Executable file → Normal file
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
0
graphics/boostUntoggled.png
Executable file → Normal file
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
0
graphics/button.png
Normal file → Executable file
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
0
graphics/disabledOverlay.png
Normal file → Executable file
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
0
graphics/favouriteUntoggled.png
Executable file → Normal file
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
0
graphics/ref1.png
Normal file → Executable file
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
0
graphics/selectedOverlay.png
Normal file → Executable file
Before Width: | Height: | Size: 313 B After Width: | Height: | Size: 313 B |
0
graphics/test1.png
Normal file → Executable file
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
0
graphics/test2.png
Normal file → Executable file
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
0
graphics/test3.png
Normal file → Executable file
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
0
graphics/test4.png
Normal file → Executable file
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |