Kick and ban clients from the context menu
The client menu gains "Kick Client from Channel", "Kick Client from Server" and "Ban Client", each asking for a reason first. Kicks use a shared prompt that enforces the protocol's 40-character limit; the ban dialog adds a duration, whose unit dropdown ends in "Permanent" — the zero-length ban the server understands. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
package com.ts3client.ui;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.event.AncestorEvent;
|
||||
import javax.swing.event.AncestorListener;
|
||||
import javax.swing.text.AttributeSet;
|
||||
import javax.swing.text.BadLocationException;
|
||||
import javax.swing.text.PlainDocument;
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
|
||||
/**
|
||||
* Asks for the reason text that goes with a moderation action. The server caps
|
||||
* such messages, so the field refuses anything longer.
|
||||
*/
|
||||
final class ReasonDialog {
|
||||
|
||||
/** The longest reason the protocol carries with a kick. */
|
||||
static final int KICK_REASON_LIMIT = 40;
|
||||
|
||||
private ReasonDialog() {
|
||||
}
|
||||
|
||||
/** @return the reason (possibly empty), or {@code null} when the dialog was cancelled */
|
||||
static String prompt(Component owner, String title, String message, int maxLength) {
|
||||
JTextField field = reasonField(maxLength);
|
||||
focusWhenShown(field);
|
||||
|
||||
JPanel panel = new JPanel();
|
||||
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
|
||||
panel.add(label(message));
|
||||
panel.add(field);
|
||||
|
||||
int result = JOptionPane.showConfirmDialog(owner, panel, title,
|
||||
JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);
|
||||
return result == JOptionPane.OK_OPTION ? field.getText().trim() : null;
|
||||
}
|
||||
|
||||
static JTextField reasonField(int maxLength) {
|
||||
JTextField field = new JTextField(24);
|
||||
field.setDocument(new LimitedDocument(maxLength));
|
||||
field.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||
fixHeight(field);
|
||||
return field;
|
||||
}
|
||||
|
||||
/** A left-aligned label with a little air below it, for use in a box layout. */
|
||||
static JLabel label(String text) {
|
||||
JLabel label = new JLabel(text);
|
||||
label.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||
label.setBorder(BorderFactory.createEmptyBorder(0, 0, 4, 0));
|
||||
return label;
|
||||
}
|
||||
|
||||
/** Keeps a component from stretching taller than it needs in a box layout. */
|
||||
static void fixHeight(Component c) {
|
||||
c.setMaximumSize(new Dimension(Integer.MAX_VALUE, c.getPreferredSize().height));
|
||||
}
|
||||
|
||||
/** Puts the caret in a field as soon as its dialog appears. */
|
||||
static void focusWhenShown(JComponent field) {
|
||||
field.addAncestorListener(new AncestorListener() {
|
||||
@Override
|
||||
public void ancestorAdded(AncestorEvent event) {
|
||||
field.requestFocusInWindow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ancestorRemoved(AncestorEvent event) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ancestorMoved(AncestorEvent event) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** A document that silently drops anything past its length limit. */
|
||||
private static final class LimitedDocument extends PlainDocument {
|
||||
|
||||
private final int limit;
|
||||
|
||||
LimitedDocument(int limit) {
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertString(int offset, String text, AttributeSet attrs) throws BadLocationException {
|
||||
if (text == null) return;
|
||||
int room = limit - getLength();
|
||||
if (room <= 0) return;
|
||||
super.insertString(offset, text.length() > room ? text.substring(0, room) : text, attrs);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user