package com.ts3client.ui; import com.formdev.flatlaf.util.ScaledEmptyBorder; import com.formdev.flatlaf.util.UIScale; import com.ts3client.net.ClientEntry; import com.ts3client.net.Group; import com.ts3client.net.TeamspeakConnection; import javax.swing.BoxLayout; import javax.swing.GrayFilter; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.SwingConstants; import java.awt.BorderLayout; import java.awt.Dimension; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Every regular server group on the server, as a scrollable checkbox list. Groups * the local client has no power to (un)assign are shown too, so the client's full * membership is visible, but greyed out and disabled. */ final class ServerGroupsDialog extends JDialog { private final TeamspeakConnection conn; private final GroupIcons groupIcons; private final int clientId; private final ServerTreePanel.Actions actions; private final JPanel list = new JPanel(); private final JLabel header = new JLabel(); /** Cached grayscale icons, keyed by the source icon's identity. */ private final Map grayscale = new HashMap<>(); ServerGroupsDialog(MainFrame owner, TeamspeakConnection conn, GroupIcons groupIcons, ClientEntry client, ServerTreePanel.Actions actions) { super(owner, "Server Groups", true); this.conn = conn; this.groupIcons = groupIcons; this.clientId = client.id; this.actions = actions; list.setLayout(new BoxLayout(list, BoxLayout.Y_AXIS)); JScrollPane scroll = new JScrollPane(list); scroll.setPreferredSize(UIScale.scale(new Dimension(280, 360))); scroll.getVerticalScrollBar().setUnitIncrement(16); header.setBorder(new ScaledEmptyBorder(8, 10, 4, 10)); header.setHorizontalAlignment(SwingConstants.LEFT); JButton close = new JButton("Close"); close.addActionListener(a -> dispose()); JPanel buttons = new JPanel(); buttons.add(close); getRootPane().setDefaultButton(close); getContentPane().setLayout(new BorderLayout()); getContentPane().add(header, BorderLayout.NORTH); getContentPane().add(scroll, BorderLayout.CENTER); getContentPane().add(buttons, BorderLayout.SOUTH); Dialogs.closeOnEscape(this); setDefaultCloseOperation(DISPOSE_ON_CLOSE); refresh(); pack(); setLocationRelativeTo(owner); } /** Rebuilds the list from the current model state; closes the dialog if the client left. */ void refresh() { ClientEntry client = conn.getModel().getClient(clientId); if (client == null) { dispose(); return; } header.setText(client.nickname); List groups = conn.getModel().allServerGroups(); list.removeAll(); for (Group g : groups) { boolean assigned = client.inServerGroup(g.id); boolean allowed = conn.getModel().canToggleGroup(g, assigned); ImageIcon icon = groupIcons.iconOf(g); JCheckBox box = new JCheckBox(g.name, allowed ? icon : grayscale(icon)); box.setSelected(assigned); box.setEnabled(allowed); box.setAlignmentX(0f); if (allowed) { box.addActionListener(a -> actions.setClientServerGroup(client, g, box.isSelected())); } list.add(box); } list.revalidate(); list.repaint(); } private ImageIcon grayscale(ImageIcon icon) { if (icon == null) return null; return grayscale.computeIfAbsent(icon, i -> new ImageIcon(GrayFilter.createDisabledImage(i.getImage()))); } }