Moved image methods to ImageApi.
Added testing implementation of updater.
65
ComposeWindow.java
Executable file → Normal file
@ -9,6 +9,7 @@ import javax.swing.JButton;
|
|||||||
import javax.swing.Box;
|
import javax.swing.Box;
|
||||||
import javax.swing.BorderFactory;
|
import javax.swing.BorderFactory;
|
||||||
import javax.swing.JOptionPane;
|
import javax.swing.JOptionPane;
|
||||||
|
import java.awt.GridLayout;
|
||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
@ -52,6 +53,7 @@ ComposeWindow extends JFrame {
|
|||||||
composition.text = "";
|
composition.text = "";
|
||||||
composition.visibility = PostVisibility.MENTIONED;
|
composition.visibility = PostVisibility.MENTIONED;
|
||||||
composition.replyToPostId = null;
|
composition.replyToPostId = null;
|
||||||
|
composition.contentWarning = null;
|
||||||
syncDisplayToComposition();
|
syncDisplayToComposition();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,10 +61,16 @@ ComposeWindow extends JFrame {
|
|||||||
submit()
|
submit()
|
||||||
{
|
{
|
||||||
syncCompositionToDisplay();
|
syncCompositionToDisplay();
|
||||||
|
|
||||||
|
if (composition.replyToPostId != null)
|
||||||
|
assert !composition.replyToPostId.trim().isEmpty();
|
||||||
|
if (composition.contentWarning != null)
|
||||||
|
assert !composition.contentWarning.trim().isEmpty();
|
||||||
|
|
||||||
display.setSubmitting(true);
|
display.setSubmitting(true);
|
||||||
api.submit(
|
api.submit(
|
||||||
composition.text, composition.visibility,
|
composition.text, composition.visibility,
|
||||||
composition.replyToPostId,
|
composition.replyToPostId, composition.contentWarning,
|
||||||
new RequestListener() {
|
new RequestListener() {
|
||||||
|
|
||||||
public void
|
public void
|
||||||
@ -105,6 +113,7 @@ ComposeWindow extends JFrame {
|
|||||||
display.setText(composition.text);
|
display.setText(composition.text);
|
||||||
display.setReplyToPostId(composition.replyToPostId);
|
display.setReplyToPostId(composition.replyToPostId);
|
||||||
display.setVisibility(stringFor(composition.visibility));
|
display.setVisibility(stringFor(composition.visibility));
|
||||||
|
display.setContentWarning(composition.contentWarning);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void
|
private void
|
||||||
@ -113,7 +122,19 @@ ComposeWindow extends JFrame {
|
|||||||
composition.text = display.getText();
|
composition.text = display.getText();
|
||||||
composition.visibility =
|
composition.visibility =
|
||||||
visibilityFrom(display.getVisibility());
|
visibilityFrom(display.getVisibility());
|
||||||
composition.replyToPostId = display.getReplyToPostId();
|
composition.replyToPostId =
|
||||||
|
nonEmpty(display.getReplyToPostId());
|
||||||
|
composition.contentWarning =
|
||||||
|
nonEmpty(display.getContentWarning());
|
||||||
|
}
|
||||||
|
|
||||||
|
// - -%- -
|
||||||
|
|
||||||
|
private static String
|
||||||
|
nonEmpty(String s)
|
||||||
|
{
|
||||||
|
if (s.trim().isEmpty()) return null;
|
||||||
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
@ -182,7 +203,7 @@ implements ActionListener {
|
|||||||
text;
|
text;
|
||||||
|
|
||||||
private JTextField
|
private JTextField
|
||||||
reply;
|
reply, contentWarning;
|
||||||
|
|
||||||
private JComboBox<String>
|
private JComboBox<String>
|
||||||
visibility;
|
visibility;
|
||||||
@ -217,6 +238,12 @@ implements ActionListener {
|
|||||||
this.visibility.setSelectedIndex(3);
|
this.visibility.setSelectedIndex(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void
|
||||||
|
setContentWarning(String contentWarning)
|
||||||
|
{
|
||||||
|
this.contentWarning.setText(contentWarning);
|
||||||
|
}
|
||||||
|
|
||||||
public String
|
public String
|
||||||
getText()
|
getText()
|
||||||
{
|
{
|
||||||
@ -229,6 +256,12 @@ implements ActionListener {
|
|||||||
return reply.getText();
|
return reply.getText();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String
|
||||||
|
getContentWarning()
|
||||||
|
{
|
||||||
|
return contentWarning.getText();
|
||||||
|
}
|
||||||
|
|
||||||
public String
|
public String
|
||||||
getVisibility()
|
getVisibility()
|
||||||
{
|
{
|
||||||
@ -265,11 +298,21 @@ implements ActionListener {
|
|||||||
{
|
{
|
||||||
this.primaire = primaire;
|
this.primaire = primaire;
|
||||||
|
|
||||||
text = new JTextArea();
|
|
||||||
text.setLineWrap(true);
|
|
||||||
text.setWrapStyleWord(true);
|
|
||||||
|
|
||||||
reply = new JTextField();
|
reply = new JTextField();
|
||||||
|
JLabel replyLabel = new JLabel("In reply to: ");
|
||||||
|
replyLabel.setLabelFor(reply);
|
||||||
|
|
||||||
|
contentWarning = new JTextField();
|
||||||
|
JLabel cwLabel = new JLabel("Content warning: ");
|
||||||
|
cwLabel.setLabelFor(contentWarning);
|
||||||
|
|
||||||
|
JPanel top = new JPanel();
|
||||||
|
top.setOpaque(false);
|
||||||
|
top.setLayout(new GridLayout(2, 2, 8, 0));
|
||||||
|
top.add(replyLabel);
|
||||||
|
top.add(reply);
|
||||||
|
top.add(cwLabel);
|
||||||
|
top.add(contentWarning);
|
||||||
|
|
||||||
visibility = new JComboBox<>(new String[] {
|
visibility = new JComboBox<>(new String[] {
|
||||||
"Public",
|
"Public",
|
||||||
@ -289,11 +332,9 @@ implements ActionListener {
|
|||||||
bottom.add(Box.createHorizontalStrut(8));
|
bottom.add(Box.createHorizontalStrut(8));
|
||||||
bottom.add(submit);
|
bottom.add(submit);
|
||||||
|
|
||||||
JPanel top = new JPanel();
|
text = new JTextArea();
|
||||||
top.setOpaque(false);
|
text.setLineWrap(true);
|
||||||
top.setLayout(new BorderLayout(8, 0));
|
text.setWrapStyleWord(true);
|
||||||
top.add(new JLabel("In reply to: "), BorderLayout.WEST);
|
|
||||||
top.add(reply);
|
|
||||||
|
|
||||||
setLayout(new BorderLayout(0, 8));
|
setLayout(new BorderLayout(0, 8));
|
||||||
add(top, BorderLayout.NORTH);
|
add(top, BorderLayout.NORTH);
|
||||||
|
33
ImageApi.java
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
|
import java.awt.Image;
|
||||||
|
import java.awt.Toolkit;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
|
||||||
|
interface
|
||||||
|
ImageApi {
|
||||||
|
|
||||||
|
public static Image
|
||||||
|
local(String name)
|
||||||
|
{
|
||||||
|
String path = "/graphics/" + name + ".png";
|
||||||
|
URL url = ImageApi.class.getResource(name);
|
||||||
|
if (url == null) return null;
|
||||||
|
return new ImageIcon(url).getImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Image
|
||||||
|
remote(String urlr)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
URL url = new URL(urlr);
|
||||||
|
Toolkit TK = Toolkit.getDefaultToolkit();
|
||||||
|
return TK.createImage(url);
|
||||||
|
}
|
||||||
|
catch (MalformedURLException eMu) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -90,7 +90,7 @@ ImageWindow extends JFrame {
|
|||||||
display.getToolTipText()
|
display.getToolTipText()
|
||||||
+ "\n(Media is of type '" + curr.type + "')"
|
+ "\n(Media is of type '" + curr.type + "')"
|
||||||
);
|
);
|
||||||
|
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,7 +99,7 @@ ImageWindow extends JFrame {
|
|||||||
ImageWindow()
|
ImageWindow()
|
||||||
{
|
{
|
||||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||||
setSize(400, 400);
|
setSize(600, 600);
|
||||||
|
|
||||||
display = new ImageComponent(this);
|
display = new ImageComponent(this);
|
||||||
showAttachments(new Attachment[0]);
|
showAttachments(new Attachment[0]);
|
||||||
@ -156,6 +156,7 @@ implements
|
|||||||
setPrev(Image image)
|
setPrev(Image image)
|
||||||
{
|
{
|
||||||
prev.setEnabled(image != null);
|
prev.setEnabled(image != null);
|
||||||
|
prev.setText(image == null ? "<" : "");
|
||||||
prev.setIcon(toIcon(image));
|
prev.setIcon(toIcon(image));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,6 +164,7 @@ implements
|
|||||||
setNext(Image image)
|
setNext(Image image)
|
||||||
{
|
{
|
||||||
next.setEnabled(image != null);
|
next.setEnabled(image != null);
|
||||||
|
next.setText(image == null ? ">" : "");
|
||||||
next.setIcon(toIcon(image));
|
next.setIcon(toIcon(image));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,14 +185,14 @@ implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mousePressed(MouseEvent eM)
|
mousePressed(MouseEvent eM)
|
||||||
{
|
{
|
||||||
dragX = eM.getX();
|
dragX = eM.getX();
|
||||||
dragY = eM.getY();
|
dragY = eM.getY();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mouseDragged(MouseEvent eM)
|
mouseDragged(MouseEvent eM)
|
||||||
{
|
{
|
||||||
@ -221,10 +223,10 @@ implements
|
|||||||
|
|
||||||
public void
|
public void
|
||||||
mouseEntered(MouseEvent eM) { }
|
mouseEntered(MouseEvent eM) { }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mouseExited(MouseEvent eM) { }
|
mouseExited(MouseEvent eM) { }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mouseClicked(MouseEvent eM) { }
|
mouseClicked(MouseEvent eM) { }
|
||||||
|
|
||||||
@ -250,7 +252,8 @@ implements
|
|||||||
{
|
{
|
||||||
if (image == null)
|
if (image == null)
|
||||||
{
|
{
|
||||||
String str = "(There are no images being displayed.)";
|
String str =
|
||||||
|
"(There are no images being displayed.)";
|
||||||
FontMetrics fm = g.getFontMetrics();
|
FontMetrics fm = g.getFontMetrics();
|
||||||
int x = (getWidth() - fm.stringWidth(str)) / 2;
|
int x = (getWidth() - fm.stringWidth(str)) / 2;
|
||||||
int y = (getHeight() + fm.getHeight()) / 2;
|
int y = (getHeight() + fm.getHeight()) / 2;
|
||||||
@ -285,15 +288,15 @@ implements
|
|||||||
{
|
{
|
||||||
this.primaire = primaire;
|
this.primaire = primaire;
|
||||||
|
|
||||||
Dimension BUTTON_SIZE = new Dimension(80, 60);
|
Dimension BUTTON_SIZE = new Dimension(48, 48);
|
||||||
|
|
||||||
setOpaque(false);
|
setOpaque(false);
|
||||||
scaleImage = true;
|
scaleImage = true;
|
||||||
zoomLevel = 100;
|
zoomLevel = 100;
|
||||||
|
|
||||||
prev = new JButton("<");
|
prev = new JButton();
|
||||||
toggle = new JButton("Show unscaled");
|
toggle = new JButton("Show unscaled");
|
||||||
next = new JButton(">");
|
next = new JButton();
|
||||||
prev.setPreferredSize(BUTTON_SIZE);
|
prev.setPreferredSize(BUTTON_SIZE);
|
||||||
next.setPreferredSize(BUTTON_SIZE);
|
next.setPreferredSize(BUTTON_SIZE);
|
||||||
prev.addActionListener(this);
|
prev.addActionListener(this);
|
||||||
@ -306,6 +309,9 @@ implements
|
|||||||
buttonArea.add(toggle);
|
buttonArea.add(toggle);
|
||||||
buttonArea.add(next);
|
buttonArea.add(next);
|
||||||
|
|
||||||
|
setPrev(null);
|
||||||
|
setNext(null);
|
||||||
|
|
||||||
setLayout(new BorderLayout());
|
setLayout(new BorderLayout());
|
||||||
add(buttonArea, BorderLayout.SOUTH);
|
add(buttonArea, BorderLayout.SOUTH);
|
||||||
add(new Painter(), BorderLayout.CENTER);
|
add(new Painter(), BorderLayout.CENTER);
|
||||||
|
22
JKomasto.java
Executable file → Normal file
@ -28,6 +28,9 @@ JKomasto {
|
|||||||
private ImageWindow
|
private ImageWindow
|
||||||
mediaWindow;
|
mediaWindow;
|
||||||
|
|
||||||
|
private TimelineWindowUpdater
|
||||||
|
timelineWindowUpdater;
|
||||||
|
|
||||||
private MastodonApi
|
private MastodonApi
|
||||||
api;
|
api;
|
||||||
|
|
||||||
@ -52,6 +55,8 @@ JKomasto {
|
|||||||
loginWindow.dispose();
|
loginWindow.dispose();
|
||||||
autoViewWindow.setCursor(null);
|
autoViewWindow.setCursor(null);
|
||||||
timelineWindow.setCursor(null);
|
timelineWindow.setCursor(null);
|
||||||
|
|
||||||
|
timelineWindowUpdater.addWindow(timelineWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PostWindow
|
public PostWindow
|
||||||
@ -87,6 +92,8 @@ JKomasto {
|
|||||||
mediaWindow.dispose();
|
mediaWindow.dispose();
|
||||||
loginWindow.setLocationByPlatform(true);
|
loginWindow.setLocationByPlatform(true);
|
||||||
loginWindow.setVisible(true);
|
loginWindow.setVisible(true);
|
||||||
|
|
||||||
|
timelineWindowUpdater = new TimelineWindowUpdater(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -123,6 +130,9 @@ TimelinePage {
|
|||||||
public TimelineType
|
public TimelineType
|
||||||
type;
|
type;
|
||||||
|
|
||||||
|
public String
|
||||||
|
accountNumId;
|
||||||
|
|
||||||
public List<Post>
|
public List<Post>
|
||||||
posts;
|
posts;
|
||||||
|
|
||||||
@ -143,6 +153,12 @@ Post {
|
|||||||
public Image
|
public Image
|
||||||
authorAvatar;
|
authorAvatar;
|
||||||
|
|
||||||
|
public String
|
||||||
|
authorNumId;
|
||||||
|
|
||||||
|
public String
|
||||||
|
boosterName;
|
||||||
|
|
||||||
public ZonedDateTime
|
public ZonedDateTime
|
||||||
date;
|
date;
|
||||||
|
|
||||||
@ -170,6 +186,9 @@ Attachment {
|
|||||||
public String
|
public String
|
||||||
url;
|
url;
|
||||||
|
|
||||||
|
public String
|
||||||
|
description;
|
||||||
|
|
||||||
public Image
|
public Image
|
||||||
image;
|
image;
|
||||||
|
|
||||||
@ -181,7 +200,8 @@ class
|
|||||||
Composition {
|
Composition {
|
||||||
|
|
||||||
public String
|
public String
|
||||||
text;
|
text,
|
||||||
|
contentWarning;
|
||||||
|
|
||||||
public PostVisibility
|
public PostVisibility
|
||||||
visibility;
|
visibility;
|
||||||
|
22
LoginWindow.java
Executable file → Normal file
@ -117,6 +117,7 @@ LoginWindow extends JFrame {
|
|||||||
requestSucceeded(Tree<String> json)
|
requestSucceeded(Tree<String> json)
|
||||||
{
|
{
|
||||||
api.setAccountDetails(json);
|
api.setAccountDetails(json);
|
||||||
|
serverContacted = true;
|
||||||
haveAccountDetails = true;
|
haveAccountDetails = true;
|
||||||
updateStatusDisplay();
|
updateStatusDisplay();
|
||||||
}
|
}
|
||||||
@ -139,14 +140,21 @@ LoginWindow extends JFrame {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
display.setCursor(null);
|
display.setCursor(null);
|
||||||
|
|
||||||
primaire.finishedLogin();
|
primaire.finishedLogin();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
useInstanceUrl()
|
useInstanceUrl()
|
||||||
{
|
{
|
||||||
|
if (display.isAutoLoginToggled()) { useCache(); return; }
|
||||||
|
|
||||||
String url = display.getInstanceUrl();
|
String url = display.getInstanceUrl();
|
||||||
|
if (url.trim().isEmpty()) {
|
||||||
|
// Should we show an error dialog..?
|
||||||
|
display.setInstanceUrl("");
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!hasProtocol(url)) {
|
if (!hasProtocol(url)) {
|
||||||
url = "https://" + url;
|
url = "https://" + url;
|
||||||
display.setInstanceUrl(url);
|
display.setInstanceUrl(url);
|
||||||
@ -157,10 +165,8 @@ LoginWindow extends JFrame {
|
|||||||
haveAccessToken = false;
|
haveAccessToken = false;
|
||||||
haveAccountDetails = false;
|
haveAccountDetails = false;
|
||||||
|
|
||||||
if (display.isAutoLoginToggled()) { useCache(); return; }
|
|
||||||
|
|
||||||
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||||
api.testUrlConnection(url, new RequestListener() {
|
api.testUrlConnection(url, new RequestListener() {
|
||||||
|
|
||||||
public void
|
public void
|
||||||
connectionFailed(IOException eIo)
|
connectionFailed(IOException eIo)
|
||||||
@ -192,7 +198,7 @@ LoginWindow extends JFrame {
|
|||||||
});
|
});
|
||||||
display.setCursor(null);
|
display.setCursor(null);
|
||||||
if (!serverContacted) return;
|
if (!serverContacted) return;
|
||||||
|
|
||||||
api.setInstanceUrl(url);
|
api.setInstanceUrl(url);
|
||||||
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||||
api.getAppCredentials(new RequestListener() {
|
api.getAppCredentials(new RequestListener() {
|
||||||
@ -259,12 +265,12 @@ LoginWindow extends JFrame {
|
|||||||
"\nWe cannot use Desktop.browse(URI) on your\n"
|
"\nWe cannot use Desktop.browse(URI) on your\n"
|
||||||
+ "computer.. You'll have to open your web\n"
|
+ "computer.. You'll have to open your web\n"
|
||||||
+ "browser yourself, and copy this URL in.";
|
+ "browser yourself, and copy this URL in.";
|
||||||
|
|
||||||
JTextField field = new JTextField();
|
JTextField field = new JTextField();
|
||||||
field.setText(uri.toString());
|
field.setText(uri.toString());
|
||||||
field.setPreferredSize(new Dimension(120, 32));
|
field.setPreferredSize(new Dimension(120, 32));
|
||||||
field.selectAll();
|
field.selectAll();
|
||||||
|
|
||||||
JOptionPane.showMessageDialog(
|
JOptionPane.showMessageDialog(
|
||||||
LoginWindow.this,
|
LoginWindow.this,
|
||||||
new Object[] { MESSAGE1, MESSAGE2, field },
|
new Object[] { MESSAGE1, MESSAGE2, field },
|
||||||
@ -283,7 +289,7 @@ LoginWindow extends JFrame {
|
|||||||
|
|
||||||
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||||
api.getAccessToken(code, new RequestListener() {
|
api.getAccessToken(code, new RequestListener() {
|
||||||
|
|
||||||
public void
|
public void
|
||||||
connectionFailed(IOException eIo)
|
connectionFailed(IOException eIo)
|
||||||
{
|
{
|
||||||
|
@ -11,7 +11,9 @@ import java.io.InputStreamReader;
|
|||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
import java.io.FileReader;
|
import java.io.FileReader;
|
||||||
import java.io.FileWriter;
|
import java.io.FileWriter;
|
||||||
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
|
||||||
class
|
class
|
||||||
MastodonApi {
|
MastodonApi {
|
||||||
@ -77,7 +79,8 @@ MastodonApi {
|
|||||||
getAppCredentials(RequestListener handler)
|
getAppCredentials(RequestListener handler)
|
||||||
{
|
{
|
||||||
assert instanceUrl != null;
|
assert instanceUrl != null;
|
||||||
try {
|
try
|
||||||
|
{
|
||||||
URL endpoint = new URL(instanceUrl + "/api/v1/apps");
|
URL endpoint = new URL(instanceUrl + "/api/v1/apps");
|
||||||
HttpURLConnection conn;
|
HttpURLConnection conn;
|
||||||
conn = (HttpURLConnection)endpoint.openConnection();
|
conn = (HttpURLConnection)endpoint.openConnection();
|
||||||
@ -164,7 +167,8 @@ MastodonApi {
|
|||||||
URL endpoint = new URL(instanceUrl + s);
|
URL endpoint = new URL(instanceUrl + s);
|
||||||
HttpURLConnection conn;
|
HttpURLConnection conn;
|
||||||
conn = (HttpURLConnection)endpoint.openConnection();
|
conn = (HttpURLConnection)endpoint.openConnection();
|
||||||
conn.setRequestProperty("Authorization", "Bearer " + token);
|
String s2 = "Bearer " + token;
|
||||||
|
conn.setRequestProperty("Authorization", s2);
|
||||||
conn.connect();
|
conn.connect();
|
||||||
|
|
||||||
doStandardJsonReturn(conn, handler);
|
doStandardJsonReturn(conn, handler);
|
||||||
@ -174,13 +178,18 @@ MastodonApi {
|
|||||||
|
|
||||||
public void
|
public void
|
||||||
getTimelinePage(
|
getTimelinePage(
|
||||||
TimelineType type, int count, String maxId, String minId,
|
TimelineType type, String accountId,
|
||||||
|
int count, String maxId, String minId,
|
||||||
RequestListener handler)
|
RequestListener handler)
|
||||||
{
|
{
|
||||||
String token = accessToken.get("access_token").value;
|
String token = accessToken.get("access_token").value;
|
||||||
|
|
||||||
String url = instanceUrl + "/api/v1";
|
String url = instanceUrl + "/api/v1";
|
||||||
switch (type)
|
if (accountId != null)
|
||||||
|
{
|
||||||
|
url += "/accounts/" + accountId + "/statuses";
|
||||||
|
}
|
||||||
|
else switch (type)
|
||||||
{
|
{
|
||||||
case FEDERATED:
|
case FEDERATED:
|
||||||
case LOCAL: url += "/timelines/public"; break;
|
case LOCAL: url += "/timelines/public"; break;
|
||||||
@ -266,13 +275,15 @@ MastodonApi {
|
|||||||
|
|
||||||
public void
|
public void
|
||||||
submit(
|
submit(
|
||||||
String text, PostVisibility visibility, String replyTo,
|
String text, PostVisibility visibility,
|
||||||
|
String replyTo, String contentWarning,
|
||||||
RequestListener handler)
|
RequestListener handler)
|
||||||
{
|
{
|
||||||
String token = accessToken.get("access_token").value;
|
String token = accessToken.get("access_token").value;
|
||||||
|
|
||||||
String visibilityParam = "direct";
|
String visibilityParam = "direct";
|
||||||
switch (visibility) {
|
switch (visibility)
|
||||||
|
{
|
||||||
case PUBLIC: visibilityParam = "public"; break;
|
case PUBLIC: visibilityParam = "public"; break;
|
||||||
case UNLISTED: visibilityParam = "unlisted"; break;
|
case UNLISTED: visibilityParam = "unlisted"; break;
|
||||||
case FOLLOWERS: visibilityParam = "private"; break;
|
case FOLLOWERS: visibilityParam = "private"; break;
|
||||||
@ -283,7 +294,8 @@ MastodonApi {
|
|||||||
String url = instanceUrl + "/api/v1/statuses";
|
String url = instanceUrl + "/api/v1/statuses";
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
text = URLEncoder.encode(text, "UTF-8");
|
text = encode(text);
|
||||||
|
contentWarning = encode(contentWarning);
|
||||||
|
|
||||||
URL endpoint = new URL(url);
|
URL endpoint = new URL(url);
|
||||||
HttpURLConnection conn;
|
HttpURLConnection conn;
|
||||||
@ -303,6 +315,9 @@ MastodonApi {
|
|||||||
if (replyTo != null) {
|
if (replyTo != null) {
|
||||||
output.write("&in_reply_to_id=" + replyTo);
|
output.write("&in_reply_to_id=" + replyTo);
|
||||||
}
|
}
|
||||||
|
if (contentWarning != null) {
|
||||||
|
output.write("&spoiler_text=" + contentWarning);
|
||||||
|
}
|
||||||
output.close();
|
output.close();
|
||||||
|
|
||||||
doStandardJsonReturn(conn, handler);
|
doStandardJsonReturn(conn, handler);
|
||||||
@ -310,10 +325,57 @@ MastodonApi {
|
|||||||
catch (IOException eIo) { handler.connectionFailed(eIo); }
|
catch (IOException eIo) { handler.connectionFailed(eIo); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void
|
||||||
|
monitorTimeline(
|
||||||
|
TimelineType type, ServerSideEventsListener handler)
|
||||||
|
{
|
||||||
|
String token = accessToken.get("access_token").value;
|
||||||
|
|
||||||
|
String url = instanceUrl + "/api/v1/streaming";
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case FEDERATED: url += "/public"; break;
|
||||||
|
case LOCAL: url += "/public/local"; break;
|
||||||
|
case HOME:
|
||||||
|
case NOTIFICATIONS: url += "/user"; break;
|
||||||
|
default: assert false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
URL endpoint = new URL(url);
|
||||||
|
HttpURLConnection conn;
|
||||||
|
conn = (HttpURLConnection)endpoint.openConnection();
|
||||||
|
String s = "Bearer " + token;
|
||||||
|
conn.setRequestProperty("Authorization", s);
|
||||||
|
conn.connect();
|
||||||
|
|
||||||
|
InputStreamReader input;
|
||||||
|
int code = conn.getResponseCode();
|
||||||
|
if (code >= 300)
|
||||||
|
{
|
||||||
|
input = new InputStreamReader(conn.getErrorStream());
|
||||||
|
Tree<String> response = JsonConverter.convert(input);
|
||||||
|
input.close();
|
||||||
|
handler.requestFailed(code, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
input = new InputStreamReader(conn.getInputStream());
|
||||||
|
BufferedReader br = new BufferedReader(input);
|
||||||
|
while (true) {
|
||||||
|
String line = br.readLine();
|
||||||
|
if (line != null) handler.lineReceived(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException eIo) { handler.connectionFailed(eIo); }
|
||||||
|
}
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
private void
|
private void
|
||||||
doStandardJsonReturn(HttpURLConnection conn, RequestListener handler)
|
doStandardJsonReturn(
|
||||||
|
HttpURLConnection conn, RequestListener handler)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
InputStreamReader input;
|
InputStreamReader input;
|
||||||
@ -334,7 +396,8 @@ MastodonApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void
|
private void
|
||||||
returnResponseInTree(HttpURLConnection conn, RequestListener handler)
|
returnResponseInTree(
|
||||||
|
HttpURLConnection conn, RequestListener handler)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
InputStreamReader input;
|
InputStreamReader input;
|
||||||
@ -371,6 +434,19 @@ MastodonApi {
|
|||||||
return doc;
|
return doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String
|
||||||
|
encode(String s)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (s == null) return null;
|
||||||
|
return URLEncoder.encode(s, "UTF-8");
|
||||||
|
}
|
||||||
|
catch (UnsupportedEncodingException eUe) {
|
||||||
|
assert false;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
public void
|
public void
|
||||||
|
39
PostWindow.java
Executable file → Normal file
@ -21,6 +21,7 @@ import java.awt.BorderLayout;
|
|||||||
import java.awt.GridLayout;
|
import java.awt.GridLayout;
|
||||||
import java.awt.Cursor;
|
import java.awt.Cursor;
|
||||||
import java.awt.Image;
|
import java.awt.Image;
|
||||||
|
import java.awt.Component;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -98,11 +99,12 @@ implements ActionListener {
|
|||||||
postDisplay.setText(post.text);
|
postDisplay.setText(post.text);
|
||||||
postDisplay.setFavourited(post.favourited);
|
postDisplay.setFavourited(post.favourited);
|
||||||
postDisplay.setBoosted(post.boosted);
|
postDisplay.setBoosted(post.boosted);
|
||||||
postDisplay.setMediaPreview(
|
postDisplay.setMediaPreview(
|
||||||
post.attachments.length == 0
|
post.attachments.length == 0
|
||||||
? null
|
? null
|
||||||
: post.attachments[0].image
|
: post.attachments[0].image
|
||||||
);
|
);
|
||||||
|
|
||||||
repliesDisplay.setReplies(replies);
|
repliesDisplay.setReplies(replies);
|
||||||
postDisplay.resetFocus();
|
postDisplay.resetFocus();
|
||||||
repaint();
|
repaint();
|
||||||
@ -111,7 +113,11 @@ implements ActionListener {
|
|||||||
public void
|
public void
|
||||||
openAuthorProfile()
|
openAuthorProfile()
|
||||||
{
|
{
|
||||||
|
TimelineWindow w = new TimelineWindow(primaire);
|
||||||
|
w.showAuthorPosts(post.authorNumId);
|
||||||
|
w.showLatestPage();
|
||||||
|
w.setLocationRelativeTo(this);
|
||||||
|
w.setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
@ -151,8 +157,9 @@ implements ActionListener {
|
|||||||
|
|
||||||
};
|
};
|
||||||
api.setPostFavourited(post.postId, favourited, handler);
|
api.setPostFavourited(post.postId, favourited, handler);
|
||||||
postDisplay.setFavouriteBoostEnabled(true);
|
|
||||||
postDisplay.setCursor(null);
|
postDisplay.setCursor(null);
|
||||||
|
postDisplay.setFavouriteBoostEnabled(true);
|
||||||
|
postDisplay.repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
@ -192,8 +199,9 @@ implements ActionListener {
|
|||||||
|
|
||||||
};
|
};
|
||||||
api.setPostBoosted(post.postId, boosted, handler);
|
api.setPostBoosted(post.postId, boosted, handler);
|
||||||
postDisplay.setFavouriteBoostEnabled(true);
|
|
||||||
postDisplay.setCursor(null);
|
postDisplay.setCursor(null);
|
||||||
|
postDisplay.setFavouriteBoostEnabled(true);
|
||||||
|
postDisplay.repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
@ -217,7 +225,7 @@ implements ActionListener {
|
|||||||
int l = Math.min(40, post.text.length());
|
int l = Math.min(40, post.text.length());
|
||||||
w.setTitle(post.text.substring(0, l));
|
w.setTitle(post.text.substring(0, l));
|
||||||
if (!w.isVisible()) {
|
if (!w.isVisible()) {
|
||||||
w.setLocation(getX(), getY() + 100);
|
w.setLocationRelativeTo(null);
|
||||||
w.setVisible(true);
|
w.setVisible(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -227,7 +235,7 @@ implements ActionListener {
|
|||||||
public void
|
public void
|
||||||
actionPerformed(ActionEvent eA)
|
actionPerformed(ActionEvent eA)
|
||||||
{
|
{
|
||||||
Object src = eA.getSource();
|
Component src = (Component)eA.getSource();
|
||||||
if (!(src instanceof JMenuItem)) return;
|
if (!(src instanceof JMenuItem)) return;
|
||||||
String text = ((JMenuItem)src).getText();
|
String text = ((JMenuItem)src).getText();
|
||||||
|
|
||||||
@ -358,7 +366,7 @@ implements ActionListener {
|
|||||||
public void
|
public void
|
||||||
actionPerformed(ActionEvent eA)
|
actionPerformed(ActionEvent eA)
|
||||||
{
|
{
|
||||||
Object src = eA.getSource();
|
Component src = (Component)eA.getSource();
|
||||||
String command = eA.getActionCommand();
|
String command = eA.getActionCommand();
|
||||||
|
|
||||||
if (src == profile)
|
if (src == profile)
|
||||||
@ -412,8 +420,8 @@ implements ActionListener {
|
|||||||
g.clearRect(0, 0, getWidth(), getHeight());
|
g.clearRect(0, 0, getWidth(), getHeight());
|
||||||
|
|
||||||
((java.awt.Graphics2D)g).setRenderingHint(
|
((java.awt.Graphics2D)g).setRenderingHint(
|
||||||
java.awt.RenderingHints.KEY_TEXT_ANTIALIASING,
|
java.awt.RenderingHints.KEY_ANTIALIASING,
|
||||||
java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON
|
java.awt.RenderingHints.VALUE_ANTIALIAS_ON
|
||||||
);
|
);
|
||||||
|
|
||||||
Font f1 = new Font("IPAGothic", Font.PLAIN, 16);
|
Font f1 = new Font("IPAGothic", Font.PLAIN, 16);
|
||||||
@ -454,13 +462,6 @@ implements ActionListener {
|
|||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
private static ImageIcon
|
|
||||||
toIcon(Image image)
|
|
||||||
{
|
|
||||||
if (image == null) return null;
|
|
||||||
return new ImageIcon(image);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<String>
|
private static List<String>
|
||||||
split(String string, int lineLength)
|
split(String string, int lineLength)
|
||||||
{
|
{
|
||||||
@ -472,7 +473,7 @@ implements ActionListener {
|
|||||||
if (word.length() >= lineLength) {
|
if (word.length() >= lineLength) {
|
||||||
word = word.substring(0, lineLength - 4) + "...";
|
word = word.substring(0, lineLength - 4) + "...";
|
||||||
}
|
}
|
||||||
if (word.equals("\n")) {
|
if (word.matches("\n")) {
|
||||||
returnee.add(empty(line));
|
returnee.add(empty(line));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -504,9 +505,9 @@ implements ActionListener {
|
|||||||
authorName = authorId = time = text = "";
|
authorName = authorId = time = text = "";
|
||||||
|
|
||||||
Dimension buttonSize = new Dimension(20, 40);
|
Dimension buttonSize = new Dimension(20, 40);
|
||||||
|
Border b = BorderFactory.createEmptyBorder(10, 10, 10, 10);
|
||||||
|
|
||||||
profile = new RoundButton();
|
profile = new RoundButton();
|
||||||
//profile.setPreferredSize(buttonSize);
|
|
||||||
profile.addActionListener(this);
|
profile.addActionListener(this);
|
||||||
|
|
||||||
favouriteBoost = new TwoToggleButton("favourite", "boost");
|
favouriteBoost = new TwoToggleButton("favourite", "boost");
|
||||||
@ -537,7 +538,7 @@ implements ActionListener {
|
|||||||
Box buttons = Box.createVerticalBox();
|
Box buttons = Box.createVerticalBox();
|
||||||
buttons.setOpaque(false);
|
buttons.setOpaque(false);
|
||||||
buttons.add(ibuttons);
|
buttons.add(ibuttons);
|
||||||
buttons.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
buttons.setBorder(b);
|
||||||
|
|
||||||
setLayout(new BorderLayout());
|
setLayout(new BorderLayout());
|
||||||
add(buttons, BorderLayout.WEST);
|
add(buttons, BorderLayout.WEST);
|
||||||
|
@ -15,3 +15,17 @@ RequestListener {
|
|||||||
requestSucceeded(Tree<String> json);
|
requestSucceeded(Tree<String> json);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface
|
||||||
|
ServerSideEventsListener {
|
||||||
|
|
||||||
|
void
|
||||||
|
connectionFailed(IOException eIo);
|
||||||
|
|
||||||
|
void
|
||||||
|
requestFailed(int httpCode, Tree<String> json);
|
||||||
|
|
||||||
|
void
|
||||||
|
lineReceived(String line);
|
||||||
|
|
||||||
|
}
|
||||||
|
298
TimelineWindow.java
Executable file → Normal file
@ -9,7 +9,6 @@ import javax.swing.JMenuItem;
|
|||||||
import javax.swing.JMenuBar;
|
import javax.swing.JMenuBar;
|
||||||
import javax.swing.JSeparator;
|
import javax.swing.JSeparator;
|
||||||
import javax.swing.Box;
|
import javax.swing.Box;
|
||||||
import javax.swing.ImageIcon;
|
|
||||||
import javax.swing.BorderFactory;
|
import javax.swing.BorderFactory;
|
||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
import java.awt.GridBagLayout;
|
import java.awt.GridBagLayout;
|
||||||
@ -30,6 +29,10 @@ import java.awt.event.ActionListener;
|
|||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.MouseListener;
|
import java.awt.event.MouseListener;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.awt.event.FocusListener;
|
||||||
|
import java.awt.event.FocusEvent;
|
||||||
|
import java.awt.event.KeyListener;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
|
||||||
import cafe.biskuteri.hinoki.Tree;
|
import cafe.biskuteri.hinoki.Tree;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -59,8 +62,6 @@ implements ActionListener {
|
|||||||
|
|
||||||
private JMenuItem
|
private JMenuItem
|
||||||
openHome,
|
openHome,
|
||||||
// umm, what about the timeline that's like, notes that your
|
|
||||||
// post was favourited or replied to? those aren't messages..
|
|
||||||
openMessages,
|
openMessages,
|
||||||
openLocal,
|
openLocal,
|
||||||
openFederated,
|
openFederated,
|
||||||
@ -82,21 +83,29 @@ implements ActionListener {
|
|||||||
setTimelineType(TimelineType type)
|
setTimelineType(TimelineType type)
|
||||||
{
|
{
|
||||||
page.type = type;
|
page.type = type;
|
||||||
|
page.accountNumId = null;
|
||||||
|
|
||||||
String s1 = type.toString();
|
String s1 = type.toString();
|
||||||
s1 = s1.charAt(0) + s1.substring(1).toLowerCase();
|
s1 = s1.charAt(0) + s1.substring(1).toLowerCase();
|
||||||
setTitle(s1 + " - JKomasto");
|
setTitle(s1 + " - JKomasto");
|
||||||
|
|
||||||
String s2 = type.toString().toLowerCase();
|
String s2 = type.toString().toLowerCase();
|
||||||
s2 = "/graphics/" + s2 + ".png";
|
display.setBackgroundImage(ImageApi.local(s2));
|
||||||
URL url = getClass().getResource(s2);
|
}
|
||||||
if (url != null) {
|
|
||||||
ImageIcon icon = new ImageIcon(url);
|
public TimelineType
|
||||||
display.setBackgroundImage(icon.getImage());
|
getTimelineType() { return page.type; }
|
||||||
}
|
|
||||||
else {
|
public void
|
||||||
display.setBackgroundImage(null);
|
showAuthorPosts(String authorNumId)
|
||||||
}
|
{
|
||||||
|
assert authorNumId != null;
|
||||||
|
|
||||||
|
page.type = TimelineType.FEDERATED;
|
||||||
|
page.accountNumId = authorNumId;
|
||||||
|
|
||||||
|
setTitle(authorNumId + " - JKomasto");
|
||||||
|
display.setBackgroundImage(ImageApi.local("profile"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
@ -104,12 +113,14 @@ implements ActionListener {
|
|||||||
{
|
{
|
||||||
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||||
api.getTimelinePage(
|
api.getTimelinePage(
|
||||||
page.type, PREVIEW_COUNT, null, null,
|
page.type, page.accountNumId,
|
||||||
|
PREVIEW_COUNT, null, null,
|
||||||
new RequestListener() {
|
new RequestListener() {
|
||||||
|
|
||||||
public void
|
public void
|
||||||
connectionFailed(IOException eIo)
|
connectionFailed(IOException eIo)
|
||||||
{
|
{
|
||||||
|
eIo.printStackTrace();
|
||||||
String s = eIo.getClass().getName();
|
String s = eIo.getClass().getName();
|
||||||
setTitle(s + " - JKomasto");
|
setTitle(s + " - JKomasto");
|
||||||
}
|
}
|
||||||
@ -117,6 +128,7 @@ implements ActionListener {
|
|||||||
public void
|
public void
|
||||||
requestFailed(int httpCode, Tree<String> json)
|
requestFailed(int httpCode, Tree<String> json)
|
||||||
{
|
{
|
||||||
|
System.err.println(json.get("error").value);
|
||||||
setTitle(httpCode + " - JKomasto");
|
setTitle(httpCode + " - JKomasto");
|
||||||
// lol...
|
// lol...
|
||||||
}
|
}
|
||||||
@ -124,9 +136,13 @@ implements ActionListener {
|
|||||||
public void
|
public void
|
||||||
requestSucceeded(Tree<String> json)
|
requestSucceeded(Tree<String> json)
|
||||||
{
|
{
|
||||||
page.posts = toPosts(json);
|
List<Post> posts = toPosts(json);
|
||||||
|
page.posts = posts;
|
||||||
display.setPosts(page.posts);
|
display.setPosts(page.posts);
|
||||||
display.setNextPageAvailable(true);
|
boolean full = posts.size() >= PREVIEW_COUNT;
|
||||||
|
display.setNextPageAvailable(full);
|
||||||
|
display.setPreviousPageAvailable(true);
|
||||||
|
display.resetFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -143,7 +159,8 @@ implements ActionListener {
|
|||||||
|
|
||||||
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||||
api.getTimelinePage(
|
api.getTimelinePage(
|
||||||
page.type, PREVIEW_COUNT, last.postId, null,
|
page.type, page.accountNumId,
|
||||||
|
PREVIEW_COUNT, last.postId, null,
|
||||||
new RequestListener() {
|
new RequestListener() {
|
||||||
|
|
||||||
public void
|
public void
|
||||||
@ -162,9 +179,19 @@ implements ActionListener {
|
|||||||
public void
|
public void
|
||||||
requestSucceeded(Tree<String> json)
|
requestSucceeded(Tree<String> json)
|
||||||
{
|
{
|
||||||
page.posts = toPosts(json);
|
List<Post> posts = toPosts(json);
|
||||||
|
if (posts.size() == 0) {
|
||||||
|
// We should probably say something
|
||||||
|
// to the user here? For now, we
|
||||||
|
// quietly cancel.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
page.posts = posts;
|
||||||
display.setPosts(page.posts);
|
display.setPosts(page.posts);
|
||||||
|
boolean full = posts.size() >= PREVIEW_COUNT;
|
||||||
|
display.setNextPageAvailable(full);
|
||||||
display.setPreviousPageAvailable(true);
|
display.setPreviousPageAvailable(true);
|
||||||
|
display.resetFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -181,7 +208,8 @@ implements ActionListener {
|
|||||||
|
|
||||||
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||||
api.getTimelinePage(
|
api.getTimelinePage(
|
||||||
page.type, PREVIEW_COUNT, null, first.postId,
|
page.type, page.accountNumId,
|
||||||
|
PREVIEW_COUNT, null, first.postId,
|
||||||
new RequestListener() {
|
new RequestListener() {
|
||||||
|
|
||||||
public void
|
public void
|
||||||
@ -200,17 +228,21 @@ implements ActionListener {
|
|||||||
public void
|
public void
|
||||||
requestSucceeded(Tree<String> json)
|
requestSucceeded(Tree<String> json)
|
||||||
{
|
{
|
||||||
page.posts = toPosts(json);
|
List<Post> posts = toPosts(json);
|
||||||
|
if (posts.size() < PREVIEW_COUNT) {
|
||||||
|
showLatestPage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
page.posts = posts;
|
||||||
display.setPosts(page.posts);
|
display.setPosts(page.posts);
|
||||||
display.setNextPageAvailable(true);
|
display.setNextPageAvailable(true);
|
||||||
display.setPreviousPageAvailable(true);
|
display.setPreviousPageAvailable(true);
|
||||||
|
display.resetFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
display.setCursor(null);
|
display.setCursor(null);
|
||||||
|
|
||||||
if (page.posts.size() < PREVIEW_COUNT) showLatestPage();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
@ -221,6 +253,15 @@ implements ActionListener {
|
|||||||
primaire.getAutoViewWindow().showPost(post);
|
primaire.getAutoViewWindow().showPost(post);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void
|
||||||
|
postOpened(Post post)
|
||||||
|
{
|
||||||
|
PostWindow w = new PostWindow(primaire);
|
||||||
|
w.showPost(post);
|
||||||
|
w.setLocationRelativeTo(this);
|
||||||
|
w.setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
actionPerformed(ActionEvent eA)
|
actionPerformed(ActionEvent eA)
|
||||||
{
|
{
|
||||||
@ -273,11 +314,18 @@ implements ActionListener {
|
|||||||
private static List<Post>
|
private static List<Post>
|
||||||
toPosts(Tree<String> json)
|
toPosts(Tree<String> json)
|
||||||
{
|
{
|
||||||
List<Post> posts = new ArrayList<>();
|
List<Post> posts = new ArrayList<>();
|
||||||
for (Tree<String> post: json.children)
|
for (Tree<String> post: json.children)
|
||||||
{
|
{
|
||||||
Post addee = new Post();
|
Post addee = new Post();
|
||||||
|
|
||||||
|
if (post.get("reblog").size() != 0) {
|
||||||
|
Tree<String> a = post.get("account");
|
||||||
|
String s = a.get("display_name").value;
|
||||||
|
addee.boosterName = s;
|
||||||
|
post = post.get("reblog");
|
||||||
|
}
|
||||||
|
|
||||||
addee.postId = post.get("id").value;
|
addee.postId = post.get("id").value;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -304,7 +352,7 @@ implements ActionListener {
|
|||||||
b.append(" \n ");
|
b.append(" \n ");
|
||||||
}
|
}
|
||||||
if (node.get(0).key.equals("/p")) {
|
if (node.get(0).key.equals("/p")) {
|
||||||
b.append(" \n\n ");
|
b.append(" \n \n ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (node.key.equals("text")) {
|
if (node.key.equals("text")) {
|
||||||
@ -323,51 +371,40 @@ implements ActionListener {
|
|||||||
else addee.contentWarning = null;
|
else addee.contentWarning = null;
|
||||||
|
|
||||||
Tree<String> account = post.get("account");
|
Tree<String> account = post.get("account");
|
||||||
if (post.get("reblog").size() != 0) {
|
addee.authorId = account.get("acct").value;
|
||||||
account = post.get("reblog").get("account");
|
|
||||||
}
|
|
||||||
addee.authorId = account.get("acct").value;
|
|
||||||
addee.authorName = account.get("username").value;
|
addee.authorName = account.get("username").value;
|
||||||
|
addee.authorNumId = account.get("id").value;
|
||||||
String s2 = account.get("display_name").value;
|
String s2 = account.get("display_name").value;
|
||||||
if (!s2.isEmpty()) addee.authorName = s2;
|
if (!s2.isEmpty()) addee.authorName = s2;
|
||||||
try {
|
String s3 = account.get("avatar").value;
|
||||||
String av = account.get("avatar").value;
|
addee.authorAvatar = ImageApi.remote(s3);
|
||||||
ImageIcon icon = new ImageIcon(new URL(av));
|
if (addee.authorAvatar == null) {
|
||||||
addee.authorAvatar = icon.getImage();
|
s3 = "defaultAvatar";
|
||||||
}
|
addee.authorAvatar = ImageApi.local(s3);
|
||||||
catch (MalformedURLException eMu) {
|
}
|
||||||
// Weird bug on their part.. We should
|
|
||||||
// probably react by using a default avatar.
|
|
||||||
}
|
|
||||||
|
|
||||||
String f = post.get("favourited").value;
|
String f = post.get("favourited").value;
|
||||||
String b = post.get("reblogged").value;
|
String b = post.get("reblogged").value;
|
||||||
addee.favourited = f.equals("true");
|
addee.favourited = f.equals("true");
|
||||||
addee.boosted = b.equals("true");
|
addee.boosted = b.equals("true");
|
||||||
|
|
||||||
Tree<String> a1 = post.get("media_attachments");
|
Tree<String> as1 = post.get("media_attachments");
|
||||||
Attachment[] a2 = new Attachment[a1.size()];
|
Attachment[] as2 = new Attachment[as1.size()];
|
||||||
for (int o = 0; o < a2.length; ++o)
|
for (int o = 0; o < as2.length; ++o)
|
||||||
{
|
{
|
||||||
a2[o] = new Attachment();
|
Tree<String> a1 = as1.get(o);
|
||||||
a2[o].type = a1.get(o).get("type").value;
|
Attachment a2 = as2[o] = new Attachment();
|
||||||
|
|
||||||
a2[o].url = a1.get(o).get("remote_url").value;
|
a2.type = a1.get("type").value;
|
||||||
if (a2[o].url == null)
|
String u1 = a1.get("remote_url").value;
|
||||||
a2[o].url = a1.get(o).get("text_url").value;
|
String u2 = a1.get("text_url").value;
|
||||||
|
a2.url = u1 == null ? u2 : u1;
|
||||||
a2[o].image = null;
|
a2.description = a1.get("description").value;
|
||||||
if (a2[o].type.equals("image")) try
|
a2.image = null;
|
||||||
{
|
if (a2.type.equals("image"))
|
||||||
URL url = new URL(a2[o].url);
|
a2.image = ImageApi.remote(a2.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;
|
addee.attachments = as2;
|
||||||
|
|
||||||
posts.add(addee);
|
posts.add(addee);
|
||||||
}
|
}
|
||||||
@ -418,6 +455,7 @@ implements ActionListener {
|
|||||||
flipToNewestPost.addActionListener(this);
|
flipToNewestPost.addActionListener(this);
|
||||||
|
|
||||||
JMenu programMenu = new JMenu("Program");
|
JMenu programMenu = new JMenu("Program");
|
||||||
|
programMenu.setMnemonic(KeyEvent.VK_P);
|
||||||
programMenu.add(openHome);
|
programMenu.add(openHome);
|
||||||
programMenu.add(openFederated);
|
programMenu.add(openFederated);
|
||||||
programMenu.add(new JSeparator());
|
programMenu.add(new JSeparator());
|
||||||
@ -426,6 +464,7 @@ implements ActionListener {
|
|||||||
programMenu.add(new JSeparator());
|
programMenu.add(new JSeparator());
|
||||||
programMenu.add(quit);
|
programMenu.add(quit);
|
||||||
JMenu timelineMenu = new JMenu("Timeline");
|
JMenu timelineMenu = new JMenu("Timeline");
|
||||||
|
timelineMenu.setMnemonic(KeyEvent.VK_T);
|
||||||
timelineMenu.add(flipToNewestPost);
|
timelineMenu.add(flipToNewestPost);
|
||||||
JMenuBar menuBar = new JMenuBar();
|
JMenuBar menuBar = new JMenuBar();
|
||||||
menuBar.add(programMenu);
|
menuBar.add(programMenu);
|
||||||
@ -449,7 +488,9 @@ implements ActionListener {
|
|||||||
|
|
||||||
class
|
class
|
||||||
TimelineComponent extends JPanel
|
TimelineComponent extends JPanel
|
||||||
implements ActionListener, MouseListener {
|
implements
|
||||||
|
ActionListener, KeyListener,
|
||||||
|
MouseListener, FocusListener {
|
||||||
|
|
||||||
private TimelineWindow
|
private TimelineWindow
|
||||||
primaire;
|
primaire;
|
||||||
@ -494,14 +535,23 @@ implements ActionListener, MouseListener {
|
|||||||
Post p = posts.get(o);
|
Post p = posts.get(o);
|
||||||
c.setTopLeft(p.authorName);
|
c.setTopLeft(p.authorName);
|
||||||
{
|
{
|
||||||
|
String f = "";
|
||||||
|
if (p.boosterName != null)
|
||||||
|
f += "b";
|
||||||
|
if (p.attachments.length > 0)
|
||||||
|
f += "a";
|
||||||
|
|
||||||
|
String t;
|
||||||
ZonedDateTime now = ZonedDateTime.now();
|
ZonedDateTime now = ZonedDateTime.now();
|
||||||
long d = ChronoUnit.SECONDS.between(p.date, now);
|
long d = ChronoUnit.SECONDS.between(p.date, now);
|
||||||
long s = Math.abs(d);
|
long s = Math.abs(d);
|
||||||
if (s < 30) c.setTopRight("now");
|
if (s < 30) t = "now";
|
||||||
else if (s < 60) c.setTopRight(d + "s");
|
else if (s < 60) t = d + "s";
|
||||||
else if (s < 3600) c.setTopRight((d / 60) + "m");
|
else if (s < 3600) t = (d / 60) + "m";
|
||||||
else if (s < 86400) c.setTopRight((d / 3600) + "h");
|
else if (s < 86400) t = (d / 3600) + "h";
|
||||||
else c.setTopRight((d / 86400) + "d");
|
else t = (d / 86400) + "d";
|
||||||
|
|
||||||
|
c.setTopRight(f + " " + t);
|
||||||
}
|
}
|
||||||
if (p.contentWarning != null)
|
if (p.contentWarning != null)
|
||||||
c.setBottom("(" + p.contentWarning + ")");
|
c.setBottom("(" + p.contentWarning + ")");
|
||||||
@ -533,11 +583,19 @@ implements ActionListener, MouseListener {
|
|||||||
public void
|
public void
|
||||||
setBackgroundImage(Image n) { backgroundImage = n; }
|
setBackgroundImage(Image n) { backgroundImage = n; }
|
||||||
|
|
||||||
|
public void
|
||||||
|
resetFocus() { postPreviews.get(0).requestFocusInWindow(); }
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
protected void
|
protected void
|
||||||
paintComponent(Graphics g)
|
paintComponent(Graphics g)
|
||||||
{
|
{
|
||||||
|
((java.awt.Graphics2D)g).setRenderingHint(
|
||||||
|
java.awt.RenderingHints.KEY_ANTIALIASING,
|
||||||
|
java.awt.RenderingHints.VALUE_ANTIALIAS_ON
|
||||||
|
);
|
||||||
|
|
||||||
int w = getWidth(), h = getHeight();
|
int w = getWidth(), h = getHeight();
|
||||||
g.clearRect(0, 0, w, h);
|
g.clearRect(0, 0, w, h);
|
||||||
int h2 = h * 5 / 10, w2 = h2;
|
int h2 = h * 5 / 10, w2 = h2;
|
||||||
@ -545,40 +603,88 @@ implements ActionListener, MouseListener {
|
|||||||
g.drawImage(backgroundImage, x, y, w2, h2, this);
|
g.drawImage(backgroundImage, x, y, w2, h2, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void
|
||||||
|
select(Object c)
|
||||||
|
{
|
||||||
|
assert c instanceof PostPreviewComponent;
|
||||||
|
PostPreviewComponent p = (PostPreviewComponent)c;
|
||||||
|
|
||||||
|
int offset = postPreviews.indexOf(p);
|
||||||
|
assert offset != -1;
|
||||||
|
if (offset < posts.size()) {
|
||||||
|
primaire.postSelected(posts.get(offset));
|
||||||
|
p.setSelected(true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
p.setSelected(false);
|
||||||
|
}
|
||||||
|
p.repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void
|
||||||
|
deselect(Object c)
|
||||||
|
{
|
||||||
|
assert c instanceof PostPreviewComponent;
|
||||||
|
PostPreviewComponent p = (PostPreviewComponent)c;
|
||||||
|
|
||||||
|
p.setSelected(false);
|
||||||
|
p.repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void
|
||||||
|
open(Object c)
|
||||||
|
{
|
||||||
|
assert c instanceof PostPreviewComponent;
|
||||||
|
PostPreviewComponent p = (PostPreviewComponent)c;
|
||||||
|
|
||||||
|
int offset = postPreviews.indexOf(p);
|
||||||
|
assert offset != -1;
|
||||||
|
if (offset < posts.size()) {
|
||||||
|
primaire.postOpened(posts.get(offset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
|
focusGained(FocusEvent eF) { select(eF.getSource()); }
|
||||||
|
|
||||||
|
public void
|
||||||
|
focusLost(FocusEvent eF) { deselect(eF.getSource()); }
|
||||||
|
|
||||||
|
public void
|
||||||
|
mouseClicked(MouseEvent eM)
|
||||||
|
{
|
||||||
|
if (eM.getClickCount() == 2) open(eM.getSource());
|
||||||
|
else select(eM.getSource());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void
|
||||||
mouseEntered(MouseEvent eM)
|
mouseEntered(MouseEvent eM)
|
||||||
{
|
{
|
||||||
if (!hoverSelect) return;
|
if (!hoverSelect) return;
|
||||||
mouseClicked(eM);
|
mouseClicked(eM);
|
||||||
}
|
}
|
||||||
|
|
||||||
// (知) First time I'm using one of these..!
|
|
||||||
|
|
||||||
public void
|
|
||||||
mouseClicked(MouseEvent eM)
|
|
||||||
{
|
|
||||||
int offset = postPreviews.indexOf(eM.getSource());
|
|
||||||
assert offset != -1;
|
|
||||||
primaire.postSelected(posts.get(offset));
|
|
||||||
postPreviews.get(offset).setSelected(true);
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mouseExited(MouseEvent eM)
|
mouseExited(MouseEvent eM)
|
||||||
{
|
{
|
||||||
if (!hoverSelect) return;
|
if (!hoverSelect) return;
|
||||||
int offset = postPreviews.indexOf(eM.getSource());
|
deselect(eM.getSource());
|
||||||
assert offset != -1;
|
|
||||||
postPreviews.get(offset).setSelected(false);
|
|
||||||
repaint();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
// (知) First time I'm using these two..!
|
||||||
mousePressed(MouseEvent eM) { }
|
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mouseReleased(MouseEvent eM) { }
|
keyPressed(KeyEvent eK)
|
||||||
|
{
|
||||||
|
if (eK.getKeyCode() != KeyEvent.VK_ENTER) return;
|
||||||
|
|
||||||
|
PostPreviewComponent selected = null;
|
||||||
|
for (PostPreviewComponent c: postPreviews)
|
||||||
|
if (c.getSelected()) selected = c;
|
||||||
|
|
||||||
|
if (selected == null) return;
|
||||||
|
open(selected);
|
||||||
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
actionPerformed(ActionEvent eA)
|
actionPerformed(ActionEvent eA)
|
||||||
@ -595,6 +701,19 @@ implements ActionListener, MouseListener {
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void
|
||||||
|
mousePressed(MouseEvent eM) { }
|
||||||
|
|
||||||
|
public void
|
||||||
|
mouseReleased(MouseEvent eM) { }
|
||||||
|
|
||||||
|
public void
|
||||||
|
keyTyped(KeyEvent eK) { }
|
||||||
|
|
||||||
|
public void
|
||||||
|
keyReleased(KeyEvent eK) { }
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
TimelineComponent(TimelineWindow primaire)
|
TimelineComponent(TimelineWindow primaire)
|
||||||
@ -608,6 +727,8 @@ implements ActionListener, MouseListener {
|
|||||||
next = new JButton(">");
|
next = new JButton(">");
|
||||||
prev.setEnabled(false);
|
prev.setEnabled(false);
|
||||||
next.setEnabled(false);
|
next.setEnabled(false);
|
||||||
|
prev.setMnemonic(KeyEvent.VK_PAGE_UP);
|
||||||
|
next.setMnemonic(KeyEvent.VK_PAGE_DOWN);
|
||||||
prev.addActionListener(this);
|
prev.addActionListener(this);
|
||||||
next.addActionListener(this);
|
next.addActionListener(this);
|
||||||
|
|
||||||
@ -632,6 +753,8 @@ implements ActionListener, MouseListener {
|
|||||||
PostPreviewComponent c = new PostPreviewComponent();
|
PostPreviewComponent c = new PostPreviewComponent();
|
||||||
c.reset();
|
c.reset();
|
||||||
c.addMouseListener(this);
|
c.addMouseListener(this);
|
||||||
|
c.addFocusListener(this);
|
||||||
|
c.addKeyListener(this);
|
||||||
centre.add(c, constraints);
|
centre.add(c, constraints);
|
||||||
postPreviews.add(c);
|
postPreviews.add(c);
|
||||||
}
|
}
|
||||||
@ -683,6 +806,9 @@ PostPreviewComponent extends JComponent {
|
|||||||
else setBackground(new Color(0, 0, 0, 25));
|
else setBackground(new Color(0, 0, 0, 25));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean
|
||||||
|
getSelected() { return selected; }
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
protected void
|
protected void
|
||||||
@ -699,8 +825,6 @@ PostPreviewComponent extends JComponent {
|
|||||||
public
|
public
|
||||||
PostPreviewComponent()
|
PostPreviewComponent()
|
||||||
{
|
{
|
||||||
selected = false;
|
|
||||||
|
|
||||||
Font f = new JLabel().getFont();
|
Font f = new JLabel().getFont();
|
||||||
Font f1 = f.deriveFont(Font.PLAIN, 12f);
|
Font f1 = f.deriveFont(Font.PLAIN, 12f);
|
||||||
Font f2 = f.deriveFont(Font.ITALIC, 12f);
|
Font f2 = f.deriveFont(Font.ITALIC, 12f);
|
||||||
@ -725,8 +849,8 @@ PostPreviewComponent extends JComponent {
|
|||||||
bottom.setFont(f3);
|
bottom.setFont(f3);
|
||||||
bottom.setOpaque(false);
|
bottom.setOpaque(false);
|
||||||
|
|
||||||
|
setFocusable(true);
|
||||||
setOpaque(false);
|
setOpaque(false);
|
||||||
setSelected(false);
|
|
||||||
setLayout(new BorderLayout());
|
setLayout(new BorderLayout());
|
||||||
add(top, BorderLayout.NORTH);
|
add(top, BorderLayout.NORTH);
|
||||||
add(bottom);
|
add(bottom);
|
||||||
|
177
TimelineWindowUpdater.java
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.io.IOException;
|
||||||
|
import cafe.biskuteri.hinoki.Tree;
|
||||||
|
|
||||||
|
class
|
||||||
|
TimelineWindowUpdater {
|
||||||
|
|
||||||
|
private JKomasto
|
||||||
|
primaire;
|
||||||
|
|
||||||
|
private MastodonApi
|
||||||
|
api;
|
||||||
|
|
||||||
|
// - -%- -
|
||||||
|
|
||||||
|
private List<TimelineWindow>
|
||||||
|
updatees;
|
||||||
|
|
||||||
|
private StringBuilder
|
||||||
|
event, data;
|
||||||
|
|
||||||
|
// - -%- -
|
||||||
|
|
||||||
|
private Thread
|
||||||
|
federated,
|
||||||
|
local,
|
||||||
|
home,
|
||||||
|
notifications;
|
||||||
|
|
||||||
|
// ---%-@-%---
|
||||||
|
|
||||||
|
public void
|
||||||
|
addWindow(TimelineWindow updatee)
|
||||||
|
{
|
||||||
|
updatees.add(updatee);
|
||||||
|
|
||||||
|
Connection c = new Connection();
|
||||||
|
c.type = updatee.getTimelineType();
|
||||||
|
Thread t = new Thread(c);
|
||||||
|
switch (c.type) {
|
||||||
|
case FEDERATED:
|
||||||
|
if (federated != null) return;
|
||||||
|
federated = t; break;
|
||||||
|
case LOCAL:
|
||||||
|
if (local != null) return;
|
||||||
|
local = t; break;
|
||||||
|
case HOME:
|
||||||
|
if (home != null) return;
|
||||||
|
home = t; break;
|
||||||
|
case NOTIFICATIONS:
|
||||||
|
if (notifications != null) return;
|
||||||
|
notifications = t; break;
|
||||||
|
default: return;
|
||||||
|
}
|
||||||
|
t.start();
|
||||||
|
|
||||||
|
System.err.println(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void
|
||||||
|
removeWindow(TimelineWindow updatee)
|
||||||
|
{
|
||||||
|
updatees.remove(updatee);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - -%- -
|
||||||
|
|
||||||
|
private void
|
||||||
|
handle(TimelineType type, String event, String data)
|
||||||
|
{
|
||||||
|
System.err.println("Handling " + event + ".");
|
||||||
|
|
||||||
|
assert !data.isEmpty();
|
||||||
|
if (event.isEmpty()) return;
|
||||||
|
|
||||||
|
boolean newPost = event.equals("update");
|
||||||
|
boolean newNotif = event.equals("notification");
|
||||||
|
if (!(newPost || newNotif)) return;
|
||||||
|
|
||||||
|
for (TimelineWindow updatee: filter(type)) {
|
||||||
|
System.err.println("Refreshing " + updatee);
|
||||||
|
updatee.showLatestPage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<TimelineWindow>
|
||||||
|
filter(TimelineType type)
|
||||||
|
{
|
||||||
|
List<TimelineWindow> returnee = new ArrayList<>();
|
||||||
|
for (TimelineWindow updatee: updatees)
|
||||||
|
if (updatee.getTimelineType() == type)
|
||||||
|
returnee.add(updatee);
|
||||||
|
return returnee;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---%-@-%---
|
||||||
|
|
||||||
|
private class
|
||||||
|
Connection
|
||||||
|
implements Runnable, ServerSideEventsListener {
|
||||||
|
|
||||||
|
private TimelineType
|
||||||
|
type;
|
||||||
|
|
||||||
|
// -=-
|
||||||
|
|
||||||
|
private StringBuilder
|
||||||
|
event, data;
|
||||||
|
|
||||||
|
// -=%=-
|
||||||
|
|
||||||
|
public void
|
||||||
|
run()
|
||||||
|
{
|
||||||
|
event = new StringBuilder();
|
||||||
|
data = new StringBuilder();
|
||||||
|
api.monitorTimeline(type, this);
|
||||||
|
// monitorTimeline should not return
|
||||||
|
// until the connection is closed.
|
||||||
|
System.err.println("Finit.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void
|
||||||
|
lineReceived(String line)
|
||||||
|
{
|
||||||
|
System.err.println("Line: " + line);
|
||||||
|
|
||||||
|
if (line.startsWith(":")) return;
|
||||||
|
|
||||||
|
if (line.isEmpty()) {
|
||||||
|
handle(type, event.toString(), data.toString());
|
||||||
|
event.delete(0, event.length());
|
||||||
|
data.delete(0, event.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.startsWith("data: "))
|
||||||
|
data.append(line.substring("data: ".length()));
|
||||||
|
|
||||||
|
if (line.startsWith("event: "))
|
||||||
|
event.append(line.substring("event: ".length()));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note that I utterly ignore https://html.spec.whatwg.org
|
||||||
|
* /multipage/server-sent-events.html#dispatchMessage.
|
||||||
|
* That is because I am not a browser.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
public void
|
||||||
|
connectionFailed(IOException eIo)
|
||||||
|
{
|
||||||
|
// sais pas dois-je faire..
|
||||||
|
eIo.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void
|
||||||
|
requestFailed(int httpCode, Tree<String> json)
|
||||||
|
{
|
||||||
|
// mo shiranu
|
||||||
|
System.err.println(httpCode + ", " + json.get("error").value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---%-@-%---
|
||||||
|
|
||||||
|
TimelineWindowUpdater(JKomasto primaire)
|
||||||
|
{
|
||||||
|
this.primaire = primaire;
|
||||||
|
this.api = primaire.getMastodonApi();
|
||||||
|
|
||||||
|
this.updatees = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
34
TwoToggleButton.java
Executable file → Normal file
@ -133,6 +133,7 @@ implements KeyListener, MouseListener, FocusListener {
|
|||||||
case MouseEvent.BUTTON1: togglePrimary(); break;
|
case MouseEvent.BUTTON1: togglePrimary(); break;
|
||||||
case MouseEvent.BUTTON3: toggleSecondary(); break;
|
case MouseEvent.BUTTON3: toggleSecondary(); break;
|
||||||
}
|
}
|
||||||
|
requestFocusInWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
@ -142,6 +143,7 @@ implements KeyListener, MouseListener, FocusListener {
|
|||||||
case KeyEvent.VK_SPACE: togglePrimary(); break;
|
case KeyEvent.VK_SPACE: togglePrimary(); break;
|
||||||
case KeyEvent.VK_ENTER: toggleSecondary(); break;
|
case KeyEvent.VK_ENTER: toggleSecondary(); break;
|
||||||
}
|
}
|
||||||
|
requestFocusInWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
@ -241,10 +243,12 @@ implements KeyListener, MouseListener, FocusListener {
|
|||||||
private Image
|
private Image
|
||||||
image;
|
image;
|
||||||
|
|
||||||
|
// - -%- -
|
||||||
|
|
||||||
private int
|
private int
|
||||||
nextEventID = ActionEvent.ACTION_FIRST;
|
nextEventID = ActionEvent.ACTION_FIRST;
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
private static Image
|
private static Image
|
||||||
button,
|
button,
|
||||||
@ -261,24 +265,20 @@ implements KeyListener, MouseListener, FocusListener {
|
|||||||
protected void
|
protected void
|
||||||
paintComponent(Graphics g)
|
paintComponent(Graphics g)
|
||||||
{
|
{
|
||||||
((java.awt.Graphics2D)g).setRenderingHint(
|
g.drawImage(button, 0, 0, this);
|
||||||
java.awt.RenderingHints.KEY_TEXT_ANTIALIASING,
|
|
||||||
java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON
|
|
||||||
);
|
|
||||||
|
|
||||||
g.drawImage(button, 0, 0, this);
|
|
||||||
if (!isEnabled())
|
if (!isEnabled())
|
||||||
g.drawImage(disabledOverlay, 0, 0, this);
|
g.drawImage(disabledOverlay, 0, 0, this);
|
||||||
if (isFocusOwner())
|
if (isFocusOwner())
|
||||||
g.drawImage(selectedOverlay, 0, 0, this);
|
g.drawImage(selectedOverlay, 0, 0, this);
|
||||||
|
|
||||||
if (image == null) return;
|
if (image == null) return;
|
||||||
|
|
||||||
int w1 = button.getWidth(this);
|
int w1 = button.getWidth(this);
|
||||||
int h1 = button.getHeight(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 w2 = image.getWidth(this);
|
||||||
int h2 = image.getHeight(this);
|
int h2 = image.getHeight(this);
|
||||||
|
if (w2 == -1) w2 = w1;
|
||||||
|
if (h2 == -1) h2 = h1;
|
||||||
if (h2 > w2) {
|
if (h2 > w2) {
|
||||||
h2 = h2 * w1 / w2;
|
h2 = h2 * w1 / w2;
|
||||||
w2 = w1;
|
w2 = w1;
|
||||||
@ -287,8 +287,14 @@ implements KeyListener, MouseListener, FocusListener {
|
|||||||
w2 = w2 * h1 / h2;
|
w2 = w2 * h1 / h2;
|
||||||
h2 = h1;
|
h2 = h1;
|
||||||
}
|
}
|
||||||
|
Shape defaultClip, roundClip;
|
||||||
|
defaultClip = g.getClip();
|
||||||
|
roundClip = new Ellipse2D.Float(6, 6, w1 - 12, h1 - 12);
|
||||||
|
|
||||||
g.setClip(roundClip);
|
g.setClip(roundClip);
|
||||||
g.drawImage(image, 0, 0, w2, h2, this);
|
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);
|
g.setClip(defaultClip);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,13 +349,13 @@ implements KeyListener, MouseListener, FocusListener {
|
|||||||
RoundButton()
|
RoundButton()
|
||||||
{
|
{
|
||||||
if (button == null) loadCommonImages();
|
if (button == null) loadCommonImages();
|
||||||
|
|
||||||
setModel(new DefaultButtonModel());
|
setModel(new DefaultButtonModel());
|
||||||
setFocusable(true);
|
setFocusable(true);
|
||||||
setOpaque(false);
|
setOpaque(false);
|
||||||
|
|
||||||
int w = button.getWidth(null);
|
int w = button.getWidth(this);
|
||||||
int h = button.getHeight(null);
|
int h = button.getHeight(this);
|
||||||
setPreferredSize(new Dimension(w, h));
|
setPreferredSize(new Dimension(w, h));
|
||||||
|
|
||||||
this.addKeyListener(this);
|
this.addKeyListener(this);
|
||||||
|
0
graphics/Flags.xcf
Executable file → Normal file
0
graphics/Hourglass.xcf
Executable file → Normal file
0
graphics/button.png
Executable file → Normal file
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
0
graphics/disabledOverlay.png
Executable file → Normal file
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
0
graphics/favouriteToggled.png
Executable file → Normal file
Before Width: | Height: | Size: 353 B After Width: | Height: | Size: 353 B |
0
graphics/ref1.png
Executable file → Normal file
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
0
graphics/selectedOverlay.png
Executable file → Normal file
Before Width: | Height: | Size: 313 B After Width: | Height: | Size: 313 B |
0
graphics/test1.png
Executable file → Normal file
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
0
graphics/test2.png
Executable file → Normal file
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
0
graphics/test3.png
Executable file → Normal file
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
0
graphics/test4.png
Executable file → Normal file
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |