package com.ts3client.ui; import com.ts3client.net.ChannelNode; import com.ts3client.net.ClientEntry; import com.ts3client.net.Group; 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. * *
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 final GroupIcons groupIcons; private ServerTreePanel treePanel; private Object currentSelection; /** The one open {@link ServerGroupsDialog}, if any, refreshed whenever the model changes. */ private ServerGroupsDialog groupsDialog; ServerTabTreeActions(MainFrame host, ServerTab tab, TeamspeakConnection conn, ChatPanel chatPanel, InfoPanel infoPanel, GroupIcons groupIcons) { this.host = host; this.tab = tab; this.conn = conn; this.chatPanel = chatPanel; this.infoPanel = infoPanel; this.groupIcons = groupIcons; } void attach(ServerTreePanel treePanel) { this.treePanel = treePanel; } /** Called by {@link ServerTabConnectionEvents} whenever the model changes, to live-refresh an open dialog. */ void refreshGroupsDialog() { if (groupsDialog != null) groupsDialog.refresh(); } /** 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(), conn.getModel(), groupIcons, 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 setClientServerGroup(ClientEntry client, Group group, boolean assign) { if (conn.isConnected()) conn.setClientServerGroup(client.databaseId, group.id, assign); } @Override public void setClientChannelGroup(ClientEntry client, Group group) { if (conn.isConnected()) conn.setClientChannelGroup(client.databaseId, client.channelId, group.id); } @Override public void showServerGroupsDialog(ClientEntry client) { if (!conn.isConnected()) return; groupsDialog = new ServerGroupsDialog(host, conn, groupIcons, client, this); groupsDialog.setVisible(true); groupsDialog = 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); } } }