package com.ts3client.ui; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSpinner; import javax.swing.SpinnerNumberModel; import java.awt.Color; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; /** * The channel editor's "Permissions" tab: the needed-power permissions set on the channel * itself, which is the subset of channel permissions TeamSpeak surfaces here. * *

These are not channel properties but permissions, so they are read with * {@code channelpermlist} and written with {@code channeladdperm}. A power left at zero is * not a permission the channel carries: clearing a spinner back to zero removes the * permission again so the channel inherits it, which is how the official client behaves. */ final class ChannelPermissionsPanel extends JPanel { /** The regular powers, in the order the official dialog lists them. */ private static final String[][] REGULAR = { {"i_channel_needed_join_power", "Join:"}, {"i_channel_needed_subscribe_power", "Subscribe:"}, {"i_channel_needed_description_view_power", "Desc. View:"}, {"i_channel_needed_modify_power", "Modify:"}, {"i_channel_needed_delete_power", "Delete:"}}; private static final String[][] FILE_TRANSFER = { {"i_ft_needed_file_browse_power", "Browse:"}, {"i_ft_needed_file_upload_power", "Upload:"}, {"i_ft_needed_file_download_power", "Download:"}, {"i_ft_needed_file_rename_power", "Rename:"}, {"i_ft_needed_directory_create_power", "Dir. Create:"}}; private final Map spinners = new LinkedHashMap<>(); /** The values the server reported, to tell an edited power from an untouched one. */ private final Map original = new LinkedHashMap<>(); private final JLabel status = new JLabel(" "); ChannelPermissionsPanel() { setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); JPanel groups = new JPanel(); groups.setLayout(new BoxLayout(groups, BoxLayout.X_AXIS)); groups.add(group("Regular Needed Powers", REGULAR)); groups.add(Box.createHorizontalStrut(8)); groups.add(group("File Transfer Needed Powers", FILE_TRANSFER)); groups.setAlignmentX(0f); add(groups); status.setAlignmentX(0f); status.setBorder(BorderFactory.createEmptyBorder(6, 2, 0, 2)); add(status); add(Box.createVerticalGlue()); setEditable(false); } /** Fills the spinners from a {@code channelpermlist} result and enables editing. */ void read(Map permissions) { original.clear(); for (Map.Entry entry : spinners.entrySet()) { int value = permissions.getOrDefault(entry.getKey(), 0); original.put(entry.getKey(), value); entry.getValue().setValue(value); } setEditable(true); setStatus(" ", false); } /** Greys the tab out with a reason, e.g. when the server refuses to show the permissions. */ void showUnavailable(String message) { setEditable(false); setStatus(message, true); } /** The permissions whose power was changed to a non-zero value. */ Map changed() { Map changed = new LinkedHashMap<>(); for (Map.Entry entry : spinners.entrySet()) { int value = (Integer) entry.getValue().getValue(); if (value != 0 && value != original.getOrDefault(entry.getKey(), 0)) { changed.put(entry.getKey(), value); } } return changed; } /** The permissions cleared back to zero, which are removed from the channel. */ List cleared() { List cleared = new ArrayList<>(); for (Map.Entry entry : spinners.entrySet()) { int value = (Integer) entry.getValue().getValue(); if (value == 0 && original.getOrDefault(entry.getKey(), 0) != 0) cleared.add(entry.getKey()); } return cleared; } void setStatus(String message, boolean error) { status.setText(message == null || message.isEmpty() ? " " : message); status.setForeground(error ? Color.RED.darker() : Theme.chatSystem()); } private void setEditable(boolean editable) { for (JSpinner spinner : spinners.values()) spinner.setEnabled(editable); } private JPanel group(String title, String[][] permissions) { JPanel panel = new JPanel(new GridBagLayout()); panel.setBorder(BorderFactory.createTitledBorder(title)); GridBagConstraints c = new GridBagConstraints(); c.insets = new Insets(3, 4, 3, 4); c.anchor = GridBagConstraints.WEST; c.fill = GridBagConstraints.HORIZONTAL; int row = 0; for (String[] permission : permissions) { JSpinner spinner = Spinners.compact(new JSpinner(new SpinnerNumberModel(0, 0, 9999, 1))); spinners.put(permission[0], spinner); c.gridx = 0; c.gridy = row; c.weightx = 0; panel.add(new JLabel(permission[1]), c); c.gridx = 1; c.weightx = 1; c.fill = GridBagConstraints.NONE; panel.add(spinner, c); c.fill = GridBagConstraints.HORIZONTAL; row++; } // Keeps the rows at the top of the group instead of centred in whatever height // the tab happens to give it. c.gridx = 0; c.gridy = row; c.weighty = 1; panel.add(Box.createGlue(), c); return panel; } }