Commit Graph

88 Commits

Author SHA1 Message Date
1fddae3f0b Always rebuild the swing jar before shading it
Without forceCreation, maven-jar-plugin kept the previous shaded
ts3-client.jar when the module's classes were unchanged, and the shade then
used it as input: the stale ts3j classes inside it won over the freshly
installed ts3j, so ts3j fixes never reached the built client without a clean.

Also updates ts3j for the empty server-group parsing fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 19:30:48 +00:00
33411fcd13 Update ts3j: fix EAX MAC failures on busy servers
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 19:09:09 +00:00
2a73900dcc Show client avatars and let the user set their own
Avatars are fetched from the server's file repository as /avatar_<id>,
verified against client_flag_avatar and cached on disk; animated GIFs play in
the info panel. Self > Set avatar... uploads or removes our own picture within
the server's size limit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 19:03:00 +00:00
dbb425403a Import bookmarks and identities from the TeamSpeak 3 client
The official client keeps what a myTeamSpeak account synchronises as protobuf
Item_Data blobs in the ProtobufItems table of settings.db. Decode them, adopt
them on first run when we have no identity yet, and offer a re-import from
Manage Bookmarks and Identities. The settings.db reader moves from contacts to
a new teamspeak package.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 19:02:52 +00:00
bab89f9b50 Add the chat log store missing from earlier commits
TeamspeakConnection and ChatPanel already use com.ts3client.chatlog, but the
package itself was never committed, so the tree did not build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 19:02:40 +00:00
f409f500fd 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>
2026-09-18 07:50:58 +00:00
22e0304f25 Add the Tools > Ban List window
Reimplements the official client's ban list: a searchable table of the
server's bans with Add / Remove / Edit, an own-ban filter and highlight,
choosable columns, and the Add/Edit dialog whose IP and name patterns
can be read as IPv4/IPv6 wildcards, fixed strings or regular expressions
(translated to the regexes banadd takes exactly as the official client
does). Editing is add-then-delete, since the protocol has no banedit.

The ban list is read from the notifybanlist burst that answers banlist,
like channelpermlist. The menu item is only shown where the server grants
b_client_ban_list.

Also fixes ServerModel numbering permissions from 0 instead of 1, which
had every selfPermissionValue() lookup reading the neighbouring
permission — including the group-assignment menus.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 23:00:18 +00:00
636090d075 Start overlapping notification sounds without delay
The sound mixer rendered as fast as the playback line accepted, running a
full device buffer ahead of real time, so a sound fired while another
played could only join the mix behind all of that queued audio, and each
further one landed later still. Pace the mixer against the clock so it
never commits more than a few frames ahead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 22:54:02 +00:00
ad62f5fc92 Update connection and chat handling 2026-09-06 19:14:42 +00:00
62d5af7cf9 Smooth incoming voice under jitter 2026-09-06 13:43:11 +00:00
a01c598fe6 Remember how tall the user made the info panel
Kept as a share of the tree/info column, so it survives new tabs, a resized
window and the next session.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 08:11:29 +00:00
02533b543d Sit the info panel's link in the viewport's corner
It overlapped the scroll bar; the viewport is what the scroll bars have
already been taken out of. The layered pane also has to report a size now,
or the split pane above it has nothing to size the panel from.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 08:11:29 +00:00
5a00015824 Float the info panel's "Show in chat tab" link in its corner
As a real label over the text, it stays reachable however long a description
runs, instead of trailing off the end of it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 07:46:42 +00:00
ffe98be649 Trim the info panel's rows
A channel's permanent/temporary type is not worth a row, and a client's
platform reads better on the version line: Version: 3.6.2 … on Linux.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 07:46:36 +00:00
65f3777673 Let a chat tab grow to fit its name
Its header was pinned to the width it was built with, clipping longer names.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 07:27:17 +00:00
9101542f0a Switch chat tabs by clicking them, and tighten their padding
A custom tab component swallows the press, so the tabbed pane never selected
the tab itself; the reorder listener already on every tab part now does it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 07:04:26 +00:00
a4a8b39410 Paint the Away button as flat as its neighbours
Inside its panel it was drawn as a filled button, which read as half-active.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 06:58:08 +00:00
f480262c39 Drop the chat's Send button
Enter sends; the button only ate width.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 06:57:40 +00:00
8e3e9401d2 Name the server and channel chat tabs after where we are
Both carry an icon now, as the private chats do.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 06:57:38 +00:00
966f3fe9b1 Log our own channel switches in the server tab
Every other client's move was logged; ours went by silently.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 06:57:23 +00:00
b85d1e5076 Connect a left-clicked bookmark on the current tab
Middle-click still opens the server in a new tab.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 06:55:29 +00:00
50b71eb12e Leave the microphone alone when muting the speakers
Deafening silenced capture too, so coming back needed two clicks.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 06:53:59 +00:00
98719d717e Darken the dark theme, and hover a tree row like a menu
The client's own look-and-feel defaults now live beside LookAndFeelManager as
FlatLaf properties files, which FlatLaf loads on top of each theme's own,
instead of a handful of UIManager.put calls.

