Initial commit: TS3J TeamSpeak 3 Java client
Swing desktop client (core/desktop/swing Maven modules) built on the ts3j protocol library, included as a submodule. Native Opus voice with voice-activation detection, push-to-talk, and audio pre-processing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
package com.ts3client.ui;
|
||||
|
||||
import com.ts3client.config.Bookmark;
|
||||
import com.ts3client.config.Bookmarks;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.DefaultListModel;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPasswordField;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextField;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Frame;
|
||||
import java.awt.GridLayout;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/** Manage saved servers: add, edit, remove and quick-connect. */
|
||||
public final class BookmarksDialog extends JDialog {
|
||||
|
||||
private final Bookmarks bookmarks;
|
||||
private final Consumer<Bookmark> onConnect;
|
||||
private final Runnable onChanged;
|
||||
private final DefaultListModel<Bookmark> listModel = new DefaultListModel<>();
|
||||
private final JList<Bookmark> list = new JList<>(listModel);
|
||||
|
||||
public BookmarksDialog(Frame owner, Bookmarks bookmarks, Consumer<Bookmark> onConnect, Runnable onChanged) {
|
||||
super(owner, "Manage Bookmarks", true);
|
||||
this.bookmarks = bookmarks;
|
||||
this.onConnect = onConnect;
|
||||
this.onChanged = onChanged;
|
||||
|
||||
reload();
|
||||
list.setVisibleRowCount(10);
|
||||
|
||||
JScrollPane scroll = new JScrollPane(list);
|
||||
scroll.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
|
||||
|
||||
JPanel buttons = new JPanel();
|
||||
buttons.setLayout(new BoxLayout(buttons, BoxLayout.Y_AXIS));
|
||||
buttons.setBorder(BorderFactory.createEmptyBorder(8, 0, 8, 8));
|
||||
addButton(buttons, "Connect", this::connectSelected);
|
||||
addButton(buttons, "Add…", this::addBookmark);
|
||||
addButton(buttons, "Edit…", this::editSelected);
|
||||
addButton(buttons, "Remove", this::removeSelected);
|
||||
buttons.add(Box.createVerticalGlue());
|
||||
addButton(buttons, "Close", this::dispose);
|
||||
|
||||
getContentPane().setLayout(new BorderLayout());
|
||||
getContentPane().add(scroll, BorderLayout.CENTER);
|
||||
getContentPane().add(buttons, BorderLayout.EAST);
|
||||
|
||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||
setSize(new Dimension(420, 300));
|
||||
setLocationRelativeTo(owner);
|
||||
}
|
||||
|
||||
private void addButton(JPanel panel, String text, Runnable action) {
|
||||
JButton b = new JButton(text);
|
||||
b.setAlignmentX(LEFT_ALIGNMENT);
|
||||
b.setMaximumSize(new Dimension(Integer.MAX_VALUE, b.getPreferredSize().height));
|
||||
b.addActionListener(e -> action.run());
|
||||
panel.add(b);
|
||||
panel.add(Box.createVerticalStrut(4));
|
||||
}
|
||||
|
||||
private void reload() {
|
||||
listModel.clear();
|
||||
for (Bookmark b : bookmarks.all()) listModel.addElement(b);
|
||||
}
|
||||
|
||||
private void connectSelected() {
|
||||
Bookmark b = list.getSelectedValue();
|
||||
if (b != null) {
|
||||
onConnect.accept(b);
|
||||
dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void addBookmark() {
|
||||
Bookmark b = new Bookmark("", "", 9987, System.getProperty("user.name", "TS3J User"), "");
|
||||
if (promptBookmark(b)) {
|
||||
bookmarks.add(b);
|
||||
persistAndRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
private void editSelected() {
|
||||
Bookmark b = list.getSelectedValue();
|
||||
if (b != null && promptBookmark(b)) {
|
||||
persistAndRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
private void removeSelected() {
|
||||
int idx = list.getSelectedIndex();
|
||||
if (idx >= 0) {
|
||||
bookmarks.remove(idx);
|
||||
persistAndRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
private void persistAndRefresh() {
|
||||
bookmarks.save();
|
||||
reload();
|
||||
if (onChanged != null) onChanged.run();
|
||||
}
|
||||
|
||||
/** Modal add/edit form. Mutates {@code b} and returns whether the user confirmed. */
|
||||
private boolean promptBookmark(Bookmark b) {
|
||||
JTextField label = new JTextField(b.label == null ? "" : b.label);
|
||||
JTextField address = new JTextField(b.address == null ? "" : b.address);
|
||||
JTextField port = new JTextField(Integer.toString(b.port));
|
||||
JTextField nick = new JTextField(b.nickname == null ? "" : b.nickname);
|
||||
JPasswordField password = new JPasswordField(b.password == null ? "" : b.password);
|
||||
|
||||
JPanel form = new JPanel(new GridLayout(0, 1, 0, 2));
|
||||
form.add(new JLabel("Label:"));
|
||||
form.add(label);
|
||||
form.add(new JLabel("Address:"));
|
||||
form.add(address);
|
||||
form.add(new JLabel("Port:"));
|
||||
form.add(port);
|
||||
form.add(new JLabel("Nickname:"));
|
||||
form.add(nick);
|
||||
form.add(new JLabel("Password (optional):"));
|
||||
form.add(password);
|
||||
|
||||
int result = JOptionPane.showConfirmDialog(this, form,
|
||||
"Bookmark", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);
|
||||
if (result != JOptionPane.OK_OPTION) return false;
|
||||
|
||||
if (address.getText().trim().isEmpty()) {
|
||||
JOptionPane.showMessageDialog(this, "Address is required.");
|
||||
return false;
|
||||
}
|
||||
b.label = label.getText().trim();
|
||||
b.address = address.getText().trim();
|
||||
try {
|
||||
b.port = Integer.parseInt(port.getText().trim());
|
||||
} catch (NumberFormatException e) {
|
||||
b.port = 9987;
|
||||
}
|
||||
b.nickname = nick.getText().trim();
|
||||
b.password = new String(password.getPassword());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user