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)
This commit is contained in:
Snowyfox 2022-05-06 16:34:22 -04:00
parent 45cfbce6dd
commit 775faa0bcc
39 changed files with 113 additions and 59 deletions

0
ClipboardApi.java Executable file → Normal file
View File

10
ComposeWindow.java Executable file → Normal file
View File

@ -44,7 +44,7 @@ ComposeWindow extends JFrame {
// ---%-@-%--- // ---%-@-%---
public void public synchronized void
setComposition(Composition composition) setComposition(Composition composition)
{ {
assert composition != null; assert composition != null;
@ -52,7 +52,7 @@ ComposeWindow extends JFrame {
syncDisplayToComposition(); syncDisplayToComposition();
} }
public void public synchronized void
newComposition() newComposition()
{ {
composition = new Composition(); composition = new Composition();
@ -63,7 +63,7 @@ ComposeWindow extends JFrame {
syncDisplayToComposition(); syncDisplayToComposition();
} }
public void public synchronized void
submit() submit()
{ {
syncCompositionToDisplay(); syncCompositionToDisplay();
@ -113,7 +113,7 @@ ComposeWindow extends JFrame {
// - -%- - // - -%- -
private void private synchronized void
syncDisplayToComposition() syncDisplayToComposition()
{ {
display.setText(composition.text); display.setText(composition.text);
@ -122,7 +122,7 @@ ComposeWindow extends JFrame {
display.setContentWarning(composition.contentWarning); display.setContentWarning(composition.contentWarning);
} }
private void private synchronized void
syncCompositionToDisplay() syncCompositionToDisplay()
{ {
composition.text = display.getText(); composition.text = display.getText();

0
ImageApi.java Executable file → Normal file
View File

4
ImageWindow.java Executable file → Normal file
View File

@ -35,7 +35,7 @@ ImageWindow extends JFrame {
// ---%-@-%--- // ---%-@-%---
public void public synchronized void
showAttachments(Attachment[] attachments) showAttachments(Attachment[] attachments)
{ {
this.attachments = attachments; this.attachments = attachments;
@ -69,7 +69,7 @@ ImageWindow extends JFrame {
// - -%- - // - -%- -
private void private synchronized void
toImage(int offset) toImage(int offset)
{ {
int last = attachments.length - 1; int last = attachments.length - 1;

0
JKomasto.java Executable file → Normal file
View File

0
KDE_Dialog_Appear.wav Executable file → Normal file
View File

0
LoginWindow.java Executable file → Normal file
View File

10
MastodonApi.java Executable file → Normal file
View File

@ -8,6 +8,7 @@ import java.net.HttpURLConnection;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.net.SocketTimeoutException;
import java.io.Reader; import java.io.Reader;
import java.io.Writer; import java.io.Writer;
import java.io.InputStream; import java.io.InputStream;
@ -443,10 +444,11 @@ MastodonApi {
try try
{ {
URL endpoint = new URL(url); URL endpoint = new URL(url);
HttpURLConnection conn = cast(endpoint.openConnection()); HttpURLConnection conn = cast(endpoint.openConnection());
String s = "Bearer " + token; String s = "Bearer " + token;
conn.setRequestProperty("Authorization", s); conn.setRequestProperty("Authorization", s);
conn.setReadTimeout(500);
conn.connect(); conn.connect();
int code = conn.getResponseCode(); int code = conn.getResponseCode();
@ -462,11 +464,15 @@ MastodonApi {
Reader input = ireader(conn.getInputStream()); Reader input = ireader(conn.getInputStream());
BufferedReader br = new BufferedReader(input); BufferedReader br = new BufferedReader(input);
Thread thread = Thread.currentThread(); Thread thread = Thread.currentThread();
while (!thread.isInterrupted()) while (true) try
{ {
String line = br.readLine(); String line = br.readLine();
if (line != null) handler.lineReceived(line); if (line != null) handler.lineReceived(line);
} }
catch (SocketTimeoutException eSt)
{
if (thread.interrupted()) break;
}
} }
catch (IOException eIo) { handler.connectionFailed(eIo); } catch (IOException eIo) { handler.connectionFailed(eIo); }
} }

3
NotificationsWindow.java Executable file → Normal file
View File

@ -50,7 +50,7 @@ NotificationsWindow extends JFrame {
// ---%-@-%--- // ---%-@-%---
public void public synchronized void
readEntity(Tree<String> entity) readEntity(Tree<String> entity)
{ {
notifications = new ArrayList<>(); notifications = new ArrayList<>();
@ -111,6 +111,7 @@ NotificationsWindow extends JFrame {
{ {
if (notifications.size() < ROW_COUNT) showLatestPage(); if (notifications.size() < ROW_COUNT) showLatestPage();
display.showNotifications(notifications); display.showNotifications(notifications);
display.repaint();
} }
} }

45
PostWindow.java Executable file → Normal file
View File

@ -66,7 +66,7 @@ PostWindow extends JFrame {
// ---%-@-%--- // ---%-@-%---
public void public synchronized void
use(Post post) use(Post post)
{ {
assert post != null; assert post != null;
@ -122,7 +122,7 @@ PostWindow extends JFrame {
use(new Post(post)); use(new Post(post));
} }
public void public synchronized void
openAuthorProfile() openAuthorProfile()
{ {
TimelineWindow w = new TimelineWindow(primaire); TimelineWindow w = new TimelineWindow(primaire);
@ -132,7 +132,7 @@ PostWindow extends JFrame {
w.setVisible(true); w.setVisible(true);
} }
public void public synchronized void
favourite(boolean favourited) favourite(boolean favourited)
{ {
display.setCursor(new Cursor(Cursor.WAIT_CURSOR)); display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
@ -174,7 +174,7 @@ PostWindow extends JFrame {
display.repaint(); display.repaint();
} }
public void public synchronized void
boost(boolean boosted) boost(boolean boosted)
{ {
display.setCursor(new Cursor(Cursor.WAIT_CURSOR)); display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
@ -216,7 +216,7 @@ PostWindow extends JFrame {
display.repaint(); display.repaint();
} }
public void public synchronized void
reply() reply()
{ {
String ownId = api.getAccountDetails().get("id").value; String ownId = api.getAccountDetails().get("id").value;
@ -227,7 +227,7 @@ PostWindow extends JFrame {
w.setVisible(true); w.setVisible(true);
} }
public void public synchronized void
openMedia() openMedia()
{ {
display.setCursor(new Cursor(Cursor.WAIT_CURSOR)); display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
@ -236,6 +236,7 @@ PostWindow extends JFrame {
ImageWindow w = new ImageWindow(); ImageWindow w = new ImageWindow();
w.setTitle("Media - " + this.getTitle()); w.setTitle("Media - " + this.getTitle());
w.setIconImage(this.getIconImage());
w.showAttachments(post.attachments); w.showAttachments(post.attachments);
w.setLocationRelativeTo(null); w.setLocationRelativeTo(null);
w.setVisible(true); w.setVisible(true);
@ -243,7 +244,7 @@ PostWindow extends JFrame {
display.setCursor(null); display.setCursor(null);
} }
public void public synchronized void
deletePost(boolean redraft) deletePost(boolean redraft)
{ {
display.setCursor(new Cursor(Cursor.WAIT_CURSOR)); display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
@ -296,19 +297,19 @@ PostWindow extends JFrame {
if (!isVisible()) dispose(); if (!isVisible()) dispose();
} }
public void public synchronized void
copyPostId() copyPostId()
{ {
ClipboardApi.serve(post.id); ClipboardApi.serve(post.id);
} }
public void public synchronized void
copyPostLink() copyPostLink()
{ {
ClipboardApi.serve(post.uri); ClipboardApi.serve(post.uri);
} }
public void public synchronized void
openReplies() openReplies()
{ {
RepliesWindow w = new RepliesWindow(primaire, this); RepliesWindow w = new RepliesWindow(primaire, this);
@ -354,6 +355,9 @@ implements ActionListener {
private RichTextPane private RichTextPane
authorName, body; authorName, body;
private JScrollPane
bodyScrollPane;
private JLabel private JLabel
authorId, time, date; authorId, time, date;
@ -486,7 +490,11 @@ implements ActionListener {
setMediaPreview(Image n) { media.setImage(n); } setMediaPreview(Image n) { media.setImage(n); }
public void 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; if (s.y > maxY) maxY = s.y;
} }
body.setPreferredSize(new Dimension(1, maxY + 10)); 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 = new RichTextPane();
body.setFont(f3); body.setFont(f3);
JScrollPane scroll = new JScrollPane( bodyScrollPane = new JScrollPane(
body, body,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER JScrollPane.HORIZONTAL_SCROLLBAR_NEVER
); );
JScrollBar vsb = scroll.getVerticalScrollBar(); JScrollBar vsb = bodyScrollPane.getVerticalScrollBar();
vsb.setPreferredSize(new Dimension(0, 0)); vsb.setPreferredSize(new Dimension(0, 0));
vsb.setUnitIncrement(16); vsb.setUnitIncrement(16);
scroll.setBorder(null); bodyScrollPane.setBorder(null);
scroll.setFocusable(true); bodyScrollPane.setFocusable(true);
JPanel centre = new JPanel(); JPanel centre = new JPanel();
centre.setOpaque(false); centre.setOpaque(false);
centre.setLayout(new BorderLayout(0, 8)); centre.setLayout(new BorderLayout(0, 8));
centre.add(top, BorderLayout.NORTH); centre.add(top, BorderLayout.NORTH);
centre.add(scroll); centre.add(bodyScrollPane);
setLayout(new BorderLayout(8, 0)); setLayout(new BorderLayout(8, 0));
add(left, BorderLayout.WEST); add(left, BorderLayout.WEST);

4
RepliesWindow.java Executable file → Normal file
View File

@ -38,7 +38,7 @@ RepliesWindow extends JFrame {
// ---%-@-%--- // ---%-@-%---
public void public synchronized void
showFor(String postId) showFor(String postId)
{ {
display.setCursor(new Cursor(Cursor.WAIT_CURSOR)); display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
@ -50,7 +50,7 @@ RepliesWindow extends JFrame {
// - -%- - // - -%- -
public void public synchronized void
postSelected(Tree<String> post) postSelected(Tree<String> post)
{ {
postWindow.readEntity(post); postWindow.readEntity(post);

0
RequestListener.java Executable file → Normal file
View File

0
RichTextPane.java Executable file → Normal file
View File

0
RudimentaryHTMLParser.java Executable file → Normal file
View File

20
TimelineWindow.java Executable file → Normal file
View File

@ -91,7 +91,7 @@ implements ActionListener {
// ---%-@-%--- // ---%-@-%---
public void public synchronized void
use(TimelinePage page) use(TimelinePage page)
{ {
assert page != null; assert page != null;
@ -356,7 +356,7 @@ implements ActionListener {
display.setCursor(null); display.setCursor(null);
} }
public void public synchronized void
setTimelineType(TimelineType type) setTimelineType(TimelineType type)
{ {
assert type != TimelineType.LIST; assert type != TimelineType.LIST;
@ -375,10 +375,10 @@ implements ActionListener {
display.repaint(); display.repaint();
} }
public TimelineType public synchronized TimelineType
getTimelineType() { return page.type; } getTimelineType() { return page.type; }
public void public synchronized void
showAuthorPosts(String authorNumId) showAuthorPosts(String authorNumId)
{ {
assert authorNumId != null; assert authorNumId != null;
@ -391,9 +391,11 @@ implements ActionListener {
display.repaint(); display.repaint();
} }
public void public synchronized void
openOwnProfile() openOwnProfile()
{ {
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
Tree<String> accountDetails = api.getAccountDetails(); Tree<String> accountDetails = api.getAccountDetails();
assert accountDetails != null; assert accountDetails != null;
String id = accountDetails.get("id").value; String id = accountDetails.get("id").value;
@ -403,6 +405,8 @@ implements ActionListener {
w.showLatestPage(); w.showLatestPage();
w.setLocationRelativeTo(this); w.setLocationRelativeTo(this);
w.setVisible(true); w.setVisible(true);
display.setCursor(null);
} }
public void public void
@ -519,7 +523,7 @@ implements ActionListener {
// - -%- - // - -%- -
public void public synchronized void
previewSelected(int index) previewSelected(int index)
{ {
if (index > page.posts.length) return; if (index > page.posts.length) return;
@ -527,7 +531,7 @@ implements ActionListener {
primaire.getAutoViewWindow().use(post); primaire.getAutoViewWindow().use(post);
} }
public void public synchronized void
previewOpened(int index) previewOpened(int index)
{ {
if (index > page.posts.length) return; if (index > page.posts.length) return;
@ -604,6 +608,8 @@ implements ActionListener {
} }
} }
// - -%- -
private static String private static String
toString(TimelineType type) toString(TimelineType type)
{ {

0
TwoToggleButton.java Executable file → Normal file
View File

76
WindowUpdater.java Executable file → Normal file
View File

@ -39,9 +39,9 @@ WindowUpdater {
public void public void
add(TimelineWindow updatee) add(TimelineWindow updatee)
{ {
if (timelineWindows.contains(updatee)) return; if (!timelineWindows.contains(updatee))
timelineWindows.add(updatee);
timelineWindows.add(updatee);
publicConn.reevaluate(); publicConn.reevaluate();
userConn.reevaluate(); userConn.reevaluate();
} }
@ -49,9 +49,9 @@ WindowUpdater {
public void public void
add(NotificationsWindow updatee) add(NotificationsWindow updatee)
{ {
if (notificationWindows.contains(updatee)) return; if (!notificationWindows.contains(updatee))
notificationWindows.add(updatee);
notificationWindows.add(updatee);
userConn.reevaluate(); userConn.reevaluate();
} }
@ -84,27 +84,51 @@ WindowUpdater {
private Thread private Thread
thread; thread;
private boolean
stopping;
private StringBuilder private StringBuilder
event, data; event, data;
// -=%=- // -=%=-
public void public void
restart() start()
{ {
if (thread != null) stop(); stopping = false;
thread = new Thread(this); thread = new Thread(this);
thread.start(); try
{
synchronized (thread)
{
thread.start();
thread.wait();
}
}
catch (InterruptedException eIt)
{
assert false;
}
} }
public void public void
stop() stop()
{ {
stopping = true;
thread.interrupt(); thread.interrupt();
thread = null; try
// We should be sure that the thread will actually {
// terminate eventually after our interrupt. I'm thread.join();
// confident enough in that. }
catch (InterruptedException eIt)
{
assert false;
}
thread = null;
/*
* That thread should notice it is interrupted
* promptly, and close.
*/
} }
public void public void
@ -119,26 +143,18 @@ WindowUpdater {
if (responsibleFor(updatee)) hasUpdatee = true; if (responsibleFor(updatee)) hasUpdatee = true;
if (!hasUpdatee && thread != null) stop(); 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 public void
run() run()
{ {
synchronized (thread)
{
thread.notifyAll();
}
event = new StringBuilder(); event = new StringBuilder();
data = new StringBuilder(); data = new StringBuilder();
api.monitorTimeline(type, this); 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 public void
connectionFailed(IOException eIo) connectionFailed(IOException eIo)
{ {

0
graphics/Federated.xcf Executable file → Normal file
View File

0
graphics/Flags.xcf Executable file → Normal file
View File

0
graphics/Hourglass.xcf Executable file → Normal file
View File

0
graphics/boostToggled.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

0
graphics/boostUntoggled.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

0
graphics/button.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

0
graphics/disabledOverlay.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

0
graphics/favouriteToggled.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 353 B

After

Width:  |  Height:  |  Size: 353 B

0
graphics/favouriteUntoggled.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

0
graphics/federated.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

0
graphics/miscToggled.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

0
graphics/miscUntoggled.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

0
graphics/ref1.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

0
graphics/replyToggled.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

0
graphics/replyUntoggled.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

0
graphics/selectedOverlay.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 313 B

After

Width:  |  Height:  |  Size: 313 B

0
graphics/test1.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

0
graphics/test2.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

0
graphics/test3.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

0
graphics/test4.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

0
notifOptions.txt Executable file → Normal file
View File

0
notifOptions.txt~ Executable file → Normal file
View File