Split ServerTab into focused collaborator classes
Extract mic/speaker/away/commander state handling into ServerTabSelfState, ServerTreePanel.Actions delegation (context menus, drag-drop, link clicks) into ServerTabTreeActions, and ConnectionListener event marshaling onto the EDT into ServerTabConnectionEvents. ServerTab keeps its full public API (636 -> 377 lines); no behavior change. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
package com.ts3client.ui;
|
||||
|
||||
import com.ts3client.net.ChannelNode;
|
||||
import com.ts3client.net.ClientEntry;
|
||||
import com.ts3client.net.TeamspeakConnection;
|
||||
import com.ts3client.text.TsLink;
|
||||
|
||||
import javax.swing.JOptionPane;
|
||||
import java.awt.Component;
|
||||
|
||||
/**
|
||||
* Context-menu and drag/drop actions for one server's tree, plus tracking which
|
||||
* node is selected so the info panel stays in sync. Also handles client/channel
|
||||
* links clicked in the chat log, which open the same menus as the tree does.
|
||||
*
|
||||
* <p>Constructed before the {@link ServerTreePanel} it drives exists — the tree's
|
||||
* constructor needs an {@link ServerTreePanel.Actions} up front — so {@link #attach}
|
||||
* wires the tree back in once it has been built.
|
||||
*/
|
||||
final class ServerTabTreeActions implements ServerTreePanel.Actions {
|
||||
|
||||
private final MainFrame host;
|
||||
private final ServerTab tab;
|
||||
private final TeamspeakConnection conn;
|
||||
private final ChatPanel chatPanel;
|
||||
private final InfoPanel infoPanel;
|
||||
private ServerTreePanel treePanel;
|
||||
|
||||
private Object currentSelection;
|
||||
|
||||
ServerTabTreeActions(MainFrame host, ServerTab tab, TeamspeakConnection conn,
|
||||
ChatPanel chatPanel, InfoPanel infoPanel) {
|
||||
this.host = host;
|
||||
this.tab = tab;
|
||||
this.conn = conn;
|
||||
this.chatPanel = chatPanel;
|
||||
this.infoPanel = infoPanel;
|
||||
}
|
||||
|
||||
void attach(ServerTreePanel treePanel) {
|
||||
this.treePanel = treePanel;
|
||||
}
|
||||
|
||||
/** Refreshes the info panel for whatever is currently selected (or clears it). */
|
||||
void renderInfo() {
|
||||
Object sel = currentSelection;
|
||||
if (sel instanceof ChannelNode) {
|
||||
infoPanel.showChannel((ChannelNode) sel);
|
||||
} else if (sel instanceof ClientEntry) {
|
||||
infoPanel.showClient((ClientEntry) sel, conn.getModel(), conn.getIcons());
|
||||
} else {
|
||||
infoPanel.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/** Drops the selection on disconnect, since the model it points into is gone. */
|
||||
void clearSelection() {
|
||||
currentSelection = null;
|
||||
infoPanel.clear();
|
||||
}
|
||||
|
||||
/** A client link in the chat log was clicked: show the same menu as the tree does. */
|
||||
void handleClientLink(TsLink.Ref ref, Component source, int x, int y) {
|
||||
ClientEntry client = conn.getModel().getClient(ref.id);
|
||||
// The id is only valid for the session the link was made in; fall back to
|
||||
// the unique id (and finally the nickname) so older links still resolve.
|
||||
if (client == null || (!ref.uniqueId.isEmpty() && !ref.uniqueId.equals(client.uniqueId))) {
|
||||
ClientEntry byUid = ref.uniqueId.isEmpty() ? null
|
||||
: conn.getModel().findClientByUniqueId(ref.uniqueId);
|
||||
if (byUid == null && !ref.name.isEmpty()) byUid = conn.getModel().findClientByName(ref.name);
|
||||
if (byUid != null) client = byUid;
|
||||
}
|
||||
if (client == null) {
|
||||
chatPanel.appendSystem("That client is no longer on the server.");
|
||||
return;
|
||||
}
|
||||
ClientMenu.build(client, client.id == conn.getSelfClientId(), this).show(source, x, y);
|
||||
}
|
||||
|
||||
void handleChannelLink(TsLink.Ref ref, Component source, int x, int y) {
|
||||
ChannelNode channel = conn.getModel().getChannel(ref.id);
|
||||
if (channel == null) {
|
||||
chatPanel.appendSystem("That channel no longer exists.");
|
||||
return;
|
||||
}
|
||||
ChannelMenu.build(channel, this).show(source, x, y);
|
||||
}
|
||||
|
||||
// ---- ServerTreePanel.Actions ----
|
||||
|
||||
@Override
|
||||
public void joinChannel(int channelId) {
|
||||
if (conn.isConnected()) conn.joinChannel(channelId, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveClientToChannel(ClientEntry client, ChannelNode target) {
|
||||
if (!conn.isConnected()) return;
|
||||
if (client.id == conn.getSelfClientId()) {
|
||||
conn.joinChannel(target.id, null);
|
||||
} else {
|
||||
conn.moveClient(client.id, target.id, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveChannel(ChannelNode channel, int newParentId, int orderPredecessorId) {
|
||||
if (conn.isConnected()) conn.moveChannel(channel.id, newParentId, orderPredecessorId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openPrivateChat(ClientEntry client) {
|
||||
chatPanel.openPrivateChat(client.id, client.nickname);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pokeClient(ClientEntry client) {
|
||||
String msg = JOptionPane.showInputDialog(host, "Poke message for " + client.nickname + ":", "Poke!");
|
||||
if (msg != null) conn.poke(client.id, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void kickClientFromChannel(ClientEntry client) {
|
||||
String reason = kickReason("Kick Client from Channel", client);
|
||||
if (reason != null) conn.kickFromChannel(client.id, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void kickClientFromServer(ClientEntry client) {
|
||||
String reason = kickReason("Kick Client from Server", client);
|
||||
if (reason != null) conn.kickFromServer(client.id, reason);
|
||||
}
|
||||
|
||||
private String kickReason(String title, ClientEntry client) {
|
||||
if (!conn.isConnected()) return null;
|
||||
return ReasonDialog.prompt(host, title, "Reason for kicking " + client.nickname + ":",
|
||||
ReasonDialog.KICK_REASON_LIMIT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void banClient(ClientEntry client) {
|
||||
if (!conn.isConnected()) return;
|
||||
BanDialog dialog = new BanDialog(host, client.nickname);
|
||||
dialog.setVisible(true);
|
||||
if (dialog.isConfirmed()) conn.banClient(client.id, dialog.getSeconds(), dialog.getReason());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toggleClientMute(ClientEntry client) {
|
||||
if (conn.getPlayback() == null) return;
|
||||
boolean now = !conn.getPlayback().isClientMuted(client.id);
|
||||
conn.getPlayback().setClientMuted(client.id, now);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findClientInTree(ClientEntry client) {
|
||||
host.selectTab(tab);
|
||||
treePanel.selectClient(client.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showConnectionInfo(ClientEntry client) {
|
||||
if (!conn.isConnected()) return;
|
||||
new ConnectionInfoDialog(host, conn, client.id, client.nickname).setVisible(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveClientToOwnChannel(ClientEntry client) {
|
||||
if (!conn.isConnected() || client.id == conn.getSelfClientId()) return;
|
||||
ClientEntry self = conn.getModel().getClient(conn.getSelfClientId());
|
||||
if (self != null) conn.moveClient(client.id, self.channelId, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChannelSubscribed(ChannelNode channel, boolean family, boolean subscribed) {
|
||||
if (conn.isConnected()) conn.setChannelsSubscribed(channel.familyIds(family), subscribed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void browseFiles(ChannelNode channel) {
|
||||
if (!conn.canTransferFiles()) return;
|
||||
new FileBrowserDialog(host, conn, channel).setVisible(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClientLocallyMuted(int clientId) {
|
||||
return conn.getPlayback() != null && conn.getPlayback().isClientMuted(clientId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectionChanged(Object userObject) {
|
||||
currentSelection = userObject;
|
||||
renderInfo();
|
||||
if (!conn.isConnected()) return;
|
||||
if (userObject instanceof ChannelNode) {
|
||||
ChannelNode ch = (ChannelNode) userObject;
|
||||
if (!ch.descriptionLoaded) conn.requestChannelInfo(ch.id);
|
||||
} else if (userObject instanceof ClientEntry) {
|
||||
conn.requestClientInfo(((ClientEntry) userObject).id);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user