The dark theme is built on three surfaces keyed to the toolbar's #282828: the
window chrome, the tab strips and menu bar one step below it at #232323, and
the content the client shows — channel tree, chat log, text fields, lists — at
#1e1e1e. Selections are #364b6f, and channel names in the tree are white; the
chat lines about server events keep the blue they had, which channel names no
longer carry. Everything FlatLaf derives (borders, scroll bars, hover states)
follows from those.

A read-only editor pane is a chat log or an information view here, not a
disabled input, so it keeps the content background rather than FlatLaf's
inactive one; and since neither a text view nor a tree fills its viewport, the
space below the last line takes their background instead of the window's.

The row under the pointer is now washed in the same colour a hovered menu is,
rather than outlined. FlatLaf states that colour as a shift of the surface
beneath it, so it is resolved against the window background exactly as the
menu bar resolves it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 22:07:19 +00:00
35f10ed14c Paint the client with FlatLaf, in a light and a dark theme
The look-and-feel is FlatLaf now, chosen light or dark on the new Design
tab and remembered in the settings. Theme keeps only the colours TeamSpeak
gives meaning to (talking, away, muted, channel and server names) and takes
its surfaces and fonts from the look-and-feel, so a switch recolours the
whole window, chat logs included: the HTML panes share one themed stylesheet
and re-parse their content when the theme changes.

Also fixes the channel tree not scrolling: the tree listened for the mouse
wheel to re-check its hover row, and AWT only forwards a wheel event to the
enclosing scroll pane when the component under the pointer has no wheel
listener of its own. The hover row now follows the viewport instead, which
also covers scrollbar drags and keyboard scrolling.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 21:06:55 +00:00
d952ffd856 Put each client limit's count beside its "Limited" choice
The counts sat on a line of their own below the choices they belong to,
which also made the Advanced tab the tallest of the four and so decided
how much of the dialog the tab strip took. Each box now keeps its
natural height, and the free space collects below them.

Advanced went from 250px to 220px of preferred height, leaving the tab
strip about a third of the dialog rather than half.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 20:11:40 +00:00
acfe25db15 Show our own name in bold, in the row and in its editor
The renderer asks the panel which id is ours rather than reading
ClientEntry.self: our own row usually arrives among the enter-view events
of the connect handshake, before the connection knows its own client id,
so that flag was left false. It is now stamped once the id is known,
which also fixes the "(you)" marker in the info panel.

The inline editor matches, and sits 2px further left so its text lines up
with the label it replaces rather than with its own border.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 19:59:55 +00:00
08e6c51e90 Leave a talking client's name alone, and rename from the whole row
Bolding the name of whoever is speaking re-measures the row while the
label keeps its old width, so a nickname spent every utterance clipped to
an ellipsis. Talking already shows in the client's icon, which is where
TeamSpeak shows it too, so the name keeps its normal weight and colour.

Renaming now starts from the tree's own double-click handling, which
works a row by its whole line, instead of Swing's, which only sees clicks
that land on the label.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 19:50:03 +00:00
acd438dd14 Don't send a rename that changes nothing
Closing the inline editor without touching the name asked the server to
rename us to the name we already hold, which it refuses with "nickname
is already in use".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 19:43:00 +00:00
6e65797f25 Rename yourself by double-clicking your own tree row
The row turns into a text field holding the current nickname, selected so
typing replaces it; Enter commits, Escape leaves the name alone. Only our
own row edits — every other double click keeps its meaning, and ours no
longer opens a private chat with ourselves.

The editor keeps the row's own icon (the inherited one knows only the
renderer's default leaf icon) and selects the text on a short delay,
after the release that opened it has been turned into a caret placement.
The tree nodes only mirror the connection's model, so the typed name is
sent to the server rather than written into the node; the row follows
when the rename is reported back.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 19:38:49 +00:00
fa99cadc44 Fill the selected tree row across its whole width
The cell renderer paints the selection only behind the label, which now
looks short next to the full-width hover outline and the full-line hit
area. The tree fills the selected rows itself, in the renderer's own
selection colour so the two meet seamlessly, which means it can no
longer clear its background as an opaque component would.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 19:16:06 +00:00
9ba0982f25 Work tree rows by their whole line, and outline the hovered one
Swing hit-tests a tree row only as far as its label, so a click past the
end of the name did nothing. Rows are now found by their vertical band
alone, which covers selection, the context menus, double-click join and
middle-click info; the expand handle to the left of a label still works
the row without selecting it.

