Show each channel's own icon in the tree
Channels carry a channel_icon_id just like groups do; track it on ChannelNode from the channel list and the create/edit events, and paint it in the same right-aligned strip that already carries a client's group icons. Spacers keep their bare look. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,8 @@ public final class ChannelNode {
|
|||||||
public boolean hasPassword;
|
public boolean hasPassword;
|
||||||
public boolean permanent;
|
public boolean permanent;
|
||||||
public int maxClients = -1;
|
public int maxClients = -1;
|
||||||
|
/** Id of the channel's custom icon in the server's file repository, or 0 for none. */
|
||||||
|
public long iconId;
|
||||||
|
|
||||||
/** Populated when the tree is rebuilt. */
|
/** Populated when the tree is rebuilt. */
|
||||||
public final List<ChannelNode> children = new ArrayList<>();
|
public final List<ChannelNode> children = new ArrayList<>();
|
||||||
|
|||||||
@@ -316,6 +316,10 @@ public final class TeamspeakConnection implements TS3Listener {
|
|||||||
node.permanent = ch.isPermanent();
|
node.permanent = ch.isPermanent();
|
||||||
node.topic = ch.getTopic() == null ? "" : ch.getTopic();
|
node.topic = ch.getTopic() == null ? "" : ch.getTopic();
|
||||||
node.maxClients = ch.getMaxClients();
|
node.maxClients = ch.getMaxClients();
|
||||||
|
// channellist omits the icon on some servers; keep what events gave us.
|
||||||
|
if (ch.get("channel_icon_id") != null) {
|
||||||
|
node.iconId = safeLong(ch.get("channel_icon_id"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -607,7 +611,8 @@ public final class TeamspeakConnection implements TS3Listener {
|
|||||||
int pid = safeInt(e, "cpid");
|
int pid = safeInt(e, "cpid");
|
||||||
if (pid == 0) pid = safeInt(e, "pid");
|
if (pid == 0) pid = safeInt(e, "pid");
|
||||||
int order = safeInt(e, "channel_order");
|
int order = safeInt(e, "channel_order");
|
||||||
model.putChannel(cid, name, pid, order);
|
ChannelNode node = model.putChannel(cid, name, pid, order);
|
||||||
|
node.iconId = safeLong(e, "channel_icon_id");
|
||||||
model.relinkChannel(cid, pid, order);
|
model.relinkChannel(cid, pid, order);
|
||||||
ui.onModelChanged();
|
ui.onModelChanged();
|
||||||
}
|
}
|
||||||
@@ -625,6 +630,7 @@ public final class TeamspeakConnection implements TS3Listener {
|
|||||||
String name = e.get("channel_name");
|
String name = e.get("channel_name");
|
||||||
if (name != null) ch.name = name;
|
if (name != null) ch.name = name;
|
||||||
if (e.get("channel_order") != null) ch.order = e.getInt("channel_order");
|
if (e.get("channel_order") != null) ch.order = e.getInt("channel_order");
|
||||||
|
if (e.get("channel_icon_id") != null) ch.iconId = safeLong(e, "channel_icon_id");
|
||||||
ui.onModelChanged();
|
ui.onModelChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -647,7 +653,7 @@ public final class TeamspeakConnection implements TS3Listener {
|
|||||||
String name = e.get("channel_name");
|
String name = e.get("channel_name");
|
||||||
int pid = safeInt(e, "cpid");
|
int pid = safeInt(e, "cpid");
|
||||||
int order = safeInt(e, "channel_order");
|
int order = safeInt(e, "channel_order");
|
||||||
model.putChannel(cid, name, pid, order);
|
model.putChannel(cid, name, pid, order).iconId = safeLong(e, "channel_icon_id");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1047,10 +1053,14 @@ public final class TeamspeakConnection implements TS3Listener {
|
|||||||
// ---- helpers ----
|
// ---- helpers ----
|
||||||
|
|
||||||
private static long safeLong(BaseEvent e, String key) {
|
private static long safeLong(BaseEvent e, String key) {
|
||||||
|
return safeLong(e.get(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return the parsed value, or 0 when it is absent or not a number */
|
||||||
|
private static long safeLong(String value) {
|
||||||
try {
|
try {
|
||||||
String v = e.get(key);
|
return value == null ? 0 : Long.parseLong(value.trim());
|
||||||
return v == null ? 0 : Long.parseLong(v.trim());
|
} catch (NumberFormatException ex) {
|
||||||
} catch (Exception ex) {
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -375,10 +375,10 @@ public final class ServerTreePanel extends JScrollPane {
|
|||||||
private static final int BADGE_MARGIN = 4;
|
private static final int BADGE_MARGIN = 4;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Paints every visible client's group icons flush with the right edge of the
|
* Paints the icons of every visible row — a client's group icons, a channel's own
|
||||||
* viewport, the way TeamSpeak lines them up. Drawing them here rather than in the
|
* icon — flush with the right edge of the viewport, the way TeamSpeak lines them up.
|
||||||
* cell renderer keeps the rows' measured widths (and thus the selection highlight)
|
* Drawing them here rather than in the cell renderer keeps the rows' measured widths
|
||||||
* tied to the label alone.
|
* (and thus the selection highlight) tied to the label alone.
|
||||||
*/
|
*/
|
||||||
private void paintBadges(Graphics g, JTree tree) {
|
private void paintBadges(Graphics g, JTree tree) {
|
||||||
Rectangle visible = tree.getVisibleRect();
|
Rectangle visible = tree.getVisibleRect();
|
||||||
@@ -390,11 +390,7 @@ public final class ServerTreePanel extends JScrollPane {
|
|||||||
|
|
||||||
TreePath path = tree.getPathForRow(row);
|
TreePath path = tree.getPathForRow(row);
|
||||||
Object obj = ((DefaultMutableTreeNode) path.getLastPathComponent()).getUserObject();
|
Object obj = ((DefaultMutableTreeNode) path.getLastPathComponent()).getUserObject();
|
||||||
if (!(obj instanceof ClientEntry)) continue;
|
List<Icon> icons = badgesOf(obj);
|
||||||
ClientEntry cl = (ClientEntry) obj;
|
|
||||||
|
|
||||||
List<Icon> icons = groupIcons.iconsOf(
|
|
||||||
model.serverGroupsOf(cl.serverGroupIds), model.channelGroup(cl.channelGroupId));
|
|
||||||
if (icons.isEmpty()) continue;
|
if (icons.isEmpty()) continue;
|
||||||
|
|
||||||
GroupIcons.Row strip = new GroupIcons.Row(icons);
|
GroupIcons.Row strip = new GroupIcons.Row(icons);
|
||||||
@@ -403,6 +399,21 @@ public final class ServerTreePanel extends JScrollPane {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** The icon strip a row shows on its right, empty when it has none (yet). */
|
||||||
|
private List<Icon> badgesOf(Object node) {
|
||||||
|
if (node instanceof ClientEntry) {
|
||||||
|
ClientEntry cl = (ClientEntry) node;
|
||||||
|
return groupIcons.iconsOf(
|
||||||
|
model.serverGroupsOf(cl.serverGroupIds), model.channelGroup(cl.channelGroupId));
|
||||||
|
}
|
||||||
|
if (node instanceof ChannelNode) {
|
||||||
|
ChannelNode ch = (ChannelNode) node;
|
||||||
|
Icon icon = Spacers.isSpacer(ch.name) ? null : groupIcons.icon(ch.iconId);
|
||||||
|
if (icon != null) return List.of(icon);
|
||||||
|
}
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws where the drop will land: an insertion line between rows, or an outline
|
* Draws where the drop will land: an insertion line between rows, or an outline
|
||||||
* around the row that will receive the dragged node.
|
* around the row that will receive the dragged node.
|
||||||
@@ -564,10 +575,7 @@ public final class ServerTreePanel extends JScrollPane {
|
|||||||
} else if (obj instanceof ClientEntry) {
|
} else if (obj instanceof ClientEntry) {
|
||||||
ClientEntry cl = (ClientEntry) obj;
|
ClientEntry cl = (ClientEntry) obj;
|
||||||
String label = cl.nickname;
|
String label = cl.nickname;
|
||||||
boolean hasIcons = !groupIcons.iconsOf(
|
if (badgesOf(cl).isEmpty()) {
|
||||||
model.serverGroupsOf(cl.serverGroupIds),
|
|
||||||
model.channelGroup(cl.channelGroupId)).isEmpty();
|
|
||||||
if (!hasIcons) {
|
|
||||||
// No icons (yet): fall back to naming the primary group inline.
|
// No icons (yet): fall back to naming the primary group inline.
|
||||||
String primaryGroup = model.primaryServerGroupName(cl.serverGroupIds);
|
String primaryGroup = model.primaryServerGroupName(cl.serverGroupIds);
|
||||||
if (primaryGroup != null) label += " [" + primaryGroup + "]";
|
if (primaryGroup != null) label += " [" + primaryGroup + "]";
|
||||||
|
|||||||
Reference in New Issue
Block a user