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:
@@ -6,13 +6,11 @@ import com.ts3client.config.IdentityStore;
|
|||||||
import com.ts3client.config.Settings;
|
import com.ts3client.config.Settings;
|
||||||
import com.ts3client.net.ChannelNode;
|
import com.ts3client.net.ChannelNode;
|
||||||
import com.ts3client.net.ClientEntry;
|
import com.ts3client.net.ClientEntry;
|
||||||
import com.ts3client.net.ConnectionListener;
|
|
||||||
import com.ts3client.net.TeamspeakConnection;
|
import com.ts3client.net.TeamspeakConnection;
|
||||||
import com.ts3client.sound.SoundNotifier;
|
import com.ts3client.sound.SoundNotifier;
|
||||||
import com.ts3client.text.TsLink;
|
import com.ts3client.text.TsLink;
|
||||||
|
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JOptionPane;
|
|
||||||
import javax.swing.JSplitPane;
|
import javax.swing.JSplitPane;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -24,10 +22,12 @@ import java.awt.Component;
|
|||||||
* client keeps several of these side by side; {@link MainFrame} shows one at a
|
* client keeps several of these side by side; {@link MainFrame} shows one at a
|
||||||
* time and owns the toolbar, menus and status bar that act on it.
|
* time and owns the toolbar, menus and status bar that act on it.
|
||||||
*
|
*
|
||||||
* <p>Everything that is per-server lives here: the connection, its microphone
|
* <p>ServerTab itself owns the connection, the panels and this tab's identity
|
||||||
* and speaker mute state, the away/commander flags and the chat history.
|
* (title/status); the mic/away/deafen flags live in {@link ServerTabSelfState},
|
||||||
|
* tree context-menu actions and selection in {@link ServerTabTreeActions}, and
|
||||||
|
* {@link com.ts3client.net.ConnectionListener} callbacks in {@link ServerTabConnectionEvents}.
|
||||||
*/
|
*/
|
||||||
final class ServerTab implements ConnectionListener, ServerTreePanel.Actions {
|
final class ServerTab implements ServerTabConnectionEvents.Listener {
|
||||||
|
|
||||||
private final MainFrame host;
|
private final MainFrame host;
|
||||||
private final Settings settings;
|
private final Settings settings;
|
||||||
@@ -35,6 +35,9 @@ final class ServerTab implements ConnectionListener, ServerTreePanel.Actions {
|
|||||||
|
|
||||||
private final TeamspeakConnection conn;
|
private final TeamspeakConnection conn;
|
||||||
private final GroupIcons groupIcons;
|
private final GroupIcons groupIcons;
|
||||||
|
private final ServerTabConnectionEvents events;
|
||||||
|
private final ServerTabSelfState selfState;
|
||||||
|
private final ServerTabTreeActions treeActions;
|
||||||
private final ServerTreePanel treePanel;
|
private final ServerTreePanel treePanel;
|
||||||
private final ChatPanel chatPanel;
|
private final ChatPanel chatPanel;
|
||||||
private final InfoPanel infoPanel = new InfoPanel();
|
private final InfoPanel infoPanel = new InfoPanel();
|
||||||
@@ -51,36 +54,36 @@ final class ServerTab implements ConnectionListener, ServerTreePanel.Actions {
|
|||||||
/** Identity used for the current connection, so it can be saved into a bookmark. */
|
/** Identity used for the current connection, so it can be saved into a bookmark. */
|
||||||
private String identityId = "";
|
private String identityId = "";
|
||||||
|
|
||||||
private boolean micMuted;
|
|
||||||
private boolean micLocalMuted;
|
|
||||||
private boolean deafened;
|
|
||||||
private boolean away;
|
|
||||||
/** Away message currently published, empty when away carries no message. */
|
|
||||||
private String awayMessage = "";
|
|
||||||
private boolean commander;
|
|
||||||
|
|
||||||
private Object currentSelection;
|
|
||||||
|
|
||||||
ServerTab(MainFrame host, Settings settings, IdentityStore identities, AudioBackend audio,
|
ServerTab(MainFrame host, Settings settings, IdentityStore identities, AudioBackend audio,
|
||||||
SoundNotifier sounds) {
|
SoundNotifier sounds) {
|
||||||
this.host = host;
|
this.host = host;
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
this.identities = identities;
|
this.identities = identities;
|
||||||
this.conn = new TeamspeakConnection(settings, audio, this, sounds);
|
|
||||||
|
// ServerTabConnectionEvents must exist before the connection (which needs a
|
||||||
|
// listener up front), and TeamspeakConnection must exist before the tree/chat
|
||||||
|
// panels and the other tab helpers that read from it — so wiring finishes with
|
||||||
|
// an explicit attach() once everything is built. Nothing fires callbacks before then.
|
||||||
|
this.events = new ServerTabConnectionEvents(host, this, this);
|
||||||
|
this.conn = new TeamspeakConnection(settings, audio, events, sounds);
|
||||||
this.groupIcons = new GroupIcons(conn.getIcons());
|
this.groupIcons = new GroupIcons(conn.getIcons());
|
||||||
this.treePanel = new ServerTreePanel(conn.getModel(), groupIcons, this);
|
this.selfState = new ServerTabSelfState(conn);
|
||||||
this.chatPanel = new ChatPanel();
|
this.chatPanel = new ChatPanel();
|
||||||
|
this.treeActions = new ServerTabTreeActions(host, this, conn, chatPanel, infoPanel);
|
||||||
|
this.treePanel = new ServerTreePanel(conn.getModel(), groupIcons, treeActions);
|
||||||
|
treeActions.attach(treePanel);
|
||||||
|
events.attach(conn, treePanel, chatPanel, selfState, treeActions);
|
||||||
|
|
||||||
chatPanel.setSendHandler(this::onSendChat);
|
chatPanel.setSendHandler(this::onSendChat);
|
||||||
chatPanel.setLinkHandler(new ChatPanel.LinkHandler() {
|
chatPanel.setLinkHandler(new ChatPanel.LinkHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void onClientLink(TsLink.Ref ref, Component source, int x, int y) {
|
public void onClientLink(TsLink.Ref ref, Component source, int x, int y) {
|
||||||
ServerTab.this.onClientLink(ref, source, x, y);
|
treeActions.handleClientLink(ref, source, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onChannelLink(TsLink.Ref ref, Component source, int x, int y) {
|
public void onChannelLink(TsLink.Ref ref, Component source, int x, int y) {
|
||||||
ServerTab.this.onChannelLink(ref, source, x, y);
|
treeActions.handleChannelLink(ref, source, x, y);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
chatPanel.setInputEnabled(false);
|
chatPanel.setInputEnabled(false);
|
||||||
@@ -158,40 +161,32 @@ final class ServerTab implements ConnectionListener, ServerTreePanel.Actions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
boolean isMicMuted() {
|
boolean isMicMuted() {
|
||||||
return micMuted;
|
return selfState.isMicMuted();
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isMicLocalMuted() {
|
boolean isMicLocalMuted() {
|
||||||
return micLocalMuted;
|
return selfState.isMicLocalMuted();
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isDeafened() {
|
boolean isDeafened() {
|
||||||
return deafened;
|
return selfState.isDeafened();
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isAway() {
|
boolean isAway() {
|
||||||
return away;
|
return selfState.isAway();
|
||||||
}
|
}
|
||||||
|
|
||||||
String awayMessage() {
|
String awayMessage() {
|
||||||
return awayMessage;
|
return selfState.awayMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isCommander() {
|
boolean isCommander() {
|
||||||
return commander;
|
return selfState.isCommander();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** What the local client looks like on this server, for the tray icon. */
|
/** What the local client looks like on this server, for the tray icon. */
|
||||||
SelfState selfState() {
|
SelfState selfState() {
|
||||||
if (!conn.isConnected()) return SelfState.DISCONNECTED;
|
return selfState.compute();
|
||||||
if (deafened) return SelfState.DEAFENED;
|
|
||||||
if (micMuted) return SelfState.MIC_MUTED;
|
|
||||||
if (micLocalMuted) return SelfState.MIC_LOCAL_MUTED;
|
|
||||||
if (away) return SelfState.AWAY;
|
|
||||||
ClientEntry self = conn.getModel().getClient(conn.getSelfClientId());
|
|
||||||
boolean talking = self != null && self.talking;
|
|
||||||
if (commander) return talking ? SelfState.COMMANDER_TALKING : SelfState.COMMANDER;
|
|
||||||
return talking ? SelfState.TALKING : SelfState.IDLE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Path of the channel we are in, or empty when not connected. */
|
/** Path of the channel we are in, or empty when not connected. */
|
||||||
@@ -215,8 +210,7 @@ final class ServerTab implements ConnectionListener, ServerTreePanel.Actions {
|
|||||||
title = address + ":" + port;
|
title = address + ":" + port;
|
||||||
chatPanel.appendSystem("Connecting to " + address + ":" + port
|
chatPanel.appendSystem("Connecting to " + address + ":" + port
|
||||||
+ (channel == null || channel.isBlank() ? "" : " (channel \"" + channel + "\")") + " …");
|
+ (channel == null || channel.isBlank() ? "" : " (channel \"" + channel + "\")") + " …");
|
||||||
onStatus("Loading identity…");
|
events.onStatus("Loading identity…");
|
||||||
host.tabUpdated(this);
|
|
||||||
|
|
||||||
// Resolving may have to generate a first identity, so keep it off the EDT.
|
// Resolving may have to generate a first identity, so keep it off the EDT.
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
@@ -225,7 +219,7 @@ final class ServerTab implements ConnectionListener, ServerTreePanel.Actions {
|
|||||||
entry = identities.resolve(settings, identityId);
|
entry = identities.resolve(settings, identityId);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
connecting = false;
|
connecting = false;
|
||||||
onError("Could not load identity: " + e.getMessage());
|
events.onError("Could not load identity: " + e.getMessage());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
@@ -250,23 +244,36 @@ final class ServerTab implements ConnectionListener, ServerTreePanel.Actions {
|
|||||||
if (conn.isConnected()) conn.disconnectBlocking("Leaving");
|
if (conn.isConnected()) conn.disconnectBlocking("Leaving");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- ServerTabConnectionEvents.Listener ----
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setStatus(String status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setTitle(String title) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setConnecting(boolean connecting) {
|
||||||
|
this.connecting = connecting;
|
||||||
|
}
|
||||||
|
|
||||||
// ---- self state ----
|
// ---- self state ----
|
||||||
|
|
||||||
void setMicMuted(boolean muted) {
|
void setMicMuted(boolean muted) {
|
||||||
micMuted = muted;
|
selfState.setMicMuted(muted);
|
||||||
conn.setMicMuted(muted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** TS3's "Local Mic Mute": silences capture without publishing a status change. */
|
/** TS3's "Local Mic Mute": silences capture without publishing a status change. */
|
||||||
void setMicLocalMuted(boolean muted) {
|
void setMicLocalMuted(boolean muted) {
|
||||||
micLocalMuted = muted;
|
selfState.setMicLocalMuted(muted);
|
||||||
conn.setMicLocalMuted(muted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDeafened(boolean deaf) {
|
void setDeafened(boolean deaf) {
|
||||||
deafened = deaf;
|
selfState.setDeafened(deaf);
|
||||||
conn.setDeafened(deaf);
|
|
||||||
if (deaf) micMuted = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Hands the capture device to (or takes it from) this connection. */
|
/** Hands the capture device to (or takes it from) this connection. */
|
||||||
@@ -279,14 +286,11 @@ final class ServerTab implements ConnectionListener, ServerTreePanel.Actions {
|
|||||||
* message outlives coming back, so toggling away again restores it
|
* message outlives coming back, so toggling away again restores it
|
||||||
*/
|
*/
|
||||||
void setAway(boolean away, String message) {
|
void setAway(boolean away, String message) {
|
||||||
this.away = away;
|
selfState.setAway(away, message);
|
||||||
if (message != null) this.awayMessage = message;
|
|
||||||
conn.setAway(away, awayMessage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setCommander(boolean commander) {
|
void setCommander(boolean commander) {
|
||||||
this.commander = commander;
|
selfState.setCommander(commander);
|
||||||
conn.setChannelCommander(commander);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setNickname(String nickname) {
|
void setNickname(String nickname) {
|
||||||
@@ -318,33 +322,6 @@ final class ServerTab implements ConnectionListener, ServerTreePanel.Actions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A client link in the chat log was clicked: show the same menu as the tree does. */
|
|
||||||
private void onClientLink(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);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onChannelLink(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);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String peerName(int clientId) {
|
private String peerName(int clientId) {
|
||||||
ClientEntry c = conn.getModel().getClient(clientId);
|
ClientEntry c = conn.getModel().getClient(clientId);
|
||||||
return c != null ? c.nickname : "Client " + clientId;
|
return c != null ? c.nickname : "Client " + clientId;
|
||||||
@@ -377,129 +354,9 @@ final class ServerTab implements ConnectionListener, ServerTreePanel.Actions {
|
|||||||
return self == null ? null : conn.getModel().getChannel(self.channelId);
|
return self == null ? null : conn.getModel().getChannel(self.channelId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- ServerTreePanel.Actions ----
|
/** Opens the file repository browser for a channel, as the "Browse Files" hotkey/menu action does. */
|
||||||
|
void browseFiles(ChannelNode channel) {
|
||||||
@Override
|
treeActions.browseFiles(channel);
|
||||||
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(this);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private 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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Collapses the info panel to nothing while its content lives in the chat tab, or restores it. */
|
/** Collapses the info panel to nothing while its content lives in the chat tab, or restores it. */
|
||||||
@@ -517,120 +374,4 @@ final class ServerTab implements ConnectionListener, ServerTreePanel.Actions {
|
|||||||
}
|
}
|
||||||
leftColumn.revalidate();
|
leftColumn.revalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- ConnectionListener (marshal to EDT) ----
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStatus(String text) {
|
|
||||||
SwingUtilities.invokeLater(() -> {
|
|
||||||
status = text;
|
|
||||||
host.tabUpdated(this);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onConnected() {
|
|
||||||
SwingUtilities.invokeLater(() -> {
|
|
||||||
connecting = false;
|
|
||||||
treePanel.setSelfClientId(conn.getSelfClientId());
|
|
||||||
micMuted = false;
|
|
||||||
micLocalMuted = false;
|
|
||||||
deafened = false;
|
|
||||||
away = false;
|
|
||||||
awayMessage = "";
|
|
||||||
commander = false;
|
|
||||||
chatPanel.setInputEnabled(true);
|
|
||||||
chatPanel.appendSystem("Connected.");
|
|
||||||
host.tabConnected(this);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDisconnected(String reason) {
|
|
||||||
SwingUtilities.invokeLater(() -> {
|
|
||||||
connecting = false;
|
|
||||||
conn.getModel().clear();
|
|
||||||
treePanel.showDisconnected();
|
|
||||||
currentSelection = null;
|
|
||||||
infoPanel.clear();
|
|
||||||
chatPanel.setInputEnabled(false);
|
|
||||||
chatPanel.closePrivateChats();
|
|
||||||
chatPanel.closeNoteTabs();
|
|
||||||
chatPanel.appendSystem("Disconnected" + (reason == null || reason.isEmpty() ? "." : ": " + reason));
|
|
||||||
host.tabDisconnected(this);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onModelChanged() {
|
|
||||||
SwingUtilities.invokeLater(() -> {
|
|
||||||
treePanel.rebuild();
|
|
||||||
renderInfo();
|
|
||||||
String name = conn.getModel().getServerName();
|
|
||||||
if (conn.isConnected() && name != null && !name.isBlank() && !name.equals(title)) {
|
|
||||||
title = name;
|
|
||||||
host.tabUpdated(this);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onInfoUpdated() {
|
|
||||||
SwingUtilities.invokeLater(this::renderInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onIconsUpdated() {
|
|
||||||
SwingUtilities.invokeLater(() -> {
|
|
||||||
treePanel.refreshRowSizes();
|
|
||||||
renderInfo();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onChat(ChatScope scope, int fromClientId, String fromName, String message) {
|
|
||||||
switch (scope) {
|
|
||||||
case PRIVATE:
|
|
||||||
chatPanel.appendPrivateMessage(fromClientId, fromName, fromClientId, fromName, message);
|
|
||||||
break;
|
|
||||||
case SERVER:
|
|
||||||
chatPanel.appendServerMessage(fromClientId, fromName, message);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
chatPanel.appendChannelMessage(fromClientId, fromName, message);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTalkStateChanged(int clientId, boolean talking) {
|
|
||||||
SwingUtilities.invokeLater(() -> {
|
|
||||||
treePanel.refreshVisual();
|
|
||||||
if (clientId == conn.getSelfClientId()) host.selfStateChanged(this);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(String message) {
|
|
||||||
SwingUtilities.invokeLater(() -> {
|
|
||||||
chatPanel.appendSystem("Error: " + message);
|
|
||||||
status = message;
|
|
||||||
host.tabUpdated(this);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onServerLog(String message) {
|
|
||||||
SwingUtilities.invokeLater(() -> chatPanel.appendServerLog(message));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPoke(String fromName, String message) {
|
|
||||||
SwingUtilities.invokeLater(() -> {
|
|
||||||
chatPanel.appendSystem("You were poked by " + fromName + ": " + message);
|
|
||||||
host.selectTab(this);
|
|
||||||
JOptionPane.showMessageDialog(host, fromName + " poked you:\n\n" + message,
|
|
||||||
"Poke", JOptionPane.INFORMATION_MESSAGE);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,160 @@
|
|||||||
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.ts3client.net.ConnectionListener;
|
||||||
|
import com.ts3client.net.TeamspeakConnection;
|
||||||
|
|
||||||
|
import javax.swing.JOptionPane;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marshals {@link ConnectionListener} callbacks onto the EDT and fans them out to
|
||||||
|
* this tab's views (tree, chat, info panel) and to {@link MainFrame}.
|
||||||
|
*
|
||||||
|
* <p>Constructed before the {@link TeamspeakConnection} it listens to exists — the
|
||||||
|
* connection's constructor needs a listener up front — so {@link #attach} wires the
|
||||||
|
* rest of the tab in once everything else has been built.
|
||||||
|
*/
|
||||||
|
final class ServerTabConnectionEvents implements ConnectionListener {
|
||||||
|
|
||||||
|
/** The handful of {@link ServerTab} fields this class updates but can't reach directly. */
|
||||||
|
interface Listener {
|
||||||
|
void setStatus(String status);
|
||||||
|
|
||||||
|
void setTitle(String title);
|
||||||
|
|
||||||
|
void setConnecting(boolean connecting);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final MainFrame host;
|
||||||
|
private final ServerTab tab;
|
||||||
|
private final Listener listener;
|
||||||
|
|
||||||
|
private TeamspeakConnection conn;
|
||||||
|
private ServerTreePanel treePanel;
|
||||||
|
private ChatPanel chatPanel;
|
||||||
|
private ServerTabSelfState selfState;
|
||||||
|
private ServerTabTreeActions treeActions;
|
||||||
|
|
||||||
|
ServerTabConnectionEvents(MainFrame host, ServerTab tab, Listener listener) {
|
||||||
|
this.host = host;
|
||||||
|
this.tab = tab;
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
void attach(TeamspeakConnection conn, ServerTreePanel treePanel, ChatPanel chatPanel,
|
||||||
|
ServerTabSelfState selfState, ServerTabTreeActions treeActions) {
|
||||||
|
this.conn = conn;
|
||||||
|
this.treePanel = treePanel;
|
||||||
|
this.chatPanel = chatPanel;
|
||||||
|
this.selfState = selfState;
|
||||||
|
this.treeActions = treeActions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStatus(String text) {
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
listener.setStatus(text);
|
||||||
|
host.tabUpdated(tab);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConnected() {
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
listener.setConnecting(false);
|
||||||
|
treePanel.setSelfClientId(conn.getSelfClientId());
|
||||||
|
selfState.resetOnConnect();
|
||||||
|
chatPanel.setInputEnabled(true);
|
||||||
|
chatPanel.appendSystem("Connected.");
|
||||||
|
host.tabConnected(tab);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisconnected(String reason) {
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
listener.setConnecting(false);
|
||||||
|
conn.getModel().clear();
|
||||||
|
treePanel.showDisconnected();
|
||||||
|
treeActions.clearSelection();
|
||||||
|
chatPanel.setInputEnabled(false);
|
||||||
|
chatPanel.closePrivateChats();
|
||||||
|
chatPanel.closeNoteTabs();
|
||||||
|
chatPanel.appendSystem("Disconnected" + (reason == null || reason.isEmpty() ? "." : ": " + reason));
|
||||||
|
host.tabDisconnected(tab);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onModelChanged() {
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
treePanel.rebuild();
|
||||||
|
treeActions.renderInfo();
|
||||||
|
String name = conn.getModel().getServerName();
|
||||||
|
if (conn.isConnected() && name != null && !name.isBlank() && !name.equals(tab.title())) {
|
||||||
|
listener.setTitle(name);
|
||||||
|
host.tabUpdated(tab);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onInfoUpdated() {
|
||||||
|
SwingUtilities.invokeLater(treeActions::renderInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onIconsUpdated() {
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
treePanel.refreshRowSizes();
|
||||||
|
treeActions.renderInfo();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChat(ChatScope scope, int fromClientId, String fromName, String message) {
|
||||||
|
switch (scope) {
|
||||||
|
case PRIVATE:
|
||||||
|
chatPanel.appendPrivateMessage(fromClientId, fromName, fromClientId, fromName, message);
|
||||||
|
break;
|
||||||
|
case SERVER:
|
||||||
|
chatPanel.appendServerMessage(fromClientId, fromName, message);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
chatPanel.appendChannelMessage(fromClientId, fromName, message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTalkStateChanged(int clientId, boolean talking) {
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
treePanel.refreshVisual();
|
||||||
|
if (clientId == conn.getSelfClientId()) host.selfStateChanged(tab);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(String message) {
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
chatPanel.appendSystem("Error: " + message);
|
||||||
|
listener.setStatus(message);
|
||||||
|
host.tabUpdated(tab);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServerLog(String message) {
|
||||||
|
SwingUtilities.invokeLater(() -> chatPanel.appendServerLog(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPoke(String fromName, String message) {
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
chatPanel.appendSystem("You were poked by " + fromName + ": " + message);
|
||||||
|
host.selectTab(tab);
|
||||||
|
JOptionPane.showMessageDialog(host, fromName + " poked you:\n\n" + message,
|
||||||
|
"Poke", JOptionPane.INFORMATION_MESSAGE);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import com.ts3client.net.ClientEntry;
|
||||||
|
import com.ts3client.net.TeamspeakConnection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mic/speaker/away/commander flags for one connection, plus forwarding them into
|
||||||
|
* {@link TeamspeakConnection}. Split out of {@link ServerTab} because the flags
|
||||||
|
* are reset together in one place (on (re)connect) and read from several
|
||||||
|
* (tray icon, toolbar, menu checkmarks).
|
||||||
|
*/
|
||||||
|
final class ServerTabSelfState {
|
||||||
|
|
||||||
|
private final TeamspeakConnection conn;
|
||||||
|
|
||||||
|
private boolean micMuted;
|
||||||
|
private boolean micLocalMuted;
|
||||||
|
private boolean deafened;
|
||||||
|
private boolean away;
|
||||||
|
/** Away message currently published, empty when away carries no message. */
|
||||||
|
private String awayMessage = "";
|
||||||
|
private boolean commander;
|
||||||
|
|
||||||
|
ServerTabSelfState(TeamspeakConnection conn) {
|
||||||
|
this.conn = conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isMicMuted() {
|
||||||
|
return micMuted;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isMicLocalMuted() {
|
||||||
|
return micLocalMuted;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isDeafened() {
|
||||||
|
return deafened;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isAway() {
|
||||||
|
return away;
|
||||||
|
}
|
||||||
|
|
||||||
|
String awayMessage() {
|
||||||
|
return awayMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isCommander() {
|
||||||
|
return commander;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setMicMuted(boolean muted) {
|
||||||
|
micMuted = muted;
|
||||||
|
conn.setMicMuted(muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** TS3's "Local Mic Mute": silences capture without publishing a status change. */
|
||||||
|
void setMicLocalMuted(boolean muted) {
|
||||||
|
micLocalMuted = muted;
|
||||||
|
conn.setMicLocalMuted(muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDeafened(boolean deaf) {
|
||||||
|
deafened = deaf;
|
||||||
|
conn.setDeafened(deaf);
|
||||||
|
if (deaf) micMuted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param message the away message, or null to keep the one already set — the
|
||||||
|
* message outlives coming back, so toggling away again restores it
|
||||||
|
*/
|
||||||
|
void setAway(boolean away, String message) {
|
||||||
|
this.away = away;
|
||||||
|
if (message != null) this.awayMessage = message;
|
||||||
|
conn.setAway(away, awayMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCommander(boolean commander) {
|
||||||
|
this.commander = commander;
|
||||||
|
conn.setChannelCommander(commander);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Clears all flags on a fresh connection; the server starts us out clean, so nothing to publish. */
|
||||||
|
void resetOnConnect() {
|
||||||
|
micMuted = false;
|
||||||
|
micLocalMuted = false;
|
||||||
|
deafened = false;
|
||||||
|
away = false;
|
||||||
|
awayMessage = "";
|
||||||
|
commander = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** What the local client looks like on this server, for the tray icon. */
|
||||||
|
SelfState compute() {
|
||||||
|
if (!conn.isConnected()) return SelfState.DISCONNECTED;
|
||||||
|
if (deafened) return SelfState.DEAFENED;
|
||||||
|
if (micMuted) return SelfState.MIC_MUTED;
|
||||||
|
if (micLocalMuted) return SelfState.MIC_LOCAL_MUTED;
|
||||||
|
if (away) return SelfState.AWAY;
|
||||||
|
ClientEntry self = conn.getModel().getClient(conn.getSelfClientId());
|
||||||
|
boolean talking = self != null && self.talking;
|
||||||
|
if (commander) return talking ? SelfState.COMMANDER_TALKING : SelfState.COMMANDER;
|
||||||
|
return talking ? SelfState.TALKING : SelfState.IDLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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