The hovered row is outlined across that same full width, so what a click
would act on is visible before making it. Spacers, which nothing can be
done with, are skipped, and the outline gives way to the drag-and-drop
indicator while a drag is in progress.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 18:56:17 +00:00
275de77c8b Bold the channel references in the log, as the client ones are
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 18:24:05 +00:00
189620df7f Link the clients and channels named in server log lines
Log lines now name clients and channels with TeamSpeak's own client:// and
channel:// links, the same ones a dragged-in reference produces, so the
chat log's existing handling turns them into the context menus.

The log is rendered with a links-only pass rather than the full BBCode
renderer: its lines are assembled by the client itself, so nothing else in
them should be interpreted — a channel called "[b]VIP" stays literal.
Channels we no longer have (a deleted one) are still named in plain text.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 18:11:14 +00:00
cb05b097a6 Add "Change Description" to the client menu
Offered for our own client as well as others, with TS3's edit icon. The
description is read back from the server before prompting, since the
model only carries one for clients whose info has been looked at.

Note that client_description is not a clientupdate property even for
ourselves — the server only takes it through clientedit — and that our
own change is not echoed back to us, so the local entry is updated here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 18:04:35 +00:00
bcc034e975 Refresh a channel's cached description when it changes
The description is fetched on demand and cached, but nothing invalidated
it: notifychanneledited never carries the text, and the separate
notifychanneldescriptionchanged was ignored. Handle that event, and
refresh locally after our own edit, since the server does not notify the
invoker.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 17:38:53 +00:00
f68c1e297c Give the channel dialog's spare height to the description
The tabs were the growing half, which left them padding empty space
below their content while the description stayed five rows tall. They
now take their natural height at the bottom of the dialog and every
spare pixel goes to the description instead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 16:12:44 +00:00
52bd180b56 Collect directory listings from the events that carry them
ftgetfilelist is answered with a notifyfilelist event per entry and a
closing notifyfilelistfinished, not with rows of the command's own reply
— and ts3j dispatches anything named notify* as an event, so every
listing came back empty. The entries are now collected for the request
in flight, which is what the icon chooser and the file browser read.

The icons themselves live in the repository's "icons" directory on newer
servers rather than loose in its root, so both places are read. They are
still addressed as /icon_<id> for download, upload and delete, which is
how the server resolves them regardless of where it keeps them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 16:03:03 +00:00
f2885d33ad Add the Create Channel dialog
The official client creates and edits channels with one dialog, so
ChannelEditDialog becomes ChannelDialog with two entry points. Creating
starts from caller-chosen defaults instead of a channelinfo and sends a
channelcreate carrying only what deviates from a plain new channel —
TeamSpeak checks a create permission per property that is present.

The server row now has a context menu with "Create Channel" and "Create
Spacer" (pre-filled with the lowest free [spacerN], since channel names
must be unique), and a channel's menu offers "Create Channel" and
"Create Sub-Channel", both starting out temporary.

Verified against the test server: a temporary channel is created, and a
permanent one a guest may not create reports the server's refusal.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 15:31:07 +00:00
d8a56566d4 Add the Edit Channel dialog
Recovers the official client's CreateChannelDialog layout so the widget
set, labels and tab order match: name/icon/password/topic/description
above Standard, Audio, Permissions and Advanced tabs.

