mirror of
https://gitlab.com/biskuteri-cafe/JKomasto2.git
synced 2025-01-08 22:44:44 +01:00
Added replies window, based on JTree
This commit is contained in:
parent
39526a145f
commit
9c75464b18
62
ClipboardApi.java
Executable file
62
ClipboardApi.java
Executable file
@ -0,0 +1,62 @@
|
|||||||
|
|
||||||
|
import java.awt.datatransfer.Clipboard;
|
||||||
|
import java.awt.datatransfer.ClipboardOwner;
|
||||||
|
import java.awt.datatransfer.Transferable;
|
||||||
|
import java.awt.datatransfer.DataFlavor;
|
||||||
|
import java.awt.Toolkit;
|
||||||
|
|
||||||
|
class
|
||||||
|
ClipboardApi
|
||||||
|
implements Transferable, ClipboardOwner {
|
||||||
|
|
||||||
|
private static final ClipboardApi
|
||||||
|
instance = new ClipboardApi();
|
||||||
|
|
||||||
|
private static String
|
||||||
|
string;
|
||||||
|
|
||||||
|
// ---%-@-%---
|
||||||
|
|
||||||
|
public static void
|
||||||
|
serve(String string)
|
||||||
|
{
|
||||||
|
assert string != null;
|
||||||
|
instance.string = string;
|
||||||
|
Toolkit tk = Toolkit.getDefaultToolkit();
|
||||||
|
Clipboard cb = tk.getSystemClipboard();
|
||||||
|
cb.setContents(instance, instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - -%- -
|
||||||
|
|
||||||
|
public String
|
||||||
|
getTransferData(DataFlavor flavour)
|
||||||
|
{
|
||||||
|
assert flavour == DataFlavor.stringFlavor;
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataFlavor[]
|
||||||
|
getTransferDataFlavors()
|
||||||
|
{
|
||||||
|
return new DataFlavor[] { DataFlavor.stringFlavor };
|
||||||
|
/*
|
||||||
|
* We should probably also support javaJVMLocalObjectMimeType,
|
||||||
|
* so that the compose window can ask for the List<Segment>.
|
||||||
|
* Although also like, if we don't store emoji shortcodes in
|
||||||
|
* the image segments, there is no point. Anyways, what is
|
||||||
|
* important is the string format first, allowing us to
|
||||||
|
* copy links or large lengths of text.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean
|
||||||
|
isDataFlavorSupported(DataFlavor flavour)
|
||||||
|
{
|
||||||
|
return flavour == DataFlavor.stringFlavor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void
|
||||||
|
lostOwnership(Clipboard clipboard, Transferable contents) { }
|
||||||
|
|
||||||
|
}
|
163
MastodonApi.java
163
MastodonApi.java
@ -180,22 +180,22 @@ MastodonApi {
|
|||||||
getTimelinePage(
|
getTimelinePage(
|
||||||
TimelineType type,
|
TimelineType type,
|
||||||
int count, String maxId, String minId,
|
int count, String maxId, String minId,
|
||||||
String accountId, String listId,
|
String accountId, String listId,
|
||||||
RequestListener handler)
|
RequestListener handler)
|
||||||
{
|
{
|
||||||
String token = accessToken.get("access_token").value;
|
String token = accessToken.get("access_token").value;
|
||||||
|
|
||||||
assert !(accountId != null && listId != null);
|
assert !(accountId != null && listId != null);
|
||||||
|
|
||||||
String url = instanceUrl + "/api/v1";
|
String url = instanceUrl + "/api/v1";
|
||||||
if (accountId != null)
|
if (accountId != null)
|
||||||
{
|
{
|
||||||
url += "/accounts/" + accountId + "/statuses";
|
url += "/accounts/" + accountId + "/statuses";
|
||||||
}
|
}
|
||||||
else if (listId != null)
|
else if (listId != null)
|
||||||
{
|
{
|
||||||
url += "/lists/" + listId;
|
url += "/lists/" + listId;
|
||||||
}
|
}
|
||||||
else switch (type)
|
else switch (type)
|
||||||
{
|
{
|
||||||
case FEDERATED:
|
case FEDERATED:
|
||||||
@ -325,11 +325,11 @@ MastodonApi {
|
|||||||
catch (IOException eIo) { handler.connectionFailed(eIo); }
|
catch (IOException eIo) { handler.connectionFailed(eIo); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
getNotifications(
|
getNotifications(
|
||||||
int count, String maxId, String minId,
|
int count, String maxId, String minId,
|
||||||
RequestListener handler)
|
RequestListener handler)
|
||||||
{
|
{
|
||||||
String token = accessToken.get("access_token").value;
|
String token = accessToken.get("access_token").value;
|
||||||
|
|
||||||
String url = instanceUrl + "/api/v1/notifications";
|
String url = instanceUrl + "/api/v1/notifications";
|
||||||
@ -349,51 +349,93 @@ MastodonApi {
|
|||||||
doStandardJsonReturn(conn, handler);
|
doStandardJsonReturn(conn, handler);
|
||||||
}
|
}
|
||||||
catch (IOException eIo) { handler.connectionFailed(eIo); }
|
catch (IOException eIo) { handler.connectionFailed(eIo); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
deletePost(String postId, RequestListener handler)
|
deletePost(String postId, RequestListener handler)
|
||||||
{
|
{
|
||||||
String token = accessToken.get("access_token").value;
|
String token = accessToken.get("access_token").value;
|
||||||
|
|
||||||
String url = instanceUrl + "/api/v1/statuses/" + postId;
|
String url = instanceUrl + "/api/v1/statuses/" + postId;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
URL endpoint = new URL(url);
|
URL endpoint = new URL(url);
|
||||||
HttpURLConnection conn;
|
HttpURLConnection conn;
|
||||||
conn = (HttpURLConnection)endpoint.openConnection();
|
conn = (HttpURLConnection)endpoint.openConnection();
|
||||||
String s1 = "Bearer " + token;
|
String s1 = "Bearer " + token;
|
||||||
conn.setRequestProperty("Authorization", s1);
|
conn.setRequestProperty("Authorization", s1);
|
||||||
conn.setRequestMethod("DELETE");
|
conn.setRequestMethod("DELETE");
|
||||||
conn.connect();
|
conn.connect();
|
||||||
|
|
||||||
doStandardJsonReturn(conn, handler);
|
doStandardJsonReturn(conn, handler);
|
||||||
}
|
}
|
||||||
catch (IOException eIo) { handler.connectionFailed(eIo); }
|
catch (IOException eIo) { handler.connectionFailed(eIo); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
getAccounts(String query, RequestListener handler)
|
getSpecificPost(String postId, RequestListener handler)
|
||||||
{
|
{
|
||||||
assert query != null;
|
String token = accessToken.get("access_token").value;
|
||||||
String token = accessToken.get("access_token").value;
|
|
||||||
|
|
||||||
String url = instanceUrl + "/api/v1/accounts/search";
|
String url = instanceUrl + "/api/v1/statuses/" + postId;
|
||||||
url += "?q=" + encode(query);
|
try
|
||||||
|
{
|
||||||
|
URL endpoint = new URL(url);
|
||||||
|
HttpURLConnection conn;
|
||||||
|
conn = (HttpURLConnection)endpoint.openConnection();
|
||||||
|
String s1 = "Bearer " + token;
|
||||||
|
conn.setRequestProperty("Authorization", s1);
|
||||||
|
conn.connect();
|
||||||
|
|
||||||
try
|
doStandardJsonReturn(conn, handler);
|
||||||
{
|
}
|
||||||
URL endpoint = new URL(url);
|
catch (IOException eIo) { handler.connectionFailed(eIo); }
|
||||||
HttpURLConnection conn;
|
}
|
||||||
conn = (HttpURLConnection)endpoint.openConnection();
|
|
||||||
String s1 = "Bearer " + token;
|
|
||||||
conn.setRequestProperty("Authorization", s1);
|
|
||||||
conn.connect();
|
|
||||||
|
|
||||||
doStandardJsonReturn(conn, handler);
|
public void
|
||||||
}
|
getPostContext(String postId, RequestListener handler)
|
||||||
catch (IOException eIo) { handler.connectionFailed(eIo); }
|
{
|
||||||
}
|
String token = accessToken.get("access_token").value;
|
||||||
|
|
||||||
|
String s1 = instanceUrl + "/api/v1/statuses/";
|
||||||
|
String s2 = postId + "/context";
|
||||||
|
String url = s1 + s2;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
URL endpoint = new URL(url);
|
||||||
|
HttpURLConnection conn;
|
||||||
|
conn = (HttpURLConnection)endpoint.openConnection();
|
||||||
|
String s3 = "Bearer " + token;
|
||||||
|
conn.setRequestProperty("Authorization", s3);
|
||||||
|
conn.connect();
|
||||||
|
|
||||||
|
doStandardJsonReturn(conn, handler);
|
||||||
|
}
|
||||||
|
catch (IOException eIo) { handler.connectionFailed(eIo); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void
|
||||||
|
getAccounts(String query, RequestListener handler)
|
||||||
|
{
|
||||||
|
assert query != null;
|
||||||
|
String token = accessToken.get("access_token").value;
|
||||||
|
|
||||||
|
String url = instanceUrl + "/api/v1/accounts/search";
|
||||||
|
url += "?q=" + encode(query);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
URL endpoint = new URL(url);
|
||||||
|
HttpURLConnection conn;
|
||||||
|
conn = (HttpURLConnection)endpoint.openConnection();
|
||||||
|
String s1 = "Bearer " + token;
|
||||||
|
conn.setRequestProperty("Authorization", s1);
|
||||||
|
conn.connect();
|
||||||
|
|
||||||
|
doStandardJsonReturn(conn, handler);
|
||||||
|
}
|
||||||
|
catch (IOException eIo) { handler.connectionFailed(eIo); }
|
||||||
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
monitorTimeline(
|
monitorTimeline(
|
||||||
@ -486,6 +528,25 @@ MastodonApi {
|
|||||||
handler.requestSucceeded(response);
|
handler.requestSucceeded(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - -%- -
|
||||||
|
|
||||||
|
public static void
|
||||||
|
debugPrint(Tree<String> tree)
|
||||||
|
{
|
||||||
|
debugPrint(tree, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void
|
||||||
|
debugPrint(Tree<String> tree, String prefix)
|
||||||
|
{
|
||||||
|
System.err.print(prefix);
|
||||||
|
System.err.print(tree.key);
|
||||||
|
System.err.print(": ");
|
||||||
|
System.err.println(tree.value);
|
||||||
|
for (Tree<String> child: tree)
|
||||||
|
debugPrint(child, prefix + " ");
|
||||||
|
}
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
private static Tree<String>
|
private static Tree<String>
|
||||||
@ -516,7 +577,7 @@ MastodonApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
public void
|
public void
|
||||||
loadCache()
|
loadCache()
|
||||||
@ -580,7 +641,7 @@ MastodonApi {
|
|||||||
w.close();
|
w.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
private static String
|
private static String
|
||||||
getCachePath()
|
getCachePath()
|
||||||
|
339
PostWindow.java
339
PostWindow.java
@ -41,8 +41,7 @@ import java.time.format.DateTimeFormatter;
|
|||||||
|
|
||||||
|
|
||||||
class
|
class
|
||||||
PostWindow extends JFrame
|
PostWindow extends JFrame {
|
||||||
implements ActionListener {
|
|
||||||
|
|
||||||
private JKomasto
|
private JKomasto
|
||||||
primaire;
|
primaire;
|
||||||
@ -56,10 +55,7 @@ implements ActionListener {
|
|||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
private PostComponent
|
private PostComponent
|
||||||
postDisplay;
|
display;
|
||||||
|
|
||||||
private RepliesComponent
|
|
||||||
repliesDisplay;
|
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
@ -83,21 +79,21 @@ implements ActionListener {
|
|||||||
|
|
||||||
String an = author.get("display_name").value;
|
String an = author.get("display_name").value;
|
||||||
if (an.isEmpty()) an = author.get("username").value;
|
if (an.isEmpty()) an = author.get("username").value;
|
||||||
postDisplay.setAuthorName(an);
|
display.setAuthorName(an);
|
||||||
postDisplay.setAuthorId(author.get("acct").value);
|
display.setAuthorId(author.get("acct").value);
|
||||||
|
|
||||||
String aid = author.get("id").value;
|
String aid = author.get("id").value;
|
||||||
String oid = api.getAccountDetails().get("id").value;
|
String oid = api.getAccountDetails().get("id").value;
|
||||||
postDisplay.setDeleteEnabled(aid.equals(oid));
|
display.setDeleteEnabled(aid.equals(oid));
|
||||||
|
|
||||||
String avurl = author.get("avatar").value;
|
String avurl = author.get("avatar").value;
|
||||||
postDisplay.setAuthorAvatar(ImageApi.remote(avurl));
|
display.setAuthorAvatar(ImageApi.remote(avurl));
|
||||||
|
|
||||||
String sdate = post.get("created_at").value;
|
String sdate = post.get("created_at").value;
|
||||||
ZonedDateTime date = ZonedDateTime.parse(sdate);
|
ZonedDateTime date = ZonedDateTime.parse(sdate);
|
||||||
date = date.withZoneSameInstant(ZoneId.systemDefault());
|
date = date.withZoneSameInstant(ZoneId.systemDefault());
|
||||||
postDisplay.setDate(DATE_FORMAT.format(date));
|
display.setDate(DATE_FORMAT.format(date));
|
||||||
postDisplay.setTime(TIME_FORMAT.format(date));
|
display.setTime(TIME_FORMAT.format(date));
|
||||||
|
|
||||||
String[][] emojiUrls = new String[emojis.size()][];
|
String[][] emojiUrls = new String[emojis.size()][];
|
||||||
for (int o = 0; o < emojiUrls.length; ++o) {
|
for (int o = 0; o < emojiUrls.length; ++o) {
|
||||||
@ -106,13 +102,13 @@ implements ActionListener {
|
|||||||
emojiUrls[o][0] = emoji.get("shortcode").value;
|
emojiUrls[o][0] = emoji.get("shortcode").value;
|
||||||
emojiUrls[o][1] = emoji.get("url").value;
|
emojiUrls[o][1] = emoji.get("url").value;
|
||||||
}
|
}
|
||||||
postDisplay.setEmojiUrls(emojiUrls);
|
display.setEmojiUrls(emojiUrls);
|
||||||
|
|
||||||
postDisplay.setHtml(post.get("content").value);
|
display.setHtml(post.get("content").value);
|
||||||
boolean f = post.get("favourited").value.equals("true");
|
boolean f = post.get("favourited").value.equals("true");
|
||||||
boolean b = post.get("reblogged").value.equals("true");
|
boolean b = post.get("reblogged").value.equals("true");
|
||||||
postDisplay.setFavourited(f);
|
display.setFavourited(f);
|
||||||
postDisplay.setBoosted(b);
|
display.setBoosted(b);
|
||||||
|
|
||||||
if (media.size() > 0)
|
if (media.size() > 0)
|
||||||
{
|
{
|
||||||
@ -121,14 +117,14 @@ implements ActionListener {
|
|||||||
String u2 = first.get("text_url").value;
|
String u2 = first.get("text_url").value;
|
||||||
String u3 = first.get("url").value;
|
String u3 = first.get("url").value;
|
||||||
String purl = u1 != null ? u1 : u2 != null ? u2 : u3;
|
String purl = u1 != null ? u1 : u2 != null ? u2 : u3;
|
||||||
postDisplay.setMediaPreview(ImageApi.remote(purl));
|
display.setMediaPreview(ImageApi.remote(purl));
|
||||||
}
|
}
|
||||||
else postDisplay.setMediaPreview(null);
|
else display.setMediaPreview(null);
|
||||||
|
|
||||||
String html = post.get("content").value;
|
String html = post.get("content").value;
|
||||||
setTitle(TimelineComponent.textApproximation(html));
|
setTitle(TimelineComponent.textApproximation(html));
|
||||||
|
|
||||||
postDisplay.resetFocus();
|
display.resetFocus();
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,9 +149,9 @@ implements ActionListener {
|
|||||||
Tree<String> boosted = post.get("reblog");
|
Tree<String> boosted = post.get("reblog");
|
||||||
if (boosted.size() > 0) post = boosted;
|
if (boosted.size() > 0) post = boosted;
|
||||||
|
|
||||||
postDisplay.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||||
postDisplay.setFavouriteBoostEnabled(false);
|
display.setFavouriteBoostEnabled(false);
|
||||||
postDisplay.paintImmediately(postDisplay.getBounds());
|
display.paintImmediately(display.getBounds());
|
||||||
RequestListener handler = new RequestListener() {
|
RequestListener handler = new RequestListener() {
|
||||||
|
|
||||||
public void
|
public void
|
||||||
@ -189,9 +185,9 @@ implements ActionListener {
|
|||||||
};
|
};
|
||||||
String postId = post.get("id").value;
|
String postId = post.get("id").value;
|
||||||
api.setPostFavourited(postId, favourited, handler);
|
api.setPostFavourited(postId, favourited, handler);
|
||||||
postDisplay.setCursor(null);
|
display.setCursor(null);
|
||||||
postDisplay.setFavouriteBoostEnabled(true);
|
display.setFavouriteBoostEnabled(true);
|
||||||
postDisplay.repaint();
|
display.repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
@ -201,9 +197,9 @@ implements ActionListener {
|
|||||||
Tree<String> boosted2 = post.get("reblog");
|
Tree<String> boosted2 = post.get("reblog");
|
||||||
if (boosted2.size() > 0) post = boosted2;
|
if (boosted2.size() > 0) post = boosted2;
|
||||||
|
|
||||||
postDisplay.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||||
postDisplay.setFavouriteBoostEnabled(false);
|
display.setFavouriteBoostEnabled(false);
|
||||||
postDisplay.paintImmediately(postDisplay.getBounds());
|
display.paintImmediately(display.getBounds());
|
||||||
RequestListener handler = new RequestListener() {
|
RequestListener handler = new RequestListener() {
|
||||||
|
|
||||||
public void
|
public void
|
||||||
@ -237,9 +233,9 @@ implements ActionListener {
|
|||||||
};
|
};
|
||||||
String postId = post.get("id").value;
|
String postId = post.get("id").value;
|
||||||
api.setPostBoosted(postId, boosted, handler);
|
api.setPostBoosted(postId, boosted, handler);
|
||||||
postDisplay.setCursor(null);
|
display.setCursor(null);
|
||||||
postDisplay.setFavouriteBoostEnabled(true);
|
display.setFavouriteBoostEnabled(true);
|
||||||
postDisplay.repaint();
|
display.repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
@ -262,7 +258,7 @@ implements ActionListener {
|
|||||||
if (vs.equals("unlisted")) v = PostVisibility.UNLISTED;
|
if (vs.equals("unlisted")) v = PostVisibility.UNLISTED;
|
||||||
if (vs.equals("private")) v = PostVisibility.FOLLOWERS;
|
if (vs.equals("private")) v = PostVisibility.FOLLOWERS;
|
||||||
if (vs.equals("direct")) v = PostVisibility.MENTIONED;
|
if (vs.equals("direct")) v = PostVisibility.MENTIONED;
|
||||||
|
|
||||||
Composition c = new Composition();
|
Composition c = new Composition();
|
||||||
c.contentWarning = cw;
|
c.contentWarning = cw;
|
||||||
c.text = id1.equals(id2) ? "" : "@" + authorId + " ";
|
c.text = id1.equals(id2) ? "" : "@" + authorId + " ";
|
||||||
@ -310,9 +306,9 @@ implements ActionListener {
|
|||||||
public void
|
public void
|
||||||
deletePost(boolean redraft)
|
deletePost(boolean redraft)
|
||||||
{
|
{
|
||||||
postDisplay.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||||
postDisplay.setDeleteEnabled(false);
|
display.setDeleteEnabled(false);
|
||||||
postDisplay.paintImmediately(postDisplay.getBounds());
|
display.paintImmediately(display.getBounds());
|
||||||
|
|
||||||
if (redraft)
|
if (redraft)
|
||||||
{
|
{
|
||||||
@ -354,7 +350,7 @@ implements ActionListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String cw = post.get("spoiler_text").value;
|
String cw = post.get("spoiler_text").value;
|
||||||
|
|
||||||
String vs = post.get("visibility").value;
|
String vs = post.get("visibility").value;
|
||||||
PostVisibility v = null;
|
PostVisibility v = null;
|
||||||
if (vs.equals("public")) v = PostVisibility.PUBLIC;
|
if (vs.equals("public")) v = PostVisibility.PUBLIC;
|
||||||
@ -407,38 +403,47 @@ implements ActionListener {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
postDisplay.setCursor(null);
|
display.setCursor(null);
|
||||||
postDisplay.setDeleteEnabled(true);
|
display.setDeleteEnabled(true);
|
||||||
postDisplay.paintImmediately(postDisplay.getBounds());
|
display.paintImmediately(display.getBounds());
|
||||||
if (!isVisible()) dispose();
|
if (!isVisible()) dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
// - -%- -
|
public void
|
||||||
|
copyPostId()
|
||||||
|
{
|
||||||
|
Tree<String> post = this.post;
|
||||||
|
Tree<String> reblogged = post.get("reblog");
|
||||||
|
if (reblogged.size() > 0) post = reblogged;
|
||||||
|
|
||||||
public void
|
ClipboardApi.serve(post.get("id").value);
|
||||||
actionPerformed(ActionEvent eA)
|
}
|
||||||
{
|
|
||||||
Component src = (Component)eA.getSource();
|
|
||||||
if (!(src instanceof JMenuItem)) return;
|
|
||||||
String text = ((JMenuItem)src).getText();
|
|
||||||
|
|
||||||
if (text.equals("Post"))
|
public void
|
||||||
{
|
copyPostLink()
|
||||||
setContentPane(postDisplay);
|
{
|
||||||
revalidate();
|
Tree<String> post = this.post;
|
||||||
/*
|
Tree<String> reblogged = post.get("reblog");
|
||||||
* (知) Setting a content pane in itself doesn't
|
if (reblogged.size() > 0) post = reblogged;
|
||||||
* do anything to the content pane. Validation
|
|
||||||
* of the validate root does. Which happens on
|
String url = post.get("url").value;
|
||||||
* window realisation, or by manual call.
|
if (url == null) url = post.get("uri").value;
|
||||||
*/
|
|
||||||
}
|
ClipboardApi.serve(url);
|
||||||
else if (text.equals("Replies"))
|
}
|
||||||
{
|
|
||||||
setContentPane(repliesDisplay);
|
public void
|
||||||
revalidate();
|
openReplies()
|
||||||
}
|
{
|
||||||
}
|
Tree<String> post = this.post;
|
||||||
|
Tree<String> boosted = post.get("reblog");
|
||||||
|
if (boosted.size() > 0) post = boosted;
|
||||||
|
|
||||||
|
RepliesWindow w = new RepliesWindow(primaire, this);
|
||||||
|
w.showFor(post.get("id").value);
|
||||||
|
w.setLocation(getX(), getY() + 100);
|
||||||
|
w.setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
@ -452,10 +457,9 @@ implements ActionListener {
|
|||||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||||
setLocationByPlatform(true);
|
setLocationByPlatform(true);
|
||||||
|
|
||||||
postDisplay = new PostComponent(this);
|
display = new PostComponent(this);
|
||||||
repliesDisplay = new RepliesComponent();
|
|
||||||
|
|
||||||
setContentPane(postDisplay);
|
setContentPane(display);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -496,6 +500,9 @@ implements ActionListener {
|
|||||||
miscMenu;
|
miscMenu;
|
||||||
|
|
||||||
private JMenuItem
|
private JMenuItem
|
||||||
|
openReplies,
|
||||||
|
copyPostId,
|
||||||
|
copyPostLink,
|
||||||
deletePost,
|
deletePost,
|
||||||
redraftPost;
|
redraftPost;
|
||||||
|
|
||||||
@ -667,17 +674,11 @@ implements ActionListener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (src == deletePost)
|
if (src == openReplies) primaire.openReplies();
|
||||||
{
|
if (src == copyPostId) primaire.copyPostId();
|
||||||
primaire.deletePost(false);
|
if (src == copyPostLink) primaire.copyPostLink();
|
||||||
return;
|
if (src == deletePost) primaire.deletePost(false);
|
||||||
}
|
if (src == redraftPost) primaire.deletePost(true);
|
||||||
|
|
||||||
if (src == redraftPost)
|
|
||||||
{
|
|
||||||
primaire.deletePost(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -731,12 +732,24 @@ implements ActionListener {
|
|||||||
nextPrev.addActionListener(this);
|
nextPrev.addActionListener(this);
|
||||||
media.addActionListener(this);
|
media.addActionListener(this);
|
||||||
|
|
||||||
|
openReplies = new JMenuItem("Browse thread");
|
||||||
|
copyPostId = new JMenuItem("Copy post ID");
|
||||||
|
copyPostLink = new JMenuItem("Copy post link");
|
||||||
deletePost = new JMenuItem("Delete post");
|
deletePost = new JMenuItem("Delete post");
|
||||||
redraftPost = new JMenuItem("Delete and redraft post");
|
redraftPost = new JMenuItem("Delete and redraft post");
|
||||||
|
openReplies.addActionListener(this);
|
||||||
|
copyPostId.addActionListener(this);
|
||||||
|
copyPostLink.addActionListener(this);
|
||||||
deletePost.addActionListener(this);
|
deletePost.addActionListener(this);
|
||||||
redraftPost.addActionListener(this);
|
redraftPost.addActionListener(this);
|
||||||
miscMenu = new JPopupMenu();
|
miscMenu = new JPopupMenu();
|
||||||
|
miscMenu.add(openReplies);
|
||||||
|
miscMenu.add(new JSeparator());
|
||||||
|
miscMenu.add(copyPostId);
|
||||||
|
miscMenu.add(copyPostLink);
|
||||||
|
miscMenu.add(new JSeparator());
|
||||||
miscMenu.add(deletePost);
|
miscMenu.add(deletePost);
|
||||||
|
miscMenu.add(new JSeparator());
|
||||||
miscMenu.add(redraftPost);
|
miscMenu.add(redraftPost);
|
||||||
|
|
||||||
Box buttons = Box.createVerticalBox();
|
Box buttons = Box.createVerticalBox();
|
||||||
@ -805,175 +818,3 @@ implements ActionListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class
|
|
||||||
RepliesComponent extends JPanel {
|
|
||||||
|
|
||||||
private List<RepliesComponent.Reply>
|
|
||||||
replies;
|
|
||||||
|
|
||||||
// - -%- -
|
|
||||||
|
|
||||||
private JButton
|
|
||||||
prevPage, nextPage;
|
|
||||||
|
|
||||||
private JLabel
|
|
||||||
pageLabel;
|
|
||||||
|
|
||||||
private ReplyPreviewComponent[]
|
|
||||||
previews;
|
|
||||||
|
|
||||||
// ---%-@-%---
|
|
||||||
|
|
||||||
public void
|
|
||||||
setReplies(List<RepliesComponent.Reply> replies)
|
|
||||||
{
|
|
||||||
assert replies != null;
|
|
||||||
this.replies = replies;
|
|
||||||
displayPage(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// - -%- -
|
|
||||||
|
|
||||||
private void
|
|
||||||
displayPage(int pageNumber)
|
|
||||||
{
|
|
||||||
assert pageNumber > 0;
|
|
||||||
assert this.replies != null;
|
|
||||||
|
|
||||||
List<RepliesComponent.Reply> page;
|
|
||||||
{
|
|
||||||
int oS = (pageNumber - 1) * 8;
|
|
||||||
int oE = Math.min(oS + 8, replies.size());
|
|
||||||
if (oS > oE) page = new ArrayList<>();
|
|
||||||
else page = this.replies.subList(oS, oE);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int o = 0; o < page.size(); ++o)
|
|
||||||
{
|
|
||||||
assert o < previews.length;
|
|
||||||
|
|
||||||
ReplyPreviewComponent preview = previews[o];
|
|
||||||
Reply reply = replies.get(o);
|
|
||||||
preview.setAuthorName(reply.author);
|
|
||||||
preview.setText(reply.text);
|
|
||||||
preview.setVisible(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int o = page.size(); o < previews.length; ++o)
|
|
||||||
{
|
|
||||||
ReplyPreviewComponent preview = previews[o];
|
|
||||||
preview.setVisible(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
int pages = 1 + ((replies.size() - 1) / 8);
|
|
||||||
pageLabel.setText(pageNumber + "/" + pages);
|
|
||||||
prevPage.setEnabled(pageNumber > 1);
|
|
||||||
nextPage.setEnabled(pageNumber < pages);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---%-@-%---
|
|
||||||
|
|
||||||
public static class
|
|
||||||
Reply {
|
|
||||||
|
|
||||||
public String
|
|
||||||
author;
|
|
||||||
|
|
||||||
public String
|
|
||||||
text;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---%-@-%---
|
|
||||||
|
|
||||||
RepliesComponent()
|
|
||||||
{
|
|
||||||
prevPage = new JButton("<");
|
|
||||||
nextPage = new JButton(">");
|
|
||||||
prevPage.setEnabled(false);
|
|
||||||
nextPage.setEnabled(false);
|
|
||||||
|
|
||||||
pageLabel = new JLabel();
|
|
||||||
|
|
||||||
Box bottom = Box.createHorizontalBox();
|
|
||||||
bottom.add(Box.createGlue());
|
|
||||||
bottom.add(prevPage);
|
|
||||||
bottom.add(Box.createHorizontalStrut(8));
|
|
||||||
bottom.add(pageLabel);
|
|
||||||
bottom.add(Box.createHorizontalStrut(8));
|
|
||||||
bottom.add(nextPage);
|
|
||||||
|
|
||||||
JPanel centre = new JPanel();
|
|
||||||
centre.setOpaque(false);
|
|
||||||
centre.setLayout(new GridLayout(0, 1, 0, 2));
|
|
||||||
|
|
||||||
previews = new ReplyPreviewComponent[8];
|
|
||||||
for (int o = 0; o < previews.length; ++o)
|
|
||||||
{
|
|
||||||
previews[o] = new ReplyPreviewComponent();
|
|
||||||
previews[o].setVisible(false);
|
|
||||||
centre.add(previews[o]);
|
|
||||||
}
|
|
||||||
|
|
||||||
setLayout(new BorderLayout(0, 8));
|
|
||||||
add(centre, BorderLayout.CENTER);
|
|
||||||
add(bottom, BorderLayout.SOUTH);
|
|
||||||
|
|
||||||
setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
|
|
||||||
|
|
||||||
setReplies(new ArrayList<>());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class
|
|
||||||
ReplyPreviewComponent extends JButton {
|
|
||||||
|
|
||||||
private String
|
|
||||||
author;
|
|
||||||
|
|
||||||
private String
|
|
||||||
text;
|
|
||||||
|
|
||||||
// ---%-@-%---
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void
|
|
||||||
setText(String text)
|
|
||||||
{
|
|
||||||
assert text != null;
|
|
||||||
this.text = text;
|
|
||||||
setText();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void
|
|
||||||
setAuthorName(String author)
|
|
||||||
{
|
|
||||||
assert author != null;
|
|
||||||
this.author = author;
|
|
||||||
setText();
|
|
||||||
}
|
|
||||||
|
|
||||||
// - -%- -
|
|
||||||
|
|
||||||
private void
|
|
||||||
setText()
|
|
||||||
{
|
|
||||||
StringBuilder text = new StringBuilder();
|
|
||||||
text.append(this.author);
|
|
||||||
text.append(" @ ");
|
|
||||||
text.append(this.text);
|
|
||||||
super.setText(text.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void
|
|
||||||
paintComponent(Graphics g)
|
|
||||||
{
|
|
||||||
g.drawString(getText(), 8, 2 * getHeight() / 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
300
RepliesWindow.java
Executable file
300
RepliesWindow.java
Executable file
@ -0,0 +1,300 @@
|
|||||||
|
|
||||||
|
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 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 void
|
||||||
|
postSelected(Tree<String> post)
|
||||||
|
{
|
||||||
|
postWindow.displayEntity(post);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
item = new TreeItem(desc);
|
||||||
|
p.add(new DefaultMutableTreeNode(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
tree.setModel(new DefaultTreeModel(root));
|
||||||
|
}
|
||||||
|
|
||||||
|
// - -%- -
|
||||||
|
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
String html = post.get("content").value;
|
||||||
|
return TimelineComponent.textApproximation(html);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -=%=-
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -6,7 +6,6 @@ import java.awt.FontMetrics;
|
|||||||
import java.awt.Image;
|
import java.awt.Image;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Toolkit;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.ListIterator;
|
import java.util.ListIterator;
|
||||||
@ -15,16 +14,10 @@ import java.awt.event.MouseMotionListener;
|
|||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.awt.event.KeyListener;
|
import java.awt.event.KeyListener;
|
||||||
import java.awt.event.KeyEvent;
|
import java.awt.event.KeyEvent;
|
||||||
import java.awt.datatransfer.Clipboard;
|
|
||||||
import java.awt.datatransfer.ClipboardOwner;
|
|
||||||
import java.awt.datatransfer.Transferable;
|
|
||||||
import java.awt.datatransfer.DataFlavor;
|
|
||||||
|
|
||||||
class
|
class
|
||||||
RichTextPane extends JComponent
|
RichTextPane extends JComponent
|
||||||
implements
|
implements MouseListener, MouseMotionListener, KeyListener {
|
||||||
MouseListener, MouseMotionListener, KeyListener,
|
|
||||||
Transferable, ClipboardOwner {
|
|
||||||
|
|
||||||
private List<Segment>
|
private List<Segment>
|
||||||
text;
|
text;
|
||||||
@ -56,6 +49,19 @@ implements
|
|||||||
return returnee;
|
return returnee;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void
|
||||||
|
copySelection()
|
||||||
|
{
|
||||||
|
assert selectionEnd != -1;
|
||||||
|
StringBuilder b = new StringBuilder();
|
||||||
|
for (Segment segment: getSelection())
|
||||||
|
{
|
||||||
|
if (segment.link != null) b.append(segment.link);
|
||||||
|
else if (segment.text != null) b.append(segment.text);
|
||||||
|
}
|
||||||
|
ClipboardApi.serve(b.toString());
|
||||||
|
}
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
protected void
|
protected void
|
||||||
@ -83,7 +89,7 @@ implements
|
|||||||
|
|
||||||
if (o > selectionStart && o < selectionEnd)
|
if (o > selectionStart && o < selectionEnd)
|
||||||
{
|
{
|
||||||
int dx = fm.stringWidth(segment.text);
|
int dx = fm.stringWidth(segment.text);
|
||||||
int dy1 = fm.getAscent();
|
int dy1 = fm.getAscent();
|
||||||
int dy2 = dy1 + fm.getDescent();
|
int dy2 = dy1 + fm.getDescent();
|
||||||
g.setColor(new Color(0, 0, 0, 15));
|
g.setColor(new Color(0, 0, 0, 15));
|
||||||
@ -139,54 +145,16 @@ implements
|
|||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String
|
|
||||||
getTransferData(DataFlavor flavour)
|
|
||||||
{
|
|
||||||
assert flavour == DataFlavor.stringFlavor;
|
|
||||||
StringBuilder b = new StringBuilder();
|
|
||||||
for (Segment segment: getSelection())
|
|
||||||
{
|
|
||||||
if (segment.link != null) b.append(segment.link);
|
|
||||||
else if (segment.text != null) b.append(segment.text);
|
|
||||||
}
|
|
||||||
return b.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public DataFlavor[]
|
|
||||||
getTransferDataFlavors()
|
|
||||||
{
|
|
||||||
return new DataFlavor[] { DataFlavor.stringFlavor };
|
|
||||||
/*
|
|
||||||
* We should probably also support javaJVMLocalObjectMimeType,
|
|
||||||
* so that the compose window can ask for the List<Segment>.
|
|
||||||
* Although also like, if we don't store emoji shortcodes in
|
|
||||||
* the image segments, there is no point. Anyways, what is
|
|
||||||
* important is the string format first, allowing us to
|
|
||||||
* copy links or large lengths of text.
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean
|
|
||||||
isDataFlavorSupported(DataFlavor flavour)
|
|
||||||
{
|
|
||||||
return flavour == DataFlavor.stringFlavor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void
|
public void
|
||||||
keyPressed(KeyEvent eK)
|
keyPressed(KeyEvent eK)
|
||||||
{
|
{
|
||||||
if (selectionEnd == -1) return;
|
if (selectionEnd == -1) return;
|
||||||
if (eK.getKeyCode() != KeyEvent.VK_C) return;
|
if (eK.getKeyCode() != KeyEvent.VK_C) return;
|
||||||
if (!eK.isControlDown()) return;
|
if (!eK.isControlDown()) return;
|
||||||
Toolkit tk = Toolkit.getDefaultToolkit();
|
copySelection();
|
||||||
Clipboard cb = tk.getSystemClipboard();
|
|
||||||
cb.setContents(this, this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void
|
|
||||||
lostOwnership(Clipboard clipboard, Transferable contents) { }
|
|
||||||
|
|
||||||
public void
|
public void
|
||||||
keyReleased(KeyEvent eK) { }
|
keyReleased(KeyEvent eK) { }
|
||||||
|
|
||||||
@ -222,7 +190,7 @@ implements
|
|||||||
while (cursor.hasNext())
|
while (cursor.hasNext())
|
||||||
{
|
{
|
||||||
Segment curr = cursor.next();
|
Segment curr = cursor.next();
|
||||||
|
|
||||||
int dx;
|
int dx;
|
||||||
if (curr.image != null) {
|
if (curr.image != null) {
|
||||||
int ow = curr.image.getIconWidth();
|
int ow = curr.image.getIconWidth();
|
||||||
@ -243,7 +211,7 @@ implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
boolean fits = x + dx < width;
|
boolean fits = x + dx < width;
|
||||||
|
|
||||||
if (fits || curr.spacer)
|
if (fits || curr.spacer)
|
||||||
{
|
{
|
||||||
curr.x = x;
|
curr.x = x;
|
||||||
@ -434,4 +402,4 @@ implements
|
|||||||
addKeyListener(this);
|
addKeyListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
BIN
graphics/test1.png
Executable file
BIN
graphics/test1.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
BIN
graphics/test2.png
Executable file
BIN
graphics/test2.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 2.2 KiB |
BIN
graphics/test3.png
Executable file
BIN
graphics/test3.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 2.2 KiB |
BIN
graphics/test4.png
Executable file
BIN
graphics/test4.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
10
notifOptions.txt
Normal file
10
notifOptions.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
KDE_Chimes_1.ogg
|
||||||
|
KDE_Dialog_Appear.wav
|
||||||
|
KDE_Event_1.ogg
|
||||||
|
KDE_Event_2.ogg
|
||||||
|
KDE_Logout_3.ogg
|
||||||
|
KDE_TypeWriter_Bell.ogg
|
||||||
|
KDE_Window_DeIconify.ogg
|
||||||
|
KDE_Window_Iconify.ogg
|
||||||
|
KDE_Window_UnMaximize.wav
|
||||||
|
pop.wav
|
9
notifOptions.txt~
Normal file
9
notifOptions.txt~
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
KDE_Chimes_1.ogg
|
||||||
|
KDE_Dialog_Appear.wav
|
||||||
|
KDE_Event_1.ogg
|
||||||
|
KDE_Event_2.ogg
|
||||||
|
KDE_Logout_3.ogg
|
||||||
|
KDE_TypeWriter_Bell.ogg
|
||||||
|
KDE_Window_DeIconify.ogg
|
||||||
|
KDE_Window_Iconify.ogg
|
||||||
|
KDE_Window_UnMaximize.wav
|
5
run
5
run
@ -1,12 +1,13 @@
|
|||||||
#!/usr/bin/make -f
|
#!/usr/bin/make -f
|
||||||
|
|
||||||
CLASSPATH=.:../Hinoki:/usr/share/java/javax.json.jar
|
CLASSPATH=.:../Hinoki:/usr/share/java/javax.json.jar
|
||||||
|
OPTIONS=
|
||||||
|
|
||||||
c:
|
c:
|
||||||
javac -cp $(CLASSPATH) *.java
|
javac -cp $(CLASSPATH) $(OPTIONS) *.java
|
||||||
|
|
||||||
r:
|
r:
|
||||||
java -cp $(CLASSPATH) -ea JKomasto
|
java -cp $(CLASSPATH) $(OPTIONS) -ea JKomasto
|
||||||
|
|
||||||
cr: c r
|
cr: c r
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user