package com.ts3client.ui; import com.ts3client.net.Group; import com.ts3client.net.IconRepository; import javax.swing.Icon; import javax.swing.ImageIcon; import java.awt.Component; import java.awt.Graphics; import java.awt.Image; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * Turns the {@link IconRepository}'s icon bytes into Swing icons for one server's * groups, caching the decoded images. Icons that still have to be downloaded simply * do not render yet; the repository repaints the view once they arrive. */ public final class GroupIcons { /** Tree rows are 16×16 like TeamSpeak's own icon set. */ private static final int SIZE = 16; private final IconRepository repository; private final Map decoded = new ConcurrentHashMap<>(); public GroupIcons(IconRepository repository) { this.repository = repository; } /** @return the group's icon, or {@code null} if it has none or it is not loaded yet */ public ImageIcon iconOf(Group group) { if (group == null || group.iconId == 0) return null; return icon(group.iconId); } public ImageIcon icon(long iconId) { ImageIcon cached = decoded.get(iconId); if (cached != null) return cached; byte[] data = repository.get(iconId); if (data == null) return null; ImageIcon icon = new ImageIcon(data); if (icon.getIconWidth() <= 0) return null; if (icon.getIconWidth() != SIZE || icon.getIconHeight() != SIZE) { icon = new ImageIcon(icon.getImage().getScaledInstance(SIZE, SIZE, Image.SCALE_SMOOTH)); } decoded.put(iconId, icon); return icon; } /** Collects the icons of a client's server groups plus its channel group, in display order. */ public List iconsOf(List serverGroups, Group channelGroup) { List icons = new ArrayList<>(); for (Group g : serverGroups) { ImageIcon icon = iconOf(g); if (icon != null) icons.add(icon); } ImageIcon channelIcon = iconOf(channelGroup); if (channelIcon != null) icons.add(channelIcon); return icons; } /** Lays several icons out in a row, so a single tree cell can show a badge strip. */ public static final class Row implements Icon { private static final int GAP = 2; private final List icons; public Row(List icons) { this.icons = icons; } @Override public void paintIcon(Component c, Graphics g, int x, int y) { int offset = x; for (Icon icon : icons) { icon.paintIcon(c, g, offset, y + (getIconHeight() - icon.getIconHeight()) / 2); offset += icon.getIconWidth() + GAP; } } @Override public int getIconWidth() { int width = 0; for (Icon icon : icons) width += icon.getIconWidth() + GAP; return Math.max(0, width - GAP); } @Override public int getIconHeight() { int height = 0; for (Icon icon : icons) height = Math.max(height, icon.getIconHeight()); return height; } } }