Core gains ChannelSettings (diffed against the original so channeledit
only carries changed properties) and ChannelAdmin (channelinfo/edit,
channel permissions, the server icon store). TeamspeakConnection now
owns the event executor so it can drain queued notify events before
reading a command's result, which channelpermlist needs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 15:11:49 +00:00
b29f518d59 Hide/gray client-menu items that can't do anything useful right now
"Find Client in Channel Tree" only makes sense when the menu was
opened somewhere other than the tree itself (chat log links, etc.),
so it's hidden when opened from the tree, and disabled elsewhere if
the client's channel isn't currently subscribed (so the tree
wouldn't actually show them). Adds "Join Channel of Client" above
"Move Client to own Channel", disabled under the same visibility
condition plus a full channel. Both use the CHANNEL_SWITCH icon.

Note: TS3 doesn't expose a channel's join-power threshold as a plain
property (only group membership powers work that way); it's a
permission resolved through channel/channel-group/server-group
inheritance that a regular client can't query, so join-power itself
isn't part of this check.
2026-08-18 15:25:41 +00:00
acf0221837 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.
2026-08-18 15:07:38 +00:00
e5b607fc29 Split ServerTab into focused collaborator classes
Extract mic/speaker/away/commander state handling into
ServerTabSelfState, ServerTreePanel.Actions delegation (context
menus, drag-drop, link clicks) into ServerTabTreeActions, and
ConnectionListener event marshaling onto the EDT into
ServerTabConnectionEvents. ServerTab keeps its full public API
(636 -> 377 lines); no behavior change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-18 13:37:58 +00:00
d722377508 Split SettingsDialog into focused tab panels
Extract the Playback/Capture and Voice Activation tabs (device pickers,
gain/volume sliders, noise reduction, VAD/PTT tuning, microphone test)
into DevicesPanel and VoiceActivationPanel, alongside the existing
NotificationsPanel/HotkeysPanel/IconPackPanel/ClientVersionPanel. A new
FormPanel base holds the shared GridBagLayout scaffolding. SettingsDialog
is now a thin coordinator (753 -> 187 lines); its public API and all
settings read/write behavior are unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-18 13:37:51 +00:00
b694845381 Split TeamspeakConnection into focused collaborator classes
Extract notify-event handling and the native-style server log/sound
announcements into ConnectionEventHandler, and getconnectioninfo
request/response + stats-snapshot building into ConnectionStatsCollector.
TeamspeakConnection remains the public facade (1828 -> 1037 lines); its
external API is unchanged. Pure refactor, no protocol/timing changes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-18 13:37:43 +00:00
cf5ba94092 Add Master Volume slider, status bar toggle, and toolbar customization menu
The toolbar now has a right-click menu to hide/show the status bar and
a short Master Volume slider (drag or scroll) that multiplies both
voice and notification volume via new Settings.effectiveOutputVolume()/
effectiveSoundVolume() helpers.

Also splits MainFrame's toolbar, menu bar and status bar construction
into their own MainToolbar/MainMenuBar/StatusBar classes, each talking
back to MainFrame only through a Listener interface.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-18 13:37:36 +00:00
e5fb87a796 Split ServerTreePanel into focused collaborator classes
Extract cell rendering, right-aligned group-icon-strip painting, the
drop-indicator JTree subclass, and drag-and-drop handling out of
ServerTreePanel into ServerTreeCellRenderer, DropIndicatorTree, and
ServerTreeDragAndDrop, leaving ServerTreePanel as the coordinator
(706 -> 284 lines). Pure refactor, no behavior change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-18 13:35:42 +00:00
de02f88f1c Add browser-style drag-to-reorder for chat and server tabs
Dragging a tab shows animated snapshots on the glass pane and swaps
with whichever neighbour it reaches the midpoint of; the real tab
model is only touched once, on release, so an in-flight mouse grab
never gets pulled out from under a rebuilt tab component.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-18 12:49:26 +00:00
71084ea309 Log server activity, scrollable chat tabs, and move info panel to a chat tab
Native TS3 logs client joins/leaves/moves, group changes and channel edits
to the server tab; minor local-only notices (mic mute, tray) no longer
spam the log. Chat tabs scroll by mouse wheel like server tabs. The info
panel can now be moved into a persistent, generically-labelled chat tab
that tracks tree selection and collapses the panel's space entirely; a
tree rebuild no longer briefly fires a null selection that reset this.
Added "Find Client in Channel Tree" to the client context menu, and tree
selection now survives model rebuilds.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-17 21:47:12 +00:00
047c89404c Prefer RECORD over XInput2 on X11: raw button releases can go missing
On at least some driver stacks, XI_RawButtonRelease is never delivered
to a client that hasn't grabbed the pointer, even though the matching
XI_RawButtonPress arrives fine — a bound mouse button then looks stuck
down forever, both when recording a hotkey and when using one. Taking
an active XIGrabDevice grab does fix delivery, but even with
owner_events set it blocks clicks from reaching every other window, so
it's not usable. XRecordInputHook taps the same core events xev sees
and isn't affected, so it now goes first on X11; XInput2 stays as the
fallback for servers where RECORD is disabled or missing, and gets a
correctness cleanup (real per-device event selection instead of the
XIAllMasterDevices pseudo-device) along the way.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-17 10:34:19 +00:00
75ae6b89b2 Report mute/hardware status correctly, add Local Mic Mute, fix stale talking indicator
- Mute/deafen status now goes out through clientupdate instead of
  clientedit, which silently rejected client_input_muted/output_muted
  since they are runtime status, not editable client properties.
- Publish client_input_hardware so other clients see "Microphone
  Disabled" instead of silence while another tab holds the capture
  device; track input/output hardware flags for other clients too,
  and show a distinct grey "disabled" icon instead of reusing the red
  "muted" one for both the tree and the info panel.
- Implement TS3's Enable/Disable/Toggle Local Mic Mute hotkeys: they
  silence capture like a real mute, but never touch the published
  mute status or play a sound.
- A speaker's "talking" indicator only ever cleared when its
  zero-length end-of-burst voice packet arrived; if that one UDP
  packet was lost, the indicator stuck until their next burst. Add a
  watchdog that clears it after 200ms of silence from that speaker
  regardless.
2026-08-17 08:43:22 +00:00