Add the contact list (friends, blocked, neutral)

Mirrors the TeamSpeak 3 client's Contacts: clients can be filed as friend,
blocked or neutral from their context menu or by dragging them into the
Contacts window, given a custom and phonetic nickname, shown under either
name or both, and per contact muted automatically, ignored in server/channel
or private chat and pokes, have their away message hidden and whispers
allowed or denied. Friends are green and blocked clients red in the tree
(optional), new contacts start from per-type defaults ("Set Defaults"), and
the global whisper policy lives in Options -> Contacts. "Change Volume..."
adds a per-client dB gain that a contact entry remembers.

Contacts are stored in ~/.ts3jclient/contacts.txt using the exact record
format the official client keeps in the Contacts table of its settings.db
(field names and codes recovered from the binary), so entries move between
the two verbatim. A small read-only pure-Java SQLite reader imports the
official client's list on first run and on demand, without a driver.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-18 07:50:58 +00:00
parent 22e0304f25
commit f409f500fd
33 changed files with 2474 additions and 23 deletions

View File

@@ -1,5 +1,6 @@
package com.ts3client.ui;
import com.ts3client.contacts.ContactType;
import com.ts3client.net.ChannelNode;
import com.ts3client.net.ClientEntry;
import com.ts3client.net.Group;
@@ -50,6 +51,11 @@ final class ClientMenu {
Icons.of(muted ? "PLAYER_ON" : "INPUT_MUTED"));
mute.addActionListener(a -> actions.toggleClientMute(client));
menu.add(mute);
JMenuItem volume = new JMenuItem("Change Volume…", Icons.of("VOLUME"));
volume.addActionListener(a -> actions.changeClientVolume(client));
menu.add(volume);
menu.addSeparator();
addContactItems(menu, client, actions);
} else {
JMenuItem me = new JMenuItem("This is you", Icons.of("PLAYER_OFF"));
me.setEnabled(false);
@@ -84,6 +90,30 @@ final class ClientMenu {
return menu;
}
/**
* The contact entries, as TeamSpeak offers them: a client not on the list can be
* added as a friend or blocked; one on it can be edited or removed.
*/
private static void addContactItems(JPopupMenu menu, ClientEntry client, ServerTreePanel.Actions actions) {
ContactType type = actions.contactTypeOf(client);
if (type == null) {
JMenuItem friend = new JMenuItem("Add as Friend", Icons.of("ADD_FRIEND"));
friend.addActionListener(a -> actions.addContact(client, ContactType.FRIEND));
menu.add(friend);
JMenuItem blocked = new JMenuItem("Add as Blocked", Icons.of("ADD_FOE"));
blocked.addActionListener(a -> actions.addContact(client, ContactType.BLOCKED));
menu.add(blocked);
return;
}
JMenuItem edit = new JMenuItem("Edit in Contacts", Icons.of("EDIT_FRIEND_FOE_STATUS"));
edit.addActionListener(a -> actions.editContact(client));
menu.add(edit);
JMenuItem remove = new JMenuItem("Remove from Contacts",
Icons.of(type == ContactType.BLOCKED ? "REMOVE_FOE" : "REMOVE_FRIEND"));
remove.addActionListener(a -> actions.removeContact(client));
menu.add(remove);
}
/**
* Whether the client's channel is actually rendered in the tree — it won't be if
* we're not subscribed to it (e.g. it needs more subscribe power than we have).