Add Set Server/Channel Group context menus and dialog
Lets moderators assign or revoke server and channel groups for a client directly from its context menu, with per-group checkboxes gated by the client's actual TS3 permissions (i_group_needed_member_ add/remove_power vs. each group's n_member_addp/removep, including TS3's -1 "unlimited" sentinel). A "Server Groups Dialog..." entry shows every server group, graying out ones the client can't touch. Group membership changes now update ClientEntry locally instead of only logging them, so the checkboxes and dialog reflect a change immediately instead of needing a reconnect. The server's default channel group (e.g. "Guest") is hidden from the channel-group menu since it's not something you assign directly.
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
package com.ts3client.ui;
|
||||
|
||||
import com.ts3client.net.ClientEntry;
|
||||
import com.ts3client.net.Group;
|
||||
import com.ts3client.net.TeamspeakConnection;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
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(new Dimension(280, 360));
|
||||
scroll.getVerticalScrollBar().setUnitIncrement(16);
|
||||
|
||||
header.setBorder(BorderFactory.createEmptyBorder(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);
|
||||
|
||||
int addPower = conn.getModel().selfPermissionValue("i_group_needed_member_add_power");
|
||||
int removePower = conn.getModel().selfPermissionValue("i_group_needed_member_remove_power");
|
||||
List<Group> groups = conn.getModel().allServerGroups();
|
||||
|
||||
list.removeAll();
|
||||
for (Group g : groups) {
|
||||
boolean assigned = contains(client.serverGroupIds, g.id);
|
||||
boolean allowed = ClientMenu.canAssign(g, assigned, addPower, removePower);
|
||||
|
||||
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())));
|
||||
}
|
||||
|
||||
private static boolean contains(int[] ids, int id) {
|
||||
for (int i : ids) if (i == id) return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user