package com.ts3client.ui; import com.formdev.flatlaf.util.ScaledEmptyBorder; import com.formdev.flatlaf.util.UIScale; import com.ts3client.net.ChannelSettings; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JSpinner; import javax.swing.JTextField; import javax.swing.SpinnerNumberModel; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; /** * The channel editor's "Advanced" tab: the phonetic name, the delete delay of a temporary * channel, voice encryption, and the two client limits. */ final class ChannelAdvancedPanel extends JPanel implements ChannelDialog.Tab { /** TeamSpeak's ceiling for {@code channel_delete_delay}, in seconds. */ private static final int MAX_DELETE_DELAY = 604800; private final JTextField phoneticName = new JTextField(); private final JSpinner deleteDelay = Spinners.compact(new JSpinner(new SpinnerNumberModel(0, 0, MAX_DELETE_DELAY, 1))); private final JButton deleteDelayMax = new JButton("max"); private final JCheckBox encrypted = new JCheckBox("Voice Data encrypted"); private final JRadioButton maxUsersUnlimited = new JRadioButton("Unlimited"); private final JRadioButton maxUsersLimited = new JRadioButton("Limited"); private final JSpinner maxUsers = Spinners.compact(new JSpinner(new SpinnerNumberModel(16, 0, 65535, 1))); private final JRadioButton familyInherited = new JRadioButton("Inherited"); private final JRadioButton familyUnlimited = new JRadioButton("Unlimited"); private final JRadioButton familyLimited = new JRadioButton("Limited"); private final JSpinner familyMaxUsers = Spinners.compact(new JSpinner(new SpinnerNumberModel(16, 0, 65535, 1))); ChannelAdvancedPanel() { setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); setBorder(new ScaledEmptyBorder(8, 8, 8, 8)); phoneticName.setToolTipText("Set phonetic nickname used for this channel by text-to-speech."); deleteDelay.setToolTipText("
Delay in seconds after temporary channels are deleted" + " after the last user left the channel.
"); deleteDelayMax.setToolTipText("Set delete delay to maximum value"); deleteDelayMax.addActionListener(a -> deleteDelay.setValue(MAX_DELETE_DELAY)); encrypted.setToolTipText("Voice data in this channel will be encrypted."); ButtonGroup users = new ButtonGroup(); users.add(maxUsersUnlimited); users.add(maxUsersLimited); maxUsersUnlimited.setToolTipText("Unlimited number of clients can join this channel."); maxUsersLimited.setToolTipText("Limit number of clients in this channel."); maxUsersUnlimited.addActionListener(a -> syncLimits()); maxUsersLimited.addActionListener(a -> syncLimits()); ButtonGroup family = new ButtonGroup(); family.add(familyInherited); family.add(familyUnlimited); family.add(familyLimited); familyInherited.setToolTipText("Inherit the client limit by parent channel."); familyUnlimited.setToolTipText("The channel subtree can be joined by unlimited number of clients."); familyLimited.setToolTipText("Limit the number of clients in the channel subtree."); for (JRadioButton radio : new JRadioButton[]{familyInherited, familyUnlimited, familyLimited}) { radio.addActionListener(a -> syncLimits()); } JPanel other = otherSettings(); other.setAlignmentX(0f); fixHeight(other); add(other); add(Box.createVerticalStrut(UIScale.scale(6))); JPanel limits = new JPanel(); limits.setLayout(new BoxLayout(limits, BoxLayout.X_AXIS)); limits.add(limitBox("Max Users", new JRadioButton[]{maxUsersUnlimited, maxUsersLimited}, maxUsers)); limits.add(Box.createHorizontalStrut(UIScale.scale(8))); limits.add(limitBox("Family Max Users", new JRadioButton[]{familyInherited, familyUnlimited, familyLimited}, familyMaxUsers)); limits.setAlignmentX(0f); fixHeight(limits); add(limits); add(Box.createVerticalGlue()); } /** The delete delay only means anything for a channel that disappears on its own. */ void setChannelType(ChannelSettings.Type type) { boolean temporary = type == ChannelSettings.Type.TEMPORARY; deleteDelay.setEnabled(temporary); deleteDelayMax.setEnabled(temporary); } @Override public void read(ChannelSettings settings) { phoneticName.setText(settings.phoneticName); deleteDelay.setValue(Math.min(MAX_DELETE_DELAY, Math.max(0, settings.deleteDelay))); encrypted.setSelected(settings.encrypted); if (settings.maxClientsUnlimited) maxUsersUnlimited.setSelected(true); else maxUsersLimited.setSelected(true); maxUsers.setValue(settings.maxClients); if (settings.familyInherited) familyInherited.setSelected(true); else if (settings.familyUnlimited) familyUnlimited.setSelected(true); else familyLimited.setSelected(true); familyMaxUsers.setValue(settings.maxFamilyClients); setChannelType(settings.type); syncLimits(); } @Override public void write(ChannelSettings settings) { settings.phoneticName = phoneticName.getText(); settings.deleteDelay = (Integer) deleteDelay.getValue(); settings.encrypted = encrypted.isSelected(); settings.maxClientsUnlimited = maxUsersUnlimited.isSelected(); settings.maxClients = (Integer) maxUsers.getValue(); settings.familyInherited = familyInherited.isSelected(); settings.familyUnlimited = familyUnlimited.isSelected(); settings.maxFamilyClients = (Integer) familyMaxUsers.getValue(); } private void syncLimits() { maxUsers.setEnabled(maxUsersLimited.isSelected()); familyMaxUsers.setEnabled(familyLimited.isSelected()); } private JPanel otherSettings() { JPanel panel = new JPanel(new GridBagLayout()); panel.setBorder(BorderFactory.createTitledBorder("Other Settings")); GridBagConstraints c = new GridBagConstraints(); c.insets = UIScale.scale(new Insets(3, 4, 3, 4)); c.anchor = GridBagConstraints.WEST; c.fill = GridBagConstraints.HORIZONTAL; c.gridx = 0; c.gridy = 0; panel.add(new JLabel("Phonetic Name:"), c); c.gridx = 1; c.weightx = 1; panel.add(phoneticName, c); c.gridx = 0; c.gridy = 1; c.weightx = 0; panel.add(new JLabel("Delete delay:"), c); JPanel delay = new JPanel(new FlowLayout(FlowLayout.LEFT, UIScale.scale(4), 0)); delay.add(deleteDelay); delay.add(deleteDelayMax); c.gridx = 1; c.weightx = 1; panel.add(delay, c); c.gridx = 1; c.gridy = 2; panel.add(encrypted, c); return panel; } /** * A limit box: its choices stacked, with the count beside the last of them — * "Limited", the only choice it belongs to. */ private JPanel limitBox(String title, JRadioButton[] choices, JSpinner spinner) { JPanel box = new JPanel(); box.setLayout(new BoxLayout(box, BoxLayout.Y_AXIS)); box.setBorder(BorderFactory.createTitledBorder(title)); for (int i = 0; i < choices.length; i++) { JRadioButton radio = choices[i]; radio.setAlignmentX(0f); if (i < choices.length - 1) { box.add(radio); continue; } JPanel row = new JPanel(new FlowLayout(FlowLayout.LEFT, UIScale.scale(6), 0)); row.setAlignmentX(0f); row.add(radio); row.add(spinner); box.add(row); } box.add(Box.createVerticalGlue()); return box; } /** Keeps a section at its natural height, so the free space collects at the bottom. */ private static void fixHeight(JPanel panel) { panel.setMaximumSize(new Dimension(Integer.MAX_VALUE, panel.getPreferredSize().height)); } }