2021-07-16 00:37:03 +02:00
|
|
|
|
2021-07-16 01:07:23 +02:00
|
|
|
import javax.swing.JFrame;
|
|
|
|
import javax.swing.JPanel;
|
2021-07-16 00:37:03 +02:00
|
|
|
import javax.swing.JComponent;
|
2021-07-17 11:06:50 +02:00
|
|
|
import javax.swing.JButton;
|
2021-07-16 00:37:03 +02:00
|
|
|
import javax.swing.JLabel;
|
2021-07-17 12:03:59 +02:00
|
|
|
import javax.swing.JMenu;
|
2021-07-31 06:01:20 +02:00
|
|
|
import javax.swing.JMenuItem;
|
2021-07-17 12:03:59 +02:00
|
|
|
import javax.swing.JMenuBar;
|
|
|
|
import javax.swing.JSeparator;
|
2022-04-19 16:08:20 +02:00
|
|
|
import javax.swing.JRadioButton;
|
|
|
|
import javax.swing.JOptionPane;
|
|
|
|
import javax.swing.ButtonGroup;
|
2021-07-16 00:37:03 +02:00
|
|
|
import javax.swing.Box;
|
2021-07-17 11:06:50 +02:00
|
|
|
import javax.swing.BorderFactory;
|
2021-07-16 00:37:03 +02:00
|
|
|
import java.awt.BorderLayout;
|
2021-07-17 11:06:50 +02:00
|
|
|
import java.awt.GridBagLayout;
|
|
|
|
import java.awt.GridBagConstraints;
|
|
|
|
import java.awt.FlowLayout;
|
2021-07-16 00:37:03 +02:00
|
|
|
import java.awt.Font;
|
2021-07-16 11:46:17 +02:00
|
|
|
import java.awt.Dimension;
|
2021-07-17 11:06:50 +02:00
|
|
|
import java.awt.Insets;
|
2022-04-12 08:37:39 +02:00
|
|
|
import java.awt.Cursor;
|
|
|
|
import java.awt.Color;
|
|
|
|
import java.awt.Graphics;
|
2022-04-12 14:48:24 +02:00
|
|
|
import java.awt.Image;
|
2021-07-17 11:06:50 +02:00
|
|
|
import java.util.List;
|
|
|
|
import java.util.ArrayList;
|
2022-04-12 13:52:07 +02:00
|
|
|
import java.net.URL;
|
|
|
|
import java.net.MalformedURLException;
|
2021-07-31 06:01:20 +02:00
|
|
|
import java.awt.event.ActionListener;
|
|
|
|
import java.awt.event.ActionEvent;
|
2022-04-12 08:37:39 +02:00
|
|
|
import java.awt.event.MouseListener;
|
|
|
|
import java.awt.event.MouseEvent;
|
2022-04-14 06:38:49 +02:00
|
|
|
import java.awt.event.FocusListener;
|
|
|
|
import java.awt.event.FocusEvent;
|
|
|
|
import java.awt.event.KeyListener;
|
|
|
|
import java.awt.event.KeyEvent;
|
2022-04-12 08:37:39 +02:00
|
|
|
|
|
|
|
import cafe.biskuteri.hinoki.Tree;
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.time.ZonedDateTime;
|
|
|
|
import java.time.ZoneId;
|
|
|
|
import java.time.Period;
|
|
|
|
import java.time.temporal.ChronoUnit;
|
|
|
|
import java.time.format.DateTimeParseException;
|
2021-07-16 00:37:03 +02:00
|
|
|
|
2021-07-16 01:07:23 +02:00
|
|
|
class
|
2021-07-31 06:01:20 +02:00
|
|
|
TimelineWindow extends JFrame
|
|
|
|
implements ActionListener {
|
2021-07-16 01:07:23 +02:00
|
|
|
|
2022-04-12 08:37:39 +02:00
|
|
|
private JKomasto
|
|
|
|
primaire;
|
|
|
|
|
|
|
|
private MastodonApi
|
|
|
|
api;
|
|
|
|
|
2022-05-02 05:05:36 +02:00
|
|
|
private WindowUpdater
|
|
|
|
windowUpdater;
|
|
|
|
|
2022-04-12 08:37:39 +02:00
|
|
|
private TimelinePage
|
|
|
|
page;
|
2021-07-16 11:46:17 +02:00
|
|
|
|
|
|
|
// - -%- -
|
|
|
|
|
2021-07-16 01:07:23 +02:00
|
|
|
private TimelineComponent
|
|
|
|
display;
|
|
|
|
|
2021-07-31 06:01:20 +02:00
|
|
|
private JMenuItem
|
|
|
|
openHome,
|
|
|
|
openMessages,
|
|
|
|
openLocal,
|
|
|
|
openFederated,
|
2022-04-16 16:54:17 +02:00
|
|
|
openNotifications,
|
2022-04-19 16:08:20 +02:00
|
|
|
openOwnProfile,
|
|
|
|
openProfile,
|
2021-07-31 06:01:20 +02:00
|
|
|
createPost,
|
|
|
|
openAutoPostView,
|
|
|
|
quit;
|
|
|
|
|
|
|
|
private JMenuItem
|
|
|
|
flipToNewestPost;
|
|
|
|
|
2022-05-02 05:05:36 +02:00
|
|
|
private boolean
|
|
|
|
showingLatest;
|
|
|
|
|
2022-04-12 08:37:39 +02:00
|
|
|
// - -%- -
|
|
|
|
|
|
|
|
private static final int
|
|
|
|
PREVIEW_COUNT = TimelineComponent.PREVIEW_COUNT;
|
2021-08-14 20:56:37 +02:00
|
|
|
|
2021-07-17 11:46:27 +02:00
|
|
|
// ---%-@-%---
|
|
|
|
|
2022-05-06 22:34:22 +02:00
|
|
|
public synchronized void
|
2022-05-06 09:05:01 +02:00
|
|
|
use(TimelinePage page)
|
|
|
|
{
|
|
|
|
assert page != null;
|
|
|
|
this.page = page;
|
2022-05-02 03:44:10 +02:00
|
|
|
|
2022-05-06 09:05:01 +02:00
|
|
|
List<PostPreviewComponent> previews;
|
2022-05-02 03:44:10 +02:00
|
|
|
previews = display.getPostPreviews();
|
|
|
|
|
2022-05-06 09:05:01 +02:00
|
|
|
int available = page.posts.length;
|
2022-05-02 03:44:10 +02:00
|
|
|
int max = previews.size();
|
|
|
|
assert available <= max;
|
|
|
|
|
|
|
|
for (int o = 0; o < available; ++o)
|
|
|
|
{
|
2022-05-06 09:05:01 +02:00
|
|
|
PostPreviewComponent preview = previews.get(o);
|
|
|
|
Post post = page.posts[o];
|
2022-05-02 03:44:10 +02:00
|
|
|
|
2022-05-06 09:05:01 +02:00
|
|
|
preview.setTopLeft(post.author.name);
|
|
|
|
if (post.boostedPost != null)
|
|
|
|
{
|
|
|
|
String s = "boosted by " + post.author.name;
|
|
|
|
preview.setTopLeft(s);
|
|
|
|
post = post.boostedPost;
|
2022-05-02 03:44:10 +02:00
|
|
|
}
|
2022-05-06 09:05:01 +02:00
|
|
|
|
|
|
|
String flags = "";
|
|
|
|
if (post.attachments.length > 0) flags += "a";
|
|
|
|
post.resolveRelativeTime();
|
|
|
|
preview.setTopRight(flags + " " + post.relativeTime);
|
|
|
|
|
|
|
|
post.resolveApproximateText();
|
|
|
|
if (post.contentWarning != null)
|
|
|
|
preview.setBottom("(" + post.contentWarning + ")");
|
|
|
|
else
|
|
|
|
preview.setBottom(post.approximateText);
|
2022-05-02 03:44:10 +02:00
|
|
|
}
|
|
|
|
for (int o = available; o < max; ++o)
|
|
|
|
{
|
|
|
|
previews.get(o).reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
boolean full = !(available < PREVIEW_COUNT);
|
2022-05-01 14:33:17 +02:00
|
|
|
display.setNextPageAvailable(full);
|
|
|
|
display.setPreviousPageAvailable(true);
|
|
|
|
display.resetFocus();
|
2022-05-06 09:05:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void
|
|
|
|
readEntity(Tree<String> postEntityArray)
|
|
|
|
{
|
|
|
|
TimelinePage page = new TimelinePage(postEntityArray);
|
|
|
|
page.type = this.page.type;
|
|
|
|
page.accountNumId = this.page.accountNumId;
|
|
|
|
page.listId = this.page.listId;
|
|
|
|
use(page);
|
2022-05-01 14:33:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void
|
|
|
|
refresh()
|
|
|
|
{
|
2022-05-02 05:05:36 +02:00
|
|
|
String firstId = null;
|
|
|
|
if (!showingLatest)
|
|
|
|
{
|
|
|
|
assert page.posts != null;
|
2022-05-06 09:05:01 +02:00
|
|
|
assert page.posts.length != 0;
|
|
|
|
firstId = page.posts[0].id;
|
2022-05-02 05:05:36 +02:00
|
|
|
}
|
2022-04-12 14:48:24 +02:00
|
|
|
|
2022-05-02 03:44:10 +02:00
|
|
|
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
|
|
|
api.getTimelinePage(
|
|
|
|
page.type,
|
|
|
|
PREVIEW_COUNT, firstId, null,
|
|
|
|
page.accountNumId, page.listId,
|
|
|
|
new RequestListener() {
|
2022-04-14 06:38:49 +02:00
|
|
|
|
2022-05-02 03:44:10 +02:00
|
|
|
public void
|
|
|
|
connectionFailed(IOException eIo)
|
|
|
|
{
|
|
|
|
JOptionPane.showMessageDialog(
|
|
|
|
TimelineWindow.this,
|
|
|
|
"Failed to fetch page.."
|
|
|
|
+ "\n" + eIo.getMessage()
|
|
|
|
);
|
|
|
|
}
|
2022-04-14 06:38:49 +02:00
|
|
|
|
2022-05-02 03:44:10 +02:00
|
|
|
public void
|
|
|
|
requestFailed(int httpCode, Tree<String> json)
|
|
|
|
{
|
|
|
|
JOptionPane.showMessageDialog(
|
|
|
|
TimelineWindow.this,
|
|
|
|
"Failed to fetch page.."
|
|
|
|
+ "\n" + json.get("error").value
|
|
|
|
+ "\n(HTTP code: " + httpCode + ")"
|
|
|
|
);
|
|
|
|
}
|
2022-04-14 06:38:49 +02:00
|
|
|
|
2022-05-02 03:44:10 +02:00
|
|
|
public void
|
|
|
|
requestSucceeded(Tree<String> json)
|
|
|
|
{
|
2022-05-06 09:05:01 +02:00
|
|
|
if (json.size() < PREVIEW_COUNT)
|
|
|
|
{
|
2022-05-02 03:44:10 +02:00
|
|
|
showLatestPage();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
readEntity(json);
|
|
|
|
}
|
2022-04-14 06:38:49 +02:00
|
|
|
|
2022-05-02 03:44:10 +02:00
|
|
|
}
|
|
|
|
);
|
|
|
|
display.setCursor(null);
|
|
|
|
}
|
2021-07-29 11:40:52 +02:00
|
|
|
|
2022-05-02 03:44:10 +02:00
|
|
|
public void
|
2022-04-12 08:37:39 +02:00
|
|
|
showLatestPage()
|
|
|
|
{
|
|
|
|
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
|
|
|
api.getTimelinePage(
|
2022-04-16 16:54:17 +02:00
|
|
|
page.type,
|
|
|
|
PREVIEW_COUNT, null, null,
|
|
|
|
page.accountNumId, page.listId,
|
2022-04-12 08:37:39 +02:00
|
|
|
new RequestListener() {
|
|
|
|
|
|
|
|
public void
|
|
|
|
connectionFailed(IOException eIo)
|
|
|
|
{
|
2022-04-28 13:36:45 +02:00
|
|
|
JOptionPane.showMessageDialog(
|
|
|
|
TimelineWindow.this,
|
|
|
|
"Failed to fetch page.."
|
|
|
|
+ "\n" + eIo.getMessage()
|
|
|
|
);
|
2022-04-12 08:37:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void
|
|
|
|
requestFailed(int httpCode, Tree<String> json)
|
|
|
|
{
|
2022-04-28 13:36:45 +02:00
|
|
|
JOptionPane.showMessageDialog(
|
|
|
|
TimelineWindow.this,
|
|
|
|
"Failed to fetch page.."
|
|
|
|
+ "\n" + json.get("error").value
|
|
|
|
+ "\n(HTTP code: " + httpCode + ")"
|
|
|
|
);
|
2022-04-12 08:37:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void
|
|
|
|
requestSucceeded(Tree<String> json)
|
|
|
|
{
|
2022-05-02 03:44:10 +02:00
|
|
|
readEntity(json);
|
2022-05-02 05:05:36 +02:00
|
|
|
showingLatest = true;
|
|
|
|
windowUpdater.add(TimelineWindow.this);
|
2022-04-12 08:37:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
);
|
|
|
|
display.setCursor(null);
|
2021-07-17 11:46:27 +02:00
|
|
|
}
|
|
|
|
|
2021-07-31 13:28:46 +02:00
|
|
|
public void
|
2022-05-02 03:44:10 +02:00
|
|
|
showNextPage()
|
2021-07-31 13:28:46 +02:00
|
|
|
{
|
2022-04-12 08:37:39 +02:00
|
|
|
assert page.posts != null;
|
2022-05-06 09:05:01 +02:00
|
|
|
assert page.posts.length != 0;
|
|
|
|
String lastId = page.posts[page.posts.length - 1].id;
|
2022-04-12 08:37:39 +02:00
|
|
|
|
2022-05-03 14:40:20 +02:00
|
|
|
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
2022-04-12 08:37:39 +02:00
|
|
|
api.getTimelinePage(
|
2022-04-16 16:54:17 +02:00
|
|
|
page.type,
|
2022-04-23 16:19:43 +02:00
|
|
|
PREVIEW_COUNT, lastId, null,
|
2022-04-16 16:54:17 +02:00
|
|
|
page.accountNumId, page.listId,
|
2022-04-12 08:37:39 +02:00
|
|
|
new RequestListener() {
|
|
|
|
|
|
|
|
public void
|
|
|
|
connectionFailed(IOException eIo)
|
|
|
|
{
|
2022-04-28 13:36:45 +02:00
|
|
|
JOptionPane.showMessageDialog(
|
|
|
|
TimelineWindow.this,
|
|
|
|
"Failed to fetch page.."
|
|
|
|
+ "\n" + eIo.getMessage()
|
|
|
|
);
|
2022-04-12 08:37:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void
|
|
|
|
requestFailed(int httpCode, Tree<String> json)
|
|
|
|
{
|
2022-04-28 13:36:45 +02:00
|
|
|
JOptionPane.showMessageDialog(
|
|
|
|
TimelineWindow.this,
|
|
|
|
"Failed to fetch page.."
|
|
|
|
+ "\n" + json.get("error").value
|
|
|
|
+ "\n(HTTP code: " + httpCode + ")"
|
|
|
|
);
|
2022-04-12 08:37:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void
|
|
|
|
requestSucceeded(Tree<String> json)
|
|
|
|
{
|
2022-05-06 09:05:01 +02:00
|
|
|
if (json.size() == 0)
|
|
|
|
{
|
2022-04-14 06:38:49 +02:00
|
|
|
// We should probably say something
|
|
|
|
// to the user here? For now, we
|
|
|
|
// quietly cancel.
|
|
|
|
return;
|
|
|
|
}
|
2022-05-03 14:40:20 +02:00
|
|
|
readEntity(json);
|
2022-05-02 05:05:36 +02:00
|
|
|
showingLatest = false;
|
|
|
|
windowUpdater.remove(TimelineWindow.this);
|
2022-04-12 08:37:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
);
|
|
|
|
display.setCursor(null);
|
2021-07-31 13:28:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void
|
2022-05-02 03:44:10 +02:00
|
|
|
showPreviousPage()
|
2021-07-31 13:28:46 +02:00
|
|
|
{
|
2022-04-12 08:37:39 +02:00
|
|
|
assert page.posts != null;
|
2022-05-06 09:05:01 +02:00
|
|
|
assert page.posts.length != 0;
|
|
|
|
String firstId = page.posts[0].id;
|
2022-04-12 08:37:39 +02:00
|
|
|
|
|
|
|
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
|
|
|
api.getTimelinePage(
|
2022-04-16 16:54:17 +02:00
|
|
|
page.type,
|
2022-04-23 16:19:43 +02:00
|
|
|
PREVIEW_COUNT, null, firstId,
|
2022-04-16 16:54:17 +02:00
|
|
|
page.accountNumId, page.listId,
|
2022-04-12 08:37:39 +02:00
|
|
|
new RequestListener() {
|
|
|
|
|
|
|
|
public void
|
|
|
|
connectionFailed(IOException eIo)
|
|
|
|
{
|
2022-04-28 13:36:45 +02:00
|
|
|
JOptionPane.showMessageDialog(
|
|
|
|
TimelineWindow.this,
|
|
|
|
"Failed to fetch page.."
|
|
|
|
+ "\n" + eIo.getMessage()
|
|
|
|
);
|
2022-04-12 08:37:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void
|
|
|
|
requestFailed(int httpCode, Tree<String> json)
|
|
|
|
{
|
2022-04-28 13:36:45 +02:00
|
|
|
JOptionPane.showMessageDialog(
|
|
|
|
TimelineWindow.this,
|
|
|
|
"Failed to fetch page.."
|
|
|
|
+ "\n" + json.get("error").value
|
|
|
|
+ "\n(HTTP code: " + httpCode + ")"
|
|
|
|
);
|
2022-04-12 08:37:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void
|
|
|
|
requestSucceeded(Tree<String> json)
|
|
|
|
{
|
2022-05-06 09:05:01 +02:00
|
|
|
if (json.size() < PREVIEW_COUNT)
|
|
|
|
{
|
2022-04-14 06:38:49 +02:00
|
|
|
showLatestPage();
|
|
|
|
return;
|
|
|
|
}
|
2022-05-02 03:44:10 +02:00
|
|
|
readEntity(json);
|
2022-05-02 05:05:36 +02:00
|
|
|
showingLatest = false;
|
|
|
|
windowUpdater.remove(TimelineWindow.this);
|
2022-04-12 08:37:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
);
|
|
|
|
display.setCursor(null);
|
2021-07-31 13:28:46 +02:00
|
|
|
}
|
|
|
|
|
2022-05-06 22:34:22 +02:00
|
|
|
public synchronized void
|
2022-05-02 03:44:10 +02:00
|
|
|
setTimelineType(TimelineType type)
|
|
|
|
{
|
2022-05-06 09:05:01 +02:00
|
|
|
assert type != TimelineType.LIST;
|
|
|
|
assert type != TimelineType.PROFILE;
|
|
|
|
|
2022-05-02 03:44:10 +02:00
|
|
|
page.type = type;
|
|
|
|
page.accountNumId = null;
|
2022-05-06 09:05:01 +02:00
|
|
|
setTitle(toString(type) + " timeline - JKomasto");
|
2022-05-02 03:44:10 +02:00
|
|
|
|
2022-05-06 09:05:01 +02:00
|
|
|
String f = type.toString().toLowerCase();
|
|
|
|
display.setBackgroundImage(ImageApi.local(f));
|
2022-05-06 15:57:17 +02:00
|
|
|
/*
|
|
|
|
* (注) Java's image renderer draws images with transparency
|
|
|
|
* darker than GIMP does. Overcompensate in lightening.
|
|
|
|
*/
|
2022-05-06 09:05:01 +02:00
|
|
|
display.repaint();
|
2022-05-02 03:44:10 +02:00
|
|
|
}
|
|
|
|
|
2022-05-06 22:34:22 +02:00
|
|
|
public synchronized TimelineType
|
2022-05-02 03:44:10 +02:00
|
|
|
getTimelineType() { return page.type; }
|
|
|
|
|
2022-05-06 22:34:22 +02:00
|
|
|
public synchronized void
|
2022-05-02 03:44:10 +02:00
|
|
|
showAuthorPosts(String authorNumId)
|
|
|
|
{
|
|
|
|
assert authorNumId != null;
|
|
|
|
|
2022-05-06 09:05:01 +02:00
|
|
|
page.type = TimelineType.PROFILE;
|
2022-05-02 03:44:10 +02:00
|
|
|
page.accountNumId = authorNumId;
|
|
|
|
setTitle(authorNumId + " - JKomasto");
|
2022-05-06 09:05:01 +02:00
|
|
|
|
2022-05-02 03:44:10 +02:00
|
|
|
display.setBackgroundImage(ImageApi.local("profile"));
|
2022-05-06 09:05:01 +02:00
|
|
|
display.repaint();
|
2022-05-02 03:44:10 +02:00
|
|
|
}
|
|
|
|
|
2022-05-06 22:34:22 +02:00
|
|
|
public synchronized void
|
2022-04-19 16:08:20 +02:00
|
|
|
openOwnProfile()
|
|
|
|
{
|
2022-05-06 22:34:22 +02:00
|
|
|
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
|
|
|
|
2022-05-13 16:32:11 +02:00
|
|
|
Tree<String> accountDetails = api.getAccountDetails();
|
|
|
|
ProfileWindow w = new ProfileWindow(primaire);
|
|
|
|
w.use(new Account(accountDetails));
|
|
|
|
w.setLocationByPlatform(true);
|
2022-04-19 16:08:20 +02:00
|
|
|
w.setVisible(true);
|
2022-05-06 22:34:22 +02:00
|
|
|
|
|
|
|
display.setCursor(null);
|
2022-04-19 16:08:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void
|
|
|
|
openProfile()
|
|
|
|
{
|
|
|
|
String query = JOptionPane.showInputDialog(
|
|
|
|
this,
|
|
|
|
"Whose account do you want to see?\n"
|
|
|
|
+ "Type an account name with the instance,\n"
|
|
|
|
+ "or a display name if you can't remember.\n",
|
|
|
|
"Profile search",
|
|
|
|
JOptionPane.PLAIN_MESSAGE
|
|
|
|
);
|
|
|
|
if (query == null) return;
|
|
|
|
|
|
|
|
class Handler implements RequestListener {
|
|
|
|
|
|
|
|
public Tree<String>
|
|
|
|
json;
|
2022-05-02 03:44:10 +02:00
|
|
|
|
2022-04-19 16:08:20 +02:00
|
|
|
// -=%=-
|
|
|
|
|
|
|
|
public void
|
|
|
|
connectionFailed(IOException eIo)
|
|
|
|
{
|
|
|
|
JOptionPane.showMessageDialog(
|
|
|
|
TimelineWindow.this,
|
|
|
|
"Tried to fetch accounts, but it failed.."
|
|
|
|
+ "\n" + eIo.getMessage()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void
|
|
|
|
requestFailed(int httpCode, Tree<String> json)
|
|
|
|
{
|
|
|
|
JOptionPane.showMessageDialog(
|
|
|
|
TimelineWindow.this,
|
|
|
|
"Tried to fetch accounts, but it failed.."
|
|
|
|
+ "\n" + json.get("error").value
|
|
|
|
+ "\n(HTTP code: " + httpCode + ")"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void
|
|
|
|
requestSucceeded(Tree<String> json) { this.json = json; }
|
|
|
|
|
|
|
|
}
|
|
|
|
// (知) Have to create a named class because
|
|
|
|
// it has to hold the variable.
|
|
|
|
Handler handler = new Handler();
|
|
|
|
api.getAccounts(query, handler);
|
|
|
|
if (handler.json == null) return;
|
|
|
|
|
2022-05-06 09:05:01 +02:00
|
|
|
if (handler.json.size() == 0)
|
|
|
|
{
|
2022-04-19 16:08:20 +02:00
|
|
|
JOptionPane.showMessageDialog(
|
|
|
|
this,
|
|
|
|
"There were no results from the query.. ☹️"
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-05-13 16:32:11 +02:00
|
|
|
Tree<String> openee = null;
|
2022-04-19 16:08:20 +02:00
|
|
|
if (query.startsWith("@")) query = query.substring(1);
|
|
|
|
|
|
|
|
List<Object> message = new ArrayList<>();
|
|
|
|
message.add("Maybe one of these?");
|
|
|
|
ButtonGroup selGroup = new ButtonGroup();
|
|
|
|
for (Tree<String> account: handler.json)
|
|
|
|
{
|
|
|
|
String dname = account.get("display_name").value;
|
|
|
|
String acct = account.get("acct").value;
|
|
|
|
if (query.equals(acct)) {
|
2022-05-13 16:32:11 +02:00
|
|
|
openee = account;
|
2022-04-19 16:08:20 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
JRadioButton b = new JRadioButton();
|
|
|
|
b.setText(dname + " (" + acct + ")");
|
|
|
|
selGroup.add(b);
|
|
|
|
message.add(b);
|
|
|
|
}
|
2022-05-13 16:32:11 +02:00
|
|
|
if (openee == null)
|
2022-04-19 16:08:20 +02:00
|
|
|
{
|
|
|
|
int response = JOptionPane.showConfirmDialog(
|
|
|
|
this,
|
|
|
|
message.toArray(),
|
|
|
|
"Search results",
|
|
|
|
JOptionPane.OK_CANCEL_OPTION
|
|
|
|
);
|
|
|
|
if (response == JOptionPane.CANCEL_OPTION) return;
|
|
|
|
for (int o = 1; o < message.size(); ++o)
|
|
|
|
{
|
|
|
|
JRadioButton b = (JRadioButton)message.get(o);
|
|
|
|
if (selGroup.isSelected(b.getModel()))
|
|
|
|
{
|
2022-05-13 16:32:11 +02:00
|
|
|
openee = handler.json.get(o - 1);
|
2022-04-19 16:08:20 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2022-05-13 16:32:11 +02:00
|
|
|
if (openee == null) return;
|
2022-05-06 09:05:01 +02:00
|
|
|
/*
|
|
|
|
* It seems like this can happen if someone
|
|
|
|
* presses escape out of the confirm dialog.
|
|
|
|
* I don't know why that doesn't map to cancel.
|
|
|
|
*/
|
2022-04-19 16:08:20 +02:00
|
|
|
}
|
|
|
|
|
2022-05-13 16:32:11 +02:00
|
|
|
ProfileWindow w = new ProfileWindow(primaire);
|
|
|
|
w.use(new Account(openee));
|
|
|
|
w.setLocationByPlatform(true);
|
2022-04-19 16:08:20 +02:00
|
|
|
w.setVisible(true);
|
|
|
|
}
|
|
|
|
|
2021-07-31 06:01:20 +02:00
|
|
|
// - -%- -
|
|
|
|
|
2022-05-06 22:34:22 +02:00
|
|
|
public synchronized void
|
2022-05-02 03:44:10 +02:00
|
|
|
previewSelected(int index)
|
2022-04-12 08:37:39 +02:00
|
|
|
{
|
2022-05-06 09:05:01 +02:00
|
|
|
if (index > page.posts.length) return;
|
|
|
|
Post post = page.posts[index - 1];
|
|
|
|
primaire.getAutoViewWindow().use(post);
|
2022-04-12 08:37:39 +02:00
|
|
|
}
|
|
|
|
|
2022-05-06 22:34:22 +02:00
|
|
|
public synchronized void
|
2022-05-02 03:44:10 +02:00
|
|
|
previewOpened(int index)
|
2022-04-14 06:38:49 +02:00
|
|
|
{
|
2022-05-06 09:05:01 +02:00
|
|
|
if (index > page.posts.length) return;
|
|
|
|
Post post = page.posts[index - 1];
|
|
|
|
|
2022-04-14 06:38:49 +02:00
|
|
|
PostWindow w = new PostWindow(primaire);
|
2022-05-06 09:05:01 +02:00
|
|
|
w.use(post);
|
2022-04-14 06:38:49 +02:00
|
|
|
w.setLocationRelativeTo(this);
|
|
|
|
w.setVisible(true);
|
|
|
|
}
|
|
|
|
|
2021-07-31 06:01:20 +02:00
|
|
|
public void
|
|
|
|
actionPerformed(ActionEvent eA)
|
|
|
|
{
|
|
|
|
Object src = eA.getSource();
|
|
|
|
|
2022-04-12 08:37:39 +02:00
|
|
|
if (src == openHome)
|
|
|
|
{
|
|
|
|
setTimelineType(TimelineType.HOME);
|
|
|
|
showLatestPage();
|
|
|
|
}
|
|
|
|
if (src == openFederated)
|
|
|
|
{
|
|
|
|
setTimelineType(TimelineType.FEDERATED);
|
|
|
|
showLatestPage();
|
|
|
|
}
|
|
|
|
if (src == openLocal)
|
|
|
|
{
|
|
|
|
setTimelineType(TimelineType.LOCAL);
|
|
|
|
showLatestPage();
|
2022-04-19 16:08:20 +02:00
|
|
|
}
|
|
|
|
if (src == openOwnProfile)
|
|
|
|
{
|
|
|
|
openOwnProfile();
|
|
|
|
}
|
|
|
|
if (src == openProfile)
|
|
|
|
{
|
|
|
|
openProfile();
|
2022-04-12 08:37:39 +02:00
|
|
|
}
|
|
|
|
if (src == createPost)
|
|
|
|
{
|
|
|
|
primaire.getComposeWindow().setVisible(true);
|
|
|
|
}
|
|
|
|
if (src == openAutoPostView)
|
|
|
|
{
|
|
|
|
PostWindow w = primaire.getAutoViewWindow();
|
|
|
|
w.setLocation(getX() + 10 + getWidth(), getY());
|
|
|
|
w.setVisible(true);
|
2022-04-16 16:54:17 +02:00
|
|
|
}
|
|
|
|
if (src == openNotifications)
|
|
|
|
{
|
2022-05-06 09:05:01 +02:00
|
|
|
NotificationsWindow w =
|
|
|
|
primaire.getNotificationsWindow();
|
|
|
|
if (!w.isVisible())
|
|
|
|
{
|
2022-05-03 14:40:20 +02:00
|
|
|
w.setLocationByPlatform(true);
|
|
|
|
w.setVisible(true);
|
|
|
|
}
|
2022-04-12 08:37:39 +02:00
|
|
|
}
|
|
|
|
if (src == flipToNewestPost)
|
|
|
|
{
|
|
|
|
showLatestPage();
|
|
|
|
}
|
2021-07-31 06:01:20 +02:00
|
|
|
if (src == quit)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Umm.. should we even have a quit option?
|
|
|
|
* Wouldn't closing every window work? By
|
|
|
|
* disposing of everyone. We won't be having any
|
|
|
|
* background threads IIRC (and they can check
|
|
|
|
* if the Swing thread is alive).
|
|
|
|
*/
|
2022-04-12 08:37:39 +02:00
|
|
|
dispose();
|
2021-07-31 06:01:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-06 22:34:22 +02:00
|
|
|
// - -%- -
|
|
|
|
|
2022-04-12 08:37:39 +02:00
|
|
|
private static String
|
2022-05-06 09:05:01 +02:00
|
|
|
toString(TimelineType type)
|
2022-04-12 08:37:39 +02:00
|
|
|
{
|
2022-05-06 09:05:01 +02:00
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case FEDERATED: return "Federated";
|
|
|
|
case LOCAL: return "Local";
|
|
|
|
case HOME: return "Home";
|
|
|
|
default: return "";
|
2022-04-12 08:37:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-16 01:07:23 +02:00
|
|
|
// ---%-@-%---
|
|
|
|
|
2022-04-12 08:37:39 +02:00
|
|
|
TimelineWindow(JKomasto primaire)
|
2021-07-16 01:07:23 +02:00
|
|
|
{
|
2022-04-12 08:37:39 +02:00
|
|
|
this.primaire = primaire;
|
|
|
|
this.api = primaire.getMastodonApi();
|
2022-05-02 05:05:36 +02:00
|
|
|
this.windowUpdater = primaire.getWindowUpdater();
|
2022-04-12 08:37:39 +02:00
|
|
|
|
|
|
|
getContentPane().setPreferredSize(new Dimension(320, 460));
|
2021-07-16 11:46:17 +02:00
|
|
|
pack();
|
|
|
|
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
|
|
|
|
2021-07-31 06:01:20 +02:00
|
|
|
openHome = new JMenuItem("Open home timeline");
|
|
|
|
openFederated = new JMenuItem("Open federated timeline");
|
2022-04-16 16:54:17 +02:00
|
|
|
openNotifications = new JMenuItem("Open notifications");
|
2022-04-19 16:08:20 +02:00
|
|
|
openOwnProfile = new JMenuItem("Open own profile");
|
|
|
|
openProfile = new JMenuItem("Open profile..");
|
2021-07-31 06:01:20 +02:00
|
|
|
createPost = new JMenuItem("Create a post");
|
|
|
|
openAutoPostView = new JMenuItem("Open auto post view");
|
|
|
|
quit = new JMenuItem("Quit");
|
|
|
|
openHome.addActionListener(this);
|
|
|
|
openFederated.addActionListener(this);
|
2022-04-16 16:54:17 +02:00
|
|
|
openNotifications.addActionListener(this);
|
2022-04-19 16:08:20 +02:00
|
|
|
openOwnProfile.addActionListener(this);
|
|
|
|
openProfile.addActionListener(this);
|
2021-07-31 06:01:20 +02:00
|
|
|
createPost.addActionListener(this);
|
|
|
|
openAutoPostView.addActionListener(this);
|
|
|
|
quit.addActionListener(this);
|
|
|
|
|
|
|
|
flipToNewestPost = new JMenuItem("Flip to newest post");
|
|
|
|
flipToNewestPost.addActionListener(this);
|
|
|
|
|
2021-07-17 12:03:59 +02:00
|
|
|
JMenu programMenu = new JMenu("Program");
|
2022-04-14 06:38:49 +02:00
|
|
|
programMenu.setMnemonic(KeyEvent.VK_P);
|
2021-07-31 06:01:20 +02:00
|
|
|
programMenu.add(openHome);
|
|
|
|
programMenu.add(openFederated);
|
2022-04-16 16:54:17 +02:00
|
|
|
programMenu.add(openNotifications);
|
2022-04-19 16:08:20 +02:00
|
|
|
programMenu.add(openOwnProfile);
|
|
|
|
programMenu.add(openProfile);
|
2021-07-17 12:03:59 +02:00
|
|
|
programMenu.add(new JSeparator());
|
2021-07-31 06:01:20 +02:00
|
|
|
programMenu.add(createPost);
|
|
|
|
programMenu.add(openAutoPostView);
|
2021-07-17 12:03:59 +02:00
|
|
|
programMenu.add(new JSeparator());
|
2021-07-31 06:01:20 +02:00
|
|
|
programMenu.add(quit);
|
2021-07-17 12:03:59 +02:00
|
|
|
JMenu timelineMenu = new JMenu("Timeline");
|
2022-04-14 06:38:49 +02:00
|
|
|
timelineMenu.setMnemonic(KeyEvent.VK_T);
|
2021-07-31 06:01:20 +02:00
|
|
|
timelineMenu.add(flipToNewestPost);
|
2021-07-17 12:03:59 +02:00
|
|
|
JMenuBar menuBar = new JMenuBar();
|
|
|
|
menuBar.add(programMenu);
|
|
|
|
menuBar.add(timelineMenu);
|
|
|
|
setJMenuBar(menuBar);
|
|
|
|
|
2022-04-12 08:37:39 +02:00
|
|
|
page = new TimelinePage();
|
2022-05-06 09:05:01 +02:00
|
|
|
page.posts = new Post[0];
|
2022-04-12 08:37:39 +02:00
|
|
|
|
2021-07-31 06:01:20 +02:00
|
|
|
display = new TimelineComponent(this);
|
2022-04-12 08:37:39 +02:00
|
|
|
display.setNextPageAvailable(false);
|
|
|
|
display.setPreviousPageAvailable(false);
|
2021-07-17 12:03:59 +02:00
|
|
|
setContentPane(display);
|
2022-04-12 14:48:24 +02:00
|
|
|
|
|
|
|
setTimelineType(TimelineType.HOME);
|
2022-05-06 15:57:17 +02:00
|
|
|
setIconImage(primaire.getProgramIcon());
|
2021-07-16 01:07:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class
|
2021-07-31 06:01:20 +02:00
|
|
|
TimelineComponent extends JPanel
|
2022-04-14 06:38:49 +02:00
|
|
|
implements
|
|
|
|
ActionListener, KeyListener,
|
|
|
|
MouseListener, FocusListener {
|
2021-07-31 06:01:20 +02:00
|
|
|
|
|
|
|
private TimelineWindow
|
|
|
|
primaire;
|
|
|
|
|
|
|
|
// - -%- -
|
2021-07-16 01:07:23 +02:00
|
|
|
|
2021-07-17 11:06:50 +02:00
|
|
|
private JButton
|
|
|
|
next, prev;
|
|
|
|
|
|
|
|
private JLabel
|
|
|
|
pageLabel;
|
|
|
|
|
2021-07-17 11:46:27 +02:00
|
|
|
private final List<PostPreviewComponent>
|
2022-04-12 08:37:39 +02:00
|
|
|
postPreviews;
|
|
|
|
|
|
|
|
private boolean
|
|
|
|
hoverSelect;
|
|
|
|
|
2022-04-12 14:48:24 +02:00
|
|
|
private Image
|
|
|
|
backgroundImage;
|
|
|
|
|
2022-04-12 08:37:39 +02:00
|
|
|
// - -%- -
|
|
|
|
|
|
|
|
static final int
|
|
|
|
PREVIEW_COUNT = 10;
|
2021-07-17 11:06:50 +02:00
|
|
|
|
2021-07-29 11:40:52 +02:00
|
|
|
// ---%-@-%---
|
|
|
|
|
2022-05-02 03:44:10 +02:00
|
|
|
public List<PostPreviewComponent>
|
|
|
|
getPostPreviews()
|
|
|
|
{
|
|
|
|
return postPreviews;
|
|
|
|
}
|
2021-07-31 06:01:20 +02:00
|
|
|
|
|
|
|
public void
|
|
|
|
setPageLabel(String label)
|
|
|
|
{
|
|
|
|
assert label != null;
|
|
|
|
pageLabel.setText("" + label);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void
|
2022-04-12 14:48:24 +02:00
|
|
|
setNextPageAvailable(boolean n) { next.setEnabled(n); }
|
2021-07-31 06:01:20 +02:00
|
|
|
|
|
|
|
public void
|
2022-04-12 14:48:24 +02:00
|
|
|
setPreviousPageAvailable(boolean n) { prev.setEnabled(n); }
|
2021-07-31 06:01:20 +02:00
|
|
|
|
2022-04-12 08:37:39 +02:00
|
|
|
public void
|
2022-04-12 14:48:24 +02:00
|
|
|
setHoverSelect(boolean n) { hoverSelect = n; }
|
|
|
|
|
|
|
|
public void
|
|
|
|
setBackgroundImage(Image n) { backgroundImage = n; }
|
2022-04-12 08:37:39 +02:00
|
|
|
|
2022-04-14 06:38:49 +02:00
|
|
|
public void
|
|
|
|
resetFocus() { postPreviews.get(0).requestFocusInWindow(); }
|
|
|
|
|
2022-04-12 14:48:24 +02:00
|
|
|
// - -%- -
|
|
|
|
|
|
|
|
protected void
|
|
|
|
paintComponent(Graphics g)
|
|
|
|
{
|
2022-05-02 03:44:10 +02:00
|
|
|
int w = getWidth(), h = getHeight();
|
2022-04-12 14:48:24 +02:00
|
|
|
g.clearRect(0, 0, w, h);
|
2022-05-06 15:57:17 +02:00
|
|
|
|
|
|
|
if (backgroundImage != null)
|
|
|
|
{
|
|
|
|
int b = h * 5 / 10;
|
|
|
|
int iw = backgroundImage.getWidth(this);
|
|
|
|
int ih = backgroundImage.getHeight(this);
|
|
|
|
if (ih > iw) {
|
|
|
|
ih = ih * b / iw;
|
|
|
|
iw = b;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
iw = iw * b / ih;
|
|
|
|
ih = b;
|
|
|
|
}
|
|
|
|
int x = w - iw, y = h - ih;
|
|
|
|
g.drawImage(backgroundImage, x, y, iw, ih, this);
|
|
|
|
}
|
2022-05-02 03:44:10 +02:00
|
|
|
|
|
|
|
((java.awt.Graphics2D)g).setRenderingHint(
|
|
|
|
java.awt.RenderingHints.KEY_ANTIALIASING,
|
|
|
|
java.awt.RenderingHints.VALUE_ANTIALIAS_ON
|
|
|
|
);
|
2022-04-12 14:48:24 +02:00
|
|
|
}
|
2021-07-31 06:01:20 +02:00
|
|
|
|
2022-04-14 06:38:49 +02:00
|
|
|
private void
|
|
|
|
select(Object c)
|
|
|
|
{
|
|
|
|
assert c instanceof PostPreviewComponent;
|
|
|
|
|
2022-05-02 03:44:10 +02:00
|
|
|
for (PostPreviewComponent p: postPreviews)
|
|
|
|
{
|
|
|
|
if (p == c) {
|
|
|
|
int index = 1 + postPreviews.indexOf(p);
|
|
|
|
primaire.previewSelected(index);
|
|
|
|
p.setSelected(true);
|
|
|
|
p.repaint();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
p.setSelected(false);
|
|
|
|
p.repaint();
|
|
|
|
}
|
|
|
|
}
|
2022-04-14 06:38:49 +02:00
|
|
|
}
|
2022-04-12 08:37:39 +02:00
|
|
|
|
2022-04-14 06:38:49 +02:00
|
|
|
private void
|
|
|
|
deselect(Object c)
|
|
|
|
{
|
|
|
|
assert c instanceof PostPreviewComponent;
|
|
|
|
PostPreviewComponent p = (PostPreviewComponent)c;
|
|
|
|
|
|
|
|
p.setSelected(false);
|
|
|
|
p.repaint();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void
|
|
|
|
open(Object c)
|
|
|
|
{
|
2022-05-02 03:44:10 +02:00
|
|
|
int o = postPreviews.indexOf(c);
|
2022-04-23 16:19:43 +02:00
|
|
|
assert o != -1;
|
2022-05-02 03:44:10 +02:00
|
|
|
primaire.previewOpened(1 + o);
|
2022-04-14 06:38:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void
|
|
|
|
focusGained(FocusEvent eF) { select(eF.getSource()); }
|
2022-04-12 08:37:39 +02:00
|
|
|
|
|
|
|
public void
|
2022-04-14 06:38:49 +02:00
|
|
|
focusLost(FocusEvent eF) { deselect(eF.getSource()); }
|
|
|
|
|
|
|
|
public void
|
2022-04-12 08:37:39 +02:00
|
|
|
mouseClicked(MouseEvent eM)
|
|
|
|
{
|
2022-04-14 06:38:49 +02:00
|
|
|
if (eM.getClickCount() == 2) open(eM.getSource());
|
|
|
|
else select(eM.getSource());
|
|
|
|
}
|
|
|
|
|
|
|
|
public void
|
|
|
|
mouseEntered(MouseEvent eM)
|
|
|
|
{
|
|
|
|
if (!hoverSelect) return;
|
|
|
|
mouseClicked(eM);
|
2022-04-12 08:37:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void
|
|
|
|
mouseExited(MouseEvent eM)
|
|
|
|
{
|
|
|
|
if (!hoverSelect) return;
|
2022-04-14 06:38:49 +02:00
|
|
|
deselect(eM.getSource());
|
2021-07-31 06:01:20 +02:00
|
|
|
}
|
|
|
|
|
2022-04-14 06:38:49 +02:00
|
|
|
// (知) First time I'm using these two..!
|
2022-04-12 08:37:39 +02:00
|
|
|
|
|
|
|
public void
|
2022-04-14 06:38:49 +02:00
|
|
|
keyPressed(KeyEvent eK)
|
|
|
|
{
|
|
|
|
if (eK.getKeyCode() != KeyEvent.VK_ENTER) return;
|
|
|
|
|
|
|
|
PostPreviewComponent selected = null;
|
|
|
|
for (PostPreviewComponent c: postPreviews)
|
2022-05-02 03:44:10 +02:00
|
|
|
if (c.isSelected()) selected = c;
|
2022-04-14 06:38:49 +02:00
|
|
|
|
|
|
|
if (selected == null) return;
|
|
|
|
open(selected);
|
|
|
|
}
|
2022-04-12 08:37:39 +02:00
|
|
|
|
2021-07-31 06:01:20 +02:00
|
|
|
public void
|
|
|
|
actionPerformed(ActionEvent eA)
|
|
|
|
{
|
|
|
|
Object src = eA.getSource();
|
|
|
|
|
2022-05-02 03:44:10 +02:00
|
|
|
if (src == next) primaire.showNextPage();
|
|
|
|
else if (src == prev) primaire.showPreviousPage();
|
2021-07-29 11:40:52 +02:00
|
|
|
/*
|
2021-07-31 06:01:20 +02:00
|
|
|
* I think the page previews will just forward to us.
|
|
|
|
* But I think they'll have to tell us where they are
|
|
|
|
* in the list somehow, because we need to show only
|
|
|
|
* one post as selected.
|
2021-07-29 11:40:52 +02:00
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
2022-04-14 06:38:49 +02:00
|
|
|
|
|
|
|
public void
|
|
|
|
mousePressed(MouseEvent eM) { }
|
|
|
|
|
|
|
|
public void
|
|
|
|
mouseReleased(MouseEvent eM) { }
|
|
|
|
|
|
|
|
public void
|
|
|
|
keyTyped(KeyEvent eK) { }
|
|
|
|
|
|
|
|
public void
|
|
|
|
keyReleased(KeyEvent eK) { }
|
|
|
|
|
2021-07-17 11:06:50 +02:00
|
|
|
// ---%-@-%---
|
|
|
|
|
2021-07-31 06:01:20 +02:00
|
|
|
TimelineComponent(TimelineWindow primaire)
|
2021-07-16 01:07:23 +02:00
|
|
|
{
|
2021-07-31 06:01:20 +02:00
|
|
|
this.primaire = primaire;
|
|
|
|
|
2022-04-12 08:37:39 +02:00
|
|
|
postPreviews = new ArrayList<>(PREVIEW_COUNT);
|
|
|
|
hoverSelect = true;
|
|
|
|
|
2021-07-17 11:06:50 +02:00
|
|
|
prev = new JButton("<");
|
|
|
|
next = new JButton(">");
|
2021-07-31 06:01:20 +02:00
|
|
|
prev.setEnabled(false);
|
|
|
|
next.setEnabled(false);
|
2022-04-14 06:38:49 +02:00
|
|
|
prev.setMnemonic(KeyEvent.VK_PAGE_UP);
|
|
|
|
next.setMnemonic(KeyEvent.VK_PAGE_DOWN);
|
2022-04-12 08:37:39 +02:00
|
|
|
prev.addActionListener(this);
|
|
|
|
next.addActionListener(this);
|
2021-07-31 06:01:20 +02:00
|
|
|
|
|
|
|
pageLabel = new JLabel("0");
|
2021-07-17 11:06:50 +02:00
|
|
|
|
|
|
|
Box bottom = Box.createHorizontalBox();
|
2022-05-06 09:05:01 +02:00
|
|
|
bottom.setBackground(null);
|
|
|
|
bottom.setOpaque(false);
|
2021-07-17 11:06:50 +02:00
|
|
|
bottom.add(Box.createGlue());
|
|
|
|
bottom.add(prev);
|
|
|
|
bottom.add(Box.createHorizontalStrut(8));
|
|
|
|
bottom.add(next);
|
|
|
|
|
|
|
|
JPanel centre = new JPanel();
|
|
|
|
centre.setOpaque(false);
|
|
|
|
centre.setLayout(new GridBagLayout());
|
|
|
|
GridBagConstraints constraints = new GridBagConstraints();
|
|
|
|
constraints.fill = GridBagConstraints.HORIZONTAL;
|
|
|
|
constraints.gridx = 0;
|
|
|
|
constraints.weightx = 1;
|
|
|
|
constraints.insets = new Insets(4, 0, 4, 0);
|
2022-04-12 08:37:39 +02:00
|
|
|
for (int n = PREVIEW_COUNT; n > 0; --n)
|
2021-07-17 11:06:50 +02:00
|
|
|
{
|
2022-04-12 08:37:39 +02:00
|
|
|
PostPreviewComponent c = new PostPreviewComponent();
|
|
|
|
c.reset();
|
|
|
|
c.addMouseListener(this);
|
2022-04-14 06:38:49 +02:00
|
|
|
c.addFocusListener(this);
|
|
|
|
c.addKeyListener(this);
|
2022-04-12 08:37:39 +02:00
|
|
|
centre.add(c, constraints);
|
|
|
|
postPreviews.add(c);
|
2021-07-17 11:06:50 +02:00
|
|
|
}
|
|
|
|
|
2021-07-16 01:07:23 +02:00
|
|
|
setLayout(new BorderLayout());
|
2021-07-17 11:06:50 +02:00
|
|
|
add(centre, BorderLayout.CENTER);
|
|
|
|
add(bottom, BorderLayout.SOUTH);
|
|
|
|
|
|
|
|
setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
|
2021-07-16 01:07:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-07-16 00:37:03 +02:00
|
|
|
class
|
2021-07-17 11:46:27 +02:00
|
|
|
PostPreviewComponent extends JComponent {
|
2021-07-16 00:37:03 +02:00
|
|
|
|
|
|
|
private JLabel
|
|
|
|
topLeft, topRight, bottom;
|
|
|
|
|
2022-04-12 14:48:24 +02:00
|
|
|
private boolean
|
|
|
|
selected;
|
|
|
|
|
2021-07-16 00:37:03 +02:00
|
|
|
// ---%-@-%---
|
|
|
|
|
|
|
|
public void
|
|
|
|
setTopLeft(String text) { topLeft.setText(text); }
|
|
|
|
|
|
|
|
public void
|
|
|
|
setTopRight(String text) { topRight.setText(text); }
|
|
|
|
|
|
|
|
public void
|
|
|
|
setBottom(String text) { bottom.setText(text); }
|
|
|
|
|
2022-04-16 14:13:38 +02:00
|
|
|
public void
|
2022-04-12 08:37:39 +02:00
|
|
|
reset()
|
|
|
|
{
|
|
|
|
setTopLeft(" ");
|
|
|
|
setTopRight(" ");
|
|
|
|
setBottom(" ");
|
|
|
|
}
|
|
|
|
|
|
|
|
public void
|
2022-05-02 03:44:10 +02:00
|
|
|
setSelected(boolean selected) { this.selected = selected; }
|
2022-04-12 08:37:39 +02:00
|
|
|
|
2022-04-14 06:38:49 +02:00
|
|
|
public boolean
|
2022-05-02 03:44:10 +02:00
|
|
|
isSelected() { return selected; }
|
2022-04-14 06:38:49 +02:00
|
|
|
|
2022-04-12 08:37:39 +02:00
|
|
|
// - -%- -
|
|
|
|
|
|
|
|
protected void
|
|
|
|
paintComponent(Graphics g)
|
|
|
|
{
|
2022-05-02 03:44:10 +02:00
|
|
|
if (selected)
|
|
|
|
{
|
|
|
|
g.setColor(new Color(0, 0, 0, 25));
|
2022-04-12 14:48:24 +02:00
|
|
|
g.fillRect(0, 0, getWidth(), getHeight());
|
|
|
|
}
|
2022-04-12 08:37:39 +02:00
|
|
|
}
|
|
|
|
|
2021-07-16 00:37:03 +02:00
|
|
|
// ---%-@-%---
|
|
|
|
|
|
|
|
public
|
2021-07-17 11:46:27 +02:00
|
|
|
PostPreviewComponent()
|
2021-07-16 00:37:03 +02:00
|
|
|
{
|
2022-05-02 03:44:10 +02:00
|
|
|
Font f1 = new Font("Dialog", Font.PLAIN, 12);
|
|
|
|
Font f2 = new Font("Dialog", Font.ITALIC, 12);
|
|
|
|
Font f3 = new Font("Dialog", Font.PLAIN, 14);
|
2021-07-16 00:37:03 +02:00
|
|
|
|
2021-07-31 06:01:20 +02:00
|
|
|
topLeft = new JLabel();
|
2022-04-12 08:37:39 +02:00
|
|
|
topLeft.setFont(f1);
|
2022-04-12 14:48:24 +02:00
|
|
|
topLeft.setOpaque(false);
|
2021-07-16 00:37:03 +02:00
|
|
|
|
2021-07-31 06:01:20 +02:00
|
|
|
topRight = new JLabel();
|
2021-07-16 00:37:03 +02:00
|
|
|
topRight.setHorizontalAlignment(JLabel.RIGHT);
|
2022-04-12 08:37:39 +02:00
|
|
|
topRight.setFont(f2);
|
2022-04-12 14:48:24 +02:00
|
|
|
topRight.setOpaque(false);
|
2021-07-16 00:37:03 +02:00
|
|
|
|
|
|
|
Box top = Box.createHorizontalBox();
|
2022-04-12 08:37:39 +02:00
|
|
|
top.setOpaque(false);
|
2021-07-16 00:37:03 +02:00
|
|
|
top.add(topLeft);
|
|
|
|
top.add(Box.createGlue());
|
|
|
|
top.add(topRight);
|
|
|
|
|
2022-04-12 14:48:24 +02:00
|
|
|
bottom = new JLabel();
|
|
|
|
bottom.setFont(f3);
|
|
|
|
bottom.setOpaque(false);
|
|
|
|
|
2022-04-16 14:13:38 +02:00
|
|
|
JPanel left = new JPanel();
|
|
|
|
left.setOpaque(false);
|
|
|
|
left.setLayout(new BorderLayout());
|
|
|
|
left.add(top, BorderLayout.NORTH);
|
|
|
|
left.add(bottom);
|
|
|
|
|
2022-04-14 06:38:49 +02:00
|
|
|
setFocusable(true);
|
2022-04-12 08:37:39 +02:00
|
|
|
setOpaque(false);
|
2022-04-16 14:13:38 +02:00
|
|
|
setLayout(new BorderLayout());
|
|
|
|
add(left);
|
2021-07-16 00:37:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|