Commit Graph

35 Commits

Author SHA1 Message Date
f30cecf51c Keep channels the user collapsed collapsed
Every tree rebuild, which any join, leave or talk-power change causes,
expanded every row again, so a collapsed channel reopened within
seconds. The tree now remembers which channels were collapsed and
opens everything else; reopening one restores what is below it. While
the find bar filters, everything opens so every match shows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 11:25:26 +00:00
b01ed43d60 Find clients and channels in the server tree
Ctrl+F in the tree opens a find bar below it that washes matching rows,
steps the selection through them and can prune the tree down to the
matches and the channels leading to them. ServerTreePanel becomes a
panel holding the scroll pane and the bar.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 15:09:45 +00:00
c743efc3b6 Keep the window responsive while the channel tree loads
Connecting to a server with ~560 channels froze the UI for over two
seconds: every model event queued its own full tree rebuild, and each
rebuild expanded row by row with Swing's variable-height layout cache,
which measures every row on each expansion.

Coalesce model-change bursts into one rebuild and switch the tree to the
large-model layout (it already has a fixed row height), which only
measures rows on screen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 23:21:52 +00:00
9d996f48a2 Scale the interface fractionally on Linux
Java2D on X11 (and so under XWayland) truncates its scale to a whole
number, drawing 150 % at 100 %. On Linux, pin Java2D to 1 and hand the
full factor to FlatLaf, taken from XSETTINGS Xft/DPI, KWin's Xwayland
scale or GDK_SCALE, or chosen in Options → Design.

The client's own sizes now go through UIScale: borders, gaps, dialog and
row sizes, icons (rendered at the scaled size, so they stay crisp),
custom-painted components and font sizes in the HTML views.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 22:33:27 +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
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
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
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
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
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
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
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
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
97148cfe9a Move a client into your own channel from its context menu
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-14 21:30:12 +02:00
6dee5106c7 Open text chat by double-clicking a client
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-14 14:48:54 +00:00
9bb1ec7060 Open connection info by middle-clicking a client
The dialog now disposes when its window button is used, too: hiding it
left the one-second refresh timer polling the server for a window
nobody could see again.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 14:39:19 +00:00
0f8de796ea 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>
2026-08-14 14:29:21 +00:00
d37c6c9ba9 Name the server node and stop it collapsing
The tree's top row showed the placeholder name, since nothing ever read the
real one; take it from initserver, which ts3j now keeps.

There is only ever one top-level row, so its expand control bought nothing and
cost horizontal space in a narrow view. Drop it, ignore collapse requests on
the root, and tighten the per-level indent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 14:15:32 +00:00
46206e3047 Show and control channel subscriptions
Track the subscription state from the notifychannelsubscribed events, draw
subscribed and unsubscribed channels apart with the pack's icons (and the
built-in glyph as a hollow cone), and offer subscribe/unsubscribe entries —
channel and family — in the channel context menu.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 13:57:25 +00:00
245ae5ecc5 Add an away button with global actions and message presets
The toolbar gains an away toggle with a drop-down: it toggles away on the
current server with no message, while the menu carries the global actions
("Set Globally Away", "Set Globally Away Status"), the saved presets and
their editor. Presets live in ~/.ts3jclient/away.properties and are
managed in a list with add/remove and double-click renaming.

Clients in the channel tree now show their away message in brackets after
the nickname instead of their primary server group.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 13:40:19 +00:00
676e95023e Draw the interface with TeamSpeak icon packs
Icon packs are read in TeamSpeak's own format — a zip (or unpacked folder)
of SVG/PNG art plus a settings.ini mapping icon keys to files — so the packs
of an installed client and the ones from its add-on site work unchanged.
Legacy packs without that mapping (default.zip) are resolved by their file
naming convention instead, picking the resolution closest to the drawn size.

The pack draws the tree, toolbar, menus, context menus, file browser and the
default group icons; a pack's FALLBACK option decides whether what it lacks
comes from default.zip, and anything still missing falls back to the icons
the client draws itself. Options → Design picks the pack and shows a viewer
of everything in it.

Vector art is rasterised with JSVG, and the pack search roots are shared
with the sound packs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 12:46:39 +00:00
f334153e63 Repaint the whole tree viewport while scrolling
The right-aligned icon strip is positioned against the viewport rather than
the row, so the pixels a blit scroll reuses are stale and the icons leave
ghosts behind. Scroll the viewport in simple mode, and widen every repaint
request to the full visible width so a row whose label changes size cannot
leave its icons behind either.

Also stop blacklisting an icon whose download failed only because the file
transfer channel was not up yet: channel rows are painted during connect,
well before that point, so their icons would never be retried.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 07:28:09 +00:00
610dce1bd9 Show each channel's own icon in the tree
Channels carry a channel_icon_id just like groups do; track it on
ChannelNode from the channel list and the create/edit events, and paint it
in the same right-aligned strip that already carries a client's group
icons. Spacers keep their bare look.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 07:09:21 +00:00
7c86a1f166 Show server and channel group icons in the tree
Download the icons a server advertises for its groups over the file
transfer channel, cache them under the config directory, and draw them as
a badge strip on each client's row, right-aligned against the tree's
visible edge like the TeamSpeak client does. Groups without a custom icon
fall back to the bundled default set.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 07:06:23 +00:00
022aef633b Drag and drop clients and channels in the server tree
Clients can be dragged into a channel and channels re-parented or
reordered, with an insertion line (or a row outline, for "into this
channel") showing where the drop lands.

Channel positions come from channel_order, which is the id of the
channel above rather than an index, so siblings are now ordered by
walking that chain and the neighbour links are repaired locally on
create, move and delete -- the server only announces the channel
that changed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 17:42:49 +00:00
707d9ebbba Tabbed chats with BBCode rendering and clickable identities
Replace the chat target dropdown with a tab strip along the bottom of the
chat panel: a Server and a Channel tab plus one closable tab per private
chat, each with its own log and an unread marker.

Render logs and channel/client descriptions as HTML through a new BBCode
renderer in core, supporting b/i/u/s, color, size, url, center and lists,
with auto-linked bare URLs. Input is escaped and tag values validated, so
server-supplied text cannot inject markup or unsafe schemes.

Sender names and TeamSpeak's own client:// links open the same context menu
as the server tree (menus extracted into ClientMenu/ChannelMenu); channel://
links open the channel menu. Links are styled by CSS class: identities bold
in the author colour, other TS protocols plain, and links leaving the client
underlined so they cannot be mistaken for an identity.

Dragging a client or channel out of the tree yields the TS3 link BBCode.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 13:50:04 +00:00
f912cab07b Implement channel file transfers
Add file up/downloads and directory operations for channel file
repositories, reachable from a channel's right-click "Browse files".

Core (com.ts3client.net.filetransfer):
- FileTransferManager drives the TS3 file protocol on the ts3j socket.
  ftinitupload/ftinitdownload results arrive out-of-band as
  notifystartupload/notifystartdownload (success) or
  notifystatusfiletransfer (failure) events, not as command responses,
  so negotiation correlates them by clientftfid via a CompletableFuture.
  The byte phase opens a raw TCP socket, sends the ftkey, then streams.
- FileTransfer: observable per-transfer handle (state, progress, cancel).
- RemoteFile: UI-facing listing DTO, keeping core free of ts3j API types.

TeamspeakConnection exposes list/mkdir/delete/rename and up/download and
manages the manager's lifecycle. Swing FileBrowserDialog provides the
browser UI with a live per-transfer progress strip.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-13 07:22:33 +00:00
0f76258a05 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>
2026-08-12 22:01:17 +00:00