package com.ts3client.ui; import com.ts3client.net.ChannelNode; import com.ts3client.net.ClientEntry; import com.ts3client.net.ConnectionListener; import com.ts3client.net.TeamspeakConnection; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import java.util.List; /** * Marshals {@link ConnectionListener} callbacks onto the EDT and fans them out to * this tab's views (tree, chat, info panel) and to {@link MainFrame}. * *

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; } /** Name of the channel we are in, or null while that is not known yet. */ private String currentChannelName() { ClientEntry self = conn.getModel().getClient(conn.getSelfClientId()); if (self == null) return null; ChannelNode ch = conn.getModel().getChannel(self.channelId); return ch == null ? null : ch.name; } @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.setServerName(null); chatPanel.setChannelName(null); chatPanel.appendSystem("Disconnected" + (reason == null || reason.isEmpty() ? "." : ": " + reason)); tab.banListServerChanged(false); host.tabDisconnected(tab); }); } @Override public void onModelChanged() { SwingUtilities.invokeLater(() -> { treePanel.rebuild(); treeActions.renderInfo(); treeActions.refreshGroupsDialog(); String name = conn.getModel().getServerName(); if (conn.isConnected() && name != null && !name.isBlank() && !name.equals(tab.title())) { listener.setTitle(name); host.tabUpdated(tab); tab.banListServerChanged(true); } if (conn.isConnected()) { chatPanel.setServerName(name); chatPanel.setChannelName(currentChannelName()); } }); } @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 fromUniqueId, String nickname, String message) { String fromName = conn.getContacts().displayName(fromUniqueId, nickname); SwingUtilities.invokeLater(() -> { switch (scope) { case PRIVATE: chatPanel.appendPrivateMessage(fromClientId, fromUniqueId, fromName, fromClientId, fromUniqueId, fromName, message); break; case SERVER: chatPanel.appendServerMessage(fromClientId, fromUniqueId, fromName, message); break; default: chatPanel.appendChannelMessage(fromClientId, fromUniqueId, fromName, message); break; } }); } @Override public void onChatHistory(ChatScope scope, List htmlEntries) { SwingUtilities.invokeLater(() -> { if (scope == ChatScope.SERVER) chatPanel.setServerHistory(htmlEntries); else if (scope == ChatScope.CHANNEL) chatPanel.setChannelHistory(htmlEntries); }); } @Override public void onPrivateChatHistory(int clientId, String nickname, List htmlEntries) { SwingUtilities.invokeLater(() -> chatPanel.setPrivateHistory(clientId, nickname, htmlEntries)); } @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 onServerLogHtml(String html) { SwingUtilities.invokeLater(() -> chatPanel.appendServerLogHtml(html)); } @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); }); } }