biskuteri-cafe-JKomasto2/RepliesWindow.java

333 lines
8.3 KiB
Java

/* copyright
This file is part of JKomasto2.
Written in 2022 by Usawashi <usawashi16@yahoo.co.jp>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
copyright */
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTree;
import javax.swing.JOptionPane;
import javax.swing.BorderFactory;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeSelectionModel;
import javax.swing.event.TreeSelectionListener;
import javax.swing.event.TreeSelectionEvent;
import java.awt.Dimension;
import java.awt.Cursor;
import java.awt.BorderLayout;
import java.util.Enumeration;
import cafe.biskuteri.hinoki.Tree;
import java.io.IOException;
class
RepliesWindow extends JFrame {
private JKomasto
primaire;
private MastodonApi
api;
private PostWindow
postWindow;
// - -%- -
private RepliesComponent
display;
// ---%-@-%---
public synchronized void
showFor(String postId)
{
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
Tree<String> thread = getThread(postId);
if (thread != null) display.showThread(thread);
display.setCursor(null);
if (thread == null) dispose();
}
// - -%- -
public synchronized void
postSelected(Tree<String> post)
{
postWindow.readEntity(post);
postWindow.setVisible(true);
}
private Tree<String>
getThread(String postId)
{
abstract class Handler implements RequestListener {
boolean
failed = false;
// -=%=-
public void
connectionFailed(IOException eIo)
{
JOptionPane.showMessageDialog(
RepliesWindow.this,
"Failed to fetch post context...."
+ "\n" + eIo.getMessage()
);
failed = true;
}
public void
requestFailed(int httpCode, Tree<String> json)
{
JOptionPane.showMessageDialog(
RepliesWindow.this,
"Failed to fetch post context...."
+ "\n" + json.get("error").value
+ "\n(HTTP code: " + httpCode + ")"
);
failed = true;
}
}
class TopPostIdGetter extends Handler {
String
topPostId;
// -=%=-
public void
requestSucceeded(Tree<String> json)
{
Tree<String> ancestors = json.get("ancestors");
if (ancestors.size() == 0) topPostId = postId;
else topPostId = ancestors.get(0).get("id").value;
}
};
class DescendantsGetter extends Handler {
Tree<String>
descendants;
// -=%=-
public void
requestSucceeded(Tree<String> json)
{
descendants = json.get("descendants");
}
};
class PostGetter extends Handler {
Tree<String>
post;
// -=%=-
public void
requestSucceeded(Tree<String> json)
{
post = json;
}
}
TopPostIdGetter phase1 = new TopPostIdGetter();
api.getPostContext(postId, phase1);
if (phase1.failed) return null;
DescendantsGetter phase2 = new DescendantsGetter();
api.getPostContext(phase1.topPostId, phase2);
if (phase2.failed) return null;
PostGetter phase3 = new PostGetter();
api.getSpecificPost(phase1.topPostId, phase3);
if (phase3.failed) return null;
Tree<String> thread = new Tree<String>();
phase3.post.key = "top";
thread.add(phase3.post);
thread.add(phase2.descendants);
return thread;
}
// ---%-@-%---
RepliesWindow(JKomasto primaire, PostWindow postWindow)
{
super("Thread");
this.primaire = primaire;
this.api = primaire.getMastodonApi();
this.postWindow = postWindow;
display = new RepliesComponent(this);
setContentPane(display);
setSize(384, 224);
setIconImage(primaire.getProgramIcon());
}
}
class
RepliesComponent extends JPanel
implements TreeSelectionListener {
private RepliesWindow
primaire;
private Tree<String>
thread;
// - -%- -
private JTree
tree;
// ---%-@-%---
public void
showThread(Tree<String> thread)
{
Enumeration<TreeNode> e;
DefaultMutableTreeNode root;
TreeItem item;
item = new TreeItem(thread.get("top"));
root = new DefaultMutableTreeNode(item);
for (Tree<String> desc: thread.get("descendants"))
{
String target = desc.get("in_reply_to_id").value;
assert target != null;
DefaultMutableTreeNode p = null;
e = root.breadthFirstEnumeration();
while (e.hasMoreElements())
{
DefaultMutableTreeNode node;
node = (DefaultMutableTreeNode)e.nextElement();
item = (TreeItem)node.getUserObject();
String postId = item.post.get("id").value;
if (postId.equals(target))
{
p = node;
break;
}
}
if (p == null)
{
assert false;
/*
* Besides descendants possibly not being in order,
* the top of the thread might be deleted and so
* thread.top gets set to the given post. Which
* sibling replies aren't replying to, resulting
* in assertion failure.
*/
continue;
}
item = new TreeItem(desc);
p.add(new DefaultMutableTreeNode(item));
}
tree.setModel(new DefaultTreeModel(root));
for (int o = 0; o < tree.getRowCount(); ++o)
tree.expandRow(o);
}
// - -%- -
public void
valueChanged(TreeSelectionEvent eT)
{
Object selected = eT.getPath().getLastPathComponent();
assert selected instanceof DefaultMutableTreeNode;
TreeItem item = (TreeItem)
((DefaultMutableTreeNode)selected)
.getUserObject();
primaire.postSelected(item.post);
}
// ---%-@-%---
private static class
TreeItem {
public Tree<String>
post;
// -=%=-
public String
toString()
{
Post post = new Post(this.post);
post.resolveApproximateText();
return post.approximateText;
}
// -=%=-
TreeItem(Tree<String> post)
{
this.post = post;
}
}
// ---%-@-%---
RepliesComponent(RepliesWindow primaire)
{
this.primaire = primaire;
tree = new JTree();
tree.setBackground(null);
DefaultTreeCellRenderer renderer;
renderer = new DefaultTreeCellRenderer();
renderer.setBackgroundNonSelectionColor(null);
renderer.setOpenIcon(null);
renderer.setClosedIcon(null);
renderer.setLeafIcon(null);
tree.setCellRenderer(renderer);
int mode = TreeSelectionModel.SINGLE_TREE_SELECTION;
tree.getSelectionModel().setSelectionMode(mode);
tree.addTreeSelectionListener(this);
tree.setFont(tree.getFont().deriveFont(16f));
setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
setLayout(new BorderLayout());
add(tree);
}
}