Files
ts3java/ts3-client/swing/src/main/java/com/ts3client/ui/ServerGroupsDialog.java
ericek111 fecb2ba8f4 Answer what the local client may do from the model
The Swing client menu decided on its own whether a group can be
(un)assigned and whether a client's channel can be joined, and the
server groups dialog borrowed the menu's check. These are rules about
the server, not about menus, so ServerModel answers them now and any
frontend asks it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 08:10:54 +00:00

112 lines
3.9 KiB
Java

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<ImageIcon, ImageIcon> 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<Group> 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())));
}
}