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)
{
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
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)
{
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
View File

0
KDE_Dialog_Appear.wav Executable file → Normal file
View File

0
LoginWindow.java Executable file → Normal file
View File

8
MastodonApi.java Executable file → Normal file
View 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
View 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
View 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
View 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
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)
{
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
View File

76
WindowUpdater.java Executable file → Normal file
View 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
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