mirror of
https://gitlab.com/biskuteri-cafe/JKomasto2.git
synced 2025-01-08 03:34:44 +01:00
Normalised all tabs to 4 spaces, for easier web viewing.
This commit is contained in:
parent
2719fabef9
commit
fa9a5edab0
@ -29,308 +29,308 @@ import cafe.biskuteri.hinoki.Tree;
|
|||||||
interface
|
interface
|
||||||
BasicHTMLParser {
|
BasicHTMLParser {
|
||||||
|
|
||||||
public static Tree<String>
|
public static Tree<String>
|
||||||
parse(String html)
|
parse(String html)
|
||||||
{
|
{
|
||||||
List<String> segments;
|
List<String> segments;
|
||||||
segments = distinguishTagsFromPcdata(html);
|
segments = distinguishTagsFromPcdata(html);
|
||||||
|
|
||||||
Tree<String> document;
|
Tree<String> document;
|
||||||
document = toNodes(segments);
|
document = toNodes(segments);
|
||||||
document = splitText(document);
|
document = splitText(document);
|
||||||
document = evaluateHtmlEscapes(document);
|
document = evaluateHtmlEscapes(document);
|
||||||
document = hierarchise(document);
|
document = hierarchise(document);
|
||||||
|
|
||||||
return document;
|
return document;
|
||||||
}
|
}
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
private static List<String>
|
private static List<String>
|
||||||
distinguishTagsFromPcdata(String html)
|
distinguishTagsFromPcdata(String html)
|
||||||
{
|
{
|
||||||
List<String> returnee = new ArrayList<>();
|
List<String> returnee = new ArrayList<>();
|
||||||
StringBuilder segment = new StringBuilder();
|
StringBuilder segment = new StringBuilder();
|
||||||
boolean inTag = false;
|
boolean inTag = false;
|
||||||
for (char c: html.toCharArray())
|
for (char c: html.toCharArray())
|
||||||
{
|
{
|
||||||
if (c == '<')
|
if (c == '<')
|
||||||
{
|
{
|
||||||
String addee = empty(segment);
|
String addee = empty(segment);
|
||||||
if (!addee.isEmpty()) returnee.add(addee);
|
if (!addee.isEmpty()) returnee.add(addee);
|
||||||
inTag = true;
|
inTag = true;
|
||||||
segment.append(c);
|
segment.append(c);
|
||||||
}
|
}
|
||||||
else if (c == '>')
|
else if (c == '>')
|
||||||
{
|
{
|
||||||
assert inTag;
|
assert inTag;
|
||||||
assert segment.length() > 0;
|
assert segment.length() > 0;
|
||||||
segment.append(c);
|
segment.append(c);
|
||||||
returnee.add(empty(segment));
|
returnee.add(empty(segment));
|
||||||
inTag = false;
|
inTag = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
segment.append(c);
|
segment.append(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
String addee = empty(segment);
|
String addee = empty(segment);
|
||||||
if (!addee.isEmpty()) returnee.add(addee);
|
if (!addee.isEmpty()) returnee.add(addee);
|
||||||
|
|
||||||
return returnee;
|
return returnee;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Tree<String>
|
private static Tree<String>
|
||||||
toNodes(List<String> segments)
|
toNodes(List<String> segments)
|
||||||
{
|
{
|
||||||
Tree<String> returnee = new Tree<String>();
|
Tree<String> returnee = new Tree<String>();
|
||||||
|
|
||||||
for (String segment: segments)
|
for (String segment: segments)
|
||||||
{
|
{
|
||||||
boolean isTag = segment.startsWith("<");
|
boolean isTag = segment.startsWith("<");
|
||||||
Tree<String> node = new Tree<String>();
|
Tree<String> node = new Tree<String>();
|
||||||
|
|
||||||
if (!isTag)
|
if (!isTag)
|
||||||
{
|
{
|
||||||
node.key = "text";
|
node.key = "text";
|
||||||
node.value = segment;
|
node.value = segment;
|
||||||
returnee.add(node);
|
returnee.add(node);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
node.key = "tag";
|
node.key = "tag";
|
||||||
|
|
||||||
String key = null, value = null;
|
String key = null, value = null;
|
||||||
StringBuilder b = new StringBuilder();
|
StringBuilder b = new StringBuilder();
|
||||||
boolean inQuotes = false, inValue = false;
|
boolean inQuotes = false, inValue = false;
|
||||||
char[] chars = segment.toCharArray();
|
char[] chars = segment.toCharArray();
|
||||||
for (int o = 1; o < chars.length - 1; ++o)
|
for (int o = 1; o < chars.length - 1; ++o)
|
||||||
{
|
{
|
||||||
char c = chars[o];
|
char c = chars[o];
|
||||||
if (c == '"')
|
if (c == '"')
|
||||||
{
|
{
|
||||||
inQuotes = !inQuotes;
|
inQuotes = !inQuotes;
|
||||||
}
|
}
|
||||||
else if (inQuotes)
|
else if (inQuotes)
|
||||||
{
|
{
|
||||||
b.append(c);
|
b.append(c);
|
||||||
}
|
}
|
||||||
else if (c == '=')
|
else if (c == '=')
|
||||||
{
|
{
|
||||||
assert b.length() > 0;
|
assert b.length() > 0;
|
||||||
key = empty(b);
|
key = empty(b);
|
||||||
inValue = true;
|
inValue = true;
|
||||||
}
|
}
|
||||||
else if (Character.isWhitespace(c))
|
else if (Character.isWhitespace(c))
|
||||||
{
|
{
|
||||||
if (b.length() > 0)
|
if (b.length() > 0)
|
||||||
{
|
{
|
||||||
if (inValue) value = empty(b);
|
if (inValue) value = empty(b);
|
||||||
else key = empty(b);
|
else key = empty(b);
|
||||||
Tree<String> attr = new Tree<String>();
|
Tree<String> attr = new Tree<String>();
|
||||||
attr.key = key;
|
attr.key = key;
|
||||||
attr.value = value;
|
attr.value = value;
|
||||||
node.add(attr);
|
node.add(attr);
|
||||||
}
|
}
|
||||||
inValue = false;
|
inValue = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
b.append(c);
|
b.append(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (b.length() > 0)
|
if (b.length() > 0)
|
||||||
{
|
{
|
||||||
if (inValue) value = empty(b);
|
if (inValue) value = empty(b);
|
||||||
else key = empty(b);
|
else key = empty(b);
|
||||||
Tree<String> attr = new Tree<String>();
|
Tree<String> attr = new Tree<String>();
|
||||||
attr.key = key;
|
attr.key = key;
|
||||||
attr.value = value;
|
attr.value = value;
|
||||||
node.add(attr);
|
node.add(attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
returnee.add(node);
|
returnee.add(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
return returnee;
|
return returnee;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Tree<String>
|
private static Tree<String>
|
||||||
splitText(Tree<String> nodes)
|
splitText(Tree<String> nodes)
|
||||||
{
|
{
|
||||||
Tree<String> returnee = new Tree<>();
|
Tree<String> returnee = new Tree<>();
|
||||||
|
|
||||||
for (Tree<String> node: nodes)
|
for (Tree<String> node: nodes)
|
||||||
{
|
{
|
||||||
if (node.key.equals("tag"))
|
if (node.key.equals("tag"))
|
||||||
{
|
{
|
||||||
returnee.add(node);
|
returnee.add(node);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
assert node.key.equals("text");
|
assert node.key.equals("text");
|
||||||
|
|
||||||
StringBuilder b = new StringBuilder();
|
StringBuilder b = new StringBuilder();
|
||||||
boolean alnum = false, calnum;
|
boolean alnum = false, calnum;
|
||||||
boolean space = false, cspace;
|
boolean space = false, cspace;
|
||||||
boolean emoji = false;
|
boolean emoji = false;
|
||||||
for (char c: node.value.toCharArray())
|
for (char c: node.value.toCharArray())
|
||||||
{
|
{
|
||||||
calnum = isMastodonAlnum(c);
|
calnum = isMastodonAlnum(c);
|
||||||
cspace = Character.isWhitespace(c);
|
cspace = Character.isWhitespace(c);
|
||||||
|
|
||||||
if (c == ':' && !emoji)
|
if (c == ':' && !emoji)
|
||||||
{
|
{
|
||||||
// See note on #isMastodonAlnum.
|
// See note on #isMastodonAlnum.
|
||||||
|
|
||||||
if (b.length() > 0)
|
|
||||||
{
|
|
||||||
Tree<String> addee = new Tree<>();
|
|
||||||
addee.key = space ? "space" : "text";
|
|
||||||
addee.value = empty(b);
|
|
||||||
returnee.add(addee);
|
|
||||||
}
|
|
||||||
emoji = true;
|
|
||||||
b.append(c);
|
|
||||||
}
|
|
||||||
else if (c == ':' && emoji)
|
|
||||||
{
|
|
||||||
assert !space;
|
|
||||||
b.append(c);
|
|
||||||
Tree<String> addee = new Tree<>();
|
|
||||||
addee.key = "emoji";
|
|
||||||
addee.value = empty(b);
|
|
||||||
returnee.add(addee);
|
|
||||||
/*
|
|
||||||
* Technically, addee.value.length()
|
|
||||||
* could be zero, which probably means
|
|
||||||
* someone just put two colons in a row,
|
|
||||||
* maybe for Haskell source code. I'd
|
|
||||||
* be surprised if Mastodon didn't escape
|
|
||||||
* it. (If they did, the next step will
|
|
||||||
* handle them.) Anyways treating it as
|
|
||||||
* an empty emoji is the correct action.
|
|
||||||
*/
|
|
||||||
emoji = false;
|
|
||||||
calnum = false;
|
|
||||||
}
|
|
||||||
else if (cspace != space)
|
|
||||||
{
|
|
||||||
if (b.length() > 0)
|
if (b.length() > 0)
|
||||||
{
|
{
|
||||||
Tree<String> addee = new Tree<>();
|
Tree<String> addee = new Tree<>();
|
||||||
addee.key = space ? "space" : "text";
|
addee.key = space ? "space" : "text";
|
||||||
addee.value = empty(b);
|
addee.value = empty(b);
|
||||||
returnee.add(addee);
|
returnee.add(addee);
|
||||||
}
|
}
|
||||||
b.append(c);
|
emoji = true;
|
||||||
}
|
b.append(c);
|
||||||
else
|
}
|
||||||
{
|
else if (c == ':' && emoji)
|
||||||
b.append(c);
|
{
|
||||||
}
|
assert !space;
|
||||||
/*
|
b.append(c);
|
||||||
* We can specially handle special
|
Tree<String> addee = new Tree<>();
|
||||||
* characters like \n, but I'll opt not to.
|
addee.key = "emoji";
|
||||||
*/
|
addee.value = empty(b);
|
||||||
|
returnee.add(addee);
|
||||||
|
/*
|
||||||
|
* Technically, addee.value.length()
|
||||||
|
* could be zero, which probably means
|
||||||
|
* someone just put two colons in a row,
|
||||||
|
* maybe for Haskell source code. I'd
|
||||||
|
* be surprised if Mastodon didn't escape
|
||||||
|
* it. (If they did, the next step will
|
||||||
|
* handle them.) Anyways treating it as
|
||||||
|
* an empty emoji is the correct action.
|
||||||
|
*/
|
||||||
|
emoji = false;
|
||||||
|
calnum = false;
|
||||||
|
}
|
||||||
|
else if (cspace != space)
|
||||||
|
{
|
||||||
|
if (b.length() > 0)
|
||||||
|
{
|
||||||
|
Tree<String> addee = new Tree<>();
|
||||||
|
addee.key = space ? "space" : "text";
|
||||||
|
addee.value = empty(b);
|
||||||
|
returnee.add(addee);
|
||||||
|
}
|
||||||
|
b.append(c);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
b.append(c);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* We can specially handle special
|
||||||
|
* characters like \n, but I'll opt not to.
|
||||||
|
*/
|
||||||
|
|
||||||
alnum = calnum;
|
alnum = calnum;
|
||||||
space = cspace;
|
space = cspace;
|
||||||
}
|
}
|
||||||
if (b.length() > 0)
|
if (b.length() > 0)
|
||||||
{
|
{
|
||||||
Tree<String> addee = new Tree<>();
|
Tree<String> addee = new Tree<>();
|
||||||
addee.key = space ? "space" : "text";
|
addee.key = space ? "space" : "text";
|
||||||
addee.value = empty(b);
|
addee.value = empty(b);
|
||||||
returnee.add(addee);
|
returnee.add(addee);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return returnee;
|
return returnee;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Tree<String>
|
private static Tree<String>
|
||||||
evaluateHtmlEscapes(Tree<String> nodes)
|
evaluateHtmlEscapes(Tree<String> nodes)
|
||||||
{
|
{
|
||||||
for (Tree<String> node: nodes)
|
for (Tree<String> node: nodes)
|
||||||
{
|
{
|
||||||
node.value = evaluateHtmlEscapes(node.value);
|
node.value = evaluateHtmlEscapes(node.value);
|
||||||
for (Tree<String> attr: node)
|
for (Tree<String> attr: node)
|
||||||
{
|
{
|
||||||
attr.key = evaluateHtmlEscapes(attr.key);
|
attr.key = evaluateHtmlEscapes(attr.key);
|
||||||
attr.value = evaluateHtmlEscapes(attr.value);
|
attr.value = evaluateHtmlEscapes(attr.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Tree<String>
|
private static Tree<String>
|
||||||
hierarchise(Tree<String> nodes)
|
hierarchise(Tree<String> nodes)
|
||||||
{
|
{
|
||||||
Tree<String> root = new Tree<String>();
|
Tree<String> root = new Tree<String>();
|
||||||
root.key = "tag";
|
root.key = "tag";
|
||||||
root.add(new Tree<>("html", null));
|
root.add(new Tree<>("html", null));
|
||||||
root.add(new Tree<>("children", null));
|
root.add(new Tree<>("children", null));
|
||||||
|
|
||||||
Deque<Tree<String>> parents = new LinkedList<>();
|
Deque<Tree<String>> parents = new LinkedList<>();
|
||||||
parents.push(root);
|
parents.push(root);
|
||||||
for (Tree<String> node: nodes)
|
for (Tree<String> node: nodes)
|
||||||
{
|
{
|
||||||
if (node.key.equals("tag"))
|
if (node.key.equals("tag"))
|
||||||
{
|
{
|
||||||
assert node.size() > 0;
|
assert node.size() > 0;
|
||||||
String tagName = node.get(0).key;
|
String tagName = node.get(0).key;
|
||||||
|
|
||||||
assert node.get("children") == null;
|
assert node.get("children") == null;
|
||||||
node.add(new Tree<>("children", null));
|
node.add(new Tree<>("children", null));
|
||||||
|
|
||||||
boolean isClosing, selfClosing;
|
boolean isClosing, selfClosing;
|
||||||
isClosing = tagName.startsWith("/");
|
isClosing = tagName.startsWith("/");
|
||||||
selfClosing = node.get("/") != null;
|
selfClosing = node.get("/") != null;
|
||||||
selfClosing |= tagName.equals("br");
|
selfClosing |= tagName.equals("br");
|
||||||
if (isClosing)
|
if (isClosing)
|
||||||
{
|
{
|
||||||
assert parents.size() > 1;
|
assert parents.size() > 1;
|
||||||
|
|
||||||
Tree<String> parent, grandparent;
|
Tree<String> parent, grandparent;
|
||||||
parent = parents.pop();
|
parent = parents.pop();
|
||||||
grandparent = parents.peek();
|
grandparent = parents.peek();
|
||||||
|
|
||||||
String pTagName = parent.get(0).key;
|
String pTagName = parent.get(0).key;
|
||||||
assert tagName.equals("/" + pTagName);
|
assert tagName.equals("/" + pTagName);
|
||||||
|
|
||||||
grandparent.get("children").add(parent);
|
grandparent.get("children").add(parent);
|
||||||
}
|
}
|
||||||
else if (selfClosing)
|
else if (selfClosing)
|
||||||
{
|
{
|
||||||
parents.peek().get("children").add(node);
|
parents.peek().get("children").add(node);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
parents.push(node);
|
parents.push(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
parents.peek().get("children").add(node);
|
parents.peek().get("children").add(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assert parents.size() == 1;
|
assert parents.size() == 1;
|
||||||
return parents.pop();
|
return parents.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String
|
private static String
|
||||||
empty(StringBuilder b)
|
empty(StringBuilder b)
|
||||||
{
|
{
|
||||||
String s = b.toString();
|
String s = b.toString();
|
||||||
b.delete(0, b.length());
|
b.delete(0, b.length());
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean
|
private static boolean
|
||||||
isMastodonAlnum(char c)
|
isMastodonAlnum(char c)
|
||||||
{
|
{
|
||||||
return Character.isLetterOrDigit(c);
|
return Character.isLetterOrDigit(c);
|
||||||
/*
|
/*
|
||||||
* Not joking. Mastodon is using the POSIX :alnum: regex
|
* Not joking. Mastodon is using the POSIX :alnum: regex
|
||||||
@ -343,44 +343,44 @@ BasicHTMLParser {
|
|||||||
* by text, then try again with the same emoji also
|
* by text, then try again with the same emoji also
|
||||||
* present elsewhere in the post at a valid position.)
|
* present elsewhere in the post at a valid position.)
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String
|
private static String
|
||||||
evaluateHtmlEscapes(String string)
|
evaluateHtmlEscapes(String string)
|
||||||
{
|
{
|
||||||
if (string == null) return string;
|
if (string == null) return string;
|
||||||
|
|
||||||
StringBuilder whole = new StringBuilder();
|
StringBuilder whole = new StringBuilder();
|
||||||
StringBuilder part = new StringBuilder();
|
StringBuilder part = new StringBuilder();
|
||||||
boolean inEscape = false;
|
boolean inEscape = false;
|
||||||
for (char c: string.toCharArray())
|
for (char c: string.toCharArray())
|
||||||
{
|
{
|
||||||
if (inEscape && c == ';')
|
if (inEscape && c == ';')
|
||||||
{
|
{
|
||||||
part.append(c);
|
part.append(c);
|
||||||
inEscape = false;
|
inEscape = false;
|
||||||
String v = empty(part);
|
String v = empty(part);
|
||||||
if (v.equals("<")) part.append('<');
|
if (v.equals("<")) part.append('<');
|
||||||
if (v.equals(">")) part.append('>');
|
if (v.equals(">")) part.append('>');
|
||||||
if (v.equals("&")) part.append('&');
|
if (v.equals("&")) part.append('&');
|
||||||
if (v.equals(""")) part.append('"');
|
if (v.equals(""")) part.append('"');
|
||||||
if (v.equals("'")) part.append('\'');
|
if (v.equals("'")) part.append('\'');
|
||||||
if (v.equals("'")) part.append('\'');
|
if (v.equals("'")) part.append('\'');
|
||||||
}
|
}
|
||||||
else if (!inEscape && c == '&')
|
else if (!inEscape && c == '&')
|
||||||
{
|
{
|
||||||
String v = empty(part);
|
String v = empty(part);
|
||||||
if (!v.isEmpty()) whole.append(v);
|
if (!v.isEmpty()) whole.append(v);
|
||||||
part.append(c);
|
part.append(c);
|
||||||
inEscape = true;
|
inEscape = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
part.append(c);
|
part.append(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
String v = empty(part);
|
String v = empty(part);
|
||||||
if (!v.isEmpty()) whole.append(v);
|
if (!v.isEmpty()) whole.append(v);
|
||||||
return whole.toString();
|
return whole.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,41 +41,41 @@ implements Transferable, ClipboardOwner {
|
|||||||
{
|
{
|
||||||
assert string != null;
|
assert string != null;
|
||||||
instance.string = string;
|
instance.string = string;
|
||||||
Toolkit tk = Toolkit.getDefaultToolkit();
|
Toolkit tk = Toolkit.getDefaultToolkit();
|
||||||
Clipboard cb = tk.getSystemClipboard();
|
Clipboard cb = tk.getSystemClipboard();
|
||||||
cb.setContents(instance, instance);
|
cb.setContents(instance, instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
public String
|
public String
|
||||||
getTransferData(DataFlavor flavour)
|
getTransferData(DataFlavor flavour)
|
||||||
{
|
{
|
||||||
assert flavour == DataFlavor.stringFlavor;
|
assert flavour == DataFlavor.stringFlavor;
|
||||||
return string;
|
return string;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataFlavor[]
|
public DataFlavor[]
|
||||||
getTransferDataFlavors()
|
getTransferDataFlavors()
|
||||||
{
|
{
|
||||||
return new DataFlavor[] { DataFlavor.stringFlavor };
|
return new DataFlavor[] { DataFlavor.stringFlavor };
|
||||||
/*
|
/*
|
||||||
* We should probably also support javaJVMLocalObjectMimeType,
|
* We should probably also support javaJVMLocalObjectMimeType,
|
||||||
* so that the compose window can ask for the List<Segment>.
|
* so that the compose window can ask for the List<Segment>.
|
||||||
* Although also like, if we don't store emoji shortcodes in
|
* Although also like, if we don't store emoji shortcodes in
|
||||||
* the image segments, there is no point. Anyways, what is
|
* the image segments, there is no point. Anyways, what is
|
||||||
* important is the string format first, allowing us to
|
* important is the string format first, allowing us to
|
||||||
* copy links or large lengths of text.
|
* copy links or large lengths of text.
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean
|
public boolean
|
||||||
isDataFlavorSupported(DataFlavor flavour)
|
isDataFlavorSupported(DataFlavor flavour)
|
||||||
{
|
{
|
||||||
return flavour == DataFlavor.stringFlavor;
|
return flavour == DataFlavor.stringFlavor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
lostOwnership(Clipboard clipboard, Transferable contents) { }
|
lostOwnership(Clipboard clipboard, Transferable contents) { }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -126,86 +126,86 @@ ComposeWindow extends JFrame {
|
|||||||
if (composition.contentWarning != null)
|
if (composition.contentWarning != null)
|
||||||
assert !composition.contentWarning.trim().isEmpty();
|
assert !composition.contentWarning.trim().isEmpty();
|
||||||
|
|
||||||
//tabs.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
//tabs.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||||
/*
|
/*
|
||||||
* setCursor only works for components that are enabled.
|
* setCursor only works for components that are enabled.
|
||||||
* I don't think there's any technical reason for this,
|
* I don't think there's any technical reason for this,
|
||||||
* but it's what it is.. rely on the enablement visuals
|
* but it's what it is.. rely on the enablement visuals
|
||||||
* or a statusbar to indicate to user.
|
* or a statusbar to indicate to user.
|
||||||
*
|
*
|
||||||
* If we really wanted it, I suspect we could use a glass
|
* If we really wanted it, I suspect we could use a glass
|
||||||
* pane (or a scroll pane with no scrolling) to have an
|
* pane (or a scroll pane with no scrolling) to have an
|
||||||
* enabled pass-through on top.
|
* enabled pass-through on top.
|
||||||
*
|
*
|
||||||
* Technically contentsDisplay and attachmentsDisplay
|
* Technically contentsDisplay and attachmentsDisplay
|
||||||
* themselves aren't disabled. But disabling the tab pane
|
* themselves aren't disabled. But disabling the tab pane
|
||||||
* covers both the tab area and content area. We can't
|
* covers both the tab area and content area. We can't
|
||||||
* just the tab area, except maybe by disabling the
|
* just the tab area, except maybe by disabling the
|
||||||
* individual tabs.
|
* individual tabs.
|
||||||
*/
|
*/
|
||||||
tabs.setEnabled(false);
|
tabs.setEnabled(false);
|
||||||
tabs.setSelectedComponent(contentsDisplay);
|
tabs.setSelectedComponent(contentsDisplay);
|
||||||
contentsDisplay.setSubmitting(true);
|
contentsDisplay.setSubmitting(true);
|
||||||
tabs.paintImmediately(tabs.getBounds());
|
tabs.paintImmediately(tabs.getBounds());
|
||||||
|
|
||||||
boolean uploadsOkay = true;
|
boolean uploadsOkay = true;
|
||||||
for (Attachment a: composition.attachments)
|
for (Attachment a: composition.attachments)
|
||||||
{
|
{
|
||||||
if (a.id != null) continue;
|
if (a.id != null) continue;
|
||||||
// Assume it had already been uploaded.
|
// Assume it had already been uploaded.
|
||||||
|
|
||||||
api.uploadFile(
|
api.uploadFile(
|
||||||
a.uploadee, a.description,
|
a.uploadee, a.description,
|
||||||
new RequestListener() {
|
new RequestListener() {
|
||||||
|
|
||||||
public void
|
public void
|
||||||
connectionFailed(IOException eIo)
|
connectionFailed(IOException eIo)
|
||||||
{
|
{
|
||||||
JOptionPane.showMessageDialog(
|
JOptionPane.showMessageDialog(
|
||||||
ComposeWindow.this,
|
ComposeWindow.this,
|
||||||
"Tried to upload attachment, failed..."
|
"Tried to upload attachment, failed..."
|
||||||
+ "\n" + eIo.getMessage()
|
+ "\n" + eIo.getMessage()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
requestFailed(int httpCode, Tree<String> json)
|
requestFailed(int httpCode, Tree<String> json)
|
||||||
{
|
{
|
||||||
JOptionPane.showMessageDialog(
|
JOptionPane.showMessageDialog(
|
||||||
ComposeWindow.this,
|
ComposeWindow.this,
|
||||||
"Tried to upload attachment, failed..."
|
"Tried to upload attachment, failed..."
|
||||||
+ "\n" + json.get("error").value
|
+ "\n" + json.get("error").value
|
||||||
+ "\n(HTTP code: " + httpCode + ")"
|
+ "\n(HTTP code: " + httpCode + ")"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
requestSucceeded(Tree<String> json)
|
requestSucceeded(Tree<String> json)
|
||||||
{
|
{
|
||||||
a.id = json.get("id").value;
|
a.id = json.get("id").value;
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
uploadsOkay &= a.id != null;
|
uploadsOkay &= a.id != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!uploadsOkay)
|
if (!uploadsOkay)
|
||||||
{
|
{
|
||||||
contentsDisplay.setSubmitting(false);
|
contentsDisplay.setSubmitting(false);
|
||||||
tabs.setEnabled(true);
|
tabs.setEnabled(true);
|
||||||
//tabs.setCursor(null);
|
//tabs.setCursor(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int amt = composition.attachments.length;
|
int amt = composition.attachments.length;
|
||||||
String[] mediaIDs = new String[amt];
|
String[] mediaIDs = new String[amt];
|
||||||
for (int o = 0; o < mediaIDs.length; ++o)
|
for (int o = 0; o < mediaIDs.length; ++o)
|
||||||
mediaIDs[o] = composition.attachments[o].id;
|
mediaIDs[o] = composition.attachments[o].id;
|
||||||
|
|
||||||
api.submit(
|
api.submit(
|
||||||
composition.text, composition.visibility,
|
composition.text, composition.visibility,
|
||||||
composition.replyToPostId, composition.contentWarning,
|
composition.replyToPostId, composition.contentWarning,
|
||||||
mediaIDs,
|
mediaIDs,
|
||||||
new RequestListener() {
|
new RequestListener() {
|
||||||
|
|
||||||
public void
|
public void
|
||||||
@ -239,8 +239,8 @@ ComposeWindow extends JFrame {
|
|||||||
);
|
);
|
||||||
|
|
||||||
contentsDisplay.setSubmitting(false);
|
contentsDisplay.setSubmitting(false);
|
||||||
tabs.setEnabled(true);
|
tabs.setEnabled(true);
|
||||||
tabs.setCursor(null);
|
tabs.setCursor(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
@ -254,14 +254,14 @@ ComposeWindow extends JFrame {
|
|||||||
d1.setVisibility(stringFor(composition.visibility));
|
d1.setVisibility(stringFor(composition.visibility));
|
||||||
d1.setContentWarning(composition.contentWarning);
|
d1.setContentWarning(composition.contentWarning);
|
||||||
|
|
||||||
AttachmentsComponent d2 = attachmentsDisplay;
|
AttachmentsComponent d2 = attachmentsDisplay;
|
||||||
d2.setAttachments(composition.attachments);
|
d2.setAttachments(composition.attachments);
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void
|
private synchronized void
|
||||||
syncCompositionToDisplay()
|
syncCompositionToDisplay()
|
||||||
{
|
{
|
||||||
Composition c = composition;
|
Composition c = composition;
|
||||||
|
|
||||||
ComposeComponent d1 = contentsDisplay;
|
ComposeComponent d1 = contentsDisplay;
|
||||||
c.text = d1.getText();
|
c.text = d1.getText();
|
||||||
@ -269,8 +269,8 @@ ComposeWindow extends JFrame {
|
|||||||
c.replyToPostId = nonEmpty(d1.getReplyToPostId());
|
c.replyToPostId = nonEmpty(d1.getReplyToPostId());
|
||||||
c.contentWarning = nonEmpty(d1.getContentWarning());
|
c.contentWarning = nonEmpty(d1.getContentWarning());
|
||||||
|
|
||||||
AttachmentsComponent d2 = attachmentsDisplay;
|
AttachmentsComponent d2 = attachmentsDisplay;
|
||||||
c.attachments = d2.getAttachments();
|
c.attachments = d2.getAttachments();
|
||||||
}
|
}
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
@ -509,18 +509,18 @@ implements ActionListener, CaretListener, KeyListener {
|
|||||||
Border b3 = BorderFactory.createLineBorder(Color.GRAY);
|
Border b3 = BorderFactory.createLineBorder(Color.GRAY);
|
||||||
Border bc = BorderFactory.createCompoundBorder(b3, b2);
|
Border bc = BorderFactory.createCompoundBorder(b3, b2);
|
||||||
|
|
||||||
TextActionPopupMenu textActionPopup;
|
TextActionPopupMenu textActionPopup;
|
||||||
textActionPopup = new TextActionPopupMenu();
|
textActionPopup = new TextActionPopupMenu();
|
||||||
|
|
||||||
reply = new JTextField();
|
reply = new JTextField();
|
||||||
JLabel replyLabel = new JLabel("In reply to: ");
|
JLabel replyLabel = new JLabel("In reply to: ");
|
||||||
replyLabel.setLabelFor(reply);
|
replyLabel.setLabelFor(reply);
|
||||||
reply.addMouseListener(textActionPopup);
|
reply.addMouseListener(textActionPopup);
|
||||||
|
|
||||||
contentWarning = new JTextField();
|
contentWarning = new JTextField();
|
||||||
JLabel cwLabel = new JLabel("Content warning: ");
|
JLabel cwLabel = new JLabel("Content warning: ");
|
||||||
cwLabel.setLabelFor(contentWarning);
|
cwLabel.setLabelFor(contentWarning);
|
||||||
contentWarning.addMouseListener(textActionPopup);
|
contentWarning.addMouseListener(textActionPopup);
|
||||||
|
|
||||||
JPanel top = new JPanel();
|
JPanel top = new JPanel();
|
||||||
top.setOpaque(false);
|
top.setOpaque(false);
|
||||||
@ -560,7 +560,7 @@ implements ActionListener, CaretListener, KeyListener {
|
|||||||
text.setBorder(bc);
|
text.setBorder(bc);
|
||||||
text.addCaretListener(this);
|
text.addCaretListener(this);
|
||||||
text.addKeyListener(this);
|
text.addKeyListener(this);
|
||||||
text.addMouseListener(textActionPopup);
|
text.addMouseListener(textActionPopup);
|
||||||
|
|
||||||
setLayout(new BorderLayout(0, 8));
|
setLayout(new BorderLayout(0, 8));
|
||||||
add(top, BorderLayout.NORTH);
|
add(top, BorderLayout.NORTH);
|
||||||
@ -592,7 +592,7 @@ implements ComponentListener, ActionListener {
|
|||||||
attachment2,
|
attachment2,
|
||||||
attachment3,
|
attachment3,
|
||||||
attachment4,
|
attachment4,
|
||||||
selected;
|
selected;
|
||||||
|
|
||||||
private JButton
|
private JButton
|
||||||
add;
|
add;
|
||||||
@ -607,83 +607,83 @@ implements ComponentListener, ActionListener {
|
|||||||
private JTextArea
|
private JTextArea
|
||||||
description;
|
description;
|
||||||
|
|
||||||
private JFileChooser
|
private JFileChooser
|
||||||
chooser;
|
chooser;
|
||||||
|
|
||||||
private UndoManager
|
private UndoManager
|
||||||
descriptionUndos;
|
descriptionUndos;
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
public void
|
public void
|
||||||
setAttachments(Attachment[] n)
|
setAttachments(Attachment[] n)
|
||||||
{
|
{
|
||||||
working.clear();
|
working.clear();
|
||||||
if (n != null) for (Attachment attachment: n)
|
if (n != null) for (Attachment attachment: n)
|
||||||
{
|
{
|
||||||
working.add(attachment);
|
working.add(attachment);
|
||||||
}
|
}
|
||||||
updateButtons();
|
updateButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Attachment[]
|
public Attachment[]
|
||||||
getAttachments()
|
getAttachments()
|
||||||
{
|
{
|
||||||
return working.toArray(new Attachment[0]);
|
return working.toArray(new Attachment[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
private void
|
private void
|
||||||
updateButtons()
|
updateButtons()
|
||||||
{
|
{
|
||||||
Dimension sz = add.getPreferredSize();
|
Dimension sz = add.getPreferredSize();
|
||||||
|
|
||||||
selections.removeAll();
|
selections.removeAll();
|
||||||
selected = null;
|
selected = null;
|
||||||
if (working.size() > 0)
|
if (working.size() > 0)
|
||||||
{
|
{
|
||||||
selections.add(attachment1);
|
selections.add(attachment1);
|
||||||
Image i = working.get(0).image;
|
Image i = working.get(0).image;
|
||||||
attachment1.setIcon(new ImageIcon(i));
|
attachment1.setIcon(new ImageIcon(i));
|
||||||
}
|
}
|
||||||
if (working.size() > 1)
|
if (working.size() > 1)
|
||||||
{
|
{
|
||||||
selections.add(attachment2);
|
selections.add(attachment2);
|
||||||
Image i = working.get(1).image;
|
Image i = working.get(1).image;
|
||||||
attachment2.setIcon(new ImageIcon(i));
|
attachment2.setIcon(new ImageIcon(i));
|
||||||
}
|
}
|
||||||
if (working.size() > 2)
|
if (working.size() > 2)
|
||||||
{
|
{
|
||||||
selections.add(attachment3);
|
selections.add(attachment3);
|
||||||
Image i = working.get(2).image;
|
Image i = working.get(2).image;
|
||||||
attachment3.setIcon(new ImageIcon(i));
|
attachment3.setIcon(new ImageIcon(i));
|
||||||
}
|
}
|
||||||
if (working.size() > 3)
|
if (working.size() > 3)
|
||||||
{
|
{
|
||||||
selections.add(attachment4);
|
selections.add(attachment4);
|
||||||
Image i = working.get(3).image;
|
Image i = working.get(3).image;
|
||||||
attachment4.setIcon(new ImageIcon(i));
|
attachment4.setIcon(new ImageIcon(i));
|
||||||
}
|
}
|
||||||
if (working.size() < 4) selections.add(add);
|
if (working.size() < 4) selections.add(add);
|
||||||
|
|
||||||
if (working.size() > 3) open(attachment4);
|
if (working.size() > 3) open(attachment4);
|
||||||
else if (working.size() > 2) open(attachment3);
|
else if (working.size() > 2) open(attachment3);
|
||||||
else if (working.size() > 1) open(attachment2);
|
else if (working.size() > 1) open(attachment2);
|
||||||
else if (working.size() > 0) open(attachment1);
|
else if (working.size() > 0) open(attachment1);
|
||||||
|
|
||||||
int bw = sz.width;
|
int bw = sz.width;
|
||||||
int hgap = 4;
|
int hgap = 4;
|
||||||
int count = selections.getComponents().length;
|
int count = selections.getComponents().length;
|
||||||
int w = count * bw + (count - 1) * hgap;
|
int w = count * bw + (count - 1) * hgap;
|
||||||
int h = bw;
|
int h = bw;
|
||||||
selections.setPreferredSize(new Dimension(w, h));
|
selections.setPreferredSize(new Dimension(w, h));
|
||||||
selections.setMaximumSize(new Dimension(w, h));
|
selections.setMaximumSize(new Dimension(w, h));
|
||||||
selections.revalidate();
|
selections.revalidate();
|
||||||
|
|
||||||
delete.setEnabled(selected != null);
|
delete.setEnabled(selected != null);
|
||||||
revert.setEnabled(selected != null);
|
revert.setEnabled(selected != null);
|
||||||
description.setEnabled(selected != null);
|
description.setEnabled(selected != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
@ -693,34 +693,34 @@ implements ComponentListener, ActionListener {
|
|||||||
|
|
||||||
if (src == add)
|
if (src == add)
|
||||||
{
|
{
|
||||||
int r = chooser.showOpenDialog(this);
|
int r = chooser.showOpenDialog(this);
|
||||||
if (r != JFileChooser.APPROVE_OPTION) return;
|
if (r != JFileChooser.APPROVE_OPTION) return;
|
||||||
|
|
||||||
File f = chooser.getSelectedFile();
|
File f = chooser.getSelectedFile();
|
||||||
|
|
||||||
Attachment a = new Attachment();
|
Attachment a = new Attachment();
|
||||||
a.uploadee = f;
|
a.uploadee = f;
|
||||||
a.description = "";
|
a.description = "";
|
||||||
a.type = "unknown";
|
a.type = "unknown";
|
||||||
|
|
||||||
String mime = "", primary = "";
|
String mime = "", primary = "";
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
mime = Files.probeContentType(f.toPath());
|
mime = Files.probeContentType(f.toPath());
|
||||||
primary = mime.split("/")[0];
|
primary = mime.split("/")[0];
|
||||||
// Too lazy to instantiate a
|
// Too lazy to instantiate a
|
||||||
// javax.activation.MimeType.
|
// javax.activation.MimeType.
|
||||||
}
|
}
|
||||||
catch (IOException eIo) { }
|
catch (IOException eIo) { }
|
||||||
if (primary.equals("image"))
|
if (primary.equals("image"))
|
||||||
{
|
{
|
||||||
String urlr = f.toURI().toString();
|
String urlr = f.toURI().toString();
|
||||||
a.image = ImageApi.remote(urlr);
|
a.image = ImageApi.remote(urlr);
|
||||||
a.type = "image";
|
a.type = "image";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selected != null) open(selected);
|
if (selected != null) open(selected);
|
||||||
// Save first before resetting
|
// Save first before resetting
|
||||||
|
|
||||||
working.add(a);
|
working.add(a);
|
||||||
updateButtons();
|
updateButtons();
|
||||||
@ -728,87 +728,87 @@ implements ComponentListener, ActionListener {
|
|||||||
|
|
||||||
if (src == delete)
|
if (src == delete)
|
||||||
{
|
{
|
||||||
assert selected != null;
|
assert selected != null;
|
||||||
working.remove(getAttachmentFor(selected));
|
working.remove(getAttachmentFor(selected));
|
||||||
updateButtons();
|
updateButtons();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (src != add && selections.isAncestorOf((Component)src))
|
if (src != add && selections.isAncestorOf((Component)src))
|
||||||
{
|
{
|
||||||
assert src instanceof JToggleButton;
|
assert src instanceof JToggleButton;
|
||||||
if (src == selected) preview(getAttachmentFor(src));
|
if (src == selected) preview(getAttachmentFor(src));
|
||||||
else open((JToggleButton)src);
|
else open((JToggleButton)src);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (src == revert)
|
if (src == revert)
|
||||||
{
|
{
|
||||||
while (descriptionUndos.canUndo())
|
while (descriptionUndos.canUndo())
|
||||||
descriptionUndos.undo();
|
descriptionUndos.undo();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Attachment
|
private Attachment
|
||||||
getAttachmentFor(Object button)
|
getAttachmentFor(Object button)
|
||||||
{
|
{
|
||||||
if (button == null) return null;
|
if (button == null) return null;
|
||||||
assert button instanceof JToggleButton;
|
assert button instanceof JToggleButton;
|
||||||
assert selections.isAncestorOf((Component)button);
|
assert selections.isAncestorOf((Component)button);
|
||||||
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
if (button == attachment4) index = 4;
|
if (button == attachment4) index = 4;
|
||||||
if (button == attachment3) index = 3;
|
if (button == attachment3) index = 3;
|
||||||
if (button == attachment2) index = 2;
|
if (button == attachment2) index = 2;
|
||||||
if (button == attachment1) index = 1;
|
if (button == attachment1) index = 1;
|
||||||
|
|
||||||
assert index != 0;
|
assert index != 0;
|
||||||
assert index <= working.size();
|
assert index <= working.size();
|
||||||
return working.get(index - 1);
|
return working.get(index - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void
|
private void
|
||||||
open(JToggleButton button)
|
open(JToggleButton button)
|
||||||
{
|
{
|
||||||
assert selections.isAncestorOf(button);
|
assert selections.isAncestorOf(button);
|
||||||
|
|
||||||
if (selected != null)
|
if (selected != null)
|
||||||
{
|
{
|
||||||
Attachment a = getAttachmentFor(selected);
|
Attachment a = getAttachmentFor(selected);
|
||||||
a.description = description.getText();
|
a.description = description.getText();
|
||||||
selected.setSelected(false);
|
selected.setSelected(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Attachment a = getAttachmentFor(button);
|
Attachment a = getAttachmentFor(button);
|
||||||
description.setText(a.description);
|
description.setText(a.description);
|
||||||
descriptionUndos.discardAllEdits();
|
descriptionUndos.discardAllEdits();
|
||||||
(selected = button).setSelected(true);
|
(selected = button).setSelected(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
componentHidden(ComponentEvent eC)
|
componentHidden(ComponentEvent eC)
|
||||||
{
|
{
|
||||||
if (selected != null) open(selected);
|
if (selected != null) open(selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void
|
private void
|
||||||
preview(Attachment a)
|
preview(Attachment a)
|
||||||
{
|
{
|
||||||
ImageWindow w = new ImageWindow();
|
ImageWindow w = new ImageWindow();
|
||||||
w.showAttachments(new Attachment[] { a } );
|
w.showAttachments(new Attachment[] { a } );
|
||||||
w.setTitle("Attachment preview");
|
w.setTitle("Attachment preview");
|
||||||
w.setVisible(true);
|
w.setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
componentShown(ComponentEvent eC) { }
|
componentShown(ComponentEvent eC) { }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
componentMoved(ComponentEvent eC) { }
|
componentMoved(ComponentEvent eC) { }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
componentResized(ComponentEvent eC) { }
|
componentResized(ComponentEvent eC) { }
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
@ -824,15 +824,15 @@ implements ComponentListener, ActionListener {
|
|||||||
Border bc1 = BorderFactory.createCompoundBorder(b3, b2);
|
Border bc1 = BorderFactory.createCompoundBorder(b3, b2);
|
||||||
Border bc2 = BorderFactory.createCompoundBorder(b4, b2);
|
Border bc2 = BorderFactory.createCompoundBorder(b4, b2);
|
||||||
|
|
||||||
TextActionPopupMenu textActionPopup;
|
TextActionPopupMenu textActionPopup;
|
||||||
textActionPopup = new TextActionPopupMenu();
|
textActionPopup = new TextActionPopupMenu();
|
||||||
|
|
||||||
chooser = new JFileChooser();
|
chooser = new JFileChooser();
|
||||||
|
|
||||||
add = new JButton("+");
|
add = new JButton("+");
|
||||||
add.setPreferredSize(new Dimension(32, 32));
|
add.setPreferredSize(new Dimension(32, 32));
|
||||||
add.setMargin(new Insets(0, 0, 0, 0));
|
add.setMargin(new Insets(0, 0, 0, 0));
|
||||||
add.addActionListener(this);
|
add.addActionListener(this);
|
||||||
attachment1 = new JToggleButton("1");
|
attachment1 = new JToggleButton("1");
|
||||||
attachment2 = new JToggleButton("2");
|
attachment2 = new JToggleButton("2");
|
||||||
attachment3 = new JToggleButton("3");
|
attachment3 = new JToggleButton("3");
|
||||||
@ -841,28 +841,28 @@ implements ComponentListener, ActionListener {
|
|||||||
attachment2.setMargin(add.getMargin());
|
attachment2.setMargin(add.getMargin());
|
||||||
attachment3.setMargin(add.getMargin());
|
attachment3.setMargin(add.getMargin());
|
||||||
attachment4.setMargin(add.getMargin());
|
attachment4.setMargin(add.getMargin());
|
||||||
attachment1.addActionListener(this);
|
attachment1.addActionListener(this);
|
||||||
attachment2.addActionListener(this);
|
attachment2.addActionListener(this);
|
||||||
attachment3.addActionListener(this);
|
attachment3.addActionListener(this);
|
||||||
attachment4.addActionListener(this);
|
attachment4.addActionListener(this);
|
||||||
|
|
||||||
selections = new JPanel();
|
selections = new JPanel();
|
||||||
selections.setOpaque(false);
|
selections.setOpaque(false);
|
||||||
selections.setLayout(new GridLayout(1, 0, 4, 0));
|
selections.setLayout(new GridLayout(1, 0, 4, 0));
|
||||||
working = new ArrayList<Attachment>();
|
working = new ArrayList<Attachment>();
|
||||||
|
|
||||||
Box top = Box.createHorizontalBox();
|
Box top = Box.createHorizontalBox();
|
||||||
top.add(selections);
|
top.add(selections);
|
||||||
top.add(Box.createGlue());
|
top.add(Box.createGlue());
|
||||||
|
|
||||||
delete = new JButton("Delete");
|
delete = new JButton("Delete");
|
||||||
revert = new JButton("Revert");
|
revert = new JButton("Revert");
|
||||||
JButton ml = new JButton("←");
|
JButton ml = new JButton("←");
|
||||||
JButton mr = new JButton("→");
|
JButton mr = new JButton("→");
|
||||||
delete.addActionListener(this);
|
delete.addActionListener(this);
|
||||||
revert.addActionListener(this);
|
revert.addActionListener(this);
|
||||||
|
|
||||||
Box bottom = Box.createHorizontalBox();
|
Box bottom = Box.createHorizontalBox();
|
||||||
bottom.add(ml);
|
bottom.add(ml);
|
||||||
bottom.add(mr);
|
bottom.add(mr);
|
||||||
bottom.add(Box.createHorizontalStrut(8));
|
bottom.add(Box.createHorizontalStrut(8));
|
||||||
@ -876,14 +876,14 @@ implements ComponentListener, ActionListener {
|
|||||||
java.awt.Font f = description.getFont();
|
java.awt.Font f = description.getFont();
|
||||||
description.setFont(f.deriveFont(16f));
|
description.setFont(f.deriveFont(16f));
|
||||||
description.setBorder(bc1);
|
description.setBorder(bc1);
|
||||||
description.addMouseListener(textActionPopup);
|
description.addMouseListener(textActionPopup);
|
||||||
descriptionLabel = new JLabel("Description");
|
descriptionLabel = new JLabel("Description");
|
||||||
descriptionLabel.setLabelFor(description);
|
descriptionLabel.setLabelFor(description);
|
||||||
descriptionUndos = new UndoManager();
|
descriptionUndos = new UndoManager();
|
||||||
description.getDocument().
|
description.getDocument().
|
||||||
addUndoableEditListener(descriptionUndos);
|
addUndoableEditListener(descriptionUndos);
|
||||||
|
|
||||||
updateButtons();
|
updateButtons();
|
||||||
|
|
||||||
JPanel row1 = new JPanel();
|
JPanel row1 = new JPanel();
|
||||||
row1.setOpaque(false);
|
row1.setOpaque(false);
|
||||||
@ -891,7 +891,7 @@ implements ComponentListener, ActionListener {
|
|||||||
row1.add(descriptionLabel, BorderLayout.NORTH);
|
row1.add(descriptionLabel, BorderLayout.NORTH);
|
||||||
row1.add(description, BorderLayout.CENTER);
|
row1.add(description, BorderLayout.CENTER);
|
||||||
|
|
||||||
Box centre = Box.createVerticalBox();
|
Box centre = Box.createVerticalBox();
|
||||||
centre.setBorder(b4);
|
centre.setBorder(b4);
|
||||||
centre.add(row1);
|
centre.add(row1);
|
||||||
|
|
||||||
@ -901,7 +901,7 @@ implements ComponentListener, ActionListener {
|
|||||||
add(bottom, BorderLayout.SOUTH);
|
add(bottom, BorderLayout.SOUTH);
|
||||||
|
|
||||||
setBorder(b1);
|
setBorder(b1);
|
||||||
this.addComponentListener(this);
|
this.addComponentListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -910,66 +910,66 @@ class
|
|||||||
TextActionPopupMenu extends JPopupMenu
|
TextActionPopupMenu extends JPopupMenu
|
||||||
implements MouseListener, ActionListener {
|
implements MouseListener, ActionListener {
|
||||||
|
|
||||||
private JMenuItem
|
private JMenuItem
|
||||||
copy,
|
copy,
|
||||||
cut,
|
cut,
|
||||||
paste;
|
paste;
|
||||||
|
|
||||||
private long
|
private long
|
||||||
pressed;
|
pressed;
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mousePressed(MouseEvent eM)
|
mousePressed(MouseEvent eM)
|
||||||
{
|
{
|
||||||
assert eM.getSource() instanceof JTextComponent;
|
assert eM.getSource() instanceof JTextComponent;
|
||||||
if (!eM.isPopupTrigger()) return;
|
if (!eM.isPopupTrigger()) return;
|
||||||
show((Component)eM.getSource(), eM.getX(), eM.getY());
|
show((Component)eM.getSource(), eM.getX(), eM.getY());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mouseReleased(MouseEvent eM)
|
mouseReleased(MouseEvent eM)
|
||||||
{
|
{
|
||||||
if (eM.getClickCount() == 0) setVisible(false);
|
if (eM.getClickCount() == 0) setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mouseClicked(MouseEvent eM) { }
|
mouseClicked(MouseEvent eM) { }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mouseEntered(MouseEvent eM) { }
|
mouseEntered(MouseEvent eM) { }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mouseExited(MouseEvent eM) { }
|
mouseExited(MouseEvent eM) { }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
actionPerformed(ActionEvent eA)
|
actionPerformed(ActionEvent eA)
|
||||||
{
|
{
|
||||||
assert getInvoker() instanceof JTextComponent;
|
assert getInvoker() instanceof JTextComponent;
|
||||||
JTextComponent inv = (JTextComponent)getInvoker();
|
JTextComponent inv = (JTextComponent)getInvoker();
|
||||||
|
|
||||||
if (eA.getSource() == copy) inv.copy();
|
if (eA.getSource() == copy) inv.copy();
|
||||||
if (eA.getSource() == cut) inv.cut();
|
if (eA.getSource() == cut) inv.cut();
|
||||||
if (eA.getSource() == paste) inv.paste();
|
if (eA.getSource() == paste) inv.paste();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
public
|
public
|
||||||
TextActionPopupMenu()
|
TextActionPopupMenu()
|
||||||
{
|
{
|
||||||
copy = new JMenuItem("Copy");
|
copy = new JMenuItem("Copy");
|
||||||
cut = new JMenuItem("Cut");
|
cut = new JMenuItem("Cut");
|
||||||
paste = new JMenuItem("Paste");
|
paste = new JMenuItem("Paste");
|
||||||
copy.addActionListener(this);
|
copy.addActionListener(this);
|
||||||
cut.addActionListener(this);
|
cut.addActionListener(this);
|
||||||
paste.addActionListener(this);
|
paste.addActionListener(this);
|
||||||
|
|
||||||
add(cut);
|
add(cut);
|
||||||
add(copy);
|
add(copy);
|
||||||
add(paste);
|
add(paste);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
466
ImageWindow.java
466
ImageWindow.java
@ -41,303 +41,303 @@ import java.net.MalformedURLException;
|
|||||||
class
|
class
|
||||||
ImageWindow extends JFrame {
|
ImageWindow extends JFrame {
|
||||||
|
|
||||||
private Attachment[]
|
private Attachment[]
|
||||||
attachments;
|
attachments;
|
||||||
|
|
||||||
private int
|
private int
|
||||||
offset;
|
offset;
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
private ImageComponent
|
private ImageComponent
|
||||||
display;
|
display;
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
public synchronized void
|
public synchronized void
|
||||||
showAttachments(Attachment[] attachments)
|
showAttachments(Attachment[] attachments)
|
||||||
{
|
{
|
||||||
this.attachments = attachments;
|
this.attachments = attachments;
|
||||||
|
|
||||||
if (attachments.length == 0) {
|
if (attachments.length == 0) {
|
||||||
display.setImage(null);
|
display.setImage(null);
|
||||||
display.setNext(null);
|
display.setNext(null);
|
||||||
display.setPrev(null);
|
display.setPrev(null);
|
||||||
display.repaint();
|
display.repaint();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
toImage(offset = 0);
|
toImage(offset = 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
toNextImage()
|
toNextImage()
|
||||||
{
|
{
|
||||||
if (attachments.length == 0) return;
|
if (attachments.length == 0) return;
|
||||||
assert offset < attachments.length - 1;
|
assert offset < attachments.length - 1;
|
||||||
toImage(++offset);
|
toImage(++offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
toPrevImage()
|
toPrevImage()
|
||||||
{
|
{
|
||||||
if (attachments.length == 0) return;
|
if (attachments.length == 0) return;
|
||||||
assert offset > 0;
|
assert offset > 0;
|
||||||
toImage(--offset);
|
toImage(--offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
private synchronized void
|
private synchronized void
|
||||||
toImage(int offset)
|
toImage(int offset)
|
||||||
{
|
{
|
||||||
int last = attachments.length - 1;
|
int last = attachments.length - 1;
|
||||||
assert offset >= 0;
|
assert offset >= 0;
|
||||||
assert offset < attachments.length;
|
assert offset < attachments.length;
|
||||||
|
|
||||||
Attachment prev, curr, next;
|
Attachment prev, curr, next;
|
||||||
curr = attachments[offset];
|
curr = attachments[offset];
|
||||||
next = offset < last ? attachments[offset + 1] : null;
|
next = offset < last ? attachments[offset + 1] : null;
|
||||||
prev = offset > 0 ? attachments[offset - 1] : null;
|
prev = offset > 0 ? attachments[offset - 1] : null;
|
||||||
|
|
||||||
display.setImage(curr.image);
|
display.setImage(curr.image);
|
||||||
display.setNext(next != null ? next.image : null);
|
display.setNext(next != null ? next.image : null);
|
||||||
display.setPrev(prev != null ? prev.image : null);
|
display.setPrev(prev != null ? prev.image : null);
|
||||||
|
|
||||||
if (!curr.type.equals("image"))
|
if (!curr.type.equals("image"))
|
||||||
display.setToolTipText(
|
display.setToolTipText(
|
||||||
display.getToolTipText()
|
display.getToolTipText()
|
||||||
+ "\n(Media is of type '" + curr.type + "')"
|
+ "\n(Media is of type '" + curr.type + "')"
|
||||||
);
|
);
|
||||||
|
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
ImageWindow()
|
ImageWindow()
|
||||||
{
|
{
|
||||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||||
setSize(600, 600);
|
setSize(600, 600);
|
||||||
|
|
||||||
display = new ImageComponent(this);
|
display = new ImageComponent(this);
|
||||||
showAttachments(new Attachment[0]);
|
showAttachments(new Attachment[0]);
|
||||||
setContentPane(display);
|
setContentPane(display);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class
|
class
|
||||||
ImageComponent extends JPanel
|
ImageComponent extends JPanel
|
||||||
implements
|
implements
|
||||||
ActionListener,
|
ActionListener,
|
||||||
MouseListener, MouseMotionListener, MouseWheelListener {
|
MouseListener, MouseMotionListener, MouseWheelListener {
|
||||||
|
|
||||||
private ImageWindow
|
private ImageWindow
|
||||||
primaire;
|
primaire;
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
private Image
|
private Image
|
||||||
image, prevImage, nextImage;
|
image, prevImage, nextImage;
|
||||||
|
|
||||||
private JPanel
|
private JPanel
|
||||||
buttonArea;
|
buttonArea;
|
||||||
|
|
||||||
private JButton
|
private JButton
|
||||||
prev, next, toggle;
|
prev, next, toggle;
|
||||||
|
|
||||||
private boolean
|
private boolean
|
||||||
scaleImage;
|
scaleImage;
|
||||||
|
|
||||||
private int
|
private int
|
||||||
xOffset, yOffset, zoomLevel;
|
xOffset, yOffset, zoomLevel;
|
||||||
|
|
||||||
private int
|
private int
|
||||||
dragX, dragY, xPOffset, yPOffset;
|
dragX, dragY, xPOffset, yPOffset;
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
public void
|
public void
|
||||||
setImage(Image image)
|
setImage(Image image)
|
||||||
{
|
{
|
||||||
this.image = image;
|
this.image = image;
|
||||||
if (image != null) {
|
if (image != null) {
|
||||||
Object p = image.getProperty("comment", this);
|
Object p = image.getProperty("comment", this);
|
||||||
String desc = p instanceof String ? (String)p : null;
|
String desc = p instanceof String ? (String)p : null;
|
||||||
setToolTipText(desc);
|
setToolTipText(desc);
|
||||||
}
|
}
|
||||||
xOffset = yOffset = xPOffset = yPOffset = 0;
|
xOffset = yOffset = xPOffset = yPOffset = 0;
|
||||||
zoomLevel = 100;
|
zoomLevel = 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
setPrev(Image image)
|
setPrev(Image image)
|
||||||
{
|
{
|
||||||
prev.setEnabled(image != null);
|
prev.setEnabled(image != null);
|
||||||
prev.setText(image == null ? "<" : "");
|
prev.setText(image == null ? "<" : "");
|
||||||
prev.setIcon(toIcon(image));
|
prev.setIcon(toIcon(image));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
setNext(Image image)
|
setNext(Image image)
|
||||||
{
|
{
|
||||||
next.setEnabled(image != null);
|
next.setEnabled(image != null);
|
||||||
next.setText(image == null ? ">" : "");
|
next.setText(image == null ? ">" : "");
|
||||||
next.setIcon(toIcon(image));
|
next.setIcon(toIcon(image));
|
||||||
}
|
}
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
public void
|
public void
|
||||||
actionPerformed(ActionEvent eA)
|
actionPerformed(ActionEvent eA)
|
||||||
{
|
{
|
||||||
if (eA.getSource() == prev) primaire.toPrevImage();
|
if (eA.getSource() == prev) primaire.toPrevImage();
|
||||||
if (eA.getSource() == next) primaire.toNextImage();
|
if (eA.getSource() == next) primaire.toNextImage();
|
||||||
if (eA.getSource() == toggle) {
|
if (eA.getSource() == toggle) {
|
||||||
scaleImage = !scaleImage;
|
scaleImage = !scaleImage;
|
||||||
if (scaleImage) toggle.setText("Show unscaled");
|
if (scaleImage) toggle.setText("Show unscaled");
|
||||||
else toggle.setText("Show scaled to window");
|
else toggle.setText("Show scaled to window");
|
||||||
setImage(this.image);
|
setImage(this.image);
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mousePressed(MouseEvent eM)
|
mousePressed(MouseEvent eM)
|
||||||
{
|
{
|
||||||
dragX = eM.getX();
|
dragX = eM.getX();
|
||||||
dragY = eM.getY();
|
dragY = eM.getY();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mouseDragged(MouseEvent eM)
|
mouseDragged(MouseEvent eM)
|
||||||
{
|
{
|
||||||
int dx = eM.getX() - dragX;
|
int dx = eM.getX() - dragX;
|
||||||
int dy = eM.getY() - dragY;
|
int dy = eM.getY() - dragY;
|
||||||
xPOffset = dx;
|
xPOffset = dx;
|
||||||
yPOffset = dy;
|
yPOffset = dy;
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mouseReleased(MouseEvent eM)
|
mouseReleased(MouseEvent eM)
|
||||||
{
|
{
|
||||||
xOffset += xPOffset;
|
xOffset += xPOffset;
|
||||||
yOffset += yPOffset;
|
yOffset += yPOffset;
|
||||||
xPOffset = yPOffset = 0;
|
xPOffset = yPOffset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mouseWheelMoved(MouseWheelEvent eMw)
|
mouseWheelMoved(MouseWheelEvent eMw)
|
||||||
{
|
{
|
||||||
zoomLevel += 10 * -eMw.getUnitsToScroll();
|
zoomLevel += 10 * -eMw.getUnitsToScroll();
|
||||||
if (zoomLevel < 50) zoomLevel = 50;
|
if (zoomLevel < 50) zoomLevel = 50;
|
||||||
if (zoomLevel > 400) zoomLevel = 400;
|
if (zoomLevel > 400) zoomLevel = 400;
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mouseEntered(MouseEvent eM) { }
|
mouseEntered(MouseEvent eM) { }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mouseExited(MouseEvent eM) { }
|
mouseExited(MouseEvent eM) { }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mouseClicked(MouseEvent eM) { }
|
mouseClicked(MouseEvent eM) { }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mouseMoved(MouseEvent eM) { }
|
mouseMoved(MouseEvent eM) { }
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
private static ImageIcon
|
private static ImageIcon
|
||||||
toIcon(Image image)
|
toIcon(Image image)
|
||||||
{
|
{
|
||||||
if (image == null) return null;
|
if (image == null) return null;
|
||||||
return new ImageIcon(image);
|
return new ImageIcon(image);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
private class
|
private class
|
||||||
Painter extends JPanel {
|
Painter extends JPanel {
|
||||||
|
|
||||||
protected void
|
protected void
|
||||||
paintComponent(Graphics g)
|
paintComponent(Graphics g)
|
||||||
{
|
{
|
||||||
if (image == null)
|
if (image == null)
|
||||||
{
|
{
|
||||||
String str =
|
String str =
|
||||||
"(There are no images being displayed.)";
|
"(There are no images being displayed.)";
|
||||||
FontMetrics fm = g.getFontMetrics();
|
FontMetrics fm = g.getFontMetrics();
|
||||||
int x = (getWidth() - fm.stringWidth(str)) / 2;
|
int x = (getWidth() - fm.stringWidth(str)) / 2;
|
||||||
int y = (getHeight() + fm.getHeight()) / 2;
|
int y = (getHeight() + fm.getHeight()) / 2;
|
||||||
g.drawString(str, x, y);
|
g.drawString(str, x, y);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int wo = image.getWidth(this);
|
int wo = image.getWidth(this);
|
||||||
int ho = image.getHeight(this);
|
int ho = image.getHeight(this);
|
||||||
int wn, hn;
|
int wn, hn;
|
||||||
if (wo > ho) {
|
if (wo > ho) {
|
||||||
wn = scaleImage ? getWidth() : wo;
|
wn = scaleImage ? getWidth() : wo;
|
||||||
hn = ho * wn / wo;
|
hn = ho * wn / wo;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
hn = scaleImage ? getHeight() : ho;
|
hn = scaleImage ? getHeight() : ho;
|
||||||
wn = wo * hn / ho;
|
wn = wo * hn / ho;
|
||||||
}
|
}
|
||||||
wn = wn * zoomLevel / 100;
|
wn = wn * zoomLevel / 100;
|
||||||
hn = hn * zoomLevel / 100;
|
hn = hn * zoomLevel / 100;
|
||||||
int x = (getWidth() - wn) / 2;
|
int x = (getWidth() - wn) / 2;
|
||||||
int y = (getHeight() - hn) / 2;
|
int y = (getHeight() - hn) / 2;
|
||||||
x += xOffset + xPOffset;
|
x += xOffset + xPOffset;
|
||||||
y += yOffset + yPOffset;
|
y += yOffset + yPOffset;
|
||||||
g.drawImage(image, x, y, wn, hn, this);
|
g.drawImage(image, x, y, wn, hn, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
ImageComponent(ImageWindow primaire)
|
ImageComponent(ImageWindow primaire)
|
||||||
{
|
{
|
||||||
this.primaire = primaire;
|
this.primaire = primaire;
|
||||||
|
|
||||||
Dimension BUTTON_SIZE = new Dimension(48, 48);
|
Dimension BUTTON_SIZE = new Dimension(48, 48);
|
||||||
|
|
||||||
setOpaque(false);
|
setOpaque(false);
|
||||||
scaleImage = true;
|
scaleImage = true;
|
||||||
zoomLevel = 100;
|
zoomLevel = 100;
|
||||||
|
|
||||||
prev = new JButton();
|
prev = new JButton();
|
||||||
toggle = new JButton("Show unscaled");
|
toggle = new JButton("Show unscaled");
|
||||||
next = new JButton();
|
next = new JButton();
|
||||||
prev.setPreferredSize(BUTTON_SIZE);
|
prev.setPreferredSize(BUTTON_SIZE);
|
||||||
next.setPreferredSize(BUTTON_SIZE);
|
next.setPreferredSize(BUTTON_SIZE);
|
||||||
prev.addActionListener(this);
|
prev.addActionListener(this);
|
||||||
toggle.addActionListener(this);
|
toggle.addActionListener(this);
|
||||||
next.addActionListener(this);
|
next.addActionListener(this);
|
||||||
|
|
||||||
buttonArea = new JPanel();
|
buttonArea = new JPanel();
|
||||||
buttonArea.setOpaque(false);
|
buttonArea.setOpaque(false);
|
||||||
buttonArea.add(prev);
|
buttonArea.add(prev);
|
||||||
buttonArea.add(toggle);
|
buttonArea.add(toggle);
|
||||||
buttonArea.add(next);
|
buttonArea.add(next);
|
||||||
|
|
||||||
setPrev(null);
|
setPrev(null);
|
||||||
setNext(null);
|
setNext(null);
|
||||||
|
|
||||||
setLayout(new BorderLayout());
|
setLayout(new BorderLayout());
|
||||||
add(buttonArea, BorderLayout.SOUTH);
|
add(buttonArea, BorderLayout.SOUTH);
|
||||||
add(new Painter(), BorderLayout.CENTER);
|
add(new Painter(), BorderLayout.CENTER);
|
||||||
|
|
||||||
addMouseListener(this);
|
addMouseListener(this);
|
||||||
addMouseMotionListener(this);
|
addMouseMotionListener(this);
|
||||||
addMouseWheelListener(this);
|
addMouseWheelListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
406
JKomasto.java
406
JKomasto.java
@ -60,20 +60,20 @@ JKomasto {
|
|||||||
private LoginWindow
|
private LoginWindow
|
||||||
loginWindow;
|
loginWindow;
|
||||||
|
|
||||||
private ImageWindow
|
private ImageWindow
|
||||||
mediaWindow;
|
mediaWindow;
|
||||||
|
|
||||||
private NotificationsWindow
|
private NotificationsWindow
|
||||||
notificationsWindow;
|
notificationsWindow;
|
||||||
|
|
||||||
private WindowUpdater
|
private WindowUpdater
|
||||||
windowUpdater;
|
windowUpdater;
|
||||||
|
|
||||||
private MastodonApi
|
private MastodonApi
|
||||||
api;
|
api;
|
||||||
|
|
||||||
private Image
|
private Image
|
||||||
programIcon;
|
programIcon;
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
@ -85,10 +85,10 @@ JKomasto {
|
|||||||
{
|
{
|
||||||
timelineWindow.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
timelineWindow.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||||
|
|
||||||
timelineWindow.showLatestPage();
|
timelineWindow.showLatestPage();
|
||||||
notificationsWindow.showLatestPage();
|
notificationsWindow.showLatestPage();
|
||||||
timelineWindow.setVisible(true);
|
timelineWindow.setVisible(true);
|
||||||
loginWindow.dispose();
|
loginWindow.dispose();
|
||||||
|
|
||||||
timelineWindow.setCursor(null);
|
timelineWindow.setCursor(null);
|
||||||
}
|
}
|
||||||
@ -99,83 +99,83 @@ JKomasto {
|
|||||||
public ComposeWindow
|
public ComposeWindow
|
||||||
getComposeWindow() { return composeWindow; }
|
getComposeWindow() { return composeWindow; }
|
||||||
|
|
||||||
public ImageWindow
|
public ImageWindow
|
||||||
getMediaWindow() { return mediaWindow; }
|
getMediaWindow() { return mediaWindow; }
|
||||||
|
|
||||||
public NotificationsWindow
|
public NotificationsWindow
|
||||||
getNotificationsWindow() { return notificationsWindow; }
|
getNotificationsWindow() { return notificationsWindow; }
|
||||||
|
|
||||||
public WindowUpdater
|
public WindowUpdater
|
||||||
getWindowUpdater() { return windowUpdater; }
|
getWindowUpdater() { return windowUpdater; }
|
||||||
|
|
||||||
public Image
|
public Image
|
||||||
getProgramIcon() { return programIcon; }
|
getProgramIcon() { return programIcon; }
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
private static class
|
private static class
|
||||||
MetalTheme extends OceanTheme {
|
MetalTheme extends OceanTheme {
|
||||||
|
|
||||||
private ColorUIResource
|
private ColorUIResource
|
||||||
lightPink = new ColorUIResource(246, 240, 240),
|
lightPink = new ColorUIResource(246, 240, 240),
|
||||||
mildPink = new ColorUIResource(238, 233, 233),
|
mildPink = new ColorUIResource(238, 233, 233),
|
||||||
white = new ColorUIResource(250, 250, 250),
|
white = new ColorUIResource(250, 250, 250),
|
||||||
darkPink = new ColorUIResource(242, 230, 230),
|
darkPink = new ColorUIResource(242, 230, 230),
|
||||||
veryDarkPink = new ColorUIResource(164, 160, 160);
|
veryDarkPink = new ColorUIResource(164, 160, 160);
|
||||||
|
|
||||||
// -=%=-
|
// -=%=-
|
||||||
|
|
||||||
public ColorUIResource
|
public ColorUIResource
|
||||||
getPrimary2() { return darkPink; }
|
getPrimary2() { return darkPink; }
|
||||||
|
|
||||||
public ColorUIResource
|
public ColorUIResource
|
||||||
getSecondary2() { return white; }
|
getSecondary2() { return white; }
|
||||||
|
|
||||||
public ColorUIResource
|
public ColorUIResource
|
||||||
getSecondary3() { return mildPink; }
|
getSecondary3() { return mildPink; }
|
||||||
|
|
||||||
public ColorUIResource
|
public ColorUIResource
|
||||||
getSecondary1() { return veryDarkPink; }
|
getSecondary1() { return veryDarkPink; }
|
||||||
|
|
||||||
public ColorUIResource
|
public ColorUIResource
|
||||||
getPrimary1() { return veryDarkPink; }
|
getPrimary1() { return veryDarkPink; }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
addCustomEntriesToTable(UIDefaults table)
|
addCustomEntriesToTable(UIDefaults table)
|
||||||
{
|
{
|
||||||
super.addCustomEntriesToTable(table);
|
super.addCustomEntriesToTable(table);
|
||||||
table.put(
|
table.put(
|
||||||
"TabbedPane.tabAreaBackground",
|
"TabbedPane.tabAreaBackground",
|
||||||
getPrimary1()
|
getPrimary1()
|
||||||
);
|
);
|
||||||
table.put(
|
table.put(
|
||||||
"TabbedPane.contentAreaColor",
|
"TabbedPane.contentAreaColor",
|
||||||
getSecondary3()
|
getSecondary3()
|
||||||
);
|
);
|
||||||
table.put(
|
table.put(
|
||||||
"TabbedPane.selected",
|
"TabbedPane.selected",
|
||||||
getSecondary3()
|
getSecondary3()
|
||||||
);
|
);
|
||||||
table.put(
|
table.put(
|
||||||
"MenuBar.gradient",
|
"MenuBar.gradient",
|
||||||
java.util.Arrays.asList(new Object[] {
|
java.util.Arrays.asList(new Object[] {
|
||||||
1f, 0f,
|
1f, 0f,
|
||||||
getWhite(),
|
getWhite(),
|
||||||
getSecondary3(),
|
getSecondary3(),
|
||||||
getSecondary1()
|
getSecondary1()
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
public static void
|
public static void
|
||||||
main(String... args)
|
main(String... args)
|
||||||
{
|
{
|
||||||
//System.setProperty("swing.boldMetal", "false");
|
//System.setProperty("swing.boldMetal", "false");
|
||||||
MetalLookAndFeel.setCurrentTheme(new MetalTheme());
|
MetalLookAndFeel.setCurrentTheme(new MetalTheme());
|
||||||
|
|
||||||
new JKomasto().loginWindow.setVisible(true);
|
new JKomasto().loginWindow.setVisible(true);
|
||||||
}
|
}
|
||||||
@ -187,24 +187,24 @@ JKomasto {
|
|||||||
{
|
{
|
||||||
api = new MastodonApi();
|
api = new MastodonApi();
|
||||||
windowUpdater = new WindowUpdater(this);
|
windowUpdater = new WindowUpdater(this);
|
||||||
programIcon = ImageApi.local("kettle");
|
programIcon = ImageApi.local("kettle");
|
||||||
|
|
||||||
timelineWindow = new TimelineWindow(this);
|
timelineWindow = new TimelineWindow(this);
|
||||||
composeWindow = new ComposeWindow(this);
|
composeWindow = new ComposeWindow(this);
|
||||||
autoViewWindow = new PostWindow(this);
|
autoViewWindow = new PostWindow(this);
|
||||||
loginWindow = new LoginWindow(this);
|
loginWindow = new LoginWindow(this);
|
||||||
mediaWindow = new ImageWindow();
|
mediaWindow = new ImageWindow();
|
||||||
notificationsWindow = new NotificationsWindow(this);
|
notificationsWindow = new NotificationsWindow(this);
|
||||||
|
|
||||||
autoViewWindow.setTitle("Auto view - JKomasto");
|
autoViewWindow.setTitle("Auto view - JKomasto");
|
||||||
|
|
||||||
composeWindow.dispose();
|
composeWindow.dispose();
|
||||||
autoViewWindow.dispose();
|
autoViewWindow.dispose();
|
||||||
timelineWindow.dispose();
|
timelineWindow.dispose();
|
||||||
mediaWindow.dispose();
|
mediaWindow.dispose();
|
||||||
notificationsWindow.dispose();
|
notificationsWindow.dispose();
|
||||||
|
|
||||||
timelineWindow.setLocationByPlatform(true);
|
timelineWindow.setLocationByPlatform(true);
|
||||||
loginWindow.setLocationByPlatform(true);
|
loginWindow.setLocationByPlatform(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,13 +236,13 @@ TimelineType {
|
|||||||
enum
|
enum
|
||||||
NotificationType {
|
NotificationType {
|
||||||
|
|
||||||
MENTION,
|
MENTION,
|
||||||
BOOST,
|
BOOST,
|
||||||
FAVOURITE,
|
FAVOURITE,
|
||||||
FOLLOW,
|
FOLLOW,
|
||||||
FOLLOWREQ,
|
FOLLOWREQ,
|
||||||
POLL,
|
POLL,
|
||||||
ALERT
|
ALERT
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,8 +257,8 @@ TimelinePage {
|
|||||||
public String
|
public String
|
||||||
accountNumId, listId;
|
accountNumId, listId;
|
||||||
|
|
||||||
public Post[]
|
public Post[]
|
||||||
posts;
|
posts;
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
@ -279,14 +279,14 @@ TimelinePage {
|
|||||||
class
|
class
|
||||||
Notification {
|
Notification {
|
||||||
|
|
||||||
public NotificationType
|
public NotificationType
|
||||||
type;
|
type;
|
||||||
|
|
||||||
public String
|
public String
|
||||||
id;
|
id;
|
||||||
|
|
||||||
public String
|
public String
|
||||||
postId, postText, actorNumId, actorName;
|
postId, postText, actorNumId, actorName;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -348,35 +348,35 @@ Post {
|
|||||||
assert text != null;
|
assert text != null;
|
||||||
if (approximateText != null) return;
|
if (approximateText != null) return;
|
||||||
|
|
||||||
Tree<String> nodes;
|
Tree<String> nodes;
|
||||||
nodes = RudimentaryHTMLParser.depthlessRead(text);
|
nodes = RudimentaryHTMLParser.depthlessRead(text);
|
||||||
if (nodes.size() == 0)
|
if (nodes.size() == 0)
|
||||||
{
|
{
|
||||||
approximateText = "-";
|
approximateText = "-";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
StringBuilder b = new StringBuilder();
|
StringBuilder b = new StringBuilder();
|
||||||
Tree<String> first = nodes.get(0);
|
Tree<String> first = nodes.get(0);
|
||||||
for (Tree<String> node: nodes)
|
for (Tree<String> node: nodes)
|
||||||
{
|
{
|
||||||
if (node.key.equals("tag"))
|
if (node.key.equals("tag"))
|
||||||
{
|
{
|
||||||
if (node.get(0).key.equals("br"))
|
if (node.get(0).key.equals("br"))
|
||||||
b.append("; ");
|
b.append("; ");
|
||||||
if (node.get(0).key.equals("p") && node != first)
|
if (node.get(0).key.equals("p") && node != first)
|
||||||
b.append("; ");
|
b.append("; ");
|
||||||
}
|
}
|
||||||
if (node.key.equals("emoji"))
|
if (node.key.equals("emoji"))
|
||||||
{
|
{
|
||||||
b.append(":" + node.value + ":");
|
b.append(":" + node.value + ":");
|
||||||
}
|
}
|
||||||
if (node.key.equals("text"))
|
if (node.key.equals("text"))
|
||||||
{
|
{
|
||||||
b.append(node.value);
|
b.append(node.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
approximateText = b.toString();
|
approximateText = b.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
@ -402,57 +402,57 @@ Post {
|
|||||||
public
|
public
|
||||||
Post(Tree<String> entity)
|
Post(Tree<String> entity)
|
||||||
{
|
{
|
||||||
id = entity.get("id").value;
|
id = entity.get("id").value;
|
||||||
|
|
||||||
uri = entity.get("url").value;
|
uri = entity.get("url").value;
|
||||||
if (uri == null) uri = entity.get("uri").value;
|
if (uri == null) uri = entity.get("uri").value;
|
||||||
|
|
||||||
author = new Account(entity.get("account"));
|
author = new Account(entity.get("account"));
|
||||||
|
|
||||||
String v = entity.get("visibility").value;
|
String v = entity.get("visibility").value;
|
||||||
boolean p = v.equals("public");
|
boolean p = v.equals("public");
|
||||||
boolean u = v.equals("unlisted");
|
boolean u = v.equals("unlisted");
|
||||||
boolean f = v.equals("private");
|
boolean f = v.equals("private");
|
||||||
boolean m = v.equals("direct");
|
boolean m = v.equals("direct");
|
||||||
if (p) visibility = PostVisibility.PUBLIC;
|
if (p) visibility = PostVisibility.PUBLIC;
|
||||||
if (u) visibility = PostVisibility.UNLISTED;
|
if (u) visibility = PostVisibility.UNLISTED;
|
||||||
if (f) visibility = PostVisibility.FOLLOWERS;
|
if (f) visibility = PostVisibility.FOLLOWERS;
|
||||||
if (m) visibility = PostVisibility.MENTIONED;
|
if (m) visibility = PostVisibility.MENTIONED;
|
||||||
|
|
||||||
dateTime =
|
dateTime =
|
||||||
ZonedDateTime.parse(entity.get("created_at").value)
|
ZonedDateTime.parse(entity.get("created_at").value)
|
||||||
.withZoneSameInstant(ZoneId.systemDefault());
|
.withZoneSameInstant(ZoneId.systemDefault());
|
||||||
date = DATE_FORMAT.format(dateTime);
|
date = DATE_FORMAT.format(dateTime);
|
||||||
time = TIME_FORMAT.format(dateTime);
|
time = TIME_FORMAT.format(dateTime);
|
||||||
|
|
||||||
text = entity.get("content").value;
|
text = entity.get("content").value;
|
||||||
String st = entity.get("spoiler_text").value;
|
String st = entity.get("spoiler_text").value;
|
||||||
contentWarning = st.trim().isEmpty() ? null : st;
|
contentWarning = st.trim().isEmpty() ? null : st;
|
||||||
|
|
||||||
String favourited = entity.get("favourited").value;
|
String favourited = entity.get("favourited").value;
|
||||||
String boosted = entity.get("reblogged").value;
|
String boosted = entity.get("reblogged").value;
|
||||||
this.favourited = favourited.equals("true");
|
this.favourited = favourited.equals("true");
|
||||||
this.boosted = boosted.equals("true");
|
this.boosted = boosted.equals("true");
|
||||||
|
|
||||||
Tree<String> media = entity.get("media_attachments");
|
Tree<String> media = entity.get("media_attachments");
|
||||||
attachments = new Attachment[media.size()];
|
attachments = new Attachment[media.size()];
|
||||||
for (int o = 0; o < attachments.length; ++o)
|
for (int o = 0; o < attachments.length; ++o)
|
||||||
{
|
{
|
||||||
attachments[o] = new Attachment(media.get(o));
|
attachments[o] = new Attachment(media.get(o));
|
||||||
}
|
}
|
||||||
|
|
||||||
Tree<String> emojis = entity.get("emojis");
|
Tree<String> emojis = entity.get("emojis");
|
||||||
emojiUrls = new String[emojis.size()][];
|
emojiUrls = new String[emojis.size()][];
|
||||||
for (int o = 0; o < emojiUrls.length; ++o)
|
for (int o = 0; o < emojiUrls.length; ++o)
|
||||||
{
|
{
|
||||||
Tree<String> emoji = emojis.get(o);
|
Tree<String> emoji = emojis.get(o);
|
||||||
String[] mapping = emojiUrls[o] = new String[2];
|
String[] mapping = emojiUrls[o] = new String[2];
|
||||||
mapping[0] = ":" + emoji.get("shortcode").value + ":";
|
mapping[0] = ":" + emoji.get("shortcode").value + ":";
|
||||||
mapping[1] = emoji.get("url").value;
|
mapping[1] = emoji.get("url").value;
|
||||||
}
|
}
|
||||||
|
|
||||||
Tree<String> boostedPost = entity.get("reblog");
|
Tree<String> boostedPost = entity.get("reblog");
|
||||||
if (boostedPost.size() > 0)
|
if (boostedPost.size() > 0)
|
||||||
this.boostedPost = new Post(boostedPost);
|
this.boostedPost = new Post(boostedPost);
|
||||||
|
|
||||||
Tree<String> mentions = entity.get("mentions");
|
Tree<String> mentions = entity.get("mentions");
|
||||||
@ -508,7 +508,7 @@ Account {
|
|||||||
resolveFormattedName()
|
resolveFormattedName()
|
||||||
{
|
{
|
||||||
assert name != null;
|
assert name != null;
|
||||||
formattedName =
|
formattedName =
|
||||||
new RichTextPane.Builder().text(name).finish();
|
new RichTextPane.Builder().text(name).finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -531,27 +531,27 @@ Account {
|
|||||||
numId = entity.get("id").value;
|
numId = entity.get("id").value;
|
||||||
id = entity.get("acct").value;
|
id = entity.get("acct").value;
|
||||||
|
|
||||||
String displayName = entity.get("display_name").value;
|
String displayName = entity.get("display_name").value;
|
||||||
String username = entity.get("username").value;
|
String username = entity.get("username").value;
|
||||||
name = displayName.isEmpty() ? username : displayName;
|
name = displayName.isEmpty() ? username : displayName;
|
||||||
|
|
||||||
avatarUrl = entity.get("avatar").value;
|
avatarUrl = entity.get("avatar").value;
|
||||||
|
|
||||||
creationDate =
|
creationDate =
|
||||||
ZonedDateTime.parse(entity.get("created_at").value)
|
ZonedDateTime.parse(entity.get("created_at").value)
|
||||||
.withZoneSameInstant(ZoneId.systemDefault());
|
.withZoneSameInstant(ZoneId.systemDefault());
|
||||||
|
|
||||||
String c1 = entity.get("following_count").value;
|
String c1 = entity.get("following_count").value;
|
||||||
String c2 = entity.get("followers_count").value;
|
String c2 = entity.get("followers_count").value;
|
||||||
String c3 = entity.get("statuses_count").value;
|
String c3 = entity.get("statuses_count").value;
|
||||||
try {
|
try {
|
||||||
followedCount = (int)Double.parseDouble(c1);
|
followedCount = (int)Double.parseDouble(c1);
|
||||||
followerCount = (int)Double.parseDouble(c2);
|
followerCount = (int)Double.parseDouble(c2);
|
||||||
postCount = (int)Double.parseDouble(c3);
|
postCount = (int)Double.parseDouble(c3);
|
||||||
}
|
}
|
||||||
catch (NumberFormatException eNf) {
|
catch (NumberFormatException eNf) {
|
||||||
assert false;
|
assert false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Tree<String> fs = entity.get("fields");
|
Tree<String> fs = entity.get("fields");
|
||||||
fields = new String[fs.size()][];
|
fields = new String[fs.size()][];
|
||||||
@ -577,20 +577,20 @@ Attachment {
|
|||||||
public String
|
public String
|
||||||
id;
|
id;
|
||||||
|
|
||||||
public String
|
public String
|
||||||
type;
|
type;
|
||||||
|
|
||||||
public String
|
public String
|
||||||
url;
|
url;
|
||||||
|
|
||||||
public String
|
public String
|
||||||
description;
|
description;
|
||||||
|
|
||||||
public Image
|
public Image
|
||||||
image;
|
image;
|
||||||
|
|
||||||
public File
|
public File
|
||||||
uploadee;
|
uploadee;
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
@ -639,8 +639,8 @@ Composition {
|
|||||||
public Attachment[]
|
public Attachment[]
|
||||||
attachments;
|
attachments;
|
||||||
|
|
||||||
private File
|
private File
|
||||||
uploadee;
|
uploadee;
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
@ -653,33 +653,33 @@ Composition {
|
|||||||
Composition c = new Composition();
|
Composition c = new Composition();
|
||||||
|
|
||||||
Tree<String> boosted = entity.get("reblog");
|
Tree<String> boosted = entity.get("reblog");
|
||||||
if (boosted.size() > 0) entity = boosted;
|
if (boosted.size() > 0) entity = boosted;
|
||||||
|
|
||||||
String st = entity.get("spoiler_text").value;
|
String st = entity.get("spoiler_text").value;
|
||||||
String ri = entity.get("id").value;
|
String ri = entity.get("id").value;
|
||||||
c.contentWarning = st.trim().isEmpty() ? null : st;
|
c.contentWarning = st.trim().isEmpty() ? null : st;
|
||||||
c.replyToPostId = ri.trim().isEmpty() ? null : ri;
|
c.replyToPostId = ri.trim().isEmpty() ? null : ri;
|
||||||
|
|
||||||
Tree<String> author = entity.get("account");
|
Tree<String> author = entity.get("account");
|
||||||
String authorId = author.get("acct").value;
|
String authorId = author.get("acct").value;
|
||||||
String authorNumId = author.get("id").value;
|
String authorNumId = author.get("id").value;
|
||||||
c.text = "";
|
c.text = "";
|
||||||
if (!authorNumId.equals(ownNumId))
|
if (!authorNumId.equals(ownNumId))
|
||||||
c.text = "@" + authorId + " ";
|
c.text = "@" + authorId + " ";
|
||||||
|
|
||||||
String visibility = entity.get("visibility").value;
|
String visibility = entity.get("visibility").value;
|
||||||
boolean p = visibility.equals("public");
|
boolean p = visibility.equals("public");
|
||||||
boolean u = visibility.equals("unlisted");
|
boolean u = visibility.equals("unlisted");
|
||||||
boolean f = visibility.equals("private");
|
boolean f = visibility.equals("private");
|
||||||
boolean m = visibility.equals("direct");
|
boolean m = visibility.equals("direct");
|
||||||
assert p || u || f || m;
|
assert p || u || f || m;
|
||||||
if (p) c.visibility = PostVisibility.PUBLIC;
|
if (p) c.visibility = PostVisibility.PUBLIC;
|
||||||
if (u) c.visibility = PostVisibility.UNLISTED;
|
if (u) c.visibility = PostVisibility.UNLISTED;
|
||||||
if (f) c.visibility = PostVisibility.FOLLOWERS;
|
if (f) c.visibility = PostVisibility.FOLLOWERS;
|
||||||
if (m) c.visibility = PostVisibility.MENTIONED;
|
if (m) c.visibility = PostVisibility.MENTIONED;
|
||||||
// Less eye strain arranged this way.
|
// Less eye strain arranged this way.
|
||||||
|
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Composition
|
public static Composition
|
||||||
@ -693,17 +693,17 @@ Composition {
|
|||||||
c.replyToPostId = entity.get("in_reply_to_id").value;
|
c.replyToPostId = entity.get("in_reply_to_id").value;
|
||||||
|
|
||||||
String visibility = entity.get("visibility").value;
|
String visibility = entity.get("visibility").value;
|
||||||
boolean p = visibility.equals("public");
|
boolean p = visibility.equals("public");
|
||||||
boolean u = visibility.equals("unlisted");
|
boolean u = visibility.equals("unlisted");
|
||||||
boolean f = visibility.equals("private");
|
boolean f = visibility.equals("private");
|
||||||
boolean m = visibility.equals("direct");
|
boolean m = visibility.equals("direct");
|
||||||
assert p || u || f || m;
|
assert p || u || f || m;
|
||||||
if (p) c.visibility = PostVisibility.PUBLIC;
|
if (p) c.visibility = PostVisibility.PUBLIC;
|
||||||
if (u) c.visibility = PostVisibility.UNLISTED;
|
if (u) c.visibility = PostVisibility.UNLISTED;
|
||||||
if (f) c.visibility = PostVisibility.FOLLOWERS;
|
if (f) c.visibility = PostVisibility.FOLLOWERS;
|
||||||
if (m) c.visibility = PostVisibility.MENTIONED;
|
if (m) c.visibility = PostVisibility.MENTIONED;
|
||||||
|
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Composition
|
public static Composition
|
||||||
@ -712,9 +712,9 @@ Composition {
|
|||||||
if (post.boostedPost != null) post = post.boostedPost;
|
if (post.boostedPost != null) post = post.boostedPost;
|
||||||
|
|
||||||
Composition c = new Composition();
|
Composition c = new Composition();
|
||||||
c.replyToPostId = post.id;
|
c.replyToPostId = post.id;
|
||||||
c.visibility = post.visibility;
|
c.visibility = post.visibility;
|
||||||
c.contentWarning = post.contentWarning;
|
c.contentWarning = post.contentWarning;
|
||||||
|
|
||||||
StringBuilder text = new StringBuilder();
|
StringBuilder text = new StringBuilder();
|
||||||
for (String id: post.mentions)
|
for (String id: post.mentions)
|
||||||
|
BIN
JKomasto2.jar
BIN
JKomasto2.jar
Binary file not shown.
554
LoginWindow.java
554
LoginWindow.java
@ -50,8 +50,8 @@ LoginWindow extends JFrame {
|
|||||||
private JKomasto
|
private JKomasto
|
||||||
primaire;
|
primaire;
|
||||||
|
|
||||||
private MastodonApi
|
private MastodonApi
|
||||||
api;
|
api;
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ LoginWindow extends JFrame {
|
|||||||
serverContacted = false,
|
serverContacted = false,
|
||||||
haveAppCredentials = false,
|
haveAppCredentials = false,
|
||||||
haveAccessToken = false,
|
haveAccessToken = false,
|
||||||
haveAccountDetails = false;
|
haveAccountDetails = false;
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
@ -81,325 +81,325 @@ LoginWindow extends JFrame {
|
|||||||
b.append(prefix4 + " Have account details\n");
|
b.append(prefix4 + " Have account details\n");
|
||||||
display.setText(b.toString());
|
display.setText(b.toString());
|
||||||
|
|
||||||
display.paintImmediately(display.getBounds(null));
|
display.paintImmediately(display.getBounds(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
useCache()
|
useCache()
|
||||||
{
|
{
|
||||||
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
api.loadCache();
|
api.loadCache();
|
||||||
haveAppCredentials = true;
|
haveAppCredentials = true;
|
||||||
haveAccessToken = true;
|
haveAccessToken = true;
|
||||||
display.setInstanceUrl(api.getInstanceUrl());
|
display.setInstanceUrl(api.getInstanceUrl());
|
||||||
updateStatusDisplay();
|
updateStatusDisplay();
|
||||||
}
|
}
|
||||||
catch (IOException eIo)
|
catch (IOException eIo)
|
||||||
{
|
{
|
||||||
JOptionPane.showMessageDialog(
|
JOptionPane.showMessageDialog(
|
||||||
this,
|
this,
|
||||||
"We couldn't get login details from the cache.."
|
"We couldn't get login details from the cache.."
|
||||||
+ "\n" + eIo.getClass() + ": " + eIo.getMessage()
|
+ "\n" + eIo.getClass() + ": " + eIo.getMessage()
|
||||||
);
|
);
|
||||||
display.setAutoLoginToggled(false);
|
display.setAutoLoginToggled(false);
|
||||||
}
|
}
|
||||||
display.setCursor(null);
|
display.setCursor(null);
|
||||||
if (!haveAccessToken) return;
|
if (!haveAccessToken) return;
|
||||||
|
|
||||||
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||||
api.getAccountDetails(new RequestListener() {
|
api.getAccountDetails(new RequestListener() {
|
||||||
|
|
||||||
public void
|
public void
|
||||||
connectionFailed(IOException eIo)
|
connectionFailed(IOException eIo)
|
||||||
{
|
{
|
||||||
JOptionPane.showMessageDialog(
|
JOptionPane.showMessageDialog(
|
||||||
LoginWindow.this,
|
LoginWindow.this,
|
||||||
"Tried to get account details, failed.."
|
"Tried to get account details, failed.."
|
||||||
+ "\n" + eIo.getMessage()
|
+ "\n" + eIo.getMessage()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
requestFailed(int httpCode, Tree<String> json)
|
requestFailed(int httpCode, Tree<String> json)
|
||||||
{
|
{
|
||||||
JOptionPane.showMessageDialog(
|
JOptionPane.showMessageDialog(
|
||||||
LoginWindow.this,
|
LoginWindow.this,
|
||||||
"Tried to get account details, failed.."
|
"Tried to get account details, failed.."
|
||||||
+ "\n" + json.get("error").value
|
+ "\n" + json.get("error").value
|
||||||
+ "\n(HTTP error code: " + httpCode + ")"
|
+ "\n(HTTP error code: " + httpCode + ")"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
requestSucceeded(Tree<String> json)
|
requestSucceeded(Tree<String> json)
|
||||||
{
|
{
|
||||||
api.setAccountDetails(json);
|
api.setAccountDetails(json);
|
||||||
serverContacted = true;
|
serverContacted = true;
|
||||||
haveAccountDetails = true;
|
haveAccountDetails = true;
|
||||||
updateStatusDisplay();
|
updateStatusDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
display.setCursor(null);
|
display.setCursor(null);
|
||||||
if (!haveAccountDetails) return;
|
if (!haveAccountDetails) return;
|
||||||
|
|
||||||
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
api.saveToCache();
|
api.saveToCache();
|
||||||
}
|
}
|
||||||
catch (IOException eIo)
|
catch (IOException eIo)
|
||||||
{
|
{
|
||||||
JOptionPane.showMessageDialog(
|
JOptionPane.showMessageDialog(
|
||||||
this,
|
this,
|
||||||
"We couldn't save login details into the cache.."
|
"We couldn't save login details into the cache.."
|
||||||
+ "\n" + eIo.getClass() + ": " + eIo.getMessage()
|
+ "\n" + eIo.getClass() + ": " + eIo.getMessage()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
display.setCursor(null);
|
display.setCursor(null);
|
||||||
|
|
||||||
primaire.finishedLogin();
|
primaire.finishedLogin();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
useInstanceUrl()
|
useInstanceUrl()
|
||||||
{
|
{
|
||||||
if (display.isAutoLoginToggled()) { useCache(); return; }
|
if (display.isAutoLoginToggled()) { useCache(); return; }
|
||||||
|
|
||||||
String url = display.getInstanceUrl();
|
String url = display.getInstanceUrl();
|
||||||
if (url.trim().isEmpty()) {
|
if (url.trim().isEmpty()) {
|
||||||
// Should we show an error dialog..?
|
// Should we show an error dialog..?
|
||||||
display.setInstanceUrl("");
|
display.setInstanceUrl("");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!hasProtocol(url)) {
|
if (!hasProtocol(url)) {
|
||||||
url = "https://" + url;
|
url = "https://" + url;
|
||||||
display.setInstanceUrl(url);
|
display.setInstanceUrl(url);
|
||||||
display.paintImmediately(display.getBounds(null));
|
display.paintImmediately(display.getBounds(null));
|
||||||
}
|
}
|
||||||
serverContacted = false;
|
serverContacted = false;
|
||||||
haveAppCredentials = false;
|
haveAppCredentials = false;
|
||||||
haveAccessToken = false;
|
haveAccessToken = false;
|
||||||
haveAccountDetails = false;
|
haveAccountDetails = false;
|
||||||
|
|
||||||
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||||
api.testUrlConnection(url, new RequestListener() {
|
api.testUrlConnection(url, new RequestListener() {
|
||||||
|
|
||||||
public void
|
public void
|
||||||
connectionFailed(IOException eIo)
|
connectionFailed(IOException eIo)
|
||||||
{
|
{
|
||||||
JOptionPane.showMessageDialog(
|
JOptionPane.showMessageDialog(
|
||||||
LoginWindow.this,
|
LoginWindow.this,
|
||||||
"Tried to connect to URL, failed.."
|
"Tried to connect to URL, failed.."
|
||||||
+ "\n" + eIo.getClass() + ": " + eIo.getMessage()
|
+ "\n" + eIo.getClass() + ": " + eIo.getMessage()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
requestFailed(int httpCode, Tree<String> response)
|
requestFailed(int httpCode, Tree<String> response)
|
||||||
{
|
{
|
||||||
JOptionPane.showMessageDialog(
|
JOptionPane.showMessageDialog(
|
||||||
LoginWindow.this,
|
LoginWindow.this,
|
||||||
"Tried to connect to URL, failed.."
|
"Tried to connect to URL, failed.."
|
||||||
+ "\n" + response.get("body").value
|
+ "\n" + response.get("body").value
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
requestSucceeded(Tree<String> response)
|
requestSucceeded(Tree<String> response)
|
||||||
{
|
{
|
||||||
serverContacted = true;
|
serverContacted = true;
|
||||||
updateStatusDisplay();
|
updateStatusDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
display.setCursor(null);
|
display.setCursor(null);
|
||||||
if (!serverContacted) return;
|
if (!serverContacted) return;
|
||||||
|
|
||||||
api.setInstanceUrl(url);
|
api.setInstanceUrl(url);
|
||||||
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||||
api.getAppCredentials(new RequestListener() {
|
api.getAppCredentials(new RequestListener() {
|
||||||
|
|
||||||
public void
|
public void
|
||||||
connectionFailed(IOException eIo)
|
connectionFailed(IOException eIo)
|
||||||
{
|
{
|
||||||
JOptionPane.showMessageDialog(
|
JOptionPane.showMessageDialog(
|
||||||
LoginWindow.this,
|
LoginWindow.this,
|
||||||
"Tried to get app credentials, failed.."
|
"Tried to get app credentials, failed.."
|
||||||
+ "\n" + eIo.getMessage()
|
+ "\n" + eIo.getMessage()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
requestFailed(int httpCode, Tree<String> json)
|
requestFailed(int httpCode, Tree<String> json)
|
||||||
{
|
{
|
||||||
JOptionPane.showMessageDialog(
|
JOptionPane.showMessageDialog(
|
||||||
LoginWindow.this,
|
LoginWindow.this,
|
||||||
"Tried to get app credentials, failed.."
|
"Tried to get app credentials, failed.."
|
||||||
+ "\n" + json.get("error").value
|
+ "\n" + json.get("error").value
|
||||||
+ "\n(HTTP error code: " + httpCode + ")"
|
+ "\n(HTTP error code: " + httpCode + ")"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
requestSucceeded(Tree<String> json)
|
requestSucceeded(Tree<String> json)
|
||||||
{
|
{
|
||||||
api.setAppCredentials(json);
|
api.setAppCredentials(json);
|
||||||
haveAppCredentials = true;
|
haveAppCredentials = true;
|
||||||
updateStatusDisplay();
|
updateStatusDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
display.setCursor(null);
|
display.setCursor(null);
|
||||||
if (!haveAppCredentials) return;
|
if (!haveAppCredentials) return;
|
||||||
|
|
||||||
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||||
|
|
||||||
URI uri = api.getAuthorisationURL();
|
URI uri = api.getAuthorisationURL();
|
||||||
|
|
||||||
final String MESSAGE1 =
|
final String MESSAGE1 =
|
||||||
"We will need you to login through a web browser,\n"
|
"We will need you to login through a web browser,\n"
|
||||||
+ "and they will give you an authorisation code\n"
|
+ "and they will give you an authorisation code\n"
|
||||||
+ "that you will paste here. Sorry..!";
|
+ "that you will paste here. Sorry..!";
|
||||||
|
|
||||||
boolean supported =
|
boolean supported =
|
||||||
Desktop.isDesktopSupported()
|
Desktop.isDesktopSupported()
|
||||||
&& Desktop.getDesktop().isSupported(Desktop.Action.BROWSE);
|
&& Desktop.getDesktop().isSupported(Desktop.Action.BROWSE);
|
||||||
if (supported)
|
if (supported)
|
||||||
{
|
{
|
||||||
JOptionPane.showMessageDialog(
|
JOptionPane.showMessageDialog(
|
||||||
LoginWindow.this,
|
LoginWindow.this,
|
||||||
MESSAGE1,
|
MESSAGE1,
|
||||||
"Authorisation code renewal",
|
"Authorisation code renewal",
|
||||||
JOptionPane.INFORMATION_MESSAGE
|
JOptionPane.INFORMATION_MESSAGE
|
||||||
);
|
);
|
||||||
try { Desktop.getDesktop().browse(uri); }
|
try { Desktop.getDesktop().browse(uri); }
|
||||||
catch (IOException eIo) { supported = false; }
|
catch (IOException eIo) { supported = false; }
|
||||||
}
|
}
|
||||||
if (!supported)
|
if (!supported)
|
||||||
{
|
{
|
||||||
final String MESSAGE2 =
|
final String MESSAGE2 =
|
||||||
"\nWe cannot use Desktop.browse(URI) on your\n"
|
"\nWe cannot use Desktop.browse(URI) on your\n"
|
||||||
+ "computer.. You'll have to open your web\n"
|
+ "computer.. You'll have to open your web\n"
|
||||||
+ "browser yourself, and copy this URL in.";
|
+ "browser yourself, and copy this URL in.";
|
||||||
|
|
||||||
JTextField field = new JTextField();
|
JTextField field = new JTextField();
|
||||||
field.setText(uri.toString());
|
field.setText(uri.toString());
|
||||||
field.setPreferredSize(new Dimension(120, 32));
|
field.setPreferredSize(new Dimension(120, 32));
|
||||||
field.selectAll();
|
field.selectAll();
|
||||||
|
|
||||||
JOptionPane.showMessageDialog(
|
JOptionPane.showMessageDialog(
|
||||||
LoginWindow.this,
|
LoginWindow.this,
|
||||||
new Object[] { MESSAGE1, MESSAGE2, field },
|
new Object[] { MESSAGE1, MESSAGE2, field },
|
||||||
"Authorisation code renewal",
|
"Authorisation code renewal",
|
||||||
JOptionPane.INFORMATION_MESSAGE
|
JOptionPane.INFORMATION_MESSAGE
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
display.receiveAuthorisationCode();
|
display.receiveAuthorisationCode();
|
||||||
display.setCursor(null);
|
display.setCursor(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
useAuthorisationCode()
|
useAuthorisationCode()
|
||||||
{
|
{
|
||||||
String code = display.getAuthorisationCode();
|
String code = display.getAuthorisationCode();
|
||||||
|
|
||||||
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||||
api.getAccessToken(code, new RequestListener() {
|
api.getAccessToken(code, new RequestListener() {
|
||||||
|
|
||||||
public void
|
public void
|
||||||
connectionFailed(IOException eIo)
|
connectionFailed(IOException eIo)
|
||||||
{
|
{
|
||||||
JOptionPane.showMessageDialog(
|
JOptionPane.showMessageDialog(
|
||||||
LoginWindow.this,
|
LoginWindow.this,
|
||||||
"Tried to get app token, failed.."
|
"Tried to get app token, failed.."
|
||||||
+ "\n" + eIo.getMessage()
|
+ "\n" + eIo.getMessage()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
requestFailed(int httpCode, Tree<String> json)
|
requestFailed(int httpCode, Tree<String> json)
|
||||||
{
|
{
|
||||||
JOptionPane.showMessageDialog(
|
JOptionPane.showMessageDialog(
|
||||||
LoginWindow.this,
|
LoginWindow.this,
|
||||||
"Tried to get app token, failed.."
|
"Tried to get app token, failed.."
|
||||||
+ "\n" + json.get("error").value
|
+ "\n" + json.get("error").value
|
||||||
+ "\n(HTTP error code: " + httpCode + ")"
|
+ "\n(HTTP error code: " + httpCode + ")"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
requestSucceeded(Tree<String> json)
|
requestSucceeded(Tree<String> json)
|
||||||
{
|
{
|
||||||
api.setAccessToken(json);
|
api.setAccessToken(json);
|
||||||
haveAccessToken = true;
|
haveAccessToken = true;
|
||||||
updateStatusDisplay();
|
updateStatusDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
display.setCursor(null);
|
display.setCursor(null);
|
||||||
if (!haveAccessToken) return;
|
if (!haveAccessToken) return;
|
||||||
|
|
||||||
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||||
api.getAccountDetails(new RequestListener() {
|
api.getAccountDetails(new RequestListener() {
|
||||||
|
|
||||||
public void
|
public void
|
||||||
connectionFailed(IOException eIo)
|
connectionFailed(IOException eIo)
|
||||||
{
|
{
|
||||||
JOptionPane.showMessageDialog(
|
JOptionPane.showMessageDialog(
|
||||||
LoginWindow.this,
|
LoginWindow.this,
|
||||||
"Tried to get account details, failed.."
|
"Tried to get account details, failed.."
|
||||||
+ "\n" + eIo.getMessage()
|
+ "\n" + eIo.getMessage()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
requestFailed(int httpCode, Tree<String> json)
|
requestFailed(int httpCode, Tree<String> json)
|
||||||
{
|
{
|
||||||
JOptionPane.showMessageDialog(
|
JOptionPane.showMessageDialog(
|
||||||
LoginWindow.this,
|
LoginWindow.this,
|
||||||
"Tried to get account details, failed.."
|
"Tried to get account details, failed.."
|
||||||
+ "\n" + json.get("error").value
|
+ "\n" + json.get("error").value
|
||||||
+ "\n(HTTP error code: " + httpCode + ")"
|
+ "\n(HTTP error code: " + httpCode + ")"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
requestSucceeded(Tree<String> json)
|
requestSucceeded(Tree<String> json)
|
||||||
{
|
{
|
||||||
api.setAccountDetails(json);
|
api.setAccountDetails(json);
|
||||||
haveAccountDetails = true;
|
haveAccountDetails = true;
|
||||||
updateStatusDisplay();
|
updateStatusDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
display.setCursor(null);
|
display.setCursor(null);
|
||||||
if (!haveAccountDetails) return;
|
if (!haveAccountDetails) return;
|
||||||
|
|
||||||
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
api.saveToCache();
|
api.saveToCache();
|
||||||
}
|
}
|
||||||
catch (IOException eIo)
|
catch (IOException eIo)
|
||||||
{
|
{
|
||||||
JOptionPane.showMessageDialog(
|
JOptionPane.showMessageDialog(
|
||||||
this,
|
this,
|
||||||
"We couldn't save login details into the cache.."
|
"We couldn't save login details into the cache.."
|
||||||
+ "\n" + eIo.getClass() + ": " + eIo.getMessage()
|
+ "\n" + eIo.getClass() + ": " + eIo.getMessage()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
display.setCursor(null);
|
display.setCursor(null);
|
||||||
|
|
||||||
primaire.finishedLogin();
|
primaire.finishedLogin();
|
||||||
}
|
}
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
public static boolean
|
public static boolean
|
||||||
hasProtocol(String url) { return url.matches("^.+://.*"); }
|
hasProtocol(String url) { return url.matches("^.+://.*"); }
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
@ -407,7 +407,7 @@ LoginWindow extends JFrame {
|
|||||||
{
|
{
|
||||||
super("JKomasto - Login");
|
super("JKomasto - Login");
|
||||||
this.primaire = primaire;
|
this.primaire = primaire;
|
||||||
this.api = primaire.getMastodonApi();
|
this.api = primaire.getMastodonApi();
|
||||||
|
|
||||||
setLocationByPlatform(true);
|
setLocationByPlatform(true);
|
||||||
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
|
||||||
@ -419,7 +419,7 @@ LoginWindow extends JFrame {
|
|||||||
setContentPane(display);
|
setContentPane(display);
|
||||||
pack();
|
pack();
|
||||||
|
|
||||||
setIconImage(primaire.getProgramIcon());
|
setIconImage(primaire.getProgramIcon());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -451,7 +451,7 @@ implements ActionListener {
|
|||||||
private JPanel
|
private JPanel
|
||||||
labelArea,
|
labelArea,
|
||||||
forField,
|
forField,
|
||||||
accountsPanel;
|
accountsPanel;
|
||||||
|
|
||||||
private JCheckBox
|
private JCheckBox
|
||||||
autoLoginToggle;
|
autoLoginToggle;
|
||||||
@ -482,11 +482,11 @@ implements ActionListener {
|
|||||||
statusDisplay.setText(status);
|
statusDisplay.setText(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
setAutoLoginToggled(boolean a) { autoLoginToggle.setSelected(a); }
|
setAutoLoginToggled(boolean a) { autoLoginToggle.setSelected(a); }
|
||||||
|
|
||||||
public boolean
|
public boolean
|
||||||
isAutoLoginToggled() { return autoLoginToggle.isSelected(); }
|
isAutoLoginToggled() { return autoLoginToggle.isSelected(); }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
actionPerformed(ActionEvent eA)
|
actionPerformed(ActionEvent eA)
|
||||||
@ -502,11 +502,11 @@ implements ActionListener {
|
|||||||
{
|
{
|
||||||
labelArea.remove(authorisationCodeLabel);
|
labelArea.remove(authorisationCodeLabel);
|
||||||
forField.remove(authorisationCodeButton);
|
forField.remove(authorisationCodeButton);
|
||||||
accountsPanel.remove(authorisationCodeField);
|
accountsPanel.remove(authorisationCodeField);
|
||||||
labelArea.add(instanceUrlLabel, BorderLayout.NORTH);
|
labelArea.add(instanceUrlLabel, BorderLayout.NORTH);
|
||||||
forField.add(instanceUrlButton, BorderLayout.EAST);
|
forField.add(instanceUrlButton, BorderLayout.EAST);
|
||||||
accountsPanel.add(instanceUrlField);
|
accountsPanel.add(instanceUrlField);
|
||||||
revalidate();
|
revalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
@ -514,11 +514,11 @@ implements ActionListener {
|
|||||||
{
|
{
|
||||||
labelArea.remove(instanceUrlLabel);
|
labelArea.remove(instanceUrlLabel);
|
||||||
forField.remove(instanceUrlButton);
|
forField.remove(instanceUrlButton);
|
||||||
accountsPanel.remove(instanceUrlField);
|
accountsPanel.remove(instanceUrlField);
|
||||||
labelArea.add(authorisationCodeLabel, BorderLayout.NORTH);
|
labelArea.add(authorisationCodeLabel, BorderLayout.NORTH);
|
||||||
forField.add(authorisationCodeButton, BorderLayout.EAST);
|
forField.add(authorisationCodeButton, BorderLayout.EAST);
|
||||||
accountsPanel.add(authorisationCodeField);
|
accountsPanel.add(authorisationCodeField);
|
||||||
revalidate();
|
revalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
@ -595,7 +595,7 @@ implements ActionListener {
|
|||||||
statusDisplay.setBorder(bi);
|
statusDisplay.setBorder(bi);
|
||||||
statusDisplay.setFont(f2);
|
statusDisplay.setFont(f2);
|
||||||
|
|
||||||
receiveInstanceUrl();
|
receiveInstanceUrl();
|
||||||
|
|
||||||
setLayout(new BorderLayout(0, 8));
|
setLayout(new BorderLayout(0, 8));
|
||||||
add(accountsPanel, BorderLayout.NORTH);
|
add(accountsPanel, BorderLayout.NORTH);
|
||||||
|
192
MastodonApi.java
192
MastodonApi.java
@ -295,7 +295,7 @@ MastodonApi {
|
|||||||
submit(
|
submit(
|
||||||
String text, PostVisibility visibility,
|
String text, PostVisibility visibility,
|
||||||
String replyTo, String contentWarning,
|
String replyTo, String contentWarning,
|
||||||
String[] mediaIDs,
|
String[] mediaIDs,
|
||||||
RequestListener handler)
|
RequestListener handler)
|
||||||
{
|
{
|
||||||
String token = accessToken.get("access_token").value;
|
String token = accessToken.get("access_token").value;
|
||||||
@ -320,7 +320,7 @@ MastodonApi {
|
|||||||
HttpURLConnection conn = cast(endpoint.openConnection());
|
HttpURLConnection conn = cast(endpoint.openConnection());
|
||||||
String s1 = "Bearer " + token;
|
String s1 = "Bearer " + token;
|
||||||
conn.setRequestProperty("Authorization", s1);
|
conn.setRequestProperty("Authorization", s1);
|
||||||
String s2 = Integer.toString(handler.hashCode());
|
String s2 = Integer.toString(handler.hashCode());
|
||||||
conn.setRequestProperty("Idempotency-Key", s2);
|
conn.setRequestProperty("Idempotency-Key", s2);
|
||||||
conn.setDoOutput(true);
|
conn.setDoOutput(true);
|
||||||
conn.setRequestMethod("POST");
|
conn.setRequestMethod("POST");
|
||||||
@ -335,9 +335,9 @@ MastodonApi {
|
|||||||
if (contentWarning != null) {
|
if (contentWarning != null) {
|
||||||
output.write("&spoiler_text=" + contentWarning);
|
output.write("&spoiler_text=" + contentWarning);
|
||||||
}
|
}
|
||||||
for (String mediaID: mediaIDs) {
|
for (String mediaID: mediaIDs) {
|
||||||
output.write("&media_ids[]=" + mediaID);
|
output.write("&media_ids[]=" + mediaID);
|
||||||
}
|
}
|
||||||
|
|
||||||
output.close();
|
output.close();
|
||||||
|
|
||||||
@ -457,77 +457,77 @@ MastodonApi {
|
|||||||
uploadFile(File file, String alt, RequestListener handler)
|
uploadFile(File file, String alt, RequestListener handler)
|
||||||
{
|
{
|
||||||
assert file != null;
|
assert file != null;
|
||||||
assert alt != null;
|
assert alt != null;
|
||||||
assert file.canRead();
|
assert file.canRead();
|
||||||
|
|
||||||
String bct =
|
String bct =
|
||||||
"multipart/form-data; "
|
"multipart/form-data; "
|
||||||
+ "boundary=\"JKomastoFileUpload\"";
|
+ "boundary=\"JKomastoFileUpload\"";
|
||||||
String fsb = "--JKomastoFileUpload\r\n";
|
String fsb = "--JKomastoFileUpload\r\n";
|
||||||
String feb = "\r\n--JKomastoFileUpload--\r\n";
|
String feb = "\r\n--JKomastoFileUpload--\r\n";
|
||||||
String fcd =
|
String fcd =
|
||||||
"Content-Disposition: form-data; "
|
"Content-Disposition: form-data; "
|
||||||
+ "name=\"file\"; "
|
+ "name=\"file\"; "
|
||||||
+ "filename=\"" + file.getName() + "\"\r\n";
|
+ "filename=\"" + file.getName() + "\"\r\n";
|
||||||
String fct = "Content-Type: image/png\r\n\r\n";
|
String fct = "Content-Type: image/png\r\n\r\n";
|
||||||
int contentLength = 0;
|
int contentLength = 0;
|
||||||
contentLength += fsb.length();
|
contentLength += fsb.length();
|
||||||
contentLength += feb.length();
|
contentLength += feb.length();
|
||||||
contentLength += fcd.length();
|
contentLength += fcd.length();
|
||||||
contentLength += fct.length();
|
contentLength += fct.length();
|
||||||
contentLength += file.length();
|
contentLength += file.length();
|
||||||
/*
|
/*
|
||||||
* (知) This was an absurdity to debug. Contrary to
|
* (知) This was an absurdity to debug. Contrary to
|
||||||
* cURL, Java sets default values for some headers,
|
* cURL, Java sets default values for some headers,
|
||||||
* some of which are restricted, meaning you can't
|
* some of which are restricted, meaning you can't
|
||||||
* arbitrarily change them. Content-Length is one
|
* arbitrarily change them. Content-Length is one
|
||||||
* of them, set to 2^14-1 bytes. I'm pretty sure
|
* of them, set to 2^14-1 bytes. I'm pretty sure
|
||||||
* the file I was uploading was under this, but
|
* the file I was uploading was under this, but
|
||||||
* anyways one of the two parties was stopping me
|
* anyways one of the two parties was stopping me
|
||||||
* from finishing transferring my form data.
|
* from finishing transferring my form data.
|
||||||
*
|
*
|
||||||
* They didn't mention this in the Javadocs.
|
* They didn't mention this in the Javadocs.
|
||||||
* I noticed HttpURLConnection#setChunkedStreamingMode
|
* I noticed HttpURLConnection#setChunkedStreamingMode
|
||||||
* and #setFixedLengthStreamingMode by accident.
|
* and #setFixedLengthStreamingMode by accident.
|
||||||
* Turns out, the latter is how I do what cURL and
|
* Turns out, the latter is how I do what cURL and
|
||||||
* Firefox are doing - precalculate the exact size
|
* Firefox are doing - precalculate the exact size
|
||||||
* of the body and set the content length to it.
|
* of the body and set the content length to it.
|
||||||
* Unfortunately, this is not flexible, we have to
|
* Unfortunately, this is not flexible, we have to
|
||||||
* be exact. Thankfully, my answers pass..
|
* be exact. Thankfully, my answers pass..
|
||||||
*
|
*
|
||||||
* On the other side, Mastodon is obtuse as usual.
|
* On the other side, Mastodon is obtuse as usual.
|
||||||
* They had code that basically throws a generic 500
|
* They had code that basically throws a generic 500
|
||||||
* upon any sort of error from their library[1]. What
|
* upon any sort of error from their library[1]. What
|
||||||
* problem the library had with my requests, I could
|
* problem the library had with my requests, I could
|
||||||
* never know. There is an undocumented requirement
|
* never know. There is an undocumented requirement
|
||||||
* that you must put a filename in the content
|
* that you must put a filename in the content
|
||||||
* disposition. That one I found by guessing.
|
* disposition. That one I found by guessing.
|
||||||
*
|
*
|
||||||
* I solved this with the help of -Djavax.net.debug,
|
* I solved this with the help of -Djavax.net.debug,
|
||||||
* which revealed to me how my headers and body
|
* which revealed to me how my headers and body
|
||||||
* differed from cURL and Firefox. If this issue
|
* differed from cURL and Firefox. If this issue
|
||||||
* happens again, I advise giving up.
|
* happens again, I advise giving up.
|
||||||
*
|
*
|
||||||
* [1] app/controllers/api/v1/media_controller.rb
|
* [1] app/controllers/api/v1/media_controller.rb
|
||||||
* #create. 3 March 2022
|
* #create. 3 March 2022
|
||||||
*/
|
*/
|
||||||
|
|
||||||
String token = accessToken.get("access_token").value;
|
String token = accessToken.get("access_token").value;
|
||||||
String url = instanceUrl + "/api/v1/media/";
|
String url = instanceUrl + "/api/v1/media/";
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
String s1 = "?description=" + encode(alt);
|
String s1 = "?description=" + encode(alt);
|
||||||
|
|
||||||
URL endpoint = new URL(url + s1);
|
URL endpoint = new URL(url + s1);
|
||||||
HttpURLConnection conn = cast(endpoint.openConnection());
|
HttpURLConnection conn = cast(endpoint.openConnection());
|
||||||
String s2 = "Bearer " + token;
|
String s2 = "Bearer " + token;
|
||||||
conn.setRequestProperty("Authorization", s2);
|
conn.setRequestProperty("Authorization", s2);
|
||||||
conn.setDoOutput(true);
|
conn.setDoOutput(true);
|
||||||
conn.setRequestMethod("POST");
|
conn.setRequestMethod("POST");
|
||||||
conn.setFixedLengthStreamingMode(contentLength);
|
conn.setFixedLengthStreamingMode(contentLength);
|
||||||
conn.setRequestProperty("Content-Type", bct);
|
conn.setRequestProperty("Content-Type", bct);
|
||||||
conn.setRequestProperty("Accept", "*/*");
|
conn.setRequestProperty("Accept", "*/*");
|
||||||
conn.connect();
|
conn.connect();
|
||||||
|
|
||||||
OutputStream ostream = conn.getOutputStream();
|
OutputStream ostream = conn.getOutputStream();
|
||||||
InputStream istream = new FileInputStream(file);
|
InputStream istream = new FileInputStream(file);
|
||||||
@ -536,12 +536,12 @@ MastodonApi {
|
|||||||
ostream.write(fcd.getBytes());
|
ostream.write(fcd.getBytes());
|
||||||
ostream.write(fct.getBytes());
|
ostream.write(fct.getBytes());
|
||||||
|
|
||||||
int c; while ((c = istream.read()) != -1)
|
int c; while ((c = istream.read()) != -1)
|
||||||
ostream.write(c);
|
ostream.write(c);
|
||||||
|
|
||||||
ostream.write(feb.getBytes());
|
ostream.write(feb.getBytes());
|
||||||
istream.close();
|
istream.close();
|
||||||
ostream.close();
|
ostream.close();
|
||||||
|
|
||||||
doStandardJsonReturn(conn, handler);
|
doStandardJsonReturn(conn, handler);
|
||||||
}
|
}
|
||||||
@ -550,7 +550,7 @@ MastodonApi {
|
|||||||
|
|
||||||
public void
|
public void
|
||||||
monitorTimeline(
|
monitorTimeline(
|
||||||
TimelineType type, ServerSideEventsListener handler)
|
TimelineType type, ServerSideEventsListener handler)
|
||||||
{
|
{
|
||||||
String token = accessToken.get("access_token").value;
|
String token = accessToken.get("access_token").value;
|
||||||
|
|
||||||
@ -584,7 +584,7 @@ MastodonApi {
|
|||||||
conn.setReadTimeout(500);
|
conn.setReadTimeout(500);
|
||||||
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 (true) try
|
while (true) try
|
||||||
{
|
{
|
||||||
String line = br.readLine();
|
String line = br.readLine();
|
||||||
@ -629,7 +629,7 @@ MastodonApi {
|
|||||||
int code = conn.getResponseCode();
|
int code = conn.getResponseCode();
|
||||||
if (code >= 300)
|
if (code >= 300)
|
||||||
{
|
{
|
||||||
Reader input = ireader(conn.getErrorStream());
|
Reader input = ireader(conn.getErrorStream());
|
||||||
Tree<String> response = fromPlain(input);
|
Tree<String> response = fromPlain(input);
|
||||||
input.close();
|
input.close();
|
||||||
handler.requestFailed(code, response);
|
handler.requestFailed(code, response);
|
||||||
@ -663,13 +663,13 @@ MastodonApi {
|
|||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
private static String
|
private static String
|
||||||
deescape(String string)
|
deescape(String string)
|
||||||
{
|
{
|
||||||
if (string == null) return string;
|
if (string == null) return string;
|
||||||
string = string.replaceAll("\n", "\\\\n");
|
string = string.replaceAll("\n", "\\\\n");
|
||||||
return string;
|
return string;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Tree<String>
|
private static Tree<String>
|
||||||
fromPlain(Reader r)
|
fromPlain(Reader r)
|
||||||
@ -699,27 +699,27 @@ MastodonApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static HttpURLConnection
|
private static HttpURLConnection
|
||||||
cast(URLConnection conn)
|
cast(URLConnection conn)
|
||||||
{
|
{
|
||||||
return (HttpURLConnection)conn;
|
return (HttpURLConnection)conn;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static InputStreamReader
|
private static InputStreamReader
|
||||||
ireader(InputStream is)
|
ireader(InputStream is)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
assert is != null;
|
assert is != null;
|
||||||
return new InputStreamReader(is);
|
return new InputStreamReader(is);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static OutputStreamWriter
|
private static OutputStreamWriter
|
||||||
owriter(OutputStream os)
|
owriter(OutputStream os)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
assert os != null;
|
assert os != null;
|
||||||
return new OutputStreamWriter(os);
|
return new OutputStreamWriter(os);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
|
@ -48,80 +48,80 @@ import java.awt.event.ComponentEvent;
|
|||||||
class
|
class
|
||||||
NotificationsWindow extends JFrame {
|
NotificationsWindow extends JFrame {
|
||||||
|
|
||||||
private JKomasto
|
private JKomasto
|
||||||
primaire;
|
primaire;
|
||||||
|
|
||||||
private List<Notification>
|
private List<Notification>
|
||||||
notifications;
|
notifications;
|
||||||
|
|
||||||
private MastodonApi
|
private MastodonApi
|
||||||
api;
|
api;
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
private NotificationsComponent
|
private NotificationsComponent
|
||||||
display;
|
display;
|
||||||
|
|
||||||
private boolean
|
private boolean
|
||||||
showingLatest;
|
showingLatest;
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
private static int
|
private static int
|
||||||
ROW_COUNT = NotificationsComponent.ROW_COUNT;
|
ROW_COUNT = NotificationsComponent.ROW_COUNT;
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
public synchronized void
|
public synchronized void
|
||||||
readEntity(Tree<String> entity)
|
readEntity(Tree<String> entity)
|
||||||
{
|
{
|
||||||
notifications = new ArrayList<>();
|
notifications = new ArrayList<>();
|
||||||
for (Tree<String> t: entity)
|
for (Tree<String> t: entity)
|
||||||
{
|
{
|
||||||
Notification n = new Notification();
|
Notification n = new Notification();
|
||||||
|
|
||||||
n.id = t.get("id").value;
|
n.id = t.get("id").value;
|
||||||
|
|
||||||
String type = t.get("type").value;
|
String type = t.get("type").value;
|
||||||
if (type.equals("favourite"))
|
if (type.equals("favourite"))
|
||||||
n.type = NotificationType.FAVOURITE;
|
n.type = NotificationType.FAVOURITE;
|
||||||
else if (type.equals("reblog"))
|
else if (type.equals("reblog"))
|
||||||
n.type = NotificationType.BOOST;
|
n.type = NotificationType.BOOST;
|
||||||
else if (type.equals("mention"))
|
else if (type.equals("mention"))
|
||||||
n.type = NotificationType.MENTION;
|
n.type = NotificationType.MENTION;
|
||||||
else if (type.equals("follow"))
|
else if (type.equals("follow"))
|
||||||
n.type = NotificationType.FOLLOW;
|
n.type = NotificationType.FOLLOW;
|
||||||
else if (type.equals("follow_request"))
|
else if (type.equals("follow_request"))
|
||||||
n.type = NotificationType.FOLLOWREQ;
|
n.type = NotificationType.FOLLOWREQ;
|
||||||
else if (type.equals("poll"))
|
else if (type.equals("poll"))
|
||||||
n.type = NotificationType.POLL;
|
n.type = NotificationType.POLL;
|
||||||
else if (type.equals("status"))
|
else if (type.equals("status"))
|
||||||
n.type = NotificationType.ALERT;
|
n.type = NotificationType.ALERT;
|
||||||
|
|
||||||
Tree<String> actor = t.get("account");
|
Tree<String> actor = t.get("account");
|
||||||
String aid, aname, adisp;
|
String aid, aname, adisp;
|
||||||
aid = actor.get("id").value;
|
aid = actor.get("id").value;
|
||||||
aname = actor.get("username").value;
|
aname = actor.get("username").value;
|
||||||
adisp = actor.get("display_name").value;
|
adisp = actor.get("display_name").value;
|
||||||
if (!adisp.isEmpty()) n.actorName = adisp;
|
if (!adisp.isEmpty()) n.actorName = adisp;
|
||||||
else n.actorName = aname;
|
else n.actorName = aname;
|
||||||
n.actorNumId = aid;
|
n.actorNumId = aid;
|
||||||
|
|
||||||
if (n.type != NotificationType.FOLLOW)
|
if (n.type != NotificationType.FOLLOW)
|
||||||
{
|
{
|
||||||
Post post = new Post(t.get("status"));
|
Post post = new Post(t.get("status"));
|
||||||
post.resolveApproximateText();
|
post.resolveApproximateText();
|
||||||
n.postId = post.id;
|
n.postId = post.id;
|
||||||
n.postText = post.approximateText;
|
n.postText = post.approximateText;
|
||||||
}
|
}
|
||||||
|
|
||||||
notifications.add(n);
|
notifications.add(n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
refresh()
|
refresh()
|
||||||
{
|
{
|
||||||
String firstId = null;
|
String firstId = null;
|
||||||
if (!showingLatest)
|
if (!showingLatest)
|
||||||
{
|
{
|
||||||
@ -134,53 +134,53 @@ NotificationsWindow extends JFrame {
|
|||||||
if (notifications.size() < ROW_COUNT) showLatestPage();
|
if (notifications.size() < ROW_COUNT) showLatestPage();
|
||||||
display.showNotifications(notifications);
|
display.showNotifications(notifications);
|
||||||
display.repaint();
|
display.repaint();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
showLatestPage()
|
showLatestPage()
|
||||||
{
|
{
|
||||||
if (fetchPage(null, null))
|
if (fetchPage(null, null))
|
||||||
{
|
{
|
||||||
display.showNotifications(notifications);
|
display.showNotifications(notifications);
|
||||||
showingLatest = true;
|
showingLatest = true;
|
||||||
primaire.getWindowUpdater().add(this);
|
primaire.getWindowUpdater().add(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
showPrevPage()
|
showPrevPage()
|
||||||
{
|
{
|
||||||
assert !notifications.isEmpty();
|
assert !notifications.isEmpty();
|
||||||
if (fetchPage(null, notifications.get(0).id))
|
if (fetchPage(null, notifications.get(0).id))
|
||||||
{
|
{
|
||||||
if (notifications.size() < ROW_COUNT) showLatestPage();
|
if (notifications.size() < ROW_COUNT) showLatestPage();
|
||||||
display.showNotifications(notifications);
|
display.showNotifications(notifications);
|
||||||
showingLatest = false;
|
showingLatest = false;
|
||||||
primaire.getWindowUpdater().remove(this);
|
primaire.getWindowUpdater().remove(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
showNextPage()
|
showNextPage()
|
||||||
{
|
{
|
||||||
assert !notifications.isEmpty();
|
assert !notifications.isEmpty();
|
||||||
int last = notifications.size() - 1;
|
int last = notifications.size() - 1;
|
||||||
if (fetchPage(notifications.get(last).id, null))
|
if (fetchPage(notifications.get(last).id, null))
|
||||||
{
|
{
|
||||||
display.showNotifications(notifications);
|
display.showNotifications(notifications);
|
||||||
showingLatest = false;
|
showingLatest = false;
|
||||||
primaire.getWindowUpdater().remove(this);
|
primaire.getWindowUpdater().remove(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
private boolean
|
private boolean
|
||||||
fetchPage(String maxId, String minId)
|
fetchPage(String maxId, String minId)
|
||||||
{
|
{
|
||||||
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||||
class Handler implements RequestListener {
|
class Handler implements RequestListener {
|
||||||
|
|
||||||
boolean
|
boolean
|
||||||
succeeded = false;
|
succeeded = false;
|
||||||
@ -207,31 +207,31 @@ NotificationsWindow extends JFrame {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Handler handler = new Handler();
|
Handler handler = new Handler();
|
||||||
api.getNotifications(ROW_COUNT, maxId, minId, handler);
|
api.getNotifications(ROW_COUNT, maxId, minId, handler);
|
||||||
display.setCursor(null);
|
display.setCursor(null);
|
||||||
repaint();
|
repaint();
|
||||||
return handler.succeeded;
|
return handler.succeeded;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
NotificationsWindow(JKomasto primaire)
|
NotificationsWindow(JKomasto primaire)
|
||||||
{
|
{
|
||||||
super("Notifications");
|
super("Notifications");
|
||||||
this.primaire = primaire;
|
this.primaire = primaire;
|
||||||
this.api = primaire.getMastodonApi();
|
this.api = primaire.getMastodonApi();
|
||||||
|
|
||||||
notifications = new ArrayList<>();
|
notifications = new ArrayList<>();
|
||||||
|
|
||||||
display = new NotificationsComponent(this);
|
display = new NotificationsComponent(this);
|
||||||
display.setPreferredSize(new Dimension(256, 260));
|
display.setPreferredSize(new Dimension(256, 260));
|
||||||
setContentPane(display);
|
setContentPane(display);
|
||||||
pack();
|
pack();
|
||||||
|
|
||||||
setIconImage(primaire.getProgramIcon());
|
setIconImage(primaire.getProgramIcon());
|
||||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,88 +239,88 @@ class
|
|||||||
NotificationsComponent extends JPanel
|
NotificationsComponent extends JPanel
|
||||||
implements ActionListener {
|
implements ActionListener {
|
||||||
|
|
||||||
private NotificationsWindow
|
private NotificationsWindow
|
||||||
primaire;
|
primaire;
|
||||||
|
|
||||||
private JButton
|
private JButton
|
||||||
prev, next;
|
prev, next;
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
private List<NotificationComponent>
|
private List<NotificationComponent>
|
||||||
rows;
|
rows;
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
static final int
|
static final int
|
||||||
ROW_COUNT = 10;
|
ROW_COUNT = 10;
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
public void
|
public void
|
||||||
showNotifications(List<Notification> notifications)
|
showNotifications(List<Notification> notifications)
|
||||||
{
|
{
|
||||||
assert notifications.size() == rows.size();
|
assert notifications.size() == rows.size();
|
||||||
for (int o = 0; o < rows.size(); ++o)
|
for (int o = 0; o < rows.size(); ++o)
|
||||||
{
|
{
|
||||||
Notification n = notifications.get(o);
|
Notification n = notifications.get(o);
|
||||||
NotificationComponent c = rows.get(o);
|
NotificationComponent c = rows.get(o);
|
||||||
c.setName(n.actorName);
|
c.setName(n.actorName);
|
||||||
switch (n.type)
|
switch (n.type)
|
||||||
{
|
{
|
||||||
case MENTION: c.setType("mentioned"); break;
|
case MENTION: c.setType("mentioned"); break;
|
||||||
case BOOST: c.setType("boosted"); break;
|
case BOOST: c.setType("boosted"); break;
|
||||||
case FAVOURITE: c.setType("favourited"); break;
|
case FAVOURITE: c.setType("favourited"); break;
|
||||||
case FOLLOW: c.setType("followed"); break;
|
case FOLLOW: c.setType("followed"); break;
|
||||||
case FOLLOWREQ: c.setType("req. follow"); break;
|
case FOLLOWREQ: c.setType("req. follow"); break;
|
||||||
case POLL: c.setType("poll ended"); break;
|
case POLL: c.setType("poll ended"); break;
|
||||||
case ALERT: c.setType("posted"); break;
|
case ALERT: c.setType("posted"); break;
|
||||||
}
|
}
|
||||||
c.setText(n.postText);
|
c.setText(n.postText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
public void
|
public void
|
||||||
actionPerformed(ActionEvent eA)
|
actionPerformed(ActionEvent eA)
|
||||||
{
|
{
|
||||||
if (eA.getSource() == prev) primaire.showPrevPage();
|
if (eA.getSource() == prev) primaire.showPrevPage();
|
||||||
if (eA.getSource() == next) primaire.showNextPage();
|
if (eA.getSource() == next) primaire.showNextPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
NotificationsComponent(NotificationsWindow primaire)
|
NotificationsComponent(NotificationsWindow primaire)
|
||||||
{
|
{
|
||||||
this.primaire = primaire;
|
this.primaire = primaire;
|
||||||
|
|
||||||
Border b = BorderFactory.createEmptyBorder(8, 8, 8, 8);
|
Border b = BorderFactory.createEmptyBorder(8, 8, 8, 8);
|
||||||
|
|
||||||
rows = new ArrayList<>();
|
rows = new ArrayList<>();
|
||||||
for (int n = ROW_COUNT; n > 0; --n)
|
for (int n = ROW_COUNT; n > 0; --n)
|
||||||
rows.add(new NotificationComponent());
|
rows.add(new NotificationComponent());
|
||||||
|
|
||||||
prev = new JButton("<");
|
prev = new JButton("<");
|
||||||
next = new JButton(">");
|
next = new JButton(">");
|
||||||
prev.addActionListener(this);
|
prev.addActionListener(this);
|
||||||
next.addActionListener(this);
|
next.addActionListener(this);
|
||||||
|
|
||||||
JPanel centre = new JPanel();
|
JPanel centre = new JPanel();
|
||||||
centre.setLayout(new GridLayout(ROW_COUNT, 1));
|
centre.setLayout(new GridLayout(ROW_COUNT, 1));
|
||||||
for (NotificationComponent c: rows) centre.add(c);
|
for (NotificationComponent c: rows) centre.add(c);
|
||||||
|
|
||||||
Box bottom = Box.createHorizontalBox();
|
Box bottom = Box.createHorizontalBox();
|
||||||
bottom.add(Box.createGlue());
|
bottom.add(Box.createGlue());
|
||||||
bottom.add(prev);
|
bottom.add(prev);
|
||||||
bottom.add(Box.createHorizontalStrut(8));
|
bottom.add(Box.createHorizontalStrut(8));
|
||||||
bottom.add(next);
|
bottom.add(next);
|
||||||
bottom.setBorder(b);
|
bottom.setBorder(b);
|
||||||
|
|
||||||
setLayout(new BorderLayout());
|
setLayout(new BorderLayout());
|
||||||
add(centre);
|
add(centre);
|
||||||
add(bottom, BorderLayout.SOUTH);
|
add(bottom, BorderLayout.SOUTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -328,145 +328,145 @@ class
|
|||||||
NotificationComponent extends JComponent
|
NotificationComponent extends JComponent
|
||||||
implements ComponentListener {
|
implements ComponentListener {
|
||||||
|
|
||||||
private JLabel
|
private JLabel
|
||||||
type;
|
type;
|
||||||
|
|
||||||
private ImageComponent
|
private ImageComponent
|
||||||
typeImg;
|
typeImg;
|
||||||
|
|
||||||
private JLabel
|
private JLabel
|
||||||
name, text;
|
name, text;
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
public void
|
public void
|
||||||
setType(String n)
|
setType(String n)
|
||||||
{
|
{
|
||||||
type.setText(n);
|
type.setText(n);
|
||||||
typeImg.image = ImageApi.local(n + "Notification");
|
typeImg.image = ImageApi.local(n + "Notification");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
setName(String n) { name.setText(n); }
|
setName(String n) { name.setText(n); }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
setText(String n) { text.setText(n); }
|
setText(String n) { text.setText(n); }
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
public void
|
public void
|
||||||
doLayout()
|
doLayout()
|
||||||
{
|
{
|
||||||
int w = getWidth(), h = getHeight();
|
int w = getWidth(), h = getHeight();
|
||||||
|
|
||||||
int x0 = w * 0/20;
|
int x0 = w * 0/20;
|
||||||
int x1 = w * 7/20;
|
int x1 = w * 7/20;
|
||||||
int x2 = w * 13/20;
|
int x2 = w * 13/20;
|
||||||
int x3 = w * 15/20;
|
int x3 = w * 15/20;
|
||||||
int x4 = w * 20/20;
|
int x4 = w * 20/20;
|
||||||
|
|
||||||
name.setLocation(x0 + 4, 0);
|
name.setLocation(x0 + 4, 0);
|
||||||
name.setSize((x1 - 4) - (x0 + 4), h);
|
name.setSize((x1 - 4) - (x0 + 4), h);
|
||||||
|
|
||||||
type.setLocation(x1 + 4, 0);
|
type.setLocation(x1 + 4, 0);
|
||||||
type.setSize((x2 - 1) - (x1 + 4), h);
|
type.setSize((x2 - 1) - (x1 + 4), h);
|
||||||
|
|
||||||
typeImg.setLocation((x2 + 1), 2);
|
typeImg.setLocation((x2 + 1), 2);
|
||||||
typeImg.setSize((x3 - 4) - (x2 + 1), (h - 2) - 2);
|
typeImg.setSize((x3 - 4) - (x2 + 1), (h - 2) - 2);
|
||||||
|
|
||||||
text.setLocation(x3 + 4, 0);
|
text.setLocation(x3 + 4, 0);
|
||||||
text.setSize((x4 - 4) - (x3 + 4), h);
|
text.setSize((x4 - 4) - (x3 + 4), h);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
componentResized(ComponentEvent eC) { doLayout(); }
|
componentResized(ComponentEvent eC) { doLayout(); }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
componentShown(ComponentEvent eC) { }
|
componentShown(ComponentEvent eC) { }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
componentHidden(ComponentEvent eC) { }
|
componentHidden(ComponentEvent eC) { }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
componentMoved(ComponentEvent eC) { }
|
componentMoved(ComponentEvent eC) { }
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
private static class
|
private static class
|
||||||
ImageComponent extends JComponent {
|
ImageComponent extends JComponent {
|
||||||
|
|
||||||
private Image
|
private Image
|
||||||
image,
|
image,
|
||||||
scaled;
|
scaled;
|
||||||
|
|
||||||
private boolean
|
private boolean
|
||||||
snapdown = false;
|
snapdown = false;
|
||||||
|
|
||||||
// -=%=-
|
// -=%=-
|
||||||
|
|
||||||
protected void
|
protected void
|
||||||
paintComponent(Graphics g)
|
paintComponent(Graphics g)
|
||||||
{
|
{
|
||||||
if (image == null) return;
|
if (image == null) return;
|
||||||
int w = getWidth(), h = getHeight();
|
int w = getWidth(), h = getHeight();
|
||||||
|
|
||||||
int ow = image.getWidth(this);
|
int ow = image.getWidth(this);
|
||||||
int oh = image.getHeight(this);
|
int oh = image.getHeight(this);
|
||||||
int nh = h;
|
int nh = h;
|
||||||
int nw = ow * nh/oh;
|
int nw = ow * nh/oh;
|
||||||
if (snapdown)
|
if (snapdown)
|
||||||
{
|
{
|
||||||
int sw, sh;
|
int sw, sh;
|
||||||
for (sw = 1; sw < nw; sw *= 2);
|
for (sw = 1; sw < nw; sw *= 2);
|
||||||
for (sh = 1; sh < nh; sh *= 2);
|
for (sh = 1; sh < nh; sh *= 2);
|
||||||
nw = sw / 2;
|
nw = sw / 2;
|
||||||
nh = sh / 2;
|
nh = sh / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scaled == null)
|
if (scaled == null)
|
||||||
scaled = image.getScaledInstance(
|
scaled = image.getScaledInstance(
|
||||||
nw, nh,
|
nw, nh,
|
||||||
Image.SCALE_SMOOTH
|
Image.SCALE_SMOOTH
|
||||||
);
|
);
|
||||||
int x = (w - nw) / 2;
|
int x = (w - nw) / 2;
|
||||||
int y = (h - nh) / 2;
|
int y = (h - nh) / 2;
|
||||||
g.drawImage(scaled, x, y, this);
|
g.drawImage(scaled, x, y, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
NotificationComponent()
|
NotificationComponent()
|
||||||
{
|
{
|
||||||
Font f = new Font("Dialog", Font.PLAIN, 12);
|
Font f = new Font("Dialog", Font.PLAIN, 12);
|
||||||
Font f1 = f.deriveFont(Font.PLAIN, 14);
|
Font f1 = f.deriveFont(Font.PLAIN, 14);
|
||||||
Font f2 = f.deriveFont(Font.PLAIN, 11);
|
Font f2 = f.deriveFont(Font.PLAIN, 11);
|
||||||
Font f3 = f.deriveFont(Font.ITALIC, 14);
|
Font f3 = f.deriveFont(Font.ITALIC, 14);
|
||||||
|
|
||||||
Color c = new Color(0, 0, 0, 25);
|
Color c = new Color(0, 0, 0, 25);
|
||||||
Border b1 = BorderFactory.createMatteBorder(0, 0, 1, 0, c);
|
Border b1 = BorderFactory.createMatteBorder(0, 0, 1, 0, c);
|
||||||
|
|
||||||
name = new JLabel();
|
name = new JLabel();
|
||||||
name.setFont(f1);
|
name.setFont(f1);
|
||||||
|
|
||||||
type = new JLabel();
|
type = new JLabel();
|
||||||
type.setFont(f2);
|
type.setFont(f2);
|
||||||
type.setHorizontalAlignment(JLabel.RIGHT);
|
type.setHorizontalAlignment(JLabel.RIGHT);
|
||||||
|
|
||||||
typeImg = new ImageComponent();
|
typeImg = new ImageComponent();
|
||||||
|
|
||||||
text = new JLabel();
|
text = new JLabel();
|
||||||
text.setFont(f3);
|
text.setFont(f3);
|
||||||
|
|
||||||
setLayout(null);
|
setLayout(null);
|
||||||
add(name);
|
add(name);
|
||||||
add(type);
|
add(type);
|
||||||
add(typeImg);
|
add(typeImg);
|
||||||
add(text);
|
add(text);
|
||||||
|
|
||||||
this.addComponentListener(this);
|
this.addComponentListener(this);
|
||||||
setBorder(b1);
|
setBorder(b1);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
484
PostWindow.java
484
PostWindow.java
@ -74,9 +74,9 @@ PostWindow extends JFrame {
|
|||||||
private MastodonApi
|
private MastodonApi
|
||||||
api;
|
api;
|
||||||
|
|
||||||
private Post
|
private Post
|
||||||
post,
|
post,
|
||||||
wrapperPost;
|
wrapperPost;
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
@ -109,21 +109,21 @@ PostWindow extends JFrame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
display.setAuthorName(post.author.name);
|
display.setAuthorName(post.author.name);
|
||||||
display.setAuthorId(post.author.id);
|
display.setAuthorId(post.author.id);
|
||||||
|
|
||||||
String oid = api.getAccountDetails().get("id").value;
|
String oid = api.getAccountDetails().get("id").value;
|
||||||
String aid = post.author.numId;
|
String aid = post.author.numId;
|
||||||
display.setDeleteEnabled(aid.equals(oid));
|
display.setDeleteEnabled(aid.equals(oid));
|
||||||
|
|
||||||
post.author.resolveAvatar();
|
post.author.resolveAvatar();
|
||||||
display.setAuthorAvatar(post.author.avatar);
|
display.setAuthorAvatar(post.author.avatar);
|
||||||
|
|
||||||
display.setDate(post.date);
|
display.setDate(post.date);
|
||||||
display.setTime(post.time);
|
display.setTime(post.time);
|
||||||
|
|
||||||
display.setEmojiUrls(post.emojiUrls);
|
display.setEmojiUrls(post.emojiUrls);
|
||||||
|
|
||||||
display.setHtml(post.text);
|
display.setHtml(post.text);
|
||||||
display.setFavourited(post.favourited);
|
display.setFavourited(post.favourited);
|
||||||
display.setBoosted(post.boosted);
|
display.setBoosted(post.boosted);
|
||||||
|
|
||||||
@ -137,29 +137,29 @@ PostWindow extends JFrame {
|
|||||||
post.resolveApproximateText();
|
post.resolveApproximateText();
|
||||||
this.setTitle(post.approximateText);
|
this.setTitle(post.approximateText);
|
||||||
|
|
||||||
display.resetFocus();
|
display.resetFocus();
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
readEntity(Tree<String> post)
|
readEntity(Tree<String> post)
|
||||||
{
|
{
|
||||||
use(new Post(post));
|
use(new Post(post));
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void
|
public synchronized void
|
||||||
openAuthorProfile()
|
openAuthorProfile()
|
||||||
{
|
{
|
||||||
ProfileWindow w = new ProfileWindow(primaire);
|
ProfileWindow w = new ProfileWindow(primaire);
|
||||||
w.use(post.author);
|
w.use(post.author);
|
||||||
w.setLocationRelativeTo(this);
|
w.setLocationRelativeTo(this);
|
||||||
w.setVisible(true);
|
w.setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void
|
public synchronized void
|
||||||
favourite(boolean favourited)
|
favourite(boolean favourited)
|
||||||
{
|
{
|
||||||
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||||
display.setFavouriteBoostEnabled(false);
|
display.setFavouriteBoostEnabled(false);
|
||||||
display.paintImmediately(display.getBounds());
|
display.paintImmediately(display.getBounds());
|
||||||
RequestListener handler = new RequestListener() {
|
RequestListener handler = new RequestListener() {
|
||||||
@ -188,11 +188,11 @@ PostWindow extends JFrame {
|
|||||||
public void
|
public void
|
||||||
requestSucceeded(Tree<String> json)
|
requestSucceeded(Tree<String> json)
|
||||||
{
|
{
|
||||||
PostWindow.this.post.favourited = favourited;
|
PostWindow.this.post.favourited = favourited;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
api.setPostFavourited(post.id, favourited, handler);
|
api.setPostFavourited(post.id, favourited, handler);
|
||||||
display.setCursor(null);
|
display.setCursor(null);
|
||||||
display.setFavouriteBoostEnabled(true);
|
display.setFavouriteBoostEnabled(true);
|
||||||
display.repaint();
|
display.repaint();
|
||||||
@ -234,7 +234,7 @@ PostWindow extends JFrame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
api.setPostBoosted(post.id, boosted, handler);
|
api.setPostBoosted(post.id, boosted, handler);
|
||||||
display.setCursor(null);
|
display.setCursor(null);
|
||||||
display.setFavouriteBoostEnabled(true);
|
display.setFavouriteBoostEnabled(true);
|
||||||
display.repaint();
|
display.repaint();
|
||||||
@ -246,9 +246,9 @@ PostWindow extends JFrame {
|
|||||||
String ownId = api.getAccountDetails().get("acct").value;
|
String ownId = api.getAccountDetails().get("acct").value;
|
||||||
Composition c = Composition.reply(this.post, ownId);
|
Composition c = Composition.reply(this.post, ownId);
|
||||||
ComposeWindow w = primaire.getComposeWindow();
|
ComposeWindow w = primaire.getComposeWindow();
|
||||||
w.setComposition(c);
|
w.setComposition(c);
|
||||||
if (!w.isVisible())
|
if (!w.isVisible())
|
||||||
{
|
{
|
||||||
w.setLocation(getX(), getY() + 100);
|
w.setLocation(getX(), getY() + 100);
|
||||||
w.setVisible(true);
|
w.setVisible(true);
|
||||||
}
|
}
|
||||||
@ -271,62 +271,62 @@ PostWindow extends JFrame {
|
|||||||
display.setCursor(null);
|
display.setCursor(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void
|
public synchronized void
|
||||||
deletePost(boolean redraft)
|
deletePost(boolean redraft)
|
||||||
{
|
{
|
||||||
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||||
display.setDeleteEnabled(false);
|
display.setDeleteEnabled(false);
|
||||||
display.paintImmediately(display.getBounds());
|
display.paintImmediately(display.getBounds());
|
||||||
|
|
||||||
final String S1 =
|
final String S1 =
|
||||||
"Are you sure you'd like to delete this post?\n";
|
"Are you sure you'd like to delete this post?\n";
|
||||||
final String S2 =
|
final String S2 =
|
||||||
"Are you sure you'd like to delete this post?\n"
|
"Are you sure you'd like to delete this post?\n"
|
||||||
+ "You are redrafting, so a composition window\n"
|
+ "You are redrafting, so a composition window\n"
|
||||||
+ "should open with its contents filled.";
|
+ "should open with its contents filled.";
|
||||||
JOptionPane dialog = new JOptionPane();
|
JOptionPane dialog = new JOptionPane();
|
||||||
dialog.setMessageType(JOptionPane.QUESTION_MESSAGE);
|
dialog.setMessageType(JOptionPane.QUESTION_MESSAGE);
|
||||||
dialog.setMessage(redraft ? S2 : S1);
|
dialog.setMessage(redraft ? S2 : S1);
|
||||||
dialog.setOptions(new String[] { "No", "Yes" });
|
dialog.setOptions(new String[] { "No", "Yes" });
|
||||||
String title = "Confirm delete";
|
String title = "Confirm delete";
|
||||||
dialog.createDialog(this, title).setVisible(true);
|
dialog.createDialog(this, title).setVisible(true);
|
||||||
if (!dialog.getValue().equals("Yes"))
|
if (!dialog.getValue().equals("Yes"))
|
||||||
{
|
{
|
||||||
display.setCursor(null);
|
display.setCursor(null);
|
||||||
display.setDeleteEnabled(true);
|
display.setDeleteEnabled(true);
|
||||||
display.paintImmediately(display.getBounds());
|
display.paintImmediately(display.getBounds());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
api.deletePost(post.id, new RequestListener() {
|
api.deletePost(post.id, new RequestListener() {
|
||||||
|
|
||||||
public void
|
public void
|
||||||
connectionFailed(IOException eIo)
|
connectionFailed(IOException eIo)
|
||||||
{
|
{
|
||||||
JOptionPane.showMessageDialog(
|
JOptionPane.showMessageDialog(
|
||||||
PostWindow.this,
|
PostWindow.this,
|
||||||
"Failed to delete post.."
|
"Failed to delete post.."
|
||||||
+ "\n" + eIo.getMessage()
|
+ "\n" + eIo.getMessage()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
requestFailed(int httpCode, Tree<String> json)
|
requestFailed(int httpCode, Tree<String> json)
|
||||||
{
|
{
|
||||||
JOptionPane.showMessageDialog(
|
JOptionPane.showMessageDialog(
|
||||||
PostWindow.this,
|
PostWindow.this,
|
||||||
"Failed to delete post.."
|
"Failed to delete post.."
|
||||||
+ "\n" + json.get("error").value
|
+ "\n" + json.get("error").value
|
||||||
+ "\n(HTTP code: " + httpCode + ")"
|
+ "\n(HTTP code: " + httpCode + ")"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
requestSucceeded(Tree<String> json)
|
requestSucceeded(Tree<String> json)
|
||||||
{
|
{
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
|
|
||||||
if (redraft)
|
if (redraft)
|
||||||
{
|
{
|
||||||
Composition c = Composition.recover(json);
|
Composition c = Composition.recover(json);
|
||||||
ComposeWindow w = new ComposeWindow(primaire);
|
ComposeWindow w = new ComposeWindow(primaire);
|
||||||
@ -334,36 +334,36 @@ PostWindow extends JFrame {
|
|||||||
w.setLocation(getX(), getY() + 100);
|
w.setLocation(getX(), getY() + 100);
|
||||||
w.setVisible(true);
|
w.setVisible(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
display.setCursor(null);
|
display.setCursor(null);
|
||||||
display.setDeleteEnabled(true);
|
display.setDeleteEnabled(true);
|
||||||
display.paintImmediately(display.getBounds());
|
display.paintImmediately(display.getBounds());
|
||||||
if (!isVisible()) dispose();
|
if (!isVisible()) dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void
|
public synchronized void
|
||||||
copyPostId()
|
copyPostId()
|
||||||
{
|
{
|
||||||
ClipboardApi.serve(post.id);
|
ClipboardApi.serve(post.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void
|
public synchronized void
|
||||||
copyPostLink()
|
copyPostLink()
|
||||||
{
|
{
|
||||||
ClipboardApi.serve(post.uri);
|
ClipboardApi.serve(post.uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void
|
public synchronized void
|
||||||
openReplies()
|
openReplies()
|
||||||
{
|
{
|
||||||
RepliesWindow w = new RepliesWindow(primaire, this);
|
RepliesWindow w = new RepliesWindow(primaire, this);
|
||||||
w.showFor(post.id);
|
w.showFor(post.id);
|
||||||
w.setLocation(getX(), getY() + 100);
|
w.setLocation(getX(), getY() + 100);
|
||||||
w.setVisible(true);
|
w.setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
@ -380,7 +380,7 @@ PostWindow extends JFrame {
|
|||||||
display = new PostComponent(this);
|
display = new PostComponent(this);
|
||||||
|
|
||||||
setContentPane(display);
|
setContentPane(display);
|
||||||
setIconImage(primaire.getProgramIcon());
|
setIconImage(primaire.getProgramIcon());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -396,23 +396,23 @@ implements ActionListener {
|
|||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
private List<RichTextPane.Segment>
|
private List<RichTextPane.Segment>
|
||||||
authorNameOr;
|
authorNameOr;
|
||||||
|
|
||||||
private RichTextPane
|
private RichTextPane
|
||||||
authorName;
|
authorName;
|
||||||
|
|
||||||
private RichTextPane3
|
private RichTextPane3
|
||||||
body;
|
body;
|
||||||
|
|
||||||
private JScrollPane
|
private JScrollPane
|
||||||
bodyScrollPane;
|
bodyScrollPane;
|
||||||
|
|
||||||
private JLabel
|
private JLabel
|
||||||
authorId, time, date;
|
authorId, time, date;
|
||||||
|
|
||||||
private String[][]
|
private String[][]
|
||||||
emojiUrls;
|
emojiUrls;
|
||||||
|
|
||||||
private TwoToggleButton
|
private TwoToggleButton
|
||||||
favouriteBoost,
|
favouriteBoost,
|
||||||
@ -423,32 +423,32 @@ implements ActionListener {
|
|||||||
profile,
|
profile,
|
||||||
media;
|
media;
|
||||||
|
|
||||||
private JPopupMenu
|
private JPopupMenu
|
||||||
miscMenu;
|
miscMenu;
|
||||||
|
|
||||||
private JMenuItem
|
private JMenuItem
|
||||||
openReplies,
|
openReplies,
|
||||||
copyPostId,
|
copyPostId,
|
||||||
copyPostLink,
|
copyPostLink,
|
||||||
deletePost,
|
deletePost,
|
||||||
redraftPost;
|
redraftPost;
|
||||||
|
|
||||||
private Image
|
private Image
|
||||||
backgroundImage;
|
backgroundImage;
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
public void
|
public void
|
||||||
setAuthorName(String n)
|
setAuthorName(String n)
|
||||||
{
|
{
|
||||||
authorNameOr = new RichTextPane.Builder().text(n).finish();
|
authorNameOr = new RichTextPane.Builder().text(n).finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
setAuthorId(String n) { authorId.setText(n); }
|
setAuthorId(String n) { authorId.setText(n); }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
setAuthorAvatar(Image n) { profile.setImage(n); }
|
setAuthorAvatar(Image n) { profile.setImage(n); }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
setDate(String n) { date.setText(n); }
|
setDate(String n) { date.setText(n); }
|
||||||
@ -456,24 +456,24 @@ implements ActionListener {
|
|||||||
public void
|
public void
|
||||||
setTime(String n) { time.setText(n); }
|
setTime(String n) { time.setText(n); }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
setEmojiUrls(String[][] n)
|
setEmojiUrls(String[][] n)
|
||||||
{
|
{
|
||||||
emojiUrls = n;
|
emojiUrls = n;
|
||||||
|
|
||||||
Map<String, Image> emojis = new HashMap<>();
|
Map<String, Image> emojis = new HashMap<>();
|
||||||
for (String[] entry: n)
|
for (String[] entry: n)
|
||||||
{
|
{
|
||||||
emojis.put(entry[0], ImageApi.remote(entry[1]));
|
emojis.put(entry[0], ImageApi.remote(entry[1]));
|
||||||
}
|
}
|
||||||
body.setEmojis(emojis);
|
body.setEmojis(emojis);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
setHtml(String n)
|
setHtml(String n)
|
||||||
{
|
{
|
||||||
body.setText(BasicHTMLParser.parse(n));
|
body.setText(BasicHTMLParser.parse(n));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
setFavourited(boolean a)
|
setFavourited(boolean a)
|
||||||
@ -497,19 +497,19 @@ implements ActionListener {
|
|||||||
favouriteBoost.setEnabled(a);
|
favouriteBoost.setEnabled(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
setDeleteEnabled(boolean a)
|
setDeleteEnabled(boolean a)
|
||||||
{
|
{
|
||||||
deletePost.setEnabled(a);
|
deletePost.setEnabled(a);
|
||||||
redraftPost.setEnabled(a);
|
redraftPost.setEnabled(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
setMediaPreview(Image n) { media.setImage(n); }
|
setMediaPreview(Image n) { media.setImage(n); }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
resetFocus()
|
resetFocus()
|
||||||
{
|
{
|
||||||
media.requestFocusInWindow();
|
media.requestFocusInWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -551,14 +551,14 @@ implements ActionListener {
|
|||||||
}
|
}
|
||||||
else if (command.startsWith("reply"))
|
else if (command.startsWith("reply"))
|
||||||
{
|
{
|
||||||
primaire.reply();
|
primaire.reply();
|
||||||
|
}
|
||||||
|
else if (command.startsWith("misc"))
|
||||||
|
{
|
||||||
|
int rx = replyMisc.getWidth() / 2;
|
||||||
|
int ry = replyMisc.getHeight() - miscMenu.getHeight();
|
||||||
|
miscMenu.show(replyMisc, rx, ry);
|
||||||
}
|
}
|
||||||
else if (command.startsWith("misc"))
|
|
||||||
{
|
|
||||||
int rx = replyMisc.getWidth() / 2;
|
|
||||||
int ry = replyMisc.getHeight() - miscMenu.getHeight();
|
|
||||||
miscMenu.show(replyMisc, rx, ry);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else miscMenu.setVisible(false);
|
else miscMenu.setVisible(false);
|
||||||
@ -567,14 +567,14 @@ implements ActionListener {
|
|||||||
{
|
{
|
||||||
if (command.startsWith("next"))
|
if (command.startsWith("next"))
|
||||||
{
|
{
|
||||||
body.nextPage();
|
body.nextPage();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
body.previousPage();
|
body.previousPage();
|
||||||
}
|
}
|
||||||
// First time an interactive element
|
// First time an interactive element
|
||||||
// doesn't call something in primaire..
|
// doesn't call something in primaire..
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -582,13 +582,13 @@ implements ActionListener {
|
|||||||
{
|
{
|
||||||
primaire.openMedia();
|
primaire.openMedia();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (src == openReplies) primaire.openReplies();
|
if (src == openReplies) primaire.openReplies();
|
||||||
if (src == copyPostId) primaire.copyPostId();
|
if (src == copyPostId) primaire.copyPostId();
|
||||||
if (src == copyPostLink) primaire.copyPostLink();
|
if (src == copyPostLink) primaire.copyPostLink();
|
||||||
if (src == deletePost) primaire.deletePost(false);
|
if (src == deletePost) primaire.deletePost(false);
|
||||||
if (src == redraftPost) primaire.deletePost(true);
|
if (src == redraftPost) primaire.deletePost(true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -597,28 +597,28 @@ implements ActionListener {
|
|||||||
{
|
{
|
||||||
g.clearRect(0, 0, getWidth(), getHeight());
|
g.clearRect(0, 0, getWidth(), getHeight());
|
||||||
|
|
||||||
int w1 = authorName.getWidth();
|
int w1 = authorName.getWidth();
|
||||||
FontMetrics fm1 = getFontMetrics(authorName.getFont());
|
FontMetrics fm1 = getFontMetrics(authorName.getFont());
|
||||||
List<RichTextPane.Segment> lay1;
|
List<RichTextPane.Segment> lay1;
|
||||||
lay1 = RichTextPane.layout(authorNameOr, fm1, w1);
|
lay1 = RichTextPane.layout(authorNameOr, fm1, w1);
|
||||||
authorName.setText(lay1);
|
authorName.setText(lay1);
|
||||||
|
|
||||||
if (backgroundImage != null)
|
if (backgroundImage != null)
|
||||||
{
|
{
|
||||||
int tw = backgroundImage.getWidth(this);
|
int tw = backgroundImage.getWidth(this);
|
||||||
int th = backgroundImage.getHeight(this);
|
int th = backgroundImage.getHeight(this);
|
||||||
if (tw != -1)
|
if (tw != -1)
|
||||||
for (int y = 0; y < getHeight(); y += th)
|
for (int y = 0; y < getHeight(); y += th)
|
||||||
for (int x = 0; x < getWidth(); x += tw)
|
for (int x = 0; x < getWidth(); x += tw)
|
||||||
{
|
{
|
||||||
g.drawImage(backgroundImage, x, y, this);
|
g.drawImage(backgroundImage, x, y, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
((java.awt.Graphics2D)g).setRenderingHint(
|
((java.awt.Graphics2D)g).setRenderingHint(
|
||||||
java.awt.RenderingHints.KEY_ANTIALIASING,
|
java.awt.RenderingHints.KEY_ANTIALIASING,
|
||||||
java.awt.RenderingHints.VALUE_ANTIALIAS_ON
|
java.awt.RenderingHints.VALUE_ANTIALIAS_ON
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
@ -648,18 +648,18 @@ implements ActionListener {
|
|||||||
{
|
{
|
||||||
this.primaire = primaire;
|
this.primaire = primaire;
|
||||||
|
|
||||||
emojiUrls = new String[0][];
|
emojiUrls = new String[0][];
|
||||||
|
|
||||||
Border b = BorderFactory.createEmptyBorder(10, 10, 10, 10);
|
Border b = BorderFactory.createEmptyBorder(10, 10, 10, 10);
|
||||||
Font f1 = new Font("MotoyaLMaru", Font.PLAIN, 18);
|
Font f1 = new Font("MotoyaLMaru", Font.PLAIN, 18);
|
||||||
Font f2 = new Font("MotoyaLMaru", Font.PLAIN, 14);
|
Font f2 = new Font("MotoyaLMaru", Font.PLAIN, 14);
|
||||||
Font f3 = new Font("MotoyaLMaru", Font.PLAIN, 18);
|
Font f3 = new Font("MotoyaLMaru", Font.PLAIN, 18);
|
||||||
|
|
||||||
profile = new RoundButton();
|
profile = new RoundButton();
|
||||||
favouriteBoost = new TwoToggleButton("favourite", "boost");
|
favouriteBoost = new TwoToggleButton("favourite", "boost");
|
||||||
replyMisc = new TwoToggleButton("reply", "misc");
|
replyMisc = new TwoToggleButton("reply", "misc");
|
||||||
nextPrev = new TwoToggleButton("next", "prev");
|
nextPrev = new TwoToggleButton("next", "prev");
|
||||||
media = new RoundButton();
|
media = new RoundButton();
|
||||||
profile.addActionListener(this);
|
profile.addActionListener(this);
|
||||||
favouriteBoost.addActionListener(this);
|
favouriteBoost.addActionListener(this);
|
||||||
replyMisc.addActionListener(this);
|
replyMisc.addActionListener(this);
|
||||||
@ -669,22 +669,22 @@ implements ActionListener {
|
|||||||
openReplies = new JMenuItem("Browse thread");
|
openReplies = new JMenuItem("Browse thread");
|
||||||
copyPostId = new JMenuItem("Copy post ID");
|
copyPostId = new JMenuItem("Copy post ID");
|
||||||
copyPostLink = new JMenuItem("Copy post link");
|
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);
|
openReplies.addActionListener(this);
|
||||||
copyPostId.addActionListener(this);
|
copyPostId.addActionListener(this);
|
||||||
copyPostLink.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(openReplies);
|
||||||
miscMenu.add(new JSeparator());
|
miscMenu.add(new JSeparator());
|
||||||
miscMenu.add(copyPostId);
|
miscMenu.add(copyPostId);
|
||||||
miscMenu.add(copyPostLink);
|
miscMenu.add(copyPostLink);
|
||||||
miscMenu.add(new JSeparator());
|
miscMenu.add(new JSeparator());
|
||||||
miscMenu.add(deletePost);
|
miscMenu.add(deletePost);
|
||||||
miscMenu.add(new JSeparator());
|
miscMenu.add(new JSeparator());
|
||||||
miscMenu.add(redraftPost);
|
miscMenu.add(redraftPost);
|
||||||
|
|
||||||
Box buttons = Box.createVerticalBox();
|
Box buttons = Box.createVerticalBox();
|
||||||
buttons.setOpaque(false);
|
buttons.setOpaque(false);
|
||||||
@ -702,59 +702,59 @@ implements ActionListener {
|
|||||||
left.setOpaque(false);
|
left.setOpaque(false);
|
||||||
left.add(buttons);
|
left.add(buttons);
|
||||||
|
|
||||||
authorId = new JLabel();
|
authorId = new JLabel();
|
||||||
authorName = new RichTextPane();
|
authorName = new RichTextPane();
|
||||||
time = new JLabel();
|
time = new JLabel();
|
||||||
date = new JLabel();
|
date = new JLabel();
|
||||||
authorId.setFont(f2);
|
authorId.setFont(f2);
|
||||||
date.setFont(f2);
|
date.setFont(f2);
|
||||||
authorName.setFont(f1);
|
authorName.setFont(f1);
|
||||||
time.setFont(f1);
|
time.setFont(f1);
|
||||||
|
|
||||||
JPanel top1 = new JPanel();
|
JPanel top1 = new JPanel();
|
||||||
top1.setOpaque(false);
|
top1.setOpaque(false);
|
||||||
top1.setLayout(new BorderLayout(8, 0));
|
top1.setLayout(new BorderLayout(8, 0));
|
||||||
top1.add(authorId);
|
top1.add(authorId);
|
||||||
top1.add(date, BorderLayout.EAST);
|
top1.add(date, BorderLayout.EAST);
|
||||||
JPanel top2 = new JPanel();
|
JPanel top2 = new JPanel();
|
||||||
top2.setOpaque(false);
|
top2.setOpaque(false);
|
||||||
top2.setLayout(new BorderLayout(8, 0));
|
top2.setLayout(new BorderLayout(8, 0));
|
||||||
top2.add(authorName);
|
top2.add(authorName);
|
||||||
top2.add(time, BorderLayout.EAST);
|
top2.add(time, BorderLayout.EAST);
|
||||||
Box top = Box.createVerticalBox();
|
Box top = Box.createVerticalBox();
|
||||||
top.add(top1);
|
top.add(top1);
|
||||||
top.add(Box.createVerticalStrut(2));
|
top.add(Box.createVerticalStrut(2));
|
||||||
top.add(top2);
|
top.add(top2);
|
||||||
|
|
||||||
body = new RichTextPane3();
|
body = new RichTextPane3();
|
||||||
body.setFont(f3);
|
body.setFont(f3);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
bodyScrollPane = 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 = bodyScrollPane.getVerticalScrollBar();
|
JScrollBar vsb = bodyScrollPane.getVerticalScrollBar();
|
||||||
vsb.setPreferredSize(new Dimension(0, 0));
|
vsb.setPreferredSize(new Dimension(0, 0));
|
||||||
vsb.setUnitIncrement(16);
|
vsb.setUnitIncrement(16);
|
||||||
bodyScrollPane.setBorder(null);
|
bodyScrollPane.setBorder(null);
|
||||||
bodyScrollPane.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(body);
|
centre.add(body);
|
||||||
|
|
||||||
setLayout(new BorderLayout(8, 0));
|
setLayout(new BorderLayout(8, 0));
|
||||||
add(left, BorderLayout.WEST);
|
add(left, BorderLayout.WEST);
|
||||||
add(centre);
|
add(centre);
|
||||||
|
|
||||||
setBorder(b);
|
setBorder(b);
|
||||||
|
|
||||||
backgroundImage = ImageApi.local("postWindow");
|
backgroundImage = ImageApi.local("postWindow");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,7 @@ ProfileWindow extends JFrame {
|
|||||||
{
|
{
|
||||||
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||||
|
|
||||||
TimelineWindow w = new TimelineWindow(primaire);
|
TimelineWindow w = new TimelineWindow(primaire);
|
||||||
w.showAuthorPosts(account.numId);
|
w.showAuthorPosts(account.numId);
|
||||||
w.showLatestPage();
|
w.showLatestPage();
|
||||||
w.setLocationRelativeTo(this);
|
w.setLocationRelativeTo(this);
|
||||||
@ -269,9 +269,9 @@ implements ActionListener {
|
|||||||
int acx = ax + (aw / 2);
|
int acx = ax + (aw / 2);
|
||||||
int acy = ay + (ah / 2);
|
int acy = ay + (ah / 2);
|
||||||
Shape defaultClip = g.getClip();
|
Shape defaultClip = g.getClip();
|
||||||
g.setClip(new Ellipse2D.Float(ax, ay, aw, ah));
|
g.setClip(new Ellipse2D.Float(ax, ay, aw, ah));
|
||||||
g.drawImage(avatar, ax, ay, aw, ah, this);
|
g.drawImage(avatar, ax, ay, aw, ah, this);
|
||||||
g.setClip(defaultClip);
|
g.setClip(defaultClip);
|
||||||
|
|
||||||
g.setColor(new Color(0, 0, 0, 50));
|
g.setColor(new Color(0, 0, 0, 50));
|
||||||
g.fillRect(0, acy - dy1, acx - dx1, 2);
|
g.fillRect(0, acy - dy1, acx - dx1, 2);
|
||||||
|
@ -189,7 +189,7 @@ RepliesWindow extends JFrame {
|
|||||||
setContentPane(display);
|
setContentPane(display);
|
||||||
setSize(384, 224);
|
setSize(384, 224);
|
||||||
|
|
||||||
setIconImage(primaire.getProgramIcon());
|
setIconImage(primaire.getProgramIcon());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -242,13 +242,13 @@ implements TreeSelectionListener {
|
|||||||
if (p == null)
|
if (p == null)
|
||||||
{
|
{
|
||||||
assert false;
|
assert false;
|
||||||
/*
|
/*
|
||||||
* Besides descendants possibly not being in order,
|
* Besides descendants possibly not being in order,
|
||||||
* the top of the thread might be deleted and so
|
* the top of the thread might be deleted and so
|
||||||
* thread.top gets set to the given post. Which
|
* thread.top gets set to the given post. Which
|
||||||
* sibling replies aren't replying to, resulting
|
* sibling replies aren't replying to, resulting
|
||||||
* in assertion failure.
|
* in assertion failure.
|
||||||
*/
|
*/
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,8 +257,8 @@ implements TreeSelectionListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tree.setModel(new DefaultTreeModel(root));
|
tree.setModel(new DefaultTreeModel(root));
|
||||||
for (int o = 0; o < tree.getRowCount(); ++o)
|
for (int o = 0; o < tree.getRowCount(); ++o)
|
||||||
tree.expandRow(o);
|
tree.expandRow(o);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
@ -24,14 +24,14 @@ import java.io.IOException;
|
|||||||
interface
|
interface
|
||||||
RequestListener {
|
RequestListener {
|
||||||
|
|
||||||
void
|
void
|
||||||
connectionFailed(IOException eIo);
|
connectionFailed(IOException eIo);
|
||||||
|
|
||||||
void
|
void
|
||||||
requestFailed(int httpCode, Tree<String> json);
|
requestFailed(int httpCode, Tree<String> json);
|
||||||
|
|
||||||
void
|
void
|
||||||
requestSucceeded(Tree<String> json);
|
requestSucceeded(Tree<String> json);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,396 +38,396 @@ class
|
|||||||
RichTextPane extends JComponent
|
RichTextPane extends JComponent
|
||||||
implements MouseListener, MouseMotionListener, KeyListener {
|
implements MouseListener, MouseMotionListener, KeyListener {
|
||||||
|
|
||||||
private List<Segment>
|
private List<Segment>
|
||||||
text;
|
text;
|
||||||
|
|
||||||
private int
|
private int
|
||||||
selectionStart, selectionEnd;
|
selectionStart, selectionEnd;
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
public void
|
public void
|
||||||
setText(List<Segment> text)
|
setText(List<Segment> text)
|
||||||
{
|
{
|
||||||
this.text = text;
|
this.text = text;
|
||||||
selectionStart = selectionEnd = -1;
|
selectionStart = selectionEnd = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Segment>
|
public List<Segment>
|
||||||
getSelection()
|
getSelection()
|
||||||
{
|
{
|
||||||
List<Segment> returnee = new LinkedList<>();
|
List<Segment> returnee = new LinkedList<>();
|
||||||
if (selectionEnd == -1) return returnee;
|
if (selectionEnd == -1) return returnee;
|
||||||
|
|
||||||
if (selectionEnd < selectionStart) {
|
if (selectionEnd < selectionStart) {
|
||||||
int t = selectionEnd;
|
int t = selectionEnd;
|
||||||
selectionEnd = selectionStart;
|
selectionEnd = selectionStart;
|
||||||
selectionStart = t;
|
selectionStart = t;
|
||||||
}
|
}
|
||||||
returnee.addAll(text.subList(selectionStart + 1, selectionEnd));
|
returnee.addAll(text.subList(selectionStart + 1, selectionEnd));
|
||||||
return returnee;
|
return returnee;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
copySelection()
|
copySelection()
|
||||||
{
|
{
|
||||||
assert selectionEnd != -1;
|
assert selectionEnd != -1;
|
||||||
StringBuilder b = new StringBuilder();
|
StringBuilder b = new StringBuilder();
|
||||||
for (Segment segment: getSelection())
|
for (Segment segment: getSelection())
|
||||||
{
|
{
|
||||||
if (segment.link != null) b.append(segment.link);
|
if (segment.link != null) b.append(segment.link);
|
||||||
else if (segment.text != null) b.append(segment.text);
|
else if (segment.text != null) b.append(segment.text);
|
||||||
}
|
}
|
||||||
ClipboardApi.serve(b.toString());
|
ClipboardApi.serve(b.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
protected void
|
protected void
|
||||||
paintComponent(Graphics g)
|
paintComponent(Graphics g)
|
||||||
{
|
{
|
||||||
g.setFont(getFont());
|
g.setFont(getFont());
|
||||||
FontMetrics fm = g.getFontMetrics(getFont());
|
FontMetrics fm = g.getFontMetrics(getFont());
|
||||||
|
|
||||||
if (isOpaque())
|
if (isOpaque())
|
||||||
g.clearRect(0, 0, getWidth(), getHeight());
|
g.clearRect(0, 0, getWidth(), getHeight());
|
||||||
|
|
||||||
((java.awt.Graphics2D)g).setRenderingHint(
|
((java.awt.Graphics2D)g).setRenderingHint(
|
||||||
java.awt.RenderingHints.KEY_TEXT_ANTIALIASING,
|
java.awt.RenderingHints.KEY_TEXT_ANTIALIASING,
|
||||||
java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON
|
java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON
|
||||||
);
|
);
|
||||||
|
|
||||||
int o = 0;
|
int o = 0;
|
||||||
for (Segment segment: text)
|
for (Segment segment: text)
|
||||||
{
|
{
|
||||||
if (segment.image != null) {
|
if (segment.image != null) {
|
||||||
int ow = segment.image.getIconWidth();
|
int ow = segment.image.getIconWidth();
|
||||||
int oh = segment.image.getIconHeight();
|
int oh = segment.image.getIconHeight();
|
||||||
int h = fm.getAscent() + fm.getDescent();
|
int h = fm.getAscent() + fm.getDescent();
|
||||||
int w = h * ow / oh;
|
int w = h * ow / oh;
|
||||||
int x = segment.x;
|
int x = segment.x;
|
||||||
int y = segment.y + fm.getDescent();
|
int y = segment.y + fm.getDescent();
|
||||||
// Interpret segment.y as specifying text baseline
|
// Interpret segment.y as specifying text baseline
|
||||||
Image img = segment.image.getImage();
|
Image img = segment.image.getImage();
|
||||||
g.drawImage(img, x, y - h, w, h, this);
|
g.drawImage(img, x, y - h, w, h, this);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
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));
|
||||||
g.fillRect(segment.x, segment.y - dy1, dx, dy2);
|
g.fillRect(segment.x, segment.y - dy1, dx, dy2);
|
||||||
g.setColor(getForeground());
|
g.setColor(getForeground());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (segment.link != null) g.setColor(Color.BLUE);
|
if (segment.link != null) g.setColor(Color.BLUE);
|
||||||
g.drawString(segment.text, segment.x, segment.y);
|
g.drawString(segment.text, segment.x, segment.y);
|
||||||
g.setColor(getForeground());
|
g.setColor(getForeground());
|
||||||
|
|
||||||
++o;
|
++o;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mousePressed(MouseEvent eM)
|
mousePressed(MouseEvent eM)
|
||||||
{
|
{
|
||||||
requestFocusInWindow();
|
requestFocusInWindow();
|
||||||
selectionStart = identify(eM.getX(), eM.getY()) - 2;
|
selectionStart = identify(eM.getX(), eM.getY()) - 2;
|
||||||
selectionEnd = -1;
|
selectionEnd = -1;
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mouseDragged(MouseEvent eM)
|
mouseDragged(MouseEvent eM)
|
||||||
{
|
{
|
||||||
selectionEnd = identify(eM.getX(), eM.getY());
|
selectionEnd = identify(eM.getX(), eM.getY());
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int
|
private int
|
||||||
identify(int x, int y)
|
identify(int x, int y)
|
||||||
{
|
{
|
||||||
FontMetrics fm = getFontMetrics(getFont());
|
FontMetrics fm = getFontMetrics(getFont());
|
||||||
int iy = fm.getAscent();
|
int iy = fm.getAscent();
|
||||||
int lh = fm.getAscent() + fm.getDescent();
|
int lh = fm.getAscent() + fm.getDescent();
|
||||||
y -= fm.getDescent();
|
y -= fm.getDescent();
|
||||||
if (y <= iy) y = iy;
|
if (y <= iy) y = iy;
|
||||||
else y += lh - ((y - iy) % lh);
|
else y += lh - ((y - iy) % lh);
|
||||||
/*
|
/*
|
||||||
* Snaps y to the next baseline. Kind of obtuse,
|
* Snaps y to the next baseline. Kind of obtuse,
|
||||||
* but it wasn't randomly derived, anyways
|
* but it wasn't randomly derived, anyways
|
||||||
* you can test it for 13, 30, 47, etc.
|
* you can test it for 13, 30, 47, etc.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int o = 0;
|
int o = 0;
|
||||||
for (Segment segment: text)
|
for (Segment segment: text)
|
||||||
{
|
{
|
||||||
if (segment.y == y && segment.x > x) break;
|
if (segment.y == y && segment.x > x) break;
|
||||||
if (segment.y > y) break;
|
if (segment.y > y) break;
|
||||||
++o;
|
++o;
|
||||||
}
|
}
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
copySelection();
|
copySelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void
|
public void
|
||||||
keyReleased(KeyEvent eK) { }
|
keyReleased(KeyEvent eK) { }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
keyTyped(KeyEvent eK) { }
|
keyTyped(KeyEvent eK) { }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mouseClicked(MouseEvent eM) { }
|
mouseClicked(MouseEvent eM) { }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mouseReleased(MouseEvent eM) { }
|
mouseReleased(MouseEvent eM) { }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mouseEntered(MouseEvent eM) { }
|
mouseEntered(MouseEvent eM) { }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mouseExited(MouseEvent eM) { }
|
mouseExited(MouseEvent eM) { }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mouseMoved(MouseEvent eM) { }
|
mouseMoved(MouseEvent eM) { }
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
public static List<Segment>
|
public static List<Segment>
|
||||||
layout(List<Segment> text, FontMetrics fm, int width)
|
layout(List<Segment> text, FontMetrics fm, int width)
|
||||||
{
|
{
|
||||||
List<Segment> copy = new LinkedList<>();
|
List<Segment> copy = new LinkedList<>();
|
||||||
for (Segment segment: text) copy.add(segment.clone());
|
for (Segment segment: text) copy.add(segment.clone());
|
||||||
text = copy;
|
text = copy;
|
||||||
ListIterator<Segment> cursor = text.listIterator();
|
ListIterator<Segment> cursor = text.listIterator();
|
||||||
int x = 0, y = fm.getAscent();
|
int x = 0, y = fm.getAscent();
|
||||||
int dy = fm.getAscent() + fm.getDescent();
|
int dy = fm.getAscent() + fm.getDescent();
|
||||||
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();
|
||||||
int oh = curr.image.getIconHeight();
|
int oh = curr.image.getIconHeight();
|
||||||
int nh = fm.getAscent() + fm.getDescent();
|
int nh = fm.getAscent() + fm.getDescent();
|
||||||
dx = nh * ow / oh;
|
dx = nh * ow / oh;
|
||||||
}
|
}
|
||||||
else if (curr.text != null) {
|
else if (curr.text != null) {
|
||||||
dx = fm.stringWidth(curr.text);
|
dx = fm.stringWidth(curr.text);
|
||||||
}
|
}
|
||||||
else if (curr.link != null) {
|
else if (curr.link != null) {
|
||||||
curr.text = curr.link;
|
curr.text = curr.link;
|
||||||
dx = fm.stringWidth(curr.link);
|
dx = fm.stringWidth(curr.link);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assert false;
|
assert false;
|
||||||
dx = 0;
|
dx = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean fits = x + dx < width;
|
boolean fits = x + dx < width;
|
||||||
|
|
||||||
if (fits || curr.spacer)
|
if (fits || curr.spacer)
|
||||||
{
|
{
|
||||||
curr.x = x;
|
curr.x = x;
|
||||||
curr.y = y;
|
curr.y = y;
|
||||||
x += dx;
|
x += dx;
|
||||||
if (curr.spacer && curr.text.equals("\n")) {
|
if (curr.spacer && curr.text.equals("\n")) {
|
||||||
y += dy;
|
y += dy;
|
||||||
x = 0;
|
x = 0;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean tooLong = dx > width;
|
boolean tooLong = dx > width;
|
||||||
boolean canFitChar = width >= fm.getMaxAdvance();
|
boolean canFitChar = width >= fm.getMaxAdvance();
|
||||||
boolean splittable = curr.image == null;
|
boolean splittable = curr.image == null;
|
||||||
/*
|
/*
|
||||||
* A bit of redundancy in my conditions, but the point is
|
* A bit of redundancy in my conditions, but the point is
|
||||||
* to exactly express the triggers in my mental model.
|
* to exactly express the triggers in my mental model.
|
||||||
* The conditions should read more like English.
|
* The conditions should read more like English.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (!tooLong || (tooLong && !splittable))
|
if (!tooLong || (tooLong && !splittable))
|
||||||
{
|
{
|
||||||
curr.x = 0;
|
curr.x = 0;
|
||||||
curr.y = y += dy;
|
curr.y = y += dy;
|
||||||
x = dx;
|
x = dx;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert tooLong && splittable;
|
assert tooLong && splittable;
|
||||||
|
|
||||||
String s = curr.text;
|
String s = curr.text;
|
||||||
int splitOffset;
|
int splitOffset;
|
||||||
for (splitOffset = 0; splitOffset < s.length(); ++splitOffset)
|
for (splitOffset = 0; splitOffset < s.length(); ++splitOffset)
|
||||||
{
|
{
|
||||||
String substring = s.substring(0, splitOffset + 1);
|
String substring = s.substring(0, splitOffset + 1);
|
||||||
if (fm.stringWidth(substring) > width) break;
|
if (fm.stringWidth(substring) > width) break;
|
||||||
}
|
}
|
||||||
if (splitOffset == 0) splitOffset = 1;
|
if (splitOffset == 0) splitOffset = 1;
|
||||||
/*
|
/*
|
||||||
* I force a split even if our width supports no characters.
|
* I force a split even if our width supports no characters.
|
||||||
* Because if I don't split, the only alternatives to infinitely
|
* Because if I don't split, the only alternatives to infinitely
|
||||||
* looping downwards is to emplace this segment or ignore it.
|
* looping downwards is to emplace this segment or ignore it.
|
||||||
*/
|
*/
|
||||||
Segment fitted = new Segment();
|
Segment fitted = new Segment();
|
||||||
fitted.text = s.substring(0, splitOffset);
|
fitted.text = s.substring(0, splitOffset);
|
||||||
fitted.link = curr.link;
|
fitted.link = curr.link;
|
||||||
fitted.x = x;
|
fitted.x = x;
|
||||||
fitted.y = y;
|
fitted.y = y;
|
||||||
cursor.add(fitted);
|
cursor.add(fitted);
|
||||||
curr.text = s.substring(splitOffset);
|
curr.text = s.substring(splitOffset);
|
||||||
y += dy;
|
y += dy;
|
||||||
x = 0;
|
x = 0;
|
||||||
cursor.add(curr); cursor.previous();
|
cursor.add(curr); cursor.previous();
|
||||||
/*
|
/*
|
||||||
* I had to use a stack and return a new list because,
|
* I had to use a stack and return a new list because,
|
||||||
* splitting can turn a long segment into several spread
|
* splitting can turn a long segment into several spread
|
||||||
* over different lines. Here curr becomes the "after-split"
|
* over different lines. Here curr becomes the "after-split"
|
||||||
* and I push it back to the stack.
|
* and I push it back to the stack.
|
||||||
*
|
*
|
||||||
* If #layout wasn't a separate method, but rather only for
|
* If #layout wasn't a separate method, but rather only for
|
||||||
* graphical painting. Then I don't need a stack nor return
|
* graphical painting. Then I don't need a stack nor return
|
||||||
* a new list. I iterate over the given one, filling the
|
* a new list. I iterate over the given one, filling the
|
||||||
* nodes' geometric information, if a split occurs I save the
|
* nodes' geometric information, if a split occurs I save the
|
||||||
* "after-split" in a variable, which in the next iteration
|
* "after-split" in a variable, which in the next iteration
|
||||||
* I use as curr instead of list.next(). The caller doesn't
|
* I use as curr instead of list.next(). The caller doesn't
|
||||||
* need to know the geometry of these intermediate segments.
|
* need to know the geometry of these intermediate segments.
|
||||||
*/
|
*/
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
public static class
|
public static class
|
||||||
Segment {
|
Segment {
|
||||||
|
|
||||||
public ImageIcon
|
public ImageIcon
|
||||||
image;
|
image;
|
||||||
|
|
||||||
public String
|
public String
|
||||||
link;
|
link;
|
||||||
|
|
||||||
public String
|
public String
|
||||||
text;
|
text;
|
||||||
|
|
||||||
public boolean
|
public boolean
|
||||||
spacer;
|
spacer;
|
||||||
|
|
||||||
public int
|
public int
|
||||||
x, y;
|
x, y;
|
||||||
|
|
||||||
// -=%=-
|
// -=%=-
|
||||||
|
|
||||||
public String
|
public String
|
||||||
toString()
|
toString()
|
||||||
{
|
{
|
||||||
StringBuilder b = new StringBuilder();
|
StringBuilder b = new StringBuilder();
|
||||||
b.append(getClass().getName() + "[");
|
b.append(getClass().getName() + "[");
|
||||||
b.append("image=" + image);
|
b.append("image=" + image);
|
||||||
b.append(",link=" + link);
|
b.append(",link=" + link);
|
||||||
b.append(",text=" + text);
|
b.append(",text=" + text);
|
||||||
b.append(",x=" + x);
|
b.append(",x=" + x);
|
||||||
b.append(",y=" + y);
|
b.append(",y=" + y);
|
||||||
b.append("]");
|
b.append("]");
|
||||||
return b.toString();
|
return b.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Segment
|
public Segment
|
||||||
clone()
|
clone()
|
||||||
{
|
{
|
||||||
Segment segment = new Segment();
|
Segment segment = new Segment();
|
||||||
segment.image = this.image;
|
segment.image = this.image;
|
||||||
segment.link = this.link;
|
segment.link = this.link;
|
||||||
segment.text = this.text;
|
segment.text = this.text;
|
||||||
segment.spacer = this.spacer;
|
segment.spacer = this.spacer;
|
||||||
segment.x = this.x;
|
segment.x = this.x;
|
||||||
segment.y = this.y;
|
segment.y = this.y;
|
||||||
return segment;
|
return segment;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class
|
public static class
|
||||||
Builder {
|
Builder {
|
||||||
|
|
||||||
private List<Segment>
|
private List<Segment>
|
||||||
returnee;
|
returnee;
|
||||||
|
|
||||||
// -=%=-
|
// -=%=-
|
||||||
|
|
||||||
public
|
public
|
||||||
Builder() { returnee = new LinkedList<>(); }
|
Builder() { returnee = new LinkedList<>(); }
|
||||||
|
|
||||||
public Builder
|
public Builder
|
||||||
image(ImageIcon image, String text)
|
image(ImageIcon image, String text)
|
||||||
{
|
{
|
||||||
Segment segment = new Segment();
|
Segment segment = new Segment();
|
||||||
segment.image = image;
|
segment.image = image;
|
||||||
segment.text = text;
|
segment.text = text;
|
||||||
returnee.add(segment);
|
returnee.add(segment);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder
|
public Builder
|
||||||
link(String link, String text)
|
link(String link, String text)
|
||||||
{
|
{
|
||||||
Segment segment = new Segment();
|
Segment segment = new Segment();
|
||||||
segment.link = link;
|
segment.link = link;
|
||||||
segment.text = text;
|
segment.text = text;
|
||||||
returnee.add(segment);
|
returnee.add(segment);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder
|
public Builder
|
||||||
text(String text)
|
text(String text)
|
||||||
{
|
{
|
||||||
Segment segment = new Segment();
|
Segment segment = new Segment();
|
||||||
segment.text = text;
|
segment.text = text;
|
||||||
returnee.add(segment);
|
returnee.add(segment);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder
|
public Builder
|
||||||
spacer(String text)
|
spacer(String text)
|
||||||
{
|
{
|
||||||
Segment segment = new Segment();
|
Segment segment = new Segment();
|
||||||
segment.text = text;
|
segment.text = text;
|
||||||
segment.spacer = true;
|
segment.spacer = true;
|
||||||
returnee.add(segment);
|
returnee.add(segment);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Segment>
|
public List<Segment>
|
||||||
finish() { return returnee; }
|
finish() { return returnee; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
RichTextPane()
|
RichTextPane()
|
||||||
{
|
{
|
||||||
text = new LinkedList<>();
|
text = new LinkedList<>();
|
||||||
|
|
||||||
addMouseListener(this);
|
addMouseListener(this);
|
||||||
addMouseMotionListener(this);
|
addMouseMotionListener(this);
|
||||||
addKeyListener(this);
|
addKeyListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -37,463 +37,463 @@ class
|
|||||||
RichTextPane2 extends JComponent
|
RichTextPane2 extends JComponent
|
||||||
implements ComponentListener {
|
implements ComponentListener {
|
||||||
|
|
||||||
private AttributedString
|
private AttributedString
|
||||||
text;
|
text;
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
public void
|
public void
|
||||||
setText(Tree<String> html, Tree<String> emojiMap)
|
setText(Tree<String> html, Tree<String> emojiMap)
|
||||||
{
|
{
|
||||||
Tree<String> commands = turnIntoCommands(html);
|
Tree<String> commands = turnIntoCommands(html);
|
||||||
|
|
||||||
class AStrSegment {
|
class AStrSegment {
|
||||||
String text;
|
String text;
|
||||||
int offset;
|
int offset;
|
||||||
Object[] values = new Object[Attribute.COUNT];
|
Object[] values = new Object[Attribute.COUNT];
|
||||||
/*
|
/*
|
||||||
{
|
{
|
||||||
values[3] = (Boolean)true;
|
values[3] = (Boolean)true;
|
||||||
values[4] = (Integer)0;
|
values[4] = (Integer)0;
|
||||||
values[5] = (Boolean)true;
|
values[5] = (Boolean)true;
|
||||||
values[6] = (Boolean)false;
|
values[6] = (Boolean)false;
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
List<AStrSegment> segments = new ArrayList<>();
|
List<AStrSegment> segments = new ArrayList<>();
|
||||||
|
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
for (Tree<String> command: commands)
|
for (Tree<String> command: commands)
|
||||||
{
|
{
|
||||||
if (command.key.equals("text"))
|
if (command.key.equals("text"))
|
||||||
{
|
{
|
||||||
StringBuilder b = new StringBuilder();
|
StringBuilder b = new StringBuilder();
|
||||||
Boolean cibl = null;
|
Boolean cibl = null;
|
||||||
Boolean cwhi = null;
|
Boolean cwhi = null;
|
||||||
for (char c: command.value.toCharArray())
|
for (char c: command.value.toCharArray())
|
||||||
{
|
{
|
||||||
Boolean ibl = isBasicLatin(c);
|
Boolean ibl = isBasicLatin(c);
|
||||||
Boolean whi = Character.isWhitespace(c);
|
Boolean whi = Character.isWhitespace(c);
|
||||||
if (!ibl.equals(cibl) || !whi.equals(cwhi))
|
if (!ibl.equals(cibl) || !whi.equals(cwhi))
|
||||||
{
|
{
|
||||||
if (b.length() > 0)
|
if (b.length() > 0)
|
||||||
{
|
{
|
||||||
assert cibl != null && cwhi != null;
|
assert cibl != null && cwhi != null;
|
||||||
AStrSegment s = new AStrSegment();
|
AStrSegment s = new AStrSegment();
|
||||||
s.offset = offset;
|
s.offset = offset;
|
||||||
s.text = b.toString();
|
s.text = b.toString();
|
||||||
s.values[3] = cibl;
|
s.values[3] = cibl;
|
||||||
s.values[6] = cwhi;
|
s.values[6] = cwhi;
|
||||||
segments.add(s);
|
segments.add(s);
|
||||||
offset += s.text.length();
|
offset += s.text.length();
|
||||||
b.delete(0, b.length());
|
b.delete(0, b.length());
|
||||||
}
|
}
|
||||||
cibl = ibl;
|
cibl = ibl;
|
||||||
cwhi = whi;
|
cwhi = whi;
|
||||||
}
|
}
|
||||||
|
|
||||||
b.append(c);
|
b.append(c);
|
||||||
}
|
}
|
||||||
if (b.length() > 0)
|
if (b.length() > 0)
|
||||||
{
|
{
|
||||||
AStrSegment s = new AStrSegment();
|
AStrSegment s = new AStrSegment();
|
||||||
s.offset = offset;
|
s.offset = offset;
|
||||||
s.text = b.toString();
|
s.text = b.toString();
|
||||||
s.values[3] = cibl;
|
s.values[3] = cibl;
|
||||||
s.values[6] = cwhi;
|
s.values[6] = cwhi;
|
||||||
segments.add(s);
|
segments.add(s);
|
||||||
offset += s.text.length();
|
offset += s.text.length();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (command.key.equals("emoji"))
|
else if (command.key.equals("emoji"))
|
||||||
{
|
{
|
||||||
AStrSegment s = new AStrSegment();
|
AStrSegment s = new AStrSegment();
|
||||||
s.offset = offset;
|
s.offset = offset;
|
||||||
s.values[3] = true;
|
s.values[3] = true;
|
||||||
s.values[6] = false;
|
s.values[6] = false;
|
||||||
|
|
||||||
String shortcode = command.value;
|
String shortcode = command.value;
|
||||||
String url = null;
|
String url = null;
|
||||||
Tree<String> m = emojiMap.get(shortcode);
|
Tree<String> m = emojiMap.get(shortcode);
|
||||||
if (m != null) url = m.value;
|
if (m != null) url = m.value;
|
||||||
Image img = ImageApi.remote(url);
|
Image img = ImageApi.remote(url);
|
||||||
if (img != null)
|
if (img != null)
|
||||||
{
|
{
|
||||||
s.text = " ";
|
s.text = " ";
|
||||||
s.values[0] = img;
|
s.values[0] = img;
|
||||||
s.values[1] = shortcode;
|
s.values[1] = shortcode;
|
||||||
segments.add(s);
|
segments.add(s);
|
||||||
offset += 1;
|
offset += 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
s.text = shortcode;
|
s.text = shortcode;
|
||||||
s.values[0] = null;
|
s.values[0] = null;
|
||||||
s.values[1] = null;
|
s.values[1] = null;
|
||||||
segments.add(s);
|
segments.add(s);
|
||||||
offset += shortcode.length();
|
offset += shortcode.length();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (command.key.equals("link"))
|
else if (command.key.equals("link"))
|
||||||
{
|
{
|
||||||
AStrSegment s = new AStrSegment();
|
AStrSegment s = new AStrSegment();
|
||||||
s.offset = offset;
|
s.offset = offset;
|
||||||
s.text = command.value;
|
s.text = command.value;
|
||||||
s.values[2] = command.get("url").value;
|
s.values[2] = command.get("url").value;
|
||||||
s.values[3] = true;
|
s.values[3] = true;
|
||||||
s.values[6] = false;
|
s.values[6] = false;
|
||||||
/*
|
/*
|
||||||
* Technically we're supposed to treat
|
* Technically we're supposed to treat
|
||||||
* the anchor text like a text node.
|
* the anchor text like a text node.
|
||||||
* As in, it could be non-Basic-Latin..
|
* As in, it could be non-Basic-Latin..
|
||||||
* I'll be Mastodon-specific again, and
|
* I'll be Mastodon-specific again, and
|
||||||
* assume it's a URL or some @ string.
|
* assume it's a URL or some @ string.
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AttributedString astr;
|
AttributedString astr;
|
||||||
StringBuilder b = new StringBuilder();
|
StringBuilder b = new StringBuilder();
|
||||||
for (AStrSegment segment: segments)
|
for (AStrSegment segment: segments)
|
||||||
{
|
{
|
||||||
b.append(segment.text);
|
b.append(segment.text);
|
||||||
}
|
}
|
||||||
astr = new AttributedString(b.toString());
|
astr = new AttributedString(b.toString());
|
||||||
for (AStrSegment segment: segments)
|
for (AStrSegment segment: segments)
|
||||||
{
|
{
|
||||||
Object[] v = segment.values;
|
Object[] v = segment.values;
|
||||||
astr.addAttribute(
|
astr.addAttribute(
|
||||||
Attribute.IMAGE, segment.values[0],
|
Attribute.IMAGE, segment.values[0],
|
||||||
segment.offset,
|
segment.offset,
|
||||||
segment.offset + segment.text.length()
|
segment.offset + segment.text.length()
|
||||||
);
|
);
|
||||||
astr.addAttribute(
|
astr.addAttribute(
|
||||||
Attribute.ALT, segment.values[1],
|
Attribute.ALT, segment.values[1],
|
||||||
segment.offset,
|
segment.offset,
|
||||||
segment.offset + segment.text.length()
|
segment.offset + segment.text.length()
|
||||||
);
|
);
|
||||||
astr.addAttribute(
|
astr.addAttribute(
|
||||||
Attribute.LINK, segment.values[2],
|
Attribute.LINK, segment.values[2],
|
||||||
segment.offset,
|
segment.offset,
|
||||||
segment.offset + segment.text.length()
|
segment.offset + segment.text.length()
|
||||||
);
|
);
|
||||||
astr.addAttribute(
|
astr.addAttribute(
|
||||||
Attribute.BASICLATIN, segment.values[3],
|
Attribute.BASICLATIN, segment.values[3],
|
||||||
segment.offset,
|
segment.offset,
|
||||||
segment.offset + segment.text.length()
|
segment.offset + segment.text.length()
|
||||||
);
|
);
|
||||||
astr.addAttribute(
|
astr.addAttribute(
|
||||||
Attribute.Y, segment.values[4],
|
Attribute.Y, segment.values[4],
|
||||||
segment.offset,
|
segment.offset,
|
||||||
segment.offset + segment.text.length()
|
segment.offset + segment.text.length()
|
||||||
);
|
);
|
||||||
astr.addAttribute(
|
astr.addAttribute(
|
||||||
Attribute.OFFSCREEN, segment.values[5],
|
Attribute.OFFSCREEN, segment.values[5],
|
||||||
segment.offset,
|
segment.offset,
|
||||||
segment.offset + segment.text.length()
|
segment.offset + segment.text.length()
|
||||||
);
|
);
|
||||||
astr.addAttribute(
|
astr.addAttribute(
|
||||||
Attribute.WHITESPACE, segment.values[6],
|
Attribute.WHITESPACE, segment.values[6],
|
||||||
segment.offset,
|
segment.offset,
|
||||||
segment.offset + segment.text.length()
|
segment.offset + segment.text.length()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.text = astr;
|
this.text = astr;
|
||||||
componentResized(null);
|
componentResized(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
public void
|
public void
|
||||||
componentResized(ComponentEvent eC)
|
componentResized(ComponentEvent eC)
|
||||||
{
|
{
|
||||||
int w = getWidth(), h = getHeight();
|
int w = getWidth(), h = getHeight();
|
||||||
|
|
||||||
// We're going to evaluate the
|
// We're going to evaluate the
|
||||||
// line and off-screen attributes.
|
// line and off-screen attributes.
|
||||||
|
|
||||||
FontMetrics fm = getFontMetrics(getFont());
|
FontMetrics fm = getFontMetrics(getFont());
|
||||||
Graphics g = getGraphics();
|
Graphics g = getGraphics();
|
||||||
int x = 0, y = fm.getAscent();
|
int x = 0, y = fm.getAscent();
|
||||||
|
|
||||||
AttributedCharacterIterator it;
|
AttributedCharacterIterator it;
|
||||||
it = text.getIterator();
|
it = text.getIterator();
|
||||||
|
|
||||||
while (it.getIndex() < it.getEndIndex())
|
while (it.getIndex() < it.getEndIndex())
|
||||||
{
|
{
|
||||||
int start = it.getIndex();
|
int start = it.getIndex();
|
||||||
int end = it.getRunLimit();
|
int end = it.getRunLimit();
|
||||||
|
|
||||||
Image img = (Image)
|
Image img = (Image)
|
||||||
it.getAttribute(Attribute.IMAGE);
|
it.getAttribute(Attribute.IMAGE);
|
||||||
Boolean ibl = (Boolean)
|
Boolean ibl = (Boolean)
|
||||||
it.getAttribute(Attribute.BASICLATIN);
|
it.getAttribute(Attribute.BASICLATIN);
|
||||||
Boolean whi = (Boolean)
|
Boolean whi = (Boolean)
|
||||||
it.getAttribute(Attribute.WHITESPACE);
|
it.getAttribute(Attribute.WHITESPACE);
|
||||||
|
|
||||||
assert ibl != null;
|
assert ibl != null;
|
||||||
assert whi != null;
|
assert whi != null;
|
||||||
|
|
||||||
if (img != null)
|
if (img != null)
|
||||||
{
|
{
|
||||||
int ow = img.getWidth(this);
|
int ow = img.getWidth(this);
|
||||||
int oh = img.getHeight(this);
|
int oh = img.getHeight(this);
|
||||||
int nh = fm.getAscent() + fm.getDescent();
|
int nh = fm.getAscent() + fm.getDescent();
|
||||||
int nw = ow * nh/oh;
|
int nw = ow * nh/oh;
|
||||||
if (x + nw > w)
|
if (x + nw > w)
|
||||||
{
|
{
|
||||||
y += fm.getAscent() + fm.getDescent();
|
y += fm.getAscent() + fm.getDescent();
|
||||||
x = nw;
|
x = nw;
|
||||||
}
|
}
|
||||||
text.addAttribute(
|
text.addAttribute(
|
||||||
Attribute.Y, (Integer)y,
|
Attribute.Y, (Integer)y,
|
||||||
start, end
|
start, end
|
||||||
);
|
);
|
||||||
text.addAttribute(
|
text.addAttribute(
|
||||||
Attribute.OFFSCREEN, (Boolean)(y > h),
|
Attribute.OFFSCREEN, (Boolean)(y > h),
|
||||||
start, end
|
start, end
|
||||||
);
|
);
|
||||||
it.setIndex(end);
|
it.setIndex(end);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int p, xOff = 0;
|
int p, xOff = 0;
|
||||||
for (p = end; p > start; --p)
|
for (p = end; p > start; --p)
|
||||||
{
|
{
|
||||||
Rectangle2D r;
|
Rectangle2D r;
|
||||||
r = fm.getStringBounds(it, start, p, g);
|
r = fm.getStringBounds(it, start, p, g);
|
||||||
xOff = (int)r.getWidth();
|
xOff = (int)r.getWidth();
|
||||||
if (x + xOff < w) break;
|
if (x + xOff < w) break;
|
||||||
}
|
}
|
||||||
if (p == end || whi)
|
if (p == end || whi)
|
||||||
{
|
{
|
||||||
x += xOff;
|
x += xOff;
|
||||||
text.addAttribute(
|
text.addAttribute(
|
||||||
Attribute.Y, (Integer)y,
|
Attribute.Y, (Integer)y,
|
||||||
start, end
|
start, end
|
||||||
);
|
);
|
||||||
text.addAttribute(
|
text.addAttribute(
|
||||||
Attribute.OFFSCREEN, (Boolean)(y > h),
|
Attribute.OFFSCREEN, (Boolean)(y > h),
|
||||||
start, end
|
start, end
|
||||||
);
|
);
|
||||||
it.setIndex(end);
|
it.setIndex(end);
|
||||||
}
|
}
|
||||||
else if (p <= start)
|
else if (p <= start)
|
||||||
{
|
{
|
||||||
y += fm.getAscent() + fm.getDescent();
|
y += fm.getAscent() + fm.getDescent();
|
||||||
x = xOff;
|
x = xOff;
|
||||||
text.addAttribute(
|
text.addAttribute(
|
||||||
Attribute.Y, (Integer)y,
|
Attribute.Y, (Integer)y,
|
||||||
start, end
|
start, end
|
||||||
);
|
);
|
||||||
text.addAttribute(
|
text.addAttribute(
|
||||||
Attribute.OFFSCREEN, (Boolean)(y > h),
|
Attribute.OFFSCREEN, (Boolean)(y > h),
|
||||||
start, end
|
start, end
|
||||||
);
|
);
|
||||||
it.setIndex(end);
|
it.setIndex(end);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
text.addAttribute(
|
text.addAttribute(
|
||||||
Attribute.Y, (Integer)y,
|
Attribute.Y, (Integer)y,
|
||||||
start, p
|
start, p
|
||||||
);
|
);
|
||||||
text.addAttribute(
|
text.addAttribute(
|
||||||
Attribute.OFFSCREEN, (Boolean)(y > h),
|
Attribute.OFFSCREEN, (Boolean)(y > h),
|
||||||
start, p
|
start, p
|
||||||
);
|
);
|
||||||
y += fm.getAscent() + fm.getDescent();
|
y += fm.getAscent() + fm.getDescent();
|
||||||
x = 0;
|
x = 0;
|
||||||
it.setIndex(p);
|
it.setIndex(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
text.addAttribute(TextAttribute.FONT, getFont());
|
text.addAttribute(TextAttribute.FONT, getFont());
|
||||||
|
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void
|
protected void
|
||||||
paintComponent(Graphics g)
|
paintComponent(Graphics g)
|
||||||
{
|
{
|
||||||
int w = getWidth(), h = getHeight();
|
int w = getWidth(), h = getHeight();
|
||||||
g.clearRect(0, 0, w, h);
|
g.clearRect(0, 0, w, h);
|
||||||
|
|
||||||
FontMetrics fm = g.getFontMetrics();
|
FontMetrics fm = g.getFontMetrics();
|
||||||
|
|
||||||
AttributedCharacterIterator it;
|
AttributedCharacterIterator it;
|
||||||
it = text.getIterator();
|
it = text.getIterator();
|
||||||
|
|
||||||
((java.awt.Graphics2D)g).setRenderingHint(
|
((java.awt.Graphics2D)g).setRenderingHint(
|
||||||
java.awt.RenderingHints.KEY_ANTIALIASING,
|
java.awt.RenderingHints.KEY_ANTIALIASING,
|
||||||
java.awt.RenderingHints.VALUE_ANTIALIAS_ON
|
java.awt.RenderingHints.VALUE_ANTIALIAS_ON
|
||||||
);
|
);
|
||||||
|
|
||||||
int x = 0, y = fm.getAscent();
|
int x = 0, y = fm.getAscent();
|
||||||
while (it.getIndex() < it.getEndIndex())
|
while (it.getIndex() < it.getEndIndex())
|
||||||
{
|
{
|
||||||
int start = it.getIndex();
|
int start = it.getIndex();
|
||||||
int end = it.getRunLimit();
|
int end = it.getRunLimit();
|
||||||
|
|
||||||
Image img = (Image)
|
Image img = (Image)
|
||||||
it.getAttribute(Attribute.IMAGE);
|
it.getAttribute(Attribute.IMAGE);
|
||||||
Boolean ibl = (Boolean)
|
Boolean ibl = (Boolean)
|
||||||
it.getAttribute(Attribute.BASICLATIN);
|
it.getAttribute(Attribute.BASICLATIN);
|
||||||
Integer ny = (Integer)
|
Integer ny = (Integer)
|
||||||
it.getAttribute(Attribute.Y);
|
it.getAttribute(Attribute.Y);
|
||||||
|
|
||||||
if (ny > y)
|
if (ny > y)
|
||||||
{
|
{
|
||||||
y = ny;
|
y = ny;
|
||||||
x = 0;
|
x = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (img != null)
|
if (img != null)
|
||||||
{
|
{
|
||||||
int ow = img.getWidth(this);
|
int ow = img.getWidth(this);
|
||||||
int oh = img.getHeight(this);
|
int oh = img.getHeight(this);
|
||||||
int nh = fm.getAscent() + fm.getDescent();
|
int nh = fm.getAscent() + fm.getDescent();
|
||||||
int nw = ow * nh/oh;
|
int nw = ow * nh/oh;
|
||||||
int iy = y + fm.getDescent() - nh;
|
int iy = y + fm.getDescent() - nh;
|
||||||
g.drawImage(img, x, iy, nw, nh, this);
|
g.drawImage(img, x, iy, nw, nh, this);
|
||||||
x += nw;
|
x += nw;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Rectangle2D r;
|
Rectangle2D r;
|
||||||
r = fm.getStringBounds(it, start, end, g);
|
r = fm.getStringBounds(it, start, end, g);
|
||||||
AttributedCharacterIterator sit;
|
AttributedCharacterIterator sit;
|
||||||
sit = text.getIterator(null, start, end);
|
sit = text.getIterator(null, start, end);
|
||||||
g.drawString(sit, x, y);
|
g.drawString(sit, x, y);
|
||||||
x += (int)r.getWidth();
|
x += (int)r.getWidth();
|
||||||
}
|
}
|
||||||
it.setIndex(end);
|
it.setIndex(end);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
componentMoved(ComponentEvent eC) { }
|
componentMoved(ComponentEvent eC) { }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
componentShown(ComponentEvent eC) { }
|
componentShown(ComponentEvent eC) { }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
componentHidden(ComponentEvent eC) { }
|
componentHidden(ComponentEvent eC) { }
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
private static Boolean
|
private static Boolean
|
||||||
isBasicLatin(char c)
|
isBasicLatin(char c)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String
|
private static String
|
||||||
toText(Tree<String> node)
|
toText(Tree<String> node)
|
||||||
{
|
{
|
||||||
Tree<String> children = node.get("children");
|
Tree<String> children = node.get("children");
|
||||||
if (children == null)
|
if (children == null)
|
||||||
{
|
{
|
||||||
boolean text = node.key.equals("text");
|
boolean text = node.key.equals("text");
|
||||||
boolean emoji = node.key.equals("emoji");
|
boolean emoji = node.key.equals("emoji");
|
||||||
assert text || emoji;
|
assert text || emoji;
|
||||||
return node.value;
|
return node.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
StringBuilder b = new StringBuilder();
|
StringBuilder b = new StringBuilder();
|
||||||
for (Tree<String> child: children)
|
for (Tree<String> child: children)
|
||||||
{
|
{
|
||||||
b.append(toText(child));
|
b.append(toText(child));
|
||||||
}
|
}
|
||||||
return b.toString();
|
return b.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Tree<String>
|
private static Tree<String>
|
||||||
turnIntoCommands(Tree<String> tag)
|
turnIntoCommands(Tree<String> tag)
|
||||||
{
|
{
|
||||||
assert tag.key.equals("tag");
|
assert tag.key.equals("tag");
|
||||||
Tree<String> returnee = new Tree<String>();
|
Tree<String> returnee = new Tree<String>();
|
||||||
|
|
||||||
String tagName = tag.get(0).key;
|
String tagName = tag.get(0).key;
|
||||||
Tree<String> children = tag.get("children");
|
Tree<String> children = tag.get("children");
|
||||||
|
|
||||||
if (tagName.equals("a"))
|
if (tagName.equals("a"))
|
||||||
{
|
{
|
||||||
String url = tag.get("href").value;
|
String url = tag.get("href").value;
|
||||||
Tree<String> addee = new Tree<>();
|
Tree<String> addee = new Tree<>();
|
||||||
addee.key = "link";
|
addee.key = "link";
|
||||||
addee.value = toText(tag);
|
addee.value = toText(tag);
|
||||||
addee.add(new Tree<>("url", url));
|
addee.add(new Tree<>("url", url));
|
||||||
returnee.add(addee);
|
returnee.add(addee);
|
||||||
}
|
}
|
||||||
else if (tagName.equals("span"))
|
else if (tagName.equals("span"))
|
||||||
{
|
{
|
||||||
Tree<String> addee = new Tree<>();
|
Tree<String> addee = new Tree<>();
|
||||||
addee.key = "text";
|
addee.key = "text";
|
||||||
addee.value = toText(tag);
|
addee.value = toText(tag);
|
||||||
returnee.add(addee);
|
returnee.add(addee);
|
||||||
}
|
}
|
||||||
else if (tagName.equals("br"))
|
else if (tagName.equals("br"))
|
||||||
{
|
{
|
||||||
returnee.add(new Tree<>("text", "\n"));
|
returnee.add(new Tree<>("text", "\n"));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (Tree<String> child: children)
|
for (Tree<String> child: children)
|
||||||
{
|
{
|
||||||
if (!child.key.equals("tag"))
|
if (!child.key.equals("tag"))
|
||||||
{
|
{
|
||||||
returnee.add(child);
|
returnee.add(child);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
child = turnIntoCommands(child);
|
child = turnIntoCommands(child);
|
||||||
for (Tree<String> command: child)
|
for (Tree<String> command: child)
|
||||||
{
|
{
|
||||||
returnee.add(command);
|
returnee.add(command);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tagName.equals("p"))
|
if (tagName.equals("p"))
|
||||||
{
|
{
|
||||||
returnee.add(new Tree<>("text", "\n"));
|
returnee.add(new Tree<>("text", "\n"));
|
||||||
returnee.add(new Tree<>("text", "\n"));
|
returnee.add(new Tree<>("text", "\n"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return returnee;
|
return returnee;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
public static class
|
public static class
|
||||||
Attribute extends AttributedCharacterIterator.Attribute {
|
Attribute extends AttributedCharacterIterator.Attribute {
|
||||||
|
|
||||||
public static final Attribute
|
public static final Attribute
|
||||||
IMAGE = new Attribute("IMAGE"),
|
IMAGE = new Attribute("IMAGE"),
|
||||||
ALT = new Attribute("ALT"),
|
ALT = new Attribute("ALT"),
|
||||||
LINK = new Attribute("LINK"),
|
LINK = new Attribute("LINK"),
|
||||||
BASICLATIN = new Attribute("BASICLATIN"),
|
BASICLATIN = new Attribute("BASICLATIN"),
|
||||||
Y = new Attribute("Y"),
|
Y = new Attribute("Y"),
|
||||||
OFFSCREEN = new Attribute("OFFSCREEN"),
|
OFFSCREEN = new Attribute("OFFSCREEN"),
|
||||||
WHITESPACE = new Attribute("WHITESPACE");
|
WHITESPACE = new Attribute("WHITESPACE");
|
||||||
|
|
||||||
public static final int
|
public static final int
|
||||||
COUNT = 7;
|
COUNT = 7;
|
||||||
|
|
||||||
// -=%=-
|
// -=%=-
|
||||||
|
|
||||||
private
|
private
|
||||||
Attribute(String name) { super(name); }
|
Attribute(String name) { super(name); }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
RichTextPane2()
|
RichTextPane2()
|
||||||
{
|
{
|
||||||
this.addComponentListener(this);
|
this.addComponentListener(this);
|
||||||
text = new AttributedString("");
|
text = new AttributedString("");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -45,232 +45,232 @@ implements
|
|||||||
ComponentListener,
|
ComponentListener,
|
||||||
MouseListener, MouseMotionListener, KeyListener {
|
MouseListener, MouseMotionListener, KeyListener {
|
||||||
|
|
||||||
private Tree<String>
|
private Tree<String>
|
||||||
html;
|
html;
|
||||||
|
|
||||||
private Map<String, Image>
|
private Map<String, Image>
|
||||||
emojis;
|
emojis;
|
||||||
|
|
||||||
private Map<Tree<String>, Position>
|
private Map<Tree<String>, Position>
|
||||||
layout;
|
layout;
|
||||||
|
|
||||||
private Tree<String>
|
private Tree<String>
|
||||||
layoutEnd, selStart, selEnd;
|
layoutEnd, selStart, selEnd;
|
||||||
|
|
||||||
private int
|
private int
|
||||||
startingLine, lastLine;
|
startingLine, lastLine;
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
public void
|
public void
|
||||||
setText(Tree<String> html)
|
setText(Tree<String> html)
|
||||||
{
|
{
|
||||||
assert html != null;
|
assert html != null;
|
||||||
|
|
||||||
this.html = html;
|
this.html = html;
|
||||||
|
|
||||||
if (!isValid()) return;
|
if (!isValid()) return;
|
||||||
|
|
||||||
assert html.key != null;
|
assert html.key != null;
|
||||||
assert html.key.equals("tag");
|
assert html.key.equals("tag");
|
||||||
assert html.get("children") != null;
|
assert html.get("children") != null;
|
||||||
|
|
||||||
FontMetrics fm = getFontMetrics(getFont());
|
FontMetrics fm = getFontMetrics(getFont());
|
||||||
Position cursor = new Position(0, 1);
|
Position cursor = new Position(0, 1);
|
||||||
|
|
||||||
// Manually negate if first element is a break.
|
// Manually negate if first element is a break.
|
||||||
Tree<String> children = html.get("children");
|
Tree<String> children = html.get("children");
|
||||||
if (children.size() > 0)
|
if (children.size() > 0)
|
||||||
{
|
{
|
||||||
Tree<String> first = children.get(0);
|
Tree<String> first = children.get(0);
|
||||||
if (first.key.equals("tag"))
|
if (first.key.equals("tag"))
|
||||||
{
|
{
|
||||||
String tagName = first.get(0).key;
|
String tagName = first.get(0).key;
|
||||||
if (tagName.equals("br")) cursor.line -= 1;
|
if (tagName.equals("br")) cursor.line -= 1;
|
||||||
if (tagName.equals("p")) cursor.line -= 2;
|
if (tagName.equals("p")) cursor.line -= 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
selStart = selEnd = null;
|
selStart = selEnd = null;
|
||||||
layout.clear();
|
layout.clear();
|
||||||
startingLine = 1;
|
startingLine = 1;
|
||||||
layout(html, fm, cursor);
|
layout(html, fm, cursor);
|
||||||
layout.put(layoutEnd, cursor.clone());
|
layout.put(layoutEnd, cursor.clone());
|
||||||
lastLine = cursor.line;
|
lastLine = cursor.line;
|
||||||
repaint();
|
repaint();
|
||||||
|
|
||||||
int iy = fm.getAscent();
|
int iy = fm.getAscent();
|
||||||
int lh = fm.getAscent() + fm.getDescent();
|
int lh = fm.getAscent() + fm.getDescent();
|
||||||
int h = snap2(cursor.line, iy, lh);
|
int h = snap2(cursor.line, iy, lh);
|
||||||
h += fm.getDescent();
|
h += fm.getDescent();
|
||||||
setPreferredSize(new Dimension(1, h));
|
setPreferredSize(new Dimension(1, h));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
setEmojis(Map<String, Image> emojis)
|
setEmojis(Map<String, Image> emojis)
|
||||||
{
|
{
|
||||||
assert emojis != null;
|
assert emojis != null;
|
||||||
this.emojis = emojis;
|
this.emojis = emojis;
|
||||||
setText(html);
|
setText(html);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
previousPage()
|
previousPage()
|
||||||
{
|
{
|
||||||
int advance = getHeightInLines();
|
int advance = getHeightInLines();
|
||||||
if (startingLine < advance) startingLine = 1;
|
if (startingLine < advance) startingLine = 1;
|
||||||
else startingLine -= advance;
|
else startingLine -= advance;
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
nextPage()
|
nextPage()
|
||||||
{
|
{
|
||||||
int advance = getHeightInLines();
|
int advance = getHeightInLines();
|
||||||
if (lastLine - startingLine < advance) return;
|
if (lastLine - startingLine < advance) return;
|
||||||
else startingLine += advance;
|
else startingLine += advance;
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
private void
|
private void
|
||||||
layout(Tree<String> node, FontMetrics fm, Position cursor)
|
layout(Tree<String> node, FontMetrics fm, Position cursor)
|
||||||
{
|
{
|
||||||
assert cursor != null;
|
assert cursor != null;
|
||||||
|
|
||||||
if (node.key.equals("space"))
|
if (node.key.equals("space"))
|
||||||
{
|
{
|
||||||
int w = fm.stringWidth(node.value);
|
int w = fm.stringWidth(node.value);
|
||||||
if (cursor.x + w < getWidth())
|
if (cursor.x + w < getWidth())
|
||||||
{
|
{
|
||||||
layout.put(node, cursor.clone());
|
layout.put(node, cursor.clone());
|
||||||
cursor.x += w;
|
cursor.x += w;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
layout.put(node, cursor.clone());
|
layout.put(node, cursor.clone());
|
||||||
++cursor.line;
|
++cursor.line;
|
||||||
cursor.x = 0;
|
cursor.x = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (node.key.equals("text"))
|
else if (node.key.equals("text"))
|
||||||
{
|
{
|
||||||
int w = fm.stringWidth(node.value);
|
int w = fm.stringWidth(node.value);
|
||||||
if (cursor.x + w < getWidth())
|
if (cursor.x + w < getWidth())
|
||||||
{
|
{
|
||||||
layout.put(node, cursor.clone());
|
layout.put(node, cursor.clone());
|
||||||
cursor.x += w;
|
cursor.x += w;
|
||||||
}
|
}
|
||||||
else if (w < getWidth())
|
else if (w < getWidth())
|
||||||
{
|
{
|
||||||
++cursor.line;
|
++cursor.line;
|
||||||
cursor.x = 0;
|
cursor.x = 0;
|
||||||
layout.put(node, cursor.clone());
|
layout.put(node, cursor.clone());
|
||||||
cursor.x += w;
|
cursor.x += w;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
StringBuilder rem = new StringBuilder();
|
StringBuilder rem = new StringBuilder();
|
||||||
rem.append(node.value);
|
rem.append(node.value);
|
||||||
int mw = getWidth();
|
int mw = getWidth();
|
||||||
int aw = mw - cursor.x;
|
int aw = mw - cursor.x;
|
||||||
|
|
||||||
w = fm.charWidth(node.value.charAt(0));
|
w = fm.charWidth(node.value.charAt(0));
|
||||||
if (w >= aw)
|
if (w >= aw)
|
||||||
{
|
{
|
||||||
++cursor.line;
|
++cursor.line;
|
||||||
cursor.x = 0;
|
cursor.x = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (rem.length() > 0)
|
while (rem.length() > 0)
|
||||||
{
|
{
|
||||||
int l = 2;
|
int l = 2;
|
||||||
for (; l <= rem.length(); ++l)
|
for (; l <= rem.length(); ++l)
|
||||||
{
|
{
|
||||||
String substr = rem.substring(0, l);
|
String substr = rem.substring(0, l);
|
||||||
w = fm.stringWidth(substr);
|
w = fm.stringWidth(substr);
|
||||||
if (w >= aw) break;
|
if (w >= aw) break;
|
||||||
}
|
}
|
||||||
String substr = rem.substring(0, --l);
|
String substr = rem.substring(0, --l);
|
||||||
w = fm.stringWidth(substr);
|
w = fm.stringWidth(substr);
|
||||||
|
|
||||||
Tree<String> temp = new Tree<>();
|
Tree<String> temp = new Tree<>();
|
||||||
temp.key = node.key;
|
temp.key = node.key;
|
||||||
temp.value = substr;
|
temp.value = substr;
|
||||||
layout.put(temp, cursor.clone());
|
layout.put(temp, cursor.clone());
|
||||||
|
|
||||||
rem.delete(0, l);
|
rem.delete(0, l);
|
||||||
boolean more = rem.length() != 0;
|
boolean more = rem.length() != 0;
|
||||||
if (more) ++cursor.line;
|
if (more) ++cursor.line;
|
||||||
cursor.x = more ? 0 : w;
|
cursor.x = more ? 0 : w;
|
||||||
aw = mw;
|
aw = mw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (node.key.equals("emoji"))
|
else if (node.key.equals("emoji"))
|
||||||
{
|
{
|
||||||
Image image = emojis.get(node.value);
|
Image image = emojis.get(node.value);
|
||||||
int w; if (image != null)
|
int w; if (image != null)
|
||||||
{
|
{
|
||||||
int ow = image.getWidth(this);
|
int ow = image.getWidth(this);
|
||||||
int oh = image.getHeight(this);
|
int oh = image.getHeight(this);
|
||||||
int h = fm.getAscent() + fm.getDescent();
|
int h = fm.getAscent() + fm.getDescent();
|
||||||
w = ow * h/oh;
|
w = ow * h/oh;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
w = fm.stringWidth(node.value);
|
w = fm.stringWidth(node.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cursor.x + w < getWidth())
|
if (cursor.x + w < getWidth())
|
||||||
{
|
{
|
||||||
layout.put(node, cursor.clone());
|
layout.put(node, cursor.clone());
|
||||||
cursor.x += w;
|
cursor.x += w;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
++cursor.line;
|
++cursor.line;
|
||||||
cursor.x = 0;
|
cursor.x = 0;
|
||||||
layout.put(node, cursor.clone());
|
layout.put(node, cursor.clone());
|
||||||
cursor.x += w;
|
cursor.x += w;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (node.key.equals("tag"))
|
else if (node.key.equals("tag"))
|
||||||
{
|
{
|
||||||
String tagName = node.get(0).key;
|
String tagName = node.get(0).key;
|
||||||
Tree<String> children = node.get("children");
|
Tree<String> children = node.get("children");
|
||||||
|
|
||||||
// We won't place tag nodes on the layout.
|
// We won't place tag nodes on the layout.
|
||||||
|
|
||||||
if (tagName.equals("br"))
|
if (tagName.equals("br"))
|
||||||
{
|
{
|
||||||
++cursor.line;
|
++cursor.line;
|
||||||
cursor.x = 0;
|
cursor.x = 0;
|
||||||
}
|
}
|
||||||
else if (tagName.equals("p"))
|
else if (tagName.equals("p"))
|
||||||
{
|
{
|
||||||
//cursor.line += 3/2;
|
//cursor.line += 3/2;
|
||||||
cursor.line += 2;
|
cursor.line += 2;
|
||||||
// We don't have vertical cursor movement
|
// We don't have vertical cursor movement
|
||||||
// other than the line. Maybe fix in the
|
// other than the line. Maybe fix in the
|
||||||
// future..?
|
// future..?
|
||||||
cursor.x = 0;
|
cursor.x = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Tree<String> child: children)
|
for (Tree<String> child: children)
|
||||||
{
|
{
|
||||||
// Shallow copy this child node,
|
// Shallow copy this child node,
|
||||||
Tree<String> aug = new Tree<>();
|
Tree<String> aug = new Tree<>();
|
||||||
aug.key = child.key;
|
aug.key = child.key;
|
||||||
aug.value = child.value;
|
aug.value = child.value;
|
||||||
for (Tree<String> gc: child) aug.add(gc);
|
for (Tree<String> gc: child) aug.add(gc);
|
||||||
|
|
||||||
// Append all of our attributes. We'd like
|
// Append all of our attributes. We'd like
|
||||||
// those like href to end up at the text
|
// those like href to end up at the text
|
||||||
// nodes. This might collide with our
|
// nodes. This might collide with our
|
||||||
// child node's attributes, for now I'll
|
// child node's attributes, for now I'll
|
||||||
// assume that's not an issue.
|
// assume that's not an issue.
|
||||||
for (int o = 1; o < node.size(); ++o)
|
for (int o = 1; o < node.size(); ++o)
|
||||||
{
|
{
|
||||||
Tree<String> attr = node.get(o);
|
Tree<String> attr = node.get(o);
|
||||||
@ -279,29 +279,29 @@ implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
layout(aug, fm, cursor);
|
layout(aug, fm, cursor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else assert false;
|
else assert false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void
|
protected void
|
||||||
paintComponent(Graphics g)
|
paintComponent(Graphics g)
|
||||||
{
|
{
|
||||||
final Color LINK_COLOUR = Color.BLUE;
|
final Color LINK_COLOUR = Color.BLUE;
|
||||||
final Color PLAIN_COLOUR = getForeground();
|
final Color PLAIN_COLOUR = getForeground();
|
||||||
final Color SEL_COLOUR = new Color(0, 0, 0, 25);
|
final Color SEL_COLOUR = new Color(0, 0, 0, 25);
|
||||||
|
|
||||||
g.setFont(getFont());
|
g.setFont(getFont());
|
||||||
FontMetrics fm = g.getFontMetrics();
|
FontMetrics fm = g.getFontMetrics();
|
||||||
int iy = fm.getAscent();
|
int iy = fm.getAscent();
|
||||||
int lh = fm.getAscent() + fm.getDescent();
|
int lh = fm.getAscent() + fm.getDescent();
|
||||||
int asc = fm.getAscent();
|
int asc = fm.getAscent();
|
||||||
int w = getWidth(), h = getHeight();
|
int w = getWidth(), h = getHeight();
|
||||||
|
|
||||||
if (isOpaque()) g.clearRect(0, 0, w, h);
|
if (isOpaque()) g.clearRect(0, 0, w, h);
|
||||||
|
|
||||||
if (selEnd != null)
|
if (selEnd != null)
|
||||||
{
|
{
|
||||||
Position ssp = layout.get(selStart);
|
Position ssp = layout.get(selStart);
|
||||||
assert ssp != null;
|
assert ssp != null;
|
||||||
Position sep = layout.get(selEnd);
|
Position sep = layout.get(selEnd);
|
||||||
@ -318,81 +318,81 @@ implements
|
|||||||
sep = ssp;
|
sep = ssp;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ls = 1 + ssp.line - startingLine;
|
int ls = 1 + ssp.line - startingLine;
|
||||||
int le = 1 + sep.line - startingLine;
|
int le = 1 + sep.line - startingLine;
|
||||||
int ys = snap2(ls, iy, lh) - asc;
|
int ys = snap2(ls, iy, lh) - asc;
|
||||||
int ye = snap2(le, iy, lh) - asc;
|
int ye = snap2(le, iy, lh) - asc;
|
||||||
|
|
||||||
g.setColor(SEL_COLOUR);
|
g.setColor(SEL_COLOUR);
|
||||||
if (ssp.line == sep.line)
|
if (ssp.line == sep.line)
|
||||||
{
|
{
|
||||||
g.fillRect(ssp.x, ys, sep.x - ssp.x, lh);
|
g.fillRect(ssp.x, ys, sep.x - ssp.x, lh);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
g.fillRect(ssp.x, ys, w - ssp.x, lh);
|
g.fillRect(ssp.x, ys, w - ssp.x, lh);
|
||||||
for (int l = ls + 1; l < le; ++l)
|
for (int l = ls + 1; l < le; ++l)
|
||||||
{
|
{
|
||||||
int y = snap2(l, iy, lh) - asc;
|
int y = snap2(l, iy, lh) - asc;
|
||||||
g.fillRect(0, y, w, lh);
|
g.fillRect(0, y, w, lh);
|
||||||
}
|
}
|
||||||
g.fillRect(0, ye, sep.x, lh);
|
g.fillRect(0, ye, sep.x, lh);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
((java.awt.Graphics2D)g).setRenderingHint(
|
((java.awt.Graphics2D)g).setRenderingHint(
|
||||||
java.awt.RenderingHints.KEY_ANTIALIASING,
|
java.awt.RenderingHints.KEY_ANTIALIASING,
|
||||||
java.awt.RenderingHints.VALUE_ANTIALIAS_ON
|
java.awt.RenderingHints.VALUE_ANTIALIAS_ON
|
||||||
);
|
);
|
||||||
|
|
||||||
g.setColor(getForeground());
|
g.setColor(getForeground());
|
||||||
for (Tree<String> node: layout.keySet())
|
for (Tree<String> node: layout.keySet())
|
||||||
{
|
{
|
||||||
Position position = layout.get(node);
|
Position position = layout.get(node);
|
||||||
int x = position.x;
|
int x = position.x;
|
||||||
int line = 1 + position.line - startingLine;
|
int line = 1 + position.line - startingLine;
|
||||||
int y = snap2(line, iy, lh);
|
int y = snap2(line, iy, lh);
|
||||||
if (y > h) continue;
|
if (y > h) continue;
|
||||||
|
|
||||||
if (node.key.equals("text"))
|
if (node.key.equals("text"))
|
||||||
{
|
{
|
||||||
boolean isLink = node.get("href") != null;
|
boolean isLink = node.get("href") != null;
|
||||||
if (isLink) g.setColor(LINK_COLOUR);
|
if (isLink) g.setColor(LINK_COLOUR);
|
||||||
g.drawString(node.value, x, y);
|
g.drawString(node.value, x, y);
|
||||||
if (isLink) g.setColor(PLAIN_COLOUR);
|
if (isLink) g.setColor(PLAIN_COLOUR);
|
||||||
}
|
}
|
||||||
else if (node.key.equals("emoji"))
|
else if (node.key.equals("emoji"))
|
||||||
{
|
{
|
||||||
Image image = emojis.get(node.value);
|
Image image = emojis.get(node.value);
|
||||||
Image scaled = emojis.get(node.value + "_scaled");
|
Image scaled = emojis.get(node.value + "_scaled");
|
||||||
if (scaled != null)
|
if (scaled != null)
|
||||||
{
|
{
|
||||||
y -= asc;
|
y -= asc;
|
||||||
g.drawImage(scaled, x, y, this);
|
g.drawImage(scaled, x, y, this);
|
||||||
}
|
}
|
||||||
else if (image != null)
|
else if (image != null)
|
||||||
{
|
{
|
||||||
scaled = image.getScaledInstance(
|
scaled = image.getScaledInstance(
|
||||||
-1, fm.getAscent() + fm.getDescent(),
|
-1, fm.getAscent() + fm.getDescent(),
|
||||||
Image.SCALE_SMOOTH
|
Image.SCALE_SMOOTH
|
||||||
);
|
);
|
||||||
// I hope #getScaledInstance knows how to
|
// I hope #getScaledInstance knows how to
|
||||||
// wait if the image is yet to be loaded.
|
// wait if the image is yet to be loaded.
|
||||||
emojis.put(node.value + "_scaled", scaled);
|
emojis.put(node.value + "_scaled", scaled);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
g.drawString(node.value, x, y);
|
g.drawString(node.value, x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else continue;
|
else continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mousePressed(MouseEvent eM)
|
mousePressed(MouseEvent eM)
|
||||||
{
|
{
|
||||||
if (eM.getButton() != MouseEvent.BUTTON1) return;
|
if (eM.getButton() != MouseEvent.BUTTON1) return;
|
||||||
selStart = identifyNodeAt(eM.getX(), eM.getY());
|
selStart = identifyNodeAt(eM.getX(), eM.getY());
|
||||||
selEnd = null;
|
selEnd = null;
|
||||||
repaint();
|
repaint();
|
||||||
@ -408,71 +408,71 @@ implements
|
|||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mouseMoved(MouseEvent eM)
|
mouseMoved(MouseEvent eM)
|
||||||
{
|
{
|
||||||
Tree<String> h = identifyNodeAt(eM.getX(), eM.getY());
|
Tree<String> h = identifyNodeAt(eM.getX(), eM.getY());
|
||||||
if (h == null || h.get("href") == null)
|
if (h == null || h.get("href") == null)
|
||||||
{
|
{
|
||||||
setToolTipText("");
|
setToolTipText("");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
setToolTipText(h.get("href").value);
|
setToolTipText(h.get("href").value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Tree<String>
|
private Tree<String>
|
||||||
identifyNodeAt(int x, int y)
|
identifyNodeAt(int x, int y)
|
||||||
{
|
{
|
||||||
FontMetrics fm = getFontMetrics(getFont());
|
FontMetrics fm = getFontMetrics(getFont());
|
||||||
int initial = fm.getAscent();
|
int initial = fm.getAscent();
|
||||||
int advance = fm.getAscent() + fm.getDescent();
|
int advance = fm.getAscent() + fm.getDescent();
|
||||||
int line = isnap2(y, initial, advance);
|
int line = isnap2(y, initial, advance);
|
||||||
|
|
||||||
Tree<String> returnee = null;
|
Tree<String> returnee = null;
|
||||||
Position closest = new Position(0, 0);
|
Position closest = new Position(0, 0);
|
||||||
for (Tree<String> node: layout.keySet())
|
for (Tree<String> node: layout.keySet())
|
||||||
{
|
{
|
||||||
Position position = layout.get(node);
|
Position position = layout.get(node);
|
||||||
assert position != null;
|
assert position != null;
|
||||||
|
|
||||||
if (position.line != line) continue;
|
if (position.line != line) continue;
|
||||||
if (position.x > x) continue;
|
if (position.x > x) continue;
|
||||||
if (position.x >= closest.x)
|
if (position.x >= closest.x)
|
||||||
{
|
{
|
||||||
returnee = node;
|
returnee = node;
|
||||||
closest = position;
|
closest = position;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return returnee;
|
return returnee;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Tree<String>
|
private Tree<String>
|
||||||
identifyNodeAfter(int x, int y)
|
identifyNodeAfter(int x, int y)
|
||||||
{
|
{
|
||||||
FontMetrics fm = getFontMetrics(getFont());
|
FontMetrics fm = getFontMetrics(getFont());
|
||||||
int initial = fm.getAscent();
|
int initial = fm.getAscent();
|
||||||
int advance = fm.getAscent() + fm.getDescent();
|
int advance = fm.getAscent() + fm.getDescent();
|
||||||
int line = isnap2(y, initial, advance);
|
int line = isnap2(y, initial, advance);
|
||||||
|
|
||||||
Tree<String> returnee = null;
|
Tree<String> returnee = null;
|
||||||
Position closest = new Position(Integer.MAX_VALUE, 0);
|
Position closest = new Position(Integer.MAX_VALUE, 0);
|
||||||
for (Tree<String> node: layout.keySet())
|
for (Tree<String> node: layout.keySet())
|
||||||
{
|
{
|
||||||
Position position = layout.get(node);
|
Position position = layout.get(node);
|
||||||
assert position != null;
|
assert position != null;
|
||||||
|
|
||||||
if (position.line != line) continue;
|
if (position.line != line) continue;
|
||||||
if (position.x < x) continue;
|
if (position.x < x) continue;
|
||||||
if (position.x < closest.x)
|
if (position.x < closest.x)
|
||||||
{
|
{
|
||||||
returnee = node;
|
returnee = node;
|
||||||
closest = position;
|
closest = position;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return returnee;
|
return returnee;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
keyPressed(KeyEvent eK)
|
keyPressed(KeyEvent eK)
|
||||||
@ -494,14 +494,14 @@ implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String
|
private String
|
||||||
getSelectedText()
|
getSelectedText()
|
||||||
{
|
{
|
||||||
assert selStart != null && selEnd != null;
|
assert selStart != null && selEnd != null;
|
||||||
|
|
||||||
Position ssp = layout.get(selStart);
|
Position ssp = layout.get(selStart);
|
||||||
Position sep = layout.get(selEnd);
|
Position sep = layout.get(selEnd);
|
||||||
assert ssp != null && sep != null;
|
assert ssp != null && sep != null;
|
||||||
if (ssp.compareTo(sep) > 0)
|
if (ssp.compareTo(sep) > 0)
|
||||||
{
|
{
|
||||||
Position temp = ssp;
|
Position temp = ssp;
|
||||||
ssp = sep;
|
ssp = sep;
|
||||||
@ -515,8 +515,8 @@ implements
|
|||||||
Position position = layout.get(node);
|
Position position = layout.get(node);
|
||||||
assert position != null;
|
assert position != null;
|
||||||
|
|
||||||
boolean after = position.compareTo(ssp) >= 0;
|
boolean after = position.compareTo(ssp) >= 0;
|
||||||
boolean before = position.compareTo(sep) < 0;
|
boolean before = position.compareTo(sep) < 0;
|
||||||
if (!(after && before)) continue;
|
if (!(after && before)) continue;
|
||||||
|
|
||||||
// Just throw them in a pile for now..
|
// Just throw them in a pile for now..
|
||||||
@ -549,35 +549,35 @@ implements
|
|||||||
boolean s = node.key.equals("space");
|
boolean s = node.key.equals("space");
|
||||||
assert t || e || s;
|
assert t || e || s;
|
||||||
b.append(node.value);
|
b.append(node.value);
|
||||||
/*
|
/*
|
||||||
* I actually want to copy the link if the node is
|
* I actually want to copy the link if the node is
|
||||||
* associated with one. However, a link has
|
* associated with one. However, a link has
|
||||||
* multiple text nodes, so I'd end up copying
|
* multiple text nodes, so I'd end up copying
|
||||||
* multiple times. The correct action is to
|
* multiple times. The correct action is to
|
||||||
* associate the nodes with the same link object,
|
* associate the nodes with the same link object,
|
||||||
* then mark that as copied. Or, associate the
|
* then mark that as copied. Or, associate the
|
||||||
* nodes with their superiors in the HTML, then
|
* nodes with their superiors in the HTML, then
|
||||||
* walk up until we find an anchor with a href.
|
* walk up until we find an anchor with a href.
|
||||||
* Then again, have to mark that as copied too.
|
* Then again, have to mark that as copied too.
|
||||||
*
|
*
|
||||||
* I can also walk the HTML and copy any that are
|
* I can also walk the HTML and copy any that are
|
||||||
* in the selected region, careful to copy an
|
* in the selected region, careful to copy an
|
||||||
* anchor's href in stead of the anchor contents.
|
* anchor's href in stead of the anchor contents.
|
||||||
* I'd need a guarantee that my walking order is
|
* I'd need a guarantee that my walking order is
|
||||||
* the same as how they were rendered on the screen.
|
* the same as how they were rendered on the screen.
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
return b.toString();
|
return b.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int
|
private int
|
||||||
getHeightInLines()
|
getHeightInLines()
|
||||||
{
|
{
|
||||||
FontMetrics fm = getFontMetrics(getFont());
|
FontMetrics fm = getFontMetrics(getFont());
|
||||||
int initial = fm.getAscent();
|
int initial = fm.getAscent();
|
||||||
int advance = fm.getAscent() + fm.getDescent();
|
int advance = fm.getAscent() + fm.getDescent();
|
||||||
return isnap2(getHeight(), initial, advance) - 1;
|
return isnap2(getHeight(), initial, advance) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
keyReleased(KeyEvent eK) { }
|
keyReleased(KeyEvent eK) { }
|
||||||
@ -597,107 +597,107 @@ implements
|
|||||||
public void
|
public void
|
||||||
mouseExited(MouseEvent eM) { }
|
mouseExited(MouseEvent eM) { }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
componentResized(ComponentEvent eC) { setText(html); }
|
componentResized(ComponentEvent eC) { setText(html); }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
componentMoved(ComponentEvent eC) { }
|
componentMoved(ComponentEvent eC) { }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
componentShown(ComponentEvent eC) { }
|
componentShown(ComponentEvent eC) { }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
componentHidden(ComponentEvent eC) { }
|
componentHidden(ComponentEvent eC) { }
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
private static int
|
private static int
|
||||||
snap2(int blocks, int initial, int advance)
|
snap2(int blocks, int initial, int advance)
|
||||||
{
|
{
|
||||||
return initial + (blocks - 1) * advance;
|
return initial + (blocks - 1) * advance;
|
||||||
// If you'd like to go behind the first line 1,
|
// If you'd like to go behind the first line 1,
|
||||||
// note that the first negative line is 0.
|
// note that the first negative line is 0.
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int
|
private static int
|
||||||
isnap2(int units, int initial, int advance)
|
isnap2(int units, int initial, int advance)
|
||||||
{
|
{
|
||||||
int offset = units - initial;
|
int offset = units - initial;
|
||||||
return 2 + bfloor(offset - 1, advance);
|
return 2 + bfloor(offset - 1, advance);
|
||||||
// Not yet sure how this behaves for negative numbers.
|
// Not yet sure how this behaves for negative numbers.
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int
|
private static int
|
||||||
bfloor(int units, int block)
|
bfloor(int units, int block)
|
||||||
{
|
{
|
||||||
if (units < 0) return (units / block) - 1;
|
if (units < 0) return (units / block) - 1;
|
||||||
else return units / block;
|
else return units / block;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
private static class
|
private static class
|
||||||
Position {
|
Position {
|
||||||
|
|
||||||
int
|
int
|
||||||
x, line;
|
x, line;
|
||||||
|
|
||||||
// -=%=-
|
// -=%=-
|
||||||
|
|
||||||
public int
|
public int
|
||||||
compareTo(Position other)
|
compareTo(Position other)
|
||||||
{
|
{
|
||||||
if (line < other.line) return -1;
|
if (line < other.line) return -1;
|
||||||
if (line > other.line) return 1;
|
if (line > other.line) return 1;
|
||||||
if (x < other.x) return -1;
|
if (x < other.x) return -1;
|
||||||
if (x > other.x) return 1;
|
if (x > other.x) return 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String
|
public String
|
||||||
toString()
|
toString()
|
||||||
{
|
{
|
||||||
return "(" + x + "," + line + ")";
|
return "(" + x + "," + line + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
// -=%=-
|
// -=%=-
|
||||||
|
|
||||||
public
|
public
|
||||||
Position(int x, int line)
|
Position(int x, int line)
|
||||||
{
|
{
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.line = line;
|
this.line = line;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Position
|
public Position
|
||||||
clone()
|
clone()
|
||||||
{
|
{
|
||||||
return new Position(x, line);
|
return new Position(x, line);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
RichTextPane3()
|
RichTextPane3()
|
||||||
{
|
{
|
||||||
layout = new HashMap<>();
|
layout = new HashMap<>();
|
||||||
layoutEnd = new Tree<>("text", "");
|
layoutEnd = new Tree<>("text", "");
|
||||||
emojis = new HashMap<>();
|
emojis = new HashMap<>();
|
||||||
|
|
||||||
Tree<String> blank = new Tree<>();
|
Tree<String> blank = new Tree<>();
|
||||||
blank.key = "tag";
|
blank.key = "tag";
|
||||||
blank.add(new Tree<>("html", null));
|
blank.add(new Tree<>("html", null));
|
||||||
blank.add(new Tree<>("children", null));
|
blank.add(new Tree<>("children", null));
|
||||||
setText(blank);
|
setText(blank);
|
||||||
|
|
||||||
this.addComponentListener(this);
|
this.addComponentListener(this);
|
||||||
this.addMouseListener(this);
|
this.addMouseListener(this);
|
||||||
this.addMouseMotionListener(this);
|
this.addMouseMotionListener(this);
|
||||||
this.addKeyListener(this);
|
this.addKeyListener(this);
|
||||||
setFocusable(true);
|
setFocusable(true);
|
||||||
// A keyboard user can still copy by tabbing in
|
// A keyboard user can still copy by tabbing in
|
||||||
// and selecting all.
|
// and selecting all.
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -31,18 +31,18 @@ RudimentaryHTMLParser {
|
|||||||
public static Tree<String>
|
public static Tree<String>
|
||||||
depthlessRead(String html)
|
depthlessRead(String html)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
return pass3(pass2(pass1(html)));
|
return pass3(pass2(pass1(html)));
|
||||||
}
|
}
|
||||||
catch (IOException eIo) {
|
catch (IOException eIo) {
|
||||||
assert false;
|
assert false;
|
||||||
/*
|
/*
|
||||||
* We use only StringReaders, which only throw an
|
* We use only StringReaders, which only throw an
|
||||||
* IOException when they are read after being closed.
|
* IOException when they are read after being closed.
|
||||||
* And we don't close them.
|
* And we don't close them.
|
||||||
*/
|
*/
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
@ -51,10 +51,10 @@ RudimentaryHTMLParser {
|
|||||||
pass1(String html)
|
pass1(String html)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
Reader r = new StringReader(html);
|
Reader r = new StringReader(html);
|
||||||
Tree<String> docu = new Tree<String>();
|
Tree<String> docu = new Tree<String>();
|
||||||
StringBuilder text = new StringBuilder();
|
StringBuilder text = new StringBuilder();
|
||||||
StringBuilder emoji = new StringBuilder();
|
StringBuilder emoji = new StringBuilder();
|
||||||
StringBuilder htmlEscape = new StringBuilder();
|
StringBuilder htmlEscape = new StringBuilder();
|
||||||
boolean quoted = false, inEmoji = false;
|
boolean quoted = false, inEmoji = false;
|
||||||
int c; while ((c = r.read()) != -1)
|
int c; while ((c = r.read()) != -1)
|
||||||
@ -110,7 +110,7 @@ RudimentaryHTMLParser {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
text.append((char)c);
|
text.append((char)c);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (text.length() > 0)
|
if (text.length() > 0)
|
||||||
@ -181,9 +181,9 @@ RudimentaryHTMLParser {
|
|||||||
return docu;
|
return docu;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Tree<String>
|
private static Tree<String>
|
||||||
pass3(Tree<String> docu)
|
pass3(Tree<String> docu)
|
||||||
{
|
{
|
||||||
Tree<String> returnee = new Tree<String>();
|
Tree<String> returnee = new Tree<String>();
|
||||||
|
|
||||||
for (Tree<String> node: docu)
|
for (Tree<String> node: docu)
|
||||||
@ -194,39 +194,39 @@ RudimentaryHTMLParser {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
StringBuilder value = new StringBuilder();
|
StringBuilder value = new StringBuilder();
|
||||||
for (String segment: whitespaceSplit(node.value))
|
for (String segment: whitespaceSplit(node.value))
|
||||||
{
|
{
|
||||||
boolean st = segment.startsWith(":");
|
boolean st = segment.startsWith(":");
|
||||||
boolean ed = segment.endsWith(":");
|
boolean ed = segment.endsWith(":");
|
||||||
|
|
||||||
if (st && ed)
|
if (st && ed)
|
||||||
{
|
{
|
||||||
Tree<String> text = new Tree<String>();
|
Tree<String> text = new Tree<String>();
|
||||||
text.key = "text";
|
text.key = "text";
|
||||||
text.value = empty(value);
|
text.value = empty(value);
|
||||||
returnee.add(text);
|
returnee.add(text);
|
||||||
|
|
||||||
Tree<String> emoji = new Tree<String>();
|
Tree<String> emoji = new Tree<String>();
|
||||||
emoji.key = "emoji";
|
emoji.key = "emoji";
|
||||||
emoji.value = segment;
|
emoji.value = segment;
|
||||||
returnee.add(emoji);
|
returnee.add(emoji);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
value.append(segment);
|
value.append(segment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (value.length() > 0)
|
if (value.length() > 0)
|
||||||
{
|
{
|
||||||
Tree<String> text = new Tree<String>();
|
Tree<String> text = new Tree<String>();
|
||||||
text.key = "text";
|
text.key = "text";
|
||||||
text.value = empty(value);
|
text.value = empty(value);
|
||||||
returnee.add(text);
|
returnee.add(text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return returnee;
|
return returnee;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String
|
private static String
|
||||||
empty(StringBuilder b)
|
empty(StringBuilder b)
|
||||||
@ -236,25 +236,25 @@ RudimentaryHTMLParser {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<String>
|
private static List<String>
|
||||||
whitespaceSplit(String text)
|
whitespaceSplit(String text)
|
||||||
{
|
{
|
||||||
List<String> returnee = new ArrayList<>();
|
List<String> returnee = new ArrayList<>();
|
||||||
StringBuilder segment = new StringBuilder();
|
StringBuilder segment = new StringBuilder();
|
||||||
boolean isWhitespace = false;
|
boolean isWhitespace = false;
|
||||||
for (char c: text.toCharArray())
|
for (char c: text.toCharArray())
|
||||||
{
|
{
|
||||||
boolean diff = isWhitespace ^ Character.isWhitespace(c);
|
boolean diff = isWhitespace ^ Character.isWhitespace(c);
|
||||||
if (diff) {
|
if (diff) {
|
||||||
returnee.add(empty(segment));
|
returnee.add(empty(segment));
|
||||||
isWhitespace = !isWhitespace;
|
isWhitespace = !isWhitespace;
|
||||||
}
|
}
|
||||||
segment.append(c);
|
segment.append(c);
|
||||||
}
|
}
|
||||||
returnee.add(empty(segment));
|
returnee.add(empty(segment));
|
||||||
|
|
||||||
return returnee;
|
return returnee;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
|
@ -90,9 +90,9 @@ implements ActionListener {
|
|||||||
openMessages,
|
openMessages,
|
||||||
openLocal,
|
openLocal,
|
||||||
openFederated,
|
openFederated,
|
||||||
openNotifications,
|
openNotifications,
|
||||||
openOwnProfile,
|
openOwnProfile,
|
||||||
openProfile,
|
openProfile,
|
||||||
createPost,
|
createPost,
|
||||||
openAutoPostView,
|
openAutoPostView,
|
||||||
quit;
|
quit;
|
||||||
@ -117,101 +117,101 @@ implements ActionListener {
|
|||||||
this.page = page;
|
this.page = page;
|
||||||
|
|
||||||
List<PostPreviewComponent> previews;
|
List<PostPreviewComponent> previews;
|
||||||
previews = display.getPostPreviews();
|
previews = display.getPostPreviews();
|
||||||
|
|
||||||
int available = page.posts.length;
|
int available = page.posts.length;
|
||||||
int max = previews.size();
|
int max = previews.size();
|
||||||
assert available <= max;
|
assert available <= max;
|
||||||
|
|
||||||
for (int o = 0; o < available; ++o)
|
for (int o = 0; o < available; ++o)
|
||||||
{
|
{
|
||||||
PostPreviewComponent preview = previews.get(o);
|
PostPreviewComponent preview = previews.get(o);
|
||||||
Post post = page.posts[o];
|
Post post = page.posts[o];
|
||||||
|
|
||||||
preview.setTopLeft(post.author.name);
|
preview.setTopLeft(post.author.name);
|
||||||
if (post.boostedPost != null)
|
if (post.boostedPost != null)
|
||||||
{
|
{
|
||||||
String s = "boosted by " + post.author.name;
|
String s = "boosted by " + post.author.name;
|
||||||
preview.setTopLeft(s);
|
preview.setTopLeft(s);
|
||||||
post = post.boostedPost;
|
post = post.boostedPost;
|
||||||
}
|
}
|
||||||
|
|
||||||
String flags = "";
|
String flags = "";
|
||||||
if (post.attachments.length > 0) flags += "a";
|
if (post.attachments.length > 0) flags += "a";
|
||||||
post.resolveRelativeTime();
|
post.resolveRelativeTime();
|
||||||
preview.setTopRight(flags + " " + post.relativeTime);
|
preview.setTopRight(flags + " " + post.relativeTime);
|
||||||
|
|
||||||
post.resolveApproximateText();
|
post.resolveApproximateText();
|
||||||
if (post.contentWarning != null)
|
if (post.contentWarning != null)
|
||||||
preview.setBottom("(" + post.contentWarning + ")");
|
preview.setBottom("(" + post.contentWarning + ")");
|
||||||
else
|
else
|
||||||
preview.setBottom(post.approximateText);
|
preview.setBottom(post.approximateText);
|
||||||
}
|
}
|
||||||
for (int o = available; o < max; ++o)
|
for (int o = available; o < max; ++o)
|
||||||
{
|
{
|
||||||
previews.get(o).reset();
|
previews.get(o).reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean full = !(available < PREVIEW_COUNT);
|
boolean full = !(available < PREVIEW_COUNT);
|
||||||
display.setNextPageAvailable(full);
|
display.setNextPageAvailable(full);
|
||||||
display.setPreviousPageAvailable(true);
|
display.setPreviousPageAvailable(true);
|
||||||
display.resetFocus();
|
display.resetFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
readEntity(Tree<String> postEntityArray)
|
readEntity(Tree<String> postEntityArray)
|
||||||
{
|
{
|
||||||
TimelinePage page = new TimelinePage(postEntityArray);
|
TimelinePage page = new TimelinePage(postEntityArray);
|
||||||
page.type = this.page.type;
|
page.type = this.page.type;
|
||||||
page.accountNumId = this.page.accountNumId;
|
page.accountNumId = this.page.accountNumId;
|
||||||
page.listId = this.page.listId;
|
page.listId = this.page.listId;
|
||||||
use(page);
|
use(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
refresh()
|
refresh()
|
||||||
{
|
{
|
||||||
String firstId = null;
|
String firstId = null;
|
||||||
if (!showingLatest)
|
if (!showingLatest)
|
||||||
{
|
{
|
||||||
assert page.posts != null;
|
assert page.posts != null;
|
||||||
assert page.posts.length != 0;
|
assert page.posts.length != 0;
|
||||||
firstId = page.posts[0].id;
|
firstId = page.posts[0].id;
|
||||||
}
|
}
|
||||||
|
|
||||||
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||||
api.getTimelinePage(
|
api.getTimelinePage(
|
||||||
page.type,
|
page.type,
|
||||||
PREVIEW_COUNT, firstId, null,
|
PREVIEW_COUNT, firstId, null,
|
||||||
page.accountNumId, page.listId,
|
page.accountNumId, page.listId,
|
||||||
new RequestListener() {
|
new RequestListener() {
|
||||||
|
|
||||||
public void
|
public void
|
||||||
connectionFailed(IOException eIo)
|
connectionFailed(IOException eIo)
|
||||||
{
|
{
|
||||||
JOptionPane.showMessageDialog(
|
JOptionPane.showMessageDialog(
|
||||||
TimelineWindow.this,
|
TimelineWindow.this,
|
||||||
"Failed to fetch page.."
|
"Failed to fetch page.."
|
||||||
+ "\n" + eIo.getMessage()
|
+ "\n" + eIo.getMessage()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
requestFailed(int httpCode, Tree<String> json)
|
requestFailed(int httpCode, Tree<String> json)
|
||||||
{
|
{
|
||||||
JOptionPane.showMessageDialog(
|
JOptionPane.showMessageDialog(
|
||||||
TimelineWindow.this,
|
TimelineWindow.this,
|
||||||
"Failed to fetch page.."
|
"Failed to fetch page.."
|
||||||
+ "\n" + json.get("error").value
|
+ "\n" + json.get("error").value
|
||||||
+ "\n(HTTP code: " + httpCode + ")"
|
+ "\n(HTTP code: " + httpCode + ")"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
requestSucceeded(Tree<String> json)
|
requestSucceeded(Tree<String> json)
|
||||||
{
|
{
|
||||||
if (json.size() < PREVIEW_COUNT)
|
if (json.size() < PREVIEW_COUNT)
|
||||||
{
|
{
|
||||||
showLatestPage();
|
showLatestPage();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -221,37 +221,37 @@ implements ActionListener {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
display.setCursor(null);
|
display.setCursor(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
showLatestPage()
|
showLatestPage()
|
||||||
{
|
{
|
||||||
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||||
api.getTimelinePage(
|
api.getTimelinePage(
|
||||||
page.type,
|
page.type,
|
||||||
PREVIEW_COUNT, null, null,
|
PREVIEW_COUNT, null, null,
|
||||||
page.accountNumId, page.listId,
|
page.accountNumId, page.listId,
|
||||||
new RequestListener() {
|
new RequestListener() {
|
||||||
|
|
||||||
public void
|
public void
|
||||||
connectionFailed(IOException eIo)
|
connectionFailed(IOException eIo)
|
||||||
{
|
{
|
||||||
JOptionPane.showMessageDialog(
|
JOptionPane.showMessageDialog(
|
||||||
TimelineWindow.this,
|
TimelineWindow.this,
|
||||||
"Failed to fetch page.."
|
"Failed to fetch page.."
|
||||||
+ "\n" + eIo.getMessage()
|
+ "\n" + eIo.getMessage()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
requestFailed(int httpCode, Tree<String> json)
|
requestFailed(int httpCode, Tree<String> json)
|
||||||
{
|
{
|
||||||
JOptionPane.showMessageDialog(
|
JOptionPane.showMessageDialog(
|
||||||
TimelineWindow.this,
|
TimelineWindow.this,
|
||||||
"Failed to fetch page.."
|
"Failed to fetch page.."
|
||||||
+ "\n" + json.get("error").value
|
+ "\n" + json.get("error").value
|
||||||
+ "\n(HTTP code: " + httpCode + ")"
|
+ "\n(HTTP code: " + httpCode + ")"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
@ -274,32 +274,32 @@ implements ActionListener {
|
|||||||
assert page.posts.length != 0;
|
assert page.posts.length != 0;
|
||||||
String lastId = page.posts[page.posts.length - 1].id;
|
String lastId = page.posts[page.posts.length - 1].id;
|
||||||
|
|
||||||
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||||
api.getTimelinePage(
|
api.getTimelinePage(
|
||||||
page.type,
|
page.type,
|
||||||
PREVIEW_COUNT, lastId, null,
|
PREVIEW_COUNT, lastId, null,
|
||||||
page.accountNumId, page.listId,
|
page.accountNumId, page.listId,
|
||||||
new RequestListener() {
|
new RequestListener() {
|
||||||
|
|
||||||
public void
|
public void
|
||||||
connectionFailed(IOException eIo)
|
connectionFailed(IOException eIo)
|
||||||
{
|
{
|
||||||
JOptionPane.showMessageDialog(
|
JOptionPane.showMessageDialog(
|
||||||
TimelineWindow.this,
|
TimelineWindow.this,
|
||||||
"Failed to fetch page.."
|
"Failed to fetch page.."
|
||||||
+ "\n" + eIo.getMessage()
|
+ "\n" + eIo.getMessage()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
requestFailed(int httpCode, Tree<String> json)
|
requestFailed(int httpCode, Tree<String> json)
|
||||||
{
|
{
|
||||||
JOptionPane.showMessageDialog(
|
JOptionPane.showMessageDialog(
|
||||||
TimelineWindow.this,
|
TimelineWindow.this,
|
||||||
"Failed to fetch page.."
|
"Failed to fetch page.."
|
||||||
+ "\n" + json.get("error").value
|
+ "\n" + json.get("error").value
|
||||||
+ "\n(HTTP code: " + httpCode + ")"
|
+ "\n(HTTP code: " + httpCode + ")"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
@ -312,7 +312,7 @@ implements ActionListener {
|
|||||||
// quietly cancel.
|
// quietly cancel.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
readEntity(json);
|
readEntity(json);
|
||||||
showingLatest = false;
|
showingLatest = false;
|
||||||
windowUpdater.remove(TimelineWindow.this);
|
windowUpdater.remove(TimelineWindow.this);
|
||||||
}
|
}
|
||||||
@ -332,36 +332,36 @@ implements ActionListener {
|
|||||||
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||||
api.getTimelinePage(
|
api.getTimelinePage(
|
||||||
page.type,
|
page.type,
|
||||||
PREVIEW_COUNT, null, firstId,
|
PREVIEW_COUNT, null, firstId,
|
||||||
page.accountNumId, page.listId,
|
page.accountNumId, page.listId,
|
||||||
new RequestListener() {
|
new RequestListener() {
|
||||||
|
|
||||||
public void
|
public void
|
||||||
connectionFailed(IOException eIo)
|
connectionFailed(IOException eIo)
|
||||||
{
|
{
|
||||||
JOptionPane.showMessageDialog(
|
JOptionPane.showMessageDialog(
|
||||||
TimelineWindow.this,
|
TimelineWindow.this,
|
||||||
"Failed to fetch page.."
|
"Failed to fetch page.."
|
||||||
+ "\n" + eIo.getMessage()
|
+ "\n" + eIo.getMessage()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
requestFailed(int httpCode, Tree<String> json)
|
requestFailed(int httpCode, Tree<String> json)
|
||||||
{
|
{
|
||||||
JOptionPane.showMessageDialog(
|
JOptionPane.showMessageDialog(
|
||||||
TimelineWindow.this,
|
TimelineWindow.this,
|
||||||
"Failed to fetch page.."
|
"Failed to fetch page.."
|
||||||
+ "\n" + json.get("error").value
|
+ "\n" + json.get("error").value
|
||||||
+ "\n(HTTP code: " + httpCode + ")"
|
+ "\n(HTTP code: " + httpCode + ")"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
requestSucceeded(Tree<String> json)
|
requestSucceeded(Tree<String> json)
|
||||||
{
|
{
|
||||||
if (json.size() < PREVIEW_COUNT)
|
if (json.size() < PREVIEW_COUNT)
|
||||||
{
|
{
|
||||||
showLatestPage();
|
showLatestPage();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -386,12 +386,12 @@ implements ActionListener {
|
|||||||
setTitle(toString(type) + " timeline - JKomasto");
|
setTitle(toString(type) + " timeline - JKomasto");
|
||||||
|
|
||||||
String f = type.toString().toLowerCase();
|
String f = type.toString().toLowerCase();
|
||||||
display.setBackgroundImage(ImageApi.local(f));
|
display.setBackgroundImage(ImageApi.local(f));
|
||||||
/*
|
/*
|
||||||
* (注) Java's image renderer draws images with transparency
|
* (注) Java's image renderer draws images with transparency
|
||||||
* darker than GIMP does. Overcompensate in lightening.
|
* darker than GIMP does. Overcompensate in lightening.
|
||||||
*/
|
*/
|
||||||
display.repaint();
|
display.repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized TimelineType
|
public synchronized TimelineType
|
||||||
@ -410,9 +410,9 @@ implements ActionListener {
|
|||||||
display.repaint();
|
display.repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void
|
public synchronized void
|
||||||
openOwnProfile()
|
openOwnProfile()
|
||||||
{
|
{
|
||||||
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
display.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
||||||
|
|
||||||
Tree<String> accountDetails = api.getAccountDetails();
|
Tree<String> accountDetails = api.getAccountDetails();
|
||||||
@ -422,118 +422,118 @@ implements ActionListener {
|
|||||||
w.setVisible(true);
|
w.setVisible(true);
|
||||||
|
|
||||||
display.setCursor(null);
|
display.setCursor(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
openProfile()
|
openProfile()
|
||||||
{
|
{
|
||||||
String query = JOptionPane.showInputDialog(
|
String query = JOptionPane.showInputDialog(
|
||||||
this,
|
this,
|
||||||
"Whose account do you want to see?\n"
|
"Whose account do you want to see?\n"
|
||||||
+ "Type an account name with the instance,\n"
|
+ "Type an account name with the instance,\n"
|
||||||
+ "or a display name if you can't remember.\n",
|
+ "or a display name if you can't remember.\n",
|
||||||
"Profile search",
|
"Profile search",
|
||||||
JOptionPane.PLAIN_MESSAGE
|
JOptionPane.PLAIN_MESSAGE
|
||||||
);
|
);
|
||||||
if (query == null) return;
|
if (query == null) return;
|
||||||
|
|
||||||
class Handler implements RequestListener {
|
class Handler implements RequestListener {
|
||||||
|
|
||||||
public Tree<String>
|
public Tree<String>
|
||||||
json;
|
json;
|
||||||
|
|
||||||
// -=%=-
|
// -=%=-
|
||||||
|
|
||||||
public void
|
public void
|
||||||
connectionFailed(IOException eIo)
|
connectionFailed(IOException eIo)
|
||||||
{
|
{
|
||||||
JOptionPane.showMessageDialog(
|
JOptionPane.showMessageDialog(
|
||||||
TimelineWindow.this,
|
TimelineWindow.this,
|
||||||
"Tried to fetch accounts, but it failed.."
|
"Tried to fetch accounts, but it failed.."
|
||||||
+ "\n" + eIo.getMessage()
|
+ "\n" + eIo.getMessage()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
requestFailed(int httpCode, Tree<String> json)
|
requestFailed(int httpCode, Tree<String> json)
|
||||||
{
|
{
|
||||||
JOptionPane.showMessageDialog(
|
JOptionPane.showMessageDialog(
|
||||||
TimelineWindow.this,
|
TimelineWindow.this,
|
||||||
"Tried to fetch accounts, but it failed.."
|
"Tried to fetch accounts, but it failed.."
|
||||||
+ "\n" + json.get("error").value
|
+ "\n" + json.get("error").value
|
||||||
+ "\n(HTTP code: " + httpCode + ")"
|
+ "\n(HTTP code: " + httpCode + ")"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
requestSucceeded(Tree<String> json) { this.json = json; }
|
requestSucceeded(Tree<String> json) { this.json = json; }
|
||||||
|
|
||||||
}
|
}
|
||||||
// (知) Have to create a named class because
|
// (知) Have to create a named class because
|
||||||
// it has to hold the variable.
|
// it has to hold the variable.
|
||||||
Handler handler = new Handler();
|
Handler handler = new Handler();
|
||||||
api.getAccounts(query, handler);
|
api.getAccounts(query, handler);
|
||||||
if (handler.json == null) return;
|
if (handler.json == null) return;
|
||||||
|
|
||||||
if (handler.json.size() == 0)
|
if (handler.json.size() == 0)
|
||||||
{
|
{
|
||||||
JOptionPane.showMessageDialog(
|
JOptionPane.showMessageDialog(
|
||||||
this,
|
this,
|
||||||
"There were no results from the query.. ☹️"
|
"There were no results from the query.. ☹️"
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Tree<String> openee = null;
|
Tree<String> openee = null;
|
||||||
if (query.startsWith("@")) query = query.substring(1);
|
if (query.startsWith("@")) query = query.substring(1);
|
||||||
|
|
||||||
List<Object> message = new ArrayList<>();
|
List<Object> message = new ArrayList<>();
|
||||||
message.add("Maybe one of these?");
|
message.add("Maybe one of these?");
|
||||||
ButtonGroup selGroup = new ButtonGroup();
|
ButtonGroup selGroup = new ButtonGroup();
|
||||||
for (Tree<String> account: handler.json)
|
for (Tree<String> account: handler.json)
|
||||||
{
|
{
|
||||||
String dname = account.get("display_name").value;
|
String dname = account.get("display_name").value;
|
||||||
String acct = account.get("acct").value;
|
String acct = account.get("acct").value;
|
||||||
if (query.equals(acct)) {
|
if (query.equals(acct)) {
|
||||||
openee = account;
|
openee = account;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
JRadioButton b = new JRadioButton();
|
JRadioButton b = new JRadioButton();
|
||||||
b.setText(dname + " (" + acct + ")");
|
b.setText(dname + " (" + acct + ")");
|
||||||
selGroup.add(b);
|
selGroup.add(b);
|
||||||
message.add(b);
|
message.add(b);
|
||||||
}
|
}
|
||||||
if (openee == null)
|
if (openee == null)
|
||||||
{
|
{
|
||||||
int response = JOptionPane.showConfirmDialog(
|
int response = JOptionPane.showConfirmDialog(
|
||||||
this,
|
this,
|
||||||
message.toArray(),
|
message.toArray(),
|
||||||
"Search results",
|
"Search results",
|
||||||
JOptionPane.OK_CANCEL_OPTION
|
JOptionPane.OK_CANCEL_OPTION
|
||||||
);
|
);
|
||||||
if (response == JOptionPane.CANCEL_OPTION) return;
|
if (response == JOptionPane.CANCEL_OPTION) return;
|
||||||
for (int o = 1; o < message.size(); ++o)
|
for (int o = 1; o < message.size(); ++o)
|
||||||
{
|
{
|
||||||
JRadioButton b = (JRadioButton)message.get(o);
|
JRadioButton b = (JRadioButton)message.get(o);
|
||||||
if (selGroup.isSelected(b.getModel()))
|
if (selGroup.isSelected(b.getModel()))
|
||||||
{
|
{
|
||||||
openee = handler.json.get(o - 1);
|
openee = handler.json.get(o - 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (openee == null) return;
|
if (openee == null) return;
|
||||||
/*
|
/*
|
||||||
* It seems like this can happen if someone
|
* It seems like this can happen if someone
|
||||||
* presses escape out of the confirm dialog.
|
* presses escape out of the confirm dialog.
|
||||||
* I don't know why that doesn't map to cancel.
|
* I don't know why that doesn't map to cancel.
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
ProfileWindow w = new ProfileWindow(primaire);
|
ProfileWindow w = new ProfileWindow(primaire);
|
||||||
w.use(new Account(openee));
|
w.use(new Account(openee));
|
||||||
w.setLocationByPlatform(true);
|
w.setLocationByPlatform(true);
|
||||||
w.setVisible(true);
|
w.setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
@ -577,13 +577,13 @@ implements ActionListener {
|
|||||||
setTimelineType(TimelineType.LOCAL);
|
setTimelineType(TimelineType.LOCAL);
|
||||||
showLatestPage();
|
showLatestPage();
|
||||||
}
|
}
|
||||||
if (src == openOwnProfile)
|
if (src == openOwnProfile)
|
||||||
{
|
{
|
||||||
openOwnProfile();
|
openOwnProfile();
|
||||||
}
|
}
|
||||||
if (src == openProfile)
|
if (src == openProfile)
|
||||||
{
|
{
|
||||||
openProfile();
|
openProfile();
|
||||||
}
|
}
|
||||||
if (src == createPost)
|
if (src == createPost)
|
||||||
{
|
{
|
||||||
@ -595,15 +595,15 @@ implements ActionListener {
|
|||||||
w.setLocation(getX() + 10 + getWidth(), getY());
|
w.setLocation(getX() + 10 + getWidth(), getY());
|
||||||
w.setVisible(true);
|
w.setVisible(true);
|
||||||
}
|
}
|
||||||
if (src == openNotifications)
|
if (src == openNotifications)
|
||||||
{
|
{
|
||||||
NotificationsWindow w =
|
NotificationsWindow w =
|
||||||
primaire.getNotificationsWindow();
|
primaire.getNotificationsWindow();
|
||||||
if (!w.isVisible())
|
if (!w.isVisible())
|
||||||
{
|
{
|
||||||
w.setLocationByPlatform(true);
|
w.setLocationByPlatform(true);
|
||||||
w.setVisible(true);
|
w.setVisible(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (src == flipToNewestPost)
|
if (src == flipToNewestPost)
|
||||||
{
|
{
|
||||||
@ -650,17 +650,17 @@ implements ActionListener {
|
|||||||
|
|
||||||
openHome = new JMenuItem("Open home timeline");
|
openHome = new JMenuItem("Open home timeline");
|
||||||
openFederated = new JMenuItem("Open federated timeline");
|
openFederated = new JMenuItem("Open federated timeline");
|
||||||
openNotifications = new JMenuItem("Open notifications");
|
openNotifications = new JMenuItem("Open notifications");
|
||||||
openOwnProfile = new JMenuItem("Open own profile");
|
openOwnProfile = new JMenuItem("Open own profile");
|
||||||
openProfile = new JMenuItem("Open profile..");
|
openProfile = new JMenuItem("Open profile..");
|
||||||
createPost = new JMenuItem("Create a post");
|
createPost = new JMenuItem("Create a post");
|
||||||
openAutoPostView = new JMenuItem("Open auto post view");
|
openAutoPostView = new JMenuItem("Open auto post view");
|
||||||
quit = new JMenuItem("Quit");
|
quit = new JMenuItem("Quit");
|
||||||
openHome.addActionListener(this);
|
openHome.addActionListener(this);
|
||||||
openFederated.addActionListener(this);
|
openFederated.addActionListener(this);
|
||||||
openNotifications.addActionListener(this);
|
openNotifications.addActionListener(this);
|
||||||
openOwnProfile.addActionListener(this);
|
openOwnProfile.addActionListener(this);
|
||||||
openProfile.addActionListener(this);
|
openProfile.addActionListener(this);
|
||||||
createPost.addActionListener(this);
|
createPost.addActionListener(this);
|
||||||
openAutoPostView.addActionListener(this);
|
openAutoPostView.addActionListener(this);
|
||||||
quit.addActionListener(this);
|
quit.addActionListener(this);
|
||||||
@ -672,9 +672,9 @@ implements ActionListener {
|
|||||||
programMenu.setMnemonic(KeyEvent.VK_P);
|
programMenu.setMnemonic(KeyEvent.VK_P);
|
||||||
programMenu.add(openHome);
|
programMenu.add(openHome);
|
||||||
programMenu.add(openFederated);
|
programMenu.add(openFederated);
|
||||||
programMenu.add(openNotifications);
|
programMenu.add(openNotifications);
|
||||||
programMenu.add(openOwnProfile);
|
programMenu.add(openOwnProfile);
|
||||||
programMenu.add(openProfile);
|
programMenu.add(openProfile);
|
||||||
programMenu.add(new JSeparator());
|
programMenu.add(new JSeparator());
|
||||||
programMenu.add(createPost);
|
programMenu.add(createPost);
|
||||||
programMenu.add(openAutoPostView);
|
programMenu.add(openAutoPostView);
|
||||||
@ -696,8 +696,8 @@ implements ActionListener {
|
|||||||
display.setPreviousPageAvailable(false);
|
display.setPreviousPageAvailable(false);
|
||||||
setContentPane(display);
|
setContentPane(display);
|
||||||
|
|
||||||
setTimelineType(TimelineType.HOME);
|
setTimelineType(TimelineType.HOME);
|
||||||
setIconImage(primaire.getProgramIcon());
|
setIconImage(primaire.getProgramIcon());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -727,8 +727,8 @@ implements
|
|||||||
private boolean
|
private boolean
|
||||||
hoverSelect;
|
hoverSelect;
|
||||||
|
|
||||||
private Image
|
private Image
|
||||||
backgroundImage;
|
backgroundImage;
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
@ -759,46 +759,46 @@ implements
|
|||||||
public void
|
public void
|
||||||
setHoverSelect(boolean n) { hoverSelect = n; }
|
setHoverSelect(boolean n) { hoverSelect = n; }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
setBackgroundImage(Image n) { backgroundImage = n; }
|
setBackgroundImage(Image n) { backgroundImage = n; }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
resetFocus() { postPreviews.get(0).requestFocusInWindow(); }
|
resetFocus() { postPreviews.get(0).requestFocusInWindow(); }
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
protected void
|
protected void
|
||||||
paintComponent(Graphics g)
|
paintComponent(Graphics g)
|
||||||
{
|
{
|
||||||
int w = getWidth(), h = getHeight();
|
int w = getWidth(), h = getHeight();
|
||||||
g.clearRect(0, 0, w, h);
|
g.clearRect(0, 0, w, h);
|
||||||
|
|
||||||
if (backgroundImage != null)
|
if (backgroundImage != null)
|
||||||
{
|
{
|
||||||
int b = h * 5 / 10;
|
int b = h * 5 / 10;
|
||||||
int iw = backgroundImage.getWidth(this);
|
int iw = backgroundImage.getWidth(this);
|
||||||
int ih = backgroundImage.getHeight(this);
|
int ih = backgroundImage.getHeight(this);
|
||||||
if (ih > iw) {
|
if (ih > iw) {
|
||||||
ih = ih * b / iw;
|
ih = ih * b / iw;
|
||||||
iw = b;
|
iw = b;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
iw = iw * b / ih;
|
iw = iw * b / ih;
|
||||||
ih = b;
|
ih = b;
|
||||||
}
|
}
|
||||||
int x = w - iw, y = h - ih;
|
int x = w - iw, y = h - ih;
|
||||||
g.drawImage(backgroundImage, x, y, iw, ih, this);
|
g.drawImage(backgroundImage, x, y, iw, ih, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
((java.awt.Graphics2D)g).setRenderingHint(
|
((java.awt.Graphics2D)g).setRenderingHint(
|
||||||
java.awt.RenderingHints.KEY_ANTIALIASING,
|
java.awt.RenderingHints.KEY_ANTIALIASING,
|
||||||
java.awt.RenderingHints.VALUE_ANTIALIAS_ON
|
java.awt.RenderingHints.VALUE_ANTIALIAS_ON
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void
|
private void
|
||||||
select(Object c)
|
select(Object c)
|
||||||
{
|
{
|
||||||
assert c instanceof PostPreviewComponent;
|
assert c instanceof PostPreviewComponent;
|
||||||
|
|
||||||
for (PostPreviewComponent p: postPreviews)
|
for (PostPreviewComponent p: postPreviews)
|
||||||
@ -814,25 +814,25 @@ implements
|
|||||||
p.repaint();
|
p.repaint();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void
|
private void
|
||||||
deselect(Object c)
|
deselect(Object c)
|
||||||
{
|
{
|
||||||
assert c instanceof PostPreviewComponent;
|
assert c instanceof PostPreviewComponent;
|
||||||
PostPreviewComponent p = (PostPreviewComponent)c;
|
PostPreviewComponent p = (PostPreviewComponent)c;
|
||||||
|
|
||||||
p.setSelected(false);
|
p.setSelected(false);
|
||||||
p.repaint();
|
p.repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void
|
private void
|
||||||
open(Object c)
|
open(Object c)
|
||||||
{
|
{
|
||||||
int o = postPreviews.indexOf(c);
|
int o = postPreviews.indexOf(c);
|
||||||
assert o != -1;
|
assert o != -1;
|
||||||
primaire.previewOpened(1 + o);
|
primaire.previewOpened(1 + o);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
focusGained(FocusEvent eF) { select(eF.getSource()); }
|
focusGained(FocusEvent eF) { select(eF.getSource()); }
|
||||||
@ -840,14 +840,14 @@ implements
|
|||||||
public void
|
public void
|
||||||
focusLost(FocusEvent eF) { deselect(eF.getSource()); }
|
focusLost(FocusEvent eF) { deselect(eF.getSource()); }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mouseClicked(MouseEvent eM)
|
mouseClicked(MouseEvent eM)
|
||||||
{
|
{
|
||||||
if (eM.getClickCount() == 2) open(eM.getSource());
|
if (eM.getClickCount() == 2) open(eM.getSource());
|
||||||
else select(eM.getSource());
|
else select(eM.getSource());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mouseEntered(MouseEvent eM)
|
mouseEntered(MouseEvent eM)
|
||||||
{
|
{
|
||||||
if (!hoverSelect) return;
|
if (!hoverSelect) return;
|
||||||
@ -968,8 +968,8 @@ PostPreviewComponent extends JComponent {
|
|||||||
private JLabel
|
private JLabel
|
||||||
topLeft, topRight, bottom;
|
topLeft, topRight, bottom;
|
||||||
|
|
||||||
private boolean
|
private boolean
|
||||||
selected;
|
selected;
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
@ -982,7 +982,7 @@ PostPreviewComponent extends JComponent {
|
|||||||
public void
|
public void
|
||||||
setBottom(String text) { bottom.setText(text); }
|
setBottom(String text) { bottom.setText(text); }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
reset()
|
reset()
|
||||||
{
|
{
|
||||||
setTopLeft(" ");
|
setTopLeft(" ");
|
||||||
@ -1001,11 +1001,11 @@ PostPreviewComponent extends JComponent {
|
|||||||
protected void
|
protected void
|
||||||
paintComponent(Graphics g)
|
paintComponent(Graphics g)
|
||||||
{
|
{
|
||||||
if (selected)
|
if (selected)
|
||||||
{
|
{
|
||||||
g.setColor(new Color(0, 0, 0, 25));
|
g.setColor(new Color(0, 0, 0, 25));
|
||||||
g.fillRect(0, 0, getWidth(), getHeight());
|
g.fillRect(0, 0, getWidth(), getHeight());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
@ -1032,20 +1032,20 @@ PostPreviewComponent extends JComponent {
|
|||||||
top.add(Box.createGlue());
|
top.add(Box.createGlue());
|
||||||
top.add(topRight);
|
top.add(topRight);
|
||||||
|
|
||||||
bottom = new JLabel();
|
bottom = new JLabel();
|
||||||
bottom.setFont(f3);
|
bottom.setFont(f3);
|
||||||
bottom.setOpaque(false);
|
bottom.setOpaque(false);
|
||||||
|
|
||||||
JPanel left = new JPanel();
|
JPanel left = new JPanel();
|
||||||
left.setOpaque(false);
|
left.setOpaque(false);
|
||||||
left.setLayout(new BorderLayout());
|
left.setLayout(new BorderLayout());
|
||||||
left.add(top, BorderLayout.NORTH);
|
left.add(top, BorderLayout.NORTH);
|
||||||
left.add(bottom);
|
left.add(bottom);
|
||||||
|
|
||||||
setFocusable(true);
|
setFocusable(true);
|
||||||
setOpaque(false);
|
setOpaque(false);
|
||||||
setLayout(new BorderLayout());
|
setLayout(new BorderLayout());
|
||||||
add(left);
|
add(left);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -56,10 +56,10 @@ implements KeyListener, MouseListener, FocusListener {
|
|||||||
primaryToggled = false,
|
primaryToggled = false,
|
||||||
secondaryToggled = false;
|
secondaryToggled = false;
|
||||||
|
|
||||||
private Image
|
private Image
|
||||||
primaryToggledIcon,
|
primaryToggledIcon,
|
||||||
secondaryToggledIcon,
|
secondaryToggledIcon,
|
||||||
primaryUntoggledIcon,
|
primaryUntoggledIcon,
|
||||||
secondaryUntoggledIcon;
|
secondaryUntoggledIcon;
|
||||||
|
|
||||||
private int
|
private int
|
||||||
@ -116,7 +116,7 @@ implements KeyListener, MouseListener, FocusListener {
|
|||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
private void
|
private void
|
||||||
announce(String name, boolean toggled)
|
announce(String name, boolean toggled)
|
||||||
{
|
{
|
||||||
ActionEvent eA = new ActionEvent(
|
ActionEvent eA = new ActionEvent(
|
||||||
@ -134,17 +134,17 @@ implements KeyListener, MouseListener, FocusListener {
|
|||||||
g.drawImage(button, 0, 0, this);
|
g.drawImage(button, 0, 0, this);
|
||||||
if (!isEnabled())
|
if (!isEnabled())
|
||||||
g.drawImage(disabledOverlay, 0, 0, this);
|
g.drawImage(disabledOverlay, 0, 0, this);
|
||||||
if (isFocusOwner())
|
if (isFocusOwner())
|
||||||
g.drawImage(selectedOverlay, 0, 0, this);
|
g.drawImage(selectedOverlay, 0, 0, this);
|
||||||
|
|
||||||
if (secondaryToggled)
|
if (secondaryToggled)
|
||||||
g.drawImage(secondaryToggledIcon, 0, 0, this);
|
g.drawImage(secondaryToggledIcon, 0, 0, this);
|
||||||
else
|
else
|
||||||
g.drawImage(secondaryUntoggledIcon, 0, 0, this);
|
g.drawImage(secondaryUntoggledIcon, 0, 0, this);
|
||||||
if (primaryToggled)
|
if (primaryToggled)
|
||||||
g.drawImage(primaryToggledIcon, 0, 0, this);
|
g.drawImage(primaryToggledIcon, 0, 0, this);
|
||||||
else
|
else
|
||||||
g.drawImage(primaryUntoggledIcon, 0, 0, this);
|
g.drawImage(primaryUntoggledIcon, 0, 0, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -176,11 +176,11 @@ implements KeyListener, MouseListener, FocusListener {
|
|||||||
requestFocusInWindow();
|
requestFocusInWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
focusGained(FocusEvent eF) { repaint(); }
|
focusGained(FocusEvent eF) { repaint(); }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
focusLost(FocusEvent eF) { repaint(); }
|
focusLost(FocusEvent eF) { repaint(); }
|
||||||
|
|
||||||
|
|
||||||
public void
|
public void
|
||||||
@ -214,8 +214,8 @@ implements KeyListener, MouseListener, FocusListener {
|
|||||||
this.secondaryName = secondaryName;
|
this.secondaryName = secondaryName;
|
||||||
|
|
||||||
setModel(new DefaultButtonModel());
|
setModel(new DefaultButtonModel());
|
||||||
setFocusable(true);
|
setFocusable(true);
|
||||||
setOpaque(false);
|
setOpaque(false);
|
||||||
|
|
||||||
int w = button.getWidth(null);
|
int w = button.getWidth(null);
|
||||||
int h = button.getHeight(null);
|
int h = button.getHeight(null);
|
||||||
@ -224,7 +224,7 @@ implements KeyListener, MouseListener, FocusListener {
|
|||||||
|
|
||||||
this.addKeyListener(this);
|
this.addKeyListener(this);
|
||||||
this.addMouseListener(this);
|
this.addMouseListener(this);
|
||||||
this.addFocusListener(this);
|
this.addFocusListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void
|
private void
|
||||||
@ -232,17 +232,17 @@ implements KeyListener, MouseListener, FocusListener {
|
|||||||
{
|
{
|
||||||
String p1 = "graphics/" + primaryName + "Toggled.png";
|
String p1 = "graphics/" + primaryName + "Toggled.png";
|
||||||
String p2 = "graphics/" + secondaryName + "Toggled.png";
|
String p2 = "graphics/" + secondaryName + "Toggled.png";
|
||||||
String p3 = "graphics/" + primaryName + "Untoggled.png";
|
String p3 = "graphics/" + primaryName + "Untoggled.png";
|
||||||
String p4 = "graphics/" + secondaryName + "Untoggled.png";
|
String p4 = "graphics/" + secondaryName + "Untoggled.png";
|
||||||
URL u1 = getClass().getResource(p1);
|
URL u1 = getClass().getResource(p1);
|
||||||
URL u2 = getClass().getResource(p2);
|
URL u2 = getClass().getResource(p2);
|
||||||
URL u3 = getClass().getResource(p3);
|
URL u3 = getClass().getResource(p3);
|
||||||
URL u4 = getClass().getResource(p4);
|
URL u4 = getClass().getResource(p4);
|
||||||
if (u1 == null) primaryToggledIcon = null;
|
if (u1 == null) primaryToggledIcon = null;
|
||||||
else primaryToggledIcon = new ImageIcon(u1).getImage();
|
else primaryToggledIcon = new ImageIcon(u1).getImage();
|
||||||
if (u2 == null) secondaryToggledIcon = null;
|
if (u2 == null) secondaryToggledIcon = null;
|
||||||
else secondaryToggledIcon = new ImageIcon(u2).getImage();
|
else secondaryToggledIcon = new ImageIcon(u2).getImage();
|
||||||
if (u3 == null) primaryUntoggledIcon = null;
|
if (u3 == null) primaryUntoggledIcon = null;
|
||||||
else primaryUntoggledIcon = new ImageIcon(u3).getImage();
|
else primaryUntoggledIcon = new ImageIcon(u3).getImage();
|
||||||
if (u4 == null) secondaryUntoggledIcon = null;
|
if (u4 == null) secondaryUntoggledIcon = null;
|
||||||
else secondaryUntoggledIcon = new ImageIcon(u4).getImage();
|
else secondaryUntoggledIcon = new ImageIcon(u4).getImage();
|
||||||
@ -270,16 +270,16 @@ class
|
|||||||
RoundButton extends AbstractButton
|
RoundButton extends AbstractButton
|
||||||
implements KeyListener, MouseListener, FocusListener {
|
implements KeyListener, MouseListener, FocusListener {
|
||||||
|
|
||||||
private Image
|
private Image
|
||||||
image;
|
image;
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
private Image
|
private Image
|
||||||
copy,
|
copy,
|
||||||
scaled;
|
scaled;
|
||||||
|
|
||||||
private int
|
private int
|
||||||
nextEventID = ActionEvent.ACTION_FIRST;
|
nextEventID = ActionEvent.ACTION_FIRST;
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
@ -289,99 +289,99 @@ implements KeyListener, MouseListener, FocusListener {
|
|||||||
selectedOverlay,
|
selectedOverlay,
|
||||||
disabledOverlay;
|
disabledOverlay;
|
||||||
|
|
||||||
private static Shape
|
private static Shape
|
||||||
roundClip;
|
roundClip;
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
public void
|
public void
|
||||||
setImage(Image n)
|
setImage(Image n)
|
||||||
{
|
{
|
||||||
image = n;
|
image = n;
|
||||||
copy = null;
|
copy = null;
|
||||||
scaled = null;
|
scaled = null;
|
||||||
|
|
||||||
if (image != null)
|
if (image != null)
|
||||||
{
|
{
|
||||||
image.flush();
|
image.flush();
|
||||||
prepareImage(image, this);
|
prepareImage(image, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
public boolean
|
public boolean
|
||||||
imageUpdate(Image img, int f, int x, int y, int w, int h)
|
imageUpdate(Image img, int f, int x, int y, int w, int h)
|
||||||
{
|
{
|
||||||
// AbstractButton overrode this to refuse updates for
|
// AbstractButton overrode this to refuse updates for
|
||||||
// images that aren't the button's icon. We don't use
|
// images that aren't the button's icon. We don't use
|
||||||
// the icon, so we're overriding it back. Also, we have
|
// the icon, so we're overriding it back. Also, we have
|
||||||
// some async work to do regarding the images.
|
// some async work to do regarding the images.
|
||||||
|
|
||||||
if ((f & (ABORT|ERROR)) != 0) return false;
|
if ((f & (ABORT|ERROR)) != 0) return false;
|
||||||
|
|
||||||
boolean all = (f & ALLBITS) != 0;
|
boolean all = (f & ALLBITS) != 0;
|
||||||
boolean frame = (f & FRAMEBITS) != 0;
|
boolean frame = (f & FRAMEBITS) != 0;
|
||||||
boolean some = (f & SOMEBITS) != 0;
|
boolean some = (f & SOMEBITS) != 0;
|
||||||
|
|
||||||
if (frame && img != this.image) return false;
|
if (frame && img != this.image) return false;
|
||||||
|
|
||||||
if (img == this.image && (all || frame))
|
if (img == this.image && (all || frame))
|
||||||
{
|
{
|
||||||
int ow = img.getWidth(null);
|
int ow = img.getWidth(null);
|
||||||
int oh = img.getHeight(null);
|
int oh = img.getHeight(null);
|
||||||
|
|
||||||
if (copy == null)
|
if (copy == null)
|
||||||
{
|
{
|
||||||
int type = BufferedImage.TYPE_INT_ARGB;
|
int type = BufferedImage.TYPE_INT_ARGB;
|
||||||
copy = new BufferedImage(ow, oh, type);
|
copy = new BufferedImage(ow, oh, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
Graphics g = copy.getGraphics();
|
Graphics g = copy.getGraphics();
|
||||||
g.drawImage(img, 0, 0, null);
|
g.drawImage(img, 0, 0, null);
|
||||||
g.dispose();
|
g.dispose();
|
||||||
|
|
||||||
int algo = Image.SCALE_SMOOTH;
|
int algo = Image.SCALE_SMOOTH;
|
||||||
Rectangle b = roundClip.getBounds();
|
Rectangle b = roundClip.getBounds();
|
||||||
int sw = ow > oh ? -1 : b.width;
|
int sw = ow > oh ? -1 : b.width;
|
||||||
int sh = oh > ow ? -1 : b.height;
|
int sh = oh > ow ? -1 : b.height;
|
||||||
scaled = copy.getScaledInstance(sw, sh, algo);
|
scaled = copy.getScaledInstance(sw, sh, algo);
|
||||||
/*
|
/*
|
||||||
* We create a scaled instance from a BufferedImage copy
|
* We create a scaled instance from a BufferedImage copy
|
||||||
* rather than this.image directly, to avoid a ClassCast
|
* rather than this.image directly, to avoid a ClassCast
|
||||||
* Exception bug in the JDK, where ColorModel was
|
* Exception bug in the JDK, where ColorModel was
|
||||||
* incorrectly casting an int array of input data into
|
* incorrectly casting an int array of input data into
|
||||||
* a byte array. I'm not sure why that bug exists nor
|
* a byte array. I'm not sure why that bug exists nor
|
||||||
* why they haven't noticed it, but.
|
* why they haven't noticed it, but.
|
||||||
*/
|
*/
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
if (img == scaled && (some || all))
|
if (img == scaled && (some || all))
|
||||||
{
|
{
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
return all ? false : true;
|
return all ? false : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void
|
protected void
|
||||||
paintComponent(Graphics g)
|
paintComponent(Graphics g)
|
||||||
{
|
{
|
||||||
g.drawImage(button, 0, 0, this);
|
g.drawImage(button, 0, 0, this);
|
||||||
if (!isEnabled())
|
if (!isEnabled())
|
||||||
g.drawImage(disabledOverlay, 0, 0, this);
|
g.drawImage(disabledOverlay, 0, 0, this);
|
||||||
if (isFocusOwner())
|
if (isFocusOwner())
|
||||||
g.drawImage(selectedOverlay, 0, 0, this);
|
g.drawImage(selectedOverlay, 0, 0, this);
|
||||||
|
|
||||||
if (scaled == null) return;
|
if (scaled == null) return;
|
||||||
|
|
||||||
Rectangle b = roundClip.getBounds();
|
Rectangle b = roundClip.getBounds();
|
||||||
Shape defaultClip = g.getClip();
|
Shape defaultClip = g.getClip();
|
||||||
g.setClip(roundClip);
|
g.setClip(roundClip);
|
||||||
g.drawImage(scaled, b.x, b.y, this);
|
g.drawImage(scaled, b.x, b.y, this);
|
||||||
getParent().repaint();
|
getParent().repaint();
|
||||||
// I don't know why, but when we repaint ourselves, our
|
// I don't know why, but when we repaint ourselves, our
|
||||||
// parent doesn't repaint, so nothing seems to happen.
|
// parent doesn't repaint, so nothing seems to happen.
|
||||||
g.setClip(defaultClip);
|
g.setClip(defaultClip);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void
|
private void
|
||||||
@ -396,31 +396,31 @@ implements KeyListener, MouseListener, FocusListener {
|
|||||||
public void
|
public void
|
||||||
keyPressed(KeyEvent eK)
|
keyPressed(KeyEvent eK)
|
||||||
{
|
{
|
||||||
if (eK.getKeyCode() != KeyEvent.VK_SPACE) return;
|
if (eK.getKeyCode() != KeyEvent.VK_SPACE) return;
|
||||||
doClick();
|
doClick();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mouseClicked(MouseEvent eM) { announce(); }
|
mouseClicked(MouseEvent eM) { announce(); }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
focusGained(FocusEvent eF) { repaint(); }
|
focusGained(FocusEvent eF) { repaint(); }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
focusLost(FocusEvent eF) { repaint(); }
|
focusLost(FocusEvent eF) { repaint(); }
|
||||||
|
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mousePressed(MouseEvent eM) { }
|
mousePressed(MouseEvent eM) { }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mouseReleased(MouseEvent eM) { }
|
mouseReleased(MouseEvent eM) { }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mouseEntered(MouseEvent eM) { }
|
mouseEntered(MouseEvent eM) { }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
mouseExited(MouseEvent eM) { }
|
mouseExited(MouseEvent eM) { }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
keyReleased(KeyEvent eK) { }
|
keyReleased(KeyEvent eK) { }
|
||||||
@ -437,16 +437,16 @@ implements KeyListener, MouseListener, FocusListener {
|
|||||||
if (button == null) loadCommonImages();
|
if (button == null) loadCommonImages();
|
||||||
|
|
||||||
setModel(new DefaultButtonModel());
|
setModel(new DefaultButtonModel());
|
||||||
setFocusable(true);
|
setFocusable(true);
|
||||||
setOpaque(false);
|
setOpaque(false);
|
||||||
|
|
||||||
int w = button.getWidth(null);
|
int w = button.getWidth(null);
|
||||||
int h = button.getHeight(null);
|
int h = button.getHeight(null);
|
||||||
setPreferredSize(new Dimension(w, h));
|
setPreferredSize(new Dimension(w, h));
|
||||||
|
|
||||||
this.addKeyListener(this);
|
this.addKeyListener(this);
|
||||||
this.addMouseListener(this);
|
this.addMouseListener(this);
|
||||||
this.addFocusListener(this);
|
this.addFocusListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
@ -463,13 +463,13 @@ implements KeyListener, MouseListener, FocusListener {
|
|||||||
disabledOverlay = new ImageIcon(u2).getImage();
|
disabledOverlay = new ImageIcon(u2).getImage();
|
||||||
selectedOverlay = new ImageIcon(u3).getImage();
|
selectedOverlay = new ImageIcon(u3).getImage();
|
||||||
|
|
||||||
int radius = 6;
|
int radius = 6;
|
||||||
roundClip = new Ellipse2D.Float(
|
roundClip = new Ellipse2D.Float(
|
||||||
radius,
|
radius,
|
||||||
radius,
|
radius,
|
||||||
button.getWidth(null) - (2 * radius),
|
button.getWidth(null) - (2 * radius),
|
||||||
button.getHeight(null) - (2 * radius)
|
button.getHeight(null) - (2 * radius)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -43,11 +43,11 @@ WindowUpdater {
|
|||||||
private List<TimelineWindow>
|
private List<TimelineWindow>
|
||||||
timelineWindows;
|
timelineWindows;
|
||||||
|
|
||||||
private List<NotificationsWindow>
|
private List<NotificationsWindow>
|
||||||
notificationWindows;
|
notificationWindows;
|
||||||
|
|
||||||
private Clip
|
private Clip
|
||||||
notificationSound;
|
notificationSound;
|
||||||
|
|
||||||
private Connection
|
private Connection
|
||||||
publicConn,
|
publicConn,
|
||||||
@ -65,14 +65,14 @@ WindowUpdater {
|
|||||||
userConn.reevaluate();
|
userConn.reevaluate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void
|
public synchronized void
|
||||||
add(NotificationsWindow updatee)
|
add(NotificationsWindow updatee)
|
||||||
{
|
{
|
||||||
if (!notificationWindows.contains(updatee))
|
if (!notificationWindows.contains(updatee))
|
||||||
notificationWindows.add(updatee);
|
notificationWindows.add(updatee);
|
||||||
|
|
||||||
userConn.reevaluate();
|
userConn.reevaluate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void
|
public synchronized void
|
||||||
remove(TimelineWindow updatee)
|
remove(TimelineWindow updatee)
|
||||||
@ -82,7 +82,7 @@ WindowUpdater {
|
|||||||
userConn.reevaluate();
|
userConn.reevaluate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void
|
public synchronized void
|
||||||
remove(NotificationsWindow updatee)
|
remove(NotificationsWindow updatee)
|
||||||
{
|
{
|
||||||
notificationWindows.remove(updatee);
|
notificationWindows.remove(updatee);
|
||||||
@ -144,21 +144,21 @@ WindowUpdater {
|
|||||||
stop()
|
stop()
|
||||||
{
|
{
|
||||||
stopping = true;
|
stopping = true;
|
||||||
thread.interrupt();
|
thread.interrupt();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
thread.join(3000);
|
thread.join(3000);
|
||||||
/*
|
/*
|
||||||
* That thread should notice it is
|
* That thread should notice it is
|
||||||
* interrupted ppromptly, and close.
|
* interrupted ppromptly, and close.
|
||||||
*/
|
*/
|
||||||
if (thread.isAlive()) printStackTrace(thread);
|
if (thread.isAlive()) printStackTrace(thread);
|
||||||
}
|
}
|
||||||
catch (InterruptedException eIt)
|
catch (InterruptedException eIt)
|
||||||
{
|
{
|
||||||
assert false;
|
assert false;
|
||||||
}
|
}
|
||||||
thread = null;
|
thread = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
@ -186,21 +186,21 @@ WindowUpdater {
|
|||||||
thread.notifyAll();
|
thread.notifyAll();
|
||||||
}
|
}
|
||||||
event = new StringBuilder();
|
event = new StringBuilder();
|
||||||
data = new StringBuilder();
|
data = new StringBuilder();
|
||||||
api.monitorTimeline(type, this);
|
api.monitorTimeline(type, this);
|
||||||
// monitorTimeline should not return until
|
// monitorTimeline should not return until
|
||||||
// the connection is closed, or this thread
|
// the connection is closed, or this thread
|
||||||
// is interrupted.
|
// is interrupted.
|
||||||
|
|
||||||
System.err.println(
|
System.err.println(
|
||||||
"Stopped monitoring."
|
"Stopped monitoring."
|
||||||
+ thread + " " + Thread.currentThread()
|
+ thread + " " + Thread.currentThread()
|
||||||
);
|
);
|
||||||
if (thread == Thread.currentThread()) thread = null;
|
if (thread == Thread.currentThread()) thread = null;
|
||||||
/*
|
/*
|
||||||
* This isn't thread safe. But I'd like the
|
* This isn't thread safe. But I'd like the
|
||||||
* restart after sleep mode, so.
|
* restart after sleep mode, so.
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
@ -209,7 +209,7 @@ WindowUpdater {
|
|||||||
if (line.startsWith(":")) return;
|
if (line.startsWith(":")) return;
|
||||||
|
|
||||||
if (line.isEmpty())
|
if (line.isEmpty())
|
||||||
{
|
{
|
||||||
handle(event.toString(), data.toString());
|
handle(event.toString(), data.toString());
|
||||||
event.delete(0, event.length());
|
event.delete(0, event.length());
|
||||||
data.delete(0, event.length());
|
data.delete(0, event.length());
|
||||||
@ -299,38 +299,38 @@ WindowUpdater {
|
|||||||
this.api = primaire.getMastodonApi();
|
this.api = primaire.getMastodonApi();
|
||||||
|
|
||||||
this.timelineWindows = new ArrayList<>();
|
this.timelineWindows = new ArrayList<>();
|
||||||
this.notificationWindows = new ArrayList<>();
|
this.notificationWindows = new ArrayList<>();
|
||||||
|
|
||||||
publicConn = new Connection();
|
publicConn = new Connection();
|
||||||
publicConn.type = TimelineType.FEDERATED;
|
publicConn.type = TimelineType.FEDERATED;
|
||||||
|
|
||||||
userConn = new Connection();
|
userConn = new Connection();
|
||||||
userConn.type = TimelineType.HOME;
|
userConn.type = TimelineType.HOME;
|
||||||
|
|
||||||
loadNotificationSound();
|
loadNotificationSound();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
loadNotificationSound()
|
loadNotificationSound()
|
||||||
{
|
{
|
||||||
URL url = getClass().getResource("KDE_Dialog_Appear.wav");
|
URL url = getClass().getResource("KDE_Dialog_Appear.wav");
|
||||||
try {
|
try {
|
||||||
Clip clip = AudioSystem.getClip();
|
Clip clip = AudioSystem.getClip();
|
||||||
clip.open(AudioSystem.getAudioInputStream(url));
|
clip.open(AudioSystem.getAudioInputStream(url));
|
||||||
notificationSound = clip;
|
notificationSound = clip;
|
||||||
}
|
}
|
||||||
catch (LineUnavailableException eLu) {
|
catch (LineUnavailableException eLu) {
|
||||||
assert false;
|
assert false;
|
||||||
}
|
}
|
||||||
catch (UnsupportedAudioFileException eUa) {
|
catch (UnsupportedAudioFileException eUa) {
|
||||||
assert false;
|
assert false;
|
||||||
}
|
}
|
||||||
catch (IOException eIo) {
|
catch (IOException eIo) {
|
||||||
assert false;
|
assert false;
|
||||||
}
|
}
|
||||||
catch (IllegalArgumentException eIa) {
|
catch (IllegalArgumentException eIa) {
|
||||||
assert false;
|
assert false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user