Fixed window updater thread issue (seemingly).
Better support for multithreading. Fixed notifications window refresh bug. Fixed autoview window bug (temporary scrollpane doesn't reset scroll position)
0
ClipboardApi.java
Executable file → Normal file
10
ComposeWindow.java
Executable file → Normal file
@ -44,7 +44,7 @@ ComposeWindow extends JFrame {
|
||||
|
||||
// ---%-@-%---
|
||||
|
||||
public void
|
||||
public synchronized void
|
||||
setComposition(Composition composition)
|
||||
{
|
||||
assert composition != null;
|
||||
@ -52,7 +52,7 @@ ComposeWindow extends JFrame {
|
||||
syncDisplayToComposition();
|
||||
}
|
||||
|
||||
public void
|
||||
public synchronized void
|
||||
newComposition()
|
||||
{
|
||||
composition = new Composition();
|
||||
@ -63,7 +63,7 @@ ComposeWindow extends JFrame {
|
||||
syncDisplayToComposition();
|
||||
}
|
||||
|
||||
public void
|
||||
public synchronized void
|
||||
submit()
|
||||
{
|
||||
syncCompositionToDisplay();
|
||||
@ -113,7 +113,7 @@ ComposeWindow extends JFrame {
|
||||
|
||||
// - -%- -
|
||||
|
||||
private void
|
||||
private synchronized void
|
||||
syncDisplayToComposition()
|
||||
{
|
||||
display.setText(composition.text);
|
||||
@ -122,7 +122,7 @@ ComposeWindow extends JFrame {
|
||||
display.setContentWarning(composition.contentWarning);
|
||||
}
|
||||
|
||||
private void
|
||||
private synchronized void
|
||||
syncCompositionToDisplay()
|
||||
{
|
||||
composition.text = display.getText();
|
||||
|
0
ImageApi.java
Executable file → Normal file
4
ImageWindow.java
Executable file → Normal file
@ -35,7 +35,7 @@ ImageWindow extends JFrame {
|
||||
|
||||
// ---%-@-%---
|
||||
|
||||
public void
|
||||
public synchronized void
|
||||
showAttachments(Attachment[] attachments)
|
||||
{
|
||||
this.attachments = attachments;
|
||||
@ -69,7 +69,7 @@ ImageWindow extends JFrame {
|
||||
|
||||
// - -%- -
|
||||
|
||||
private void
|
||||
private synchronized void
|
||||
toImage(int offset)
|
||||
{
|
||||
int last = attachments.length - 1;
|
||||
|
0
JKomasto.java
Executable file → Normal file
0
KDE_Dialog_Appear.wav
Executable file → Normal file
0
LoginWindow.java
Executable file → Normal file
8
MastodonApi.java
Executable file → Normal file
@ -8,6 +8,7 @@ import java.net.HttpURLConnection;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URLEncoder;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.io.InputStream;
|
||||
@ -447,6 +448,7 @@ MastodonApi {
|
||||
HttpURLConnection conn = cast(endpoint.openConnection());
|
||||
String s = "Bearer " + token;
|
||||
conn.setRequestProperty("Authorization", s);
|
||||
conn.setReadTimeout(500);
|
||||
conn.connect();
|
||||
|
||||
int code = conn.getResponseCode();
|
||||
@ -462,11 +464,15 @@ MastodonApi {
|
||||
Reader input = ireader(conn.getInputStream());
|
||||
BufferedReader br = new BufferedReader(input);
|
||||
Thread thread = Thread.currentThread();
|
||||
while (!thread.isInterrupted())
|
||||
while (true) try
|
||||
{
|
||||
String line = br.readLine();
|
||||
if (line != null) handler.lineReceived(line);
|
||||
}
|
||||
catch (SocketTimeoutException eSt)
|
||||
{
|
||||
if (thread.interrupted()) break;
|
||||
}
|
||||
}
|
||||
catch (IOException eIo) { handler.connectionFailed(eIo); }
|
||||
}
|
||||
|
3
NotificationsWindow.java
Executable file → Normal file
@ -50,7 +50,7 @@ NotificationsWindow extends JFrame {
|
||||
|
||||
// ---%-@-%---
|
||||
|
||||
public void
|
||||
public synchronized void
|
||||
readEntity(Tree<String> entity)
|
||||
{
|
||||
notifications = new ArrayList<>();
|
||||
@ -111,6 +111,7 @@ NotificationsWindow extends JFrame {
|
||||
{
|
||||
if (notifications.size() < ROW_COUNT) showLatestPage();
|
||||
display.showNotifications(notifications);
|
||||
display.repaint();
|
||||
}
|
||||
}
|
||||
|
||||
|
45
PostWindow.java
Executable file → Normal file
@ -66,7 +66,7 @@ PostWindow extends JFrame {
|
||||
|
||||
// ---%-@-%---
|
||||
|
||||
public void
|
||||
public synchronized void
|
||||
use(Post post)
|
||||
{
|
||||
assert post != null;
|
||||
@ -122,7 +122,7 @@ PostWindow extends JFrame {
|
||||
use(new Post(post));
|
||||
}
|
||||
|
||||
public void
|
||||
public synchronized void
|
||||
openAuthorProfile()
|
||||
{
|
||||
TimelineWindow w = new TimelineWindow(primaire);
|
||||
@ -132,7 +132,7 @@ PostWindow extends JFrame {
|
||||
w.setVisible(true);
|
||||
}
|
||||
|
||||
public void
|
||||
public synchronized void
|
||||
favourite(boolean favourited)
|
||||
{
|
||||
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||
@ -174,7 +174,7 @@ PostWindow extends JFrame {
|
||||
display.repaint();
|
||||
}
|
||||
|
||||
public void
|
||||
public synchronized void
|
||||
boost(boolean boosted)
|
||||
{
|
||||
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||
@ -216,7 +216,7 @@ PostWindow extends JFrame {
|
||||
display.repaint();
|
||||
}
|
||||
|
||||
public void
|
||||
public synchronized void
|
||||
reply()
|
||||
{
|
||||
String ownId = api.getAccountDetails().get("id").value;
|
||||
@ -227,7 +227,7 @@ PostWindow extends JFrame {
|
||||
w.setVisible(true);
|
||||
}
|
||||
|
||||
public void
|
||||
public synchronized void
|
||||
openMedia()
|
||||
{
|
||||
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||
@ -236,6 +236,7 @@ PostWindow extends JFrame {
|
||||
|
||||
ImageWindow w = new ImageWindow();
|
||||
w.setTitle("Media - " + this.getTitle());
|
||||
w.setIconImage(this.getIconImage());
|
||||
w.showAttachments(post.attachments);
|
||||
w.setLocationRelativeTo(null);
|
||||
w.setVisible(true);
|
||||
@ -243,7 +244,7 @@ PostWindow extends JFrame {
|
||||
display.setCursor(null);
|
||||
}
|
||||
|
||||
public void
|
||||
public synchronized void
|
||||
deletePost(boolean redraft)
|
||||
{
|
||||
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||
@ -296,19 +297,19 @@ PostWindow extends JFrame {
|
||||
if (!isVisible()) dispose();
|
||||
}
|
||||
|
||||
public void
|
||||
public synchronized void
|
||||
copyPostId()
|
||||
{
|
||||
ClipboardApi.serve(post.id);
|
||||
}
|
||||
|
||||
public void
|
||||
public synchronized void
|
||||
copyPostLink()
|
||||
{
|
||||
ClipboardApi.serve(post.uri);
|
||||
}
|
||||
|
||||
public void
|
||||
public synchronized void
|
||||
openReplies()
|
||||
{
|
||||
RepliesWindow w = new RepliesWindow(primaire, this);
|
||||
@ -354,6 +355,9 @@ implements ActionListener {
|
||||
private RichTextPane
|
||||
authorName, body;
|
||||
|
||||
private JScrollPane
|
||||
bodyScrollPane;
|
||||
|
||||
private JLabel
|
||||
authorId, time, date;
|
||||
|
||||
@ -486,7 +490,11 @@ implements ActionListener {
|
||||
setMediaPreview(Image n) { media.setImage(n); }
|
||||
|
||||
public void
|
||||
resetFocus() { media.requestFocusInWindow(); }
|
||||
resetFocus()
|
||||
{
|
||||
bodyScrollPane.getVerticalScrollBar().setValue(0);
|
||||
media.requestFocusInWindow();
|
||||
}
|
||||
|
||||
// - -%- -
|
||||
|
||||
@ -576,6 +584,11 @@ implements ActionListener {
|
||||
if (s.y > maxY) maxY = s.y;
|
||||
}
|
||||
body.setPreferredSize(new Dimension(1, maxY + 10));
|
||||
|
||||
((java.awt.Graphics2D)g).setRenderingHint(
|
||||
java.awt.RenderingHints.KEY_ANTIALIASING,
|
||||
java.awt.RenderingHints.VALUE_ANTIALIAS_ON
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -664,22 +677,22 @@ implements ActionListener {
|
||||
body = new RichTextPane();
|
||||
body.setFont(f3);
|
||||
|
||||
JScrollPane scroll = new JScrollPane(
|
||||
bodyScrollPane = new JScrollPane(
|
||||
body,
|
||||
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
|
||||
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER
|
||||
);
|
||||
JScrollBar vsb = scroll.getVerticalScrollBar();
|
||||
JScrollBar vsb = bodyScrollPane.getVerticalScrollBar();
|
||||
vsb.setPreferredSize(new Dimension(0, 0));
|
||||
vsb.setUnitIncrement(16);
|
||||
scroll.setBorder(null);
|
||||
scroll.setFocusable(true);
|
||||
bodyScrollPane.setBorder(null);
|
||||
bodyScrollPane.setFocusable(true);
|
||||
|
||||
JPanel centre = new JPanel();
|
||||
centre.setOpaque(false);
|
||||
centre.setLayout(new BorderLayout(0, 8));
|
||||
centre.add(top, BorderLayout.NORTH);
|
||||
centre.add(scroll);
|
||||
centre.add(bodyScrollPane);
|
||||
|
||||
setLayout(new BorderLayout(8, 0));
|
||||
add(left, BorderLayout.WEST);
|
||||
|
4
RepliesWindow.java
Executable file → Normal file
@ -38,7 +38,7 @@ RepliesWindow extends JFrame {
|
||||
|
||||
// ---%-@-%---
|
||||
|
||||
public void
|
||||
public synchronized void
|
||||
showFor(String postId)
|
||||
{
|
||||
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||
@ -50,7 +50,7 @@ RepliesWindow extends JFrame {
|
||||
|
||||
// - -%- -
|
||||
|
||||
public void
|
||||
public synchronized void
|
||||
postSelected(Tree<String> post)
|
||||
{
|
||||
postWindow.readEntity(post);
|
||||
|
0
RequestListener.java
Executable file → Normal file
0
RichTextPane.java
Executable file → Normal file
0
RudimentaryHTMLParser.java
Executable file → Normal file
20
TimelineWindow.java
Executable file → Normal file
@ -91,7 +91,7 @@ implements ActionListener {
|
||||
|
||||
// ---%-@-%---
|
||||
|
||||
public void
|
||||
public synchronized void
|
||||
use(TimelinePage page)
|
||||
{
|
||||
assert page != null;
|
||||
@ -356,7 +356,7 @@ implements ActionListener {
|
||||
display.setCursor(null);
|
||||
}
|
||||
|
||||
public void
|
||||
public synchronized void
|
||||
setTimelineType(TimelineType type)
|
||||
{
|
||||
assert type != TimelineType.LIST;
|
||||
@ -375,10 +375,10 @@ implements ActionListener {
|
||||
display.repaint();
|
||||
}
|
||||
|
||||
public TimelineType
|
||||
public synchronized TimelineType
|
||||
getTimelineType() { return page.type; }
|
||||
|
||||
public void
|
||||
public synchronized void
|
||||
showAuthorPosts(String authorNumId)
|
||||
{
|
||||
assert authorNumId != null;
|
||||
@ -391,9 +391,11 @@ implements ActionListener {
|
||||
display.repaint();
|
||||
}
|
||||
|
||||
public void
|
||||
public synchronized void
|
||||
openOwnProfile()
|
||||
{
|
||||
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||
|
||||
Tree<String> accountDetails = api.getAccountDetails();
|
||||
assert accountDetails != null;
|
||||
String id = accountDetails.get("id").value;
|
||||
@ -403,6 +405,8 @@ implements ActionListener {
|
||||
w.showLatestPage();
|
||||
w.setLocationRelativeTo(this);
|
||||
w.setVisible(true);
|
||||
|
||||
display.setCursor(null);
|
||||
}
|
||||
|
||||
public void
|
||||
@ -519,7 +523,7 @@ implements ActionListener {
|
||||
|
||||
// - -%- -
|
||||
|
||||
public void
|
||||
public synchronized void
|
||||
previewSelected(int index)
|
||||
{
|
||||
if (index > page.posts.length) return;
|
||||
@ -527,7 +531,7 @@ implements ActionListener {
|
||||
primaire.getAutoViewWindow().use(post);
|
||||
}
|
||||
|
||||
public void
|
||||
public synchronized void
|
||||
previewOpened(int index)
|
||||
{
|
||||
if (index > page.posts.length) return;
|
||||
@ -604,6 +608,8 @@ implements ActionListener {
|
||||
}
|
||||
}
|
||||
|
||||
// - -%- -
|
||||
|
||||
private static String
|
||||
toString(TimelineType type)
|
||||
{
|
||||
|
0
TwoToggleButton.java
Executable file → Normal file
76
WindowUpdater.java
Executable file → Normal file
@ -39,9 +39,9 @@ WindowUpdater {
|
||||
public void
|
||||
add(TimelineWindow updatee)
|
||||
{
|
||||
if (timelineWindows.contains(updatee)) return;
|
||||
if (!timelineWindows.contains(updatee))
|
||||
timelineWindows.add(updatee);
|
||||
|
||||
timelineWindows.add(updatee);
|
||||
publicConn.reevaluate();
|
||||
userConn.reevaluate();
|
||||
}
|
||||
@ -49,9 +49,9 @@ WindowUpdater {
|
||||
public void
|
||||
add(NotificationsWindow updatee)
|
||||
{
|
||||
if (notificationWindows.contains(updatee)) return;
|
||||
if (!notificationWindows.contains(updatee))
|
||||
notificationWindows.add(updatee);
|
||||
|
||||
notificationWindows.add(updatee);
|
||||
userConn.reevaluate();
|
||||
}
|
||||
|
||||
@ -84,27 +84,51 @@ WindowUpdater {
|
||||
private Thread
|
||||
thread;
|
||||
|
||||
private boolean
|
||||
stopping;
|
||||
|
||||
private StringBuilder
|
||||
event, data;
|
||||
|
||||
// -=%=-
|
||||
|
||||
public void
|
||||
restart()
|
||||
start()
|
||||
{
|
||||
if (thread != null) stop();
|
||||
stopping = false;
|
||||
thread = new Thread(this);
|
||||
thread.start();
|
||||
try
|
||||
{
|
||||
synchronized (thread)
|
||||
{
|
||||
thread.start();
|
||||
thread.wait();
|
||||
}
|
||||
}
|
||||
catch (InterruptedException eIt)
|
||||
{
|
||||
assert false;
|
||||
}
|
||||
}
|
||||
|
||||
public void
|
||||
stop()
|
||||
{
|
||||
stopping = true;
|
||||
thread.interrupt();
|
||||
thread = null;
|
||||
// We should be sure that the thread will actually
|
||||
// terminate eventually after our interrupt. I'm
|
||||
// confident enough in that.
|
||||
try
|
||||
{
|
||||
thread.join();
|
||||
}
|
||||
catch (InterruptedException eIt)
|
||||
{
|
||||
assert false;
|
||||
}
|
||||
thread = null;
|
||||
/*
|
||||
* That thread should notice it is interrupted
|
||||
* promptly, and close.
|
||||
*/
|
||||
}
|
||||
|
||||
public void
|
||||
@ -119,26 +143,18 @@ WindowUpdater {
|
||||
if (responsibleFor(updatee)) hasUpdatee = true;
|
||||
|
||||
if (!hasUpdatee && thread != null) stop();
|
||||
if (hasUpdatee && thread == null) restart();
|
||||
if (hasUpdatee && thread == null) start();
|
||||
}
|
||||
|
||||
// -=-
|
||||
|
||||
private boolean
|
||||
responsibleFor(TimelineWindow updatee)
|
||||
{
|
||||
return type == updatee.getTimelineType();
|
||||
}
|
||||
|
||||
private boolean
|
||||
responsibleFor(NotificationsWindow updatee)
|
||||
{
|
||||
return type == TimelineType.HOME;
|
||||
}
|
||||
|
||||
public void
|
||||
run()
|
||||
{
|
||||
synchronized (thread)
|
||||
{
|
||||
thread.notifyAll();
|
||||
}
|
||||
event = new StringBuilder();
|
||||
data = new StringBuilder();
|
||||
api.monitorTimeline(type, this);
|
||||
@ -208,6 +224,18 @@ WindowUpdater {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean
|
||||
responsibleFor(TimelineWindow updatee)
|
||||
{
|
||||
return type == updatee.getTimelineType();
|
||||
}
|
||||
|
||||
private boolean
|
||||
responsibleFor(NotificationsWindow updatee)
|
||||
{
|
||||
return type == TimelineType.HOME;
|
||||
}
|
||||
|
||||
public void
|
||||
connectionFailed(IOException eIo)
|
||||
{
|
||||
|
0
graphics/Federated.xcf
Executable file → Normal file
0
graphics/Flags.xcf
Executable file → Normal file
0
graphics/Hourglass.xcf
Executable file → Normal file
0
graphics/boostToggled.png
Executable file → Normal file
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
0
graphics/boostUntoggled.png
Executable file → Normal file
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
0
graphics/button.png
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/favouriteUntoggled.png
Executable file → Normal file
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
0
graphics/federated.png
Executable file → Normal file
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
0
graphics/miscToggled.png
Executable file → Normal file
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
0
graphics/miscUntoggled.png
Executable file → Normal file
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
0
graphics/ref1.png
Executable file → Normal file
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
0
graphics/replyToggled.png
Executable file → Normal file
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
0
graphics/replyUntoggled.png
Executable file → Normal file
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 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 |