Extract cell rendering, right-aligned group-icon-strip painting, the drop-indicator JTree subclass, and drag-and-drop handling out of ServerTreePanel into ServerTreeCellRenderer, DropIndicatorTree, and ServerTreeDragAndDrop, leaving ServerTreePanel as the coordinator (706 -> 284 lines). Pure refactor, no behavior change. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
81 lines
3.2 KiB
Java
81 lines
3.2 KiB
Java
package com.ts3client.ui;
|
|
|
|
import com.ts3client.net.ChannelNode;
|
|
import com.ts3client.net.ClientEntry;
|
|
|
|
import javax.swing.ImageIcon;
|
|
import javax.swing.JTree;
|
|
import javax.swing.tree.DefaultMutableTreeNode;
|
|
import javax.swing.tree.DefaultTreeCellRenderer;
|
|
import java.awt.Component;
|
|
|
|
/**
|
|
* Draws a {@link ServerTreePanel} row: the status icon and the label. The group
|
|
* icon strip is painted separately, right-aligned, by {@link DropIndicatorTree}.
|
|
*/
|
|
final class ServerTreeCellRenderer extends DefaultTreeCellRenderer {
|
|
|
|
@Override
|
|
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel,
|
|
boolean expanded, boolean leaf, int row,
|
|
boolean hasFocus) {
|
|
super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
|
|
setBackgroundNonSelectionColor(Theme.TREE_BG);
|
|
setBackgroundSelectionColor(Theme.TREE_SELECTION);
|
|
setBorderSelectionColor(Theme.TREE_SELECTION);
|
|
|
|
Object obj = ((DefaultMutableTreeNode) value).getUserObject();
|
|
if (obj instanceof ChannelNode) {
|
|
ChannelNode c = (ChannelNode) obj;
|
|
Spacers.Spacer spacer = Spacers.parse(c.name);
|
|
if (spacer != null) {
|
|
setText(Spacers.render(spacer, 40));
|
|
setIcon(null);
|
|
setForeground(Theme.IDLE_CLIENT);
|
|
setFont(Theme.UI_FONT);
|
|
} else {
|
|
setText(c.name);
|
|
setIcon(iconFor(c));
|
|
setForeground(Theme.CHANNEL_TEXT);
|
|
setFont(Theme.UI_BOLD);
|
|
}
|
|
} else if (obj instanceof ClientEntry) {
|
|
ClientEntry cl = (ClientEntry) obj;
|
|
String label = cl.nickname;
|
|
if (cl.away && !cl.awayMessage.isEmpty()) label += " [" + cl.awayMessage + "]";
|
|
setText(label);
|
|
setIcon(iconFor(cl));
|
|
setForeground(cl.talking ? Theme.TALKING : Theme.TREE_TEXT);
|
|
setFont(cl.talking ? Theme.UI_BOLD : Theme.UI_FONT);
|
|
} else {
|
|
// root / server
|
|
setText(String.valueOf(obj));
|
|
setIcon(Icons.server());
|
|
setForeground(Theme.SERVER_TEXT);
|
|
setFont(Theme.UI_BOLD);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
private ImageIcon iconFor(ChannelNode c) {
|
|
if (c.hasPassword) return Icons.channelLocked(c.subscribed);
|
|
if (c.maxClients >= 0 && c.clients.size() >= c.maxClients) return Icons.channelFull(c.subscribed);
|
|
return Icons.channel(c.subscribed);
|
|
}
|
|
|
|
/** The client's state, in the order the official client gives them priority. */
|
|
private ImageIcon iconFor(ClientEntry cl) {
|
|
if (cl.isQuery()) return Icons.clientQuery();
|
|
if (!cl.outputHardware) return Icons.speakerDisabled();
|
|
if (cl.outputMuted) return Icons.speakerMuted();
|
|
if (!cl.inputHardware) return Icons.micDisabled();
|
|
if (cl.inputMuted) return Icons.micMuted();
|
|
if (cl.away) return Icons.clientAway();
|
|
if (cl.channelCommander) {
|
|
return cl.talking ? Icons.clientCommanderTalking() : Icons.clientCommander();
|
|
}
|
|
if (cl.talking) return Icons.clientTalking();
|
|
return Icons.clientIdle();
|
|
}
|
|
}
|