package com.ts3client.ui; import javax.swing.ImageIcon; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.image.BufferedImage; /** * The user interface's icons, taken from the active {@link IconTheme} pack and * named by TeamSpeak's icon keys. * *
The icons the client cannot do without are also drawn programmatically here, * so a machine with no icon pack installed still gets a complete (if plain) set. * Everything else — menu and dialog decoration — simply stays blank without a pack, * which is why {@link #of(String)} may return {@code null}. */ public final class Icons { private static final int SZ = IconTheme.SIZE; private Icons() { } private interface Painter { void paint(Graphics2D g); } /** @return the pack's icon for a TeamSpeak icon key, or {@code null} when it has none */ public static ImageIcon of(String key) { return IconTheme.icon(key); } /** @return the pack's icon at a given size, or {@code null} when it has none */ public static ImageIcon of(String key, int size) { return IconTheme.icon(key, size); } private static ImageIcon themed(String key, Painter fallback) { return themed(key, SZ, fallback); } private static ImageIcon themed(String key, int size, Painter fallback) { ImageIcon icon = IconTheme.icon(key, size); return icon != null ? icon : make(fallback); } private static ImageIcon make(Painter p) { BufferedImage img = new BufferedImage(SZ, SZ, BufferedImage.TYPE_INT_ARGB); Graphics2D g = img.createGraphics(); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); p.paint(g); g.dispose(); return new ImageIcon(img); } // ---- tree icons ---- public static ImageIcon server() { return themed("SERVER_GREEN", Icons::paintServer); } public static ImageIcon channel() { return themed("CHANNEL_GREEN", g -> paintChannel(g, new Color(0x3E7CB1), false)); } /** A channel nobody can join without its password. */ public static ImageIcon channelLocked() { return themed("CHANNEL_PRIVATE", g -> paintChannel(g, new Color(0x8A6D3B), true)); } /** A channel that has reached its client limit. */ public static ImageIcon channelFull() { return themed("CHANNEL_RED", g -> paintChannel(g, new Color(0xA53F3F), false)); } // ---- client status icons ---- public static ImageIcon clientIdle() { return themed("PLAYER_OFF", g -> paintPerson(g, Theme.IDLE_CLIENT)); } public static ImageIcon clientTalking() { return themed("PLAYER_ON", g -> paintPerson(g, Theme.TALKING)); } public static ImageIcon clientAway() { return themed("AWAY", g -> paintPerson(g, Theme.AWAY)); } /** A channel commander that is not talking. */ public static ImageIcon clientCommander() { return themed("PLAYER_COMMANDER_OFF", g -> paintPerson(g, Theme.IDLE_CLIENT)); } /** A channel commander that is talking. */ public static ImageIcon clientCommanderTalking() { return themed("PLAYER_COMMANDER_ON", g -> paintPerson(g, Theme.TALKING)); } public static ImageIcon clientQuery() { return themed("SERVER_QUERY", g -> paintPerson(g, Theme.IDLE_CLIENT)); } public static ImageIcon micMuted() { return themed("INPUT_MUTED", Icons::paintMicMuted); } public static ImageIcon speakerMuted() { return themed("OUTPUT_MUTED", Icons::paintSpeakerMuted); } // ---- toolbar / action icons ---- public static ImageIcon connect() { return themed("CONNECT", IconTheme.TOOLBAR_SIZE, Icons::paintConnect); } public static ImageIcon disconnect() { return themed("DISCONNECT", IconTheme.TOOLBAR_SIZE, Icons::paintDisconnect); } public static ImageIcon mic() { return themed("CAPTURE", IconTheme.TOOLBAR_SIZE, Icons::paintMic); } public static ImageIcon speaker() { return themed("PLAYBACK", IconTheme.TOOLBAR_SIZE, Icons::paintSpeaker); } public static ImageIcon micMutedLarge() { return themed("INPUT_MUTED", IconTheme.TOOLBAR_SIZE, Icons::paintMicMuted); } public static ImageIcon speakerMutedLarge() { return themed("OUTPUT_MUTED", IconTheme.TOOLBAR_SIZE, Icons::paintSpeakerMuted); } /** Marks the server that currently owns the microphone. */ public static ImageIcon micActive() { return themed("ACTIVATE_MICROPHONE", IconTheme.TOOLBAR_SIZE, Icons::paintMicActive); } /** The same marker at tab-label size. */ public static ImageIcon micActiveSmall() { return themed("ACTIVATE_MICROPHONE", Icons::paintMicActive); } /** The toolbar's away marker. */ public static ImageIcon away() { return themed("AWAY", IconTheme.TOOLBAR_SIZE, g -> paintPerson(g, Theme.AWAY)); } public static ImageIcon settings() { return themed("SETTINGS", IconTheme.TOOLBAR_SIZE, Icons::paintSettings); } public static ImageIcon app() { return make(Icons::paintApp); } // ---- built-in painters ---- private static void paintServer(Graphics2D g) { g.setColor(new Color(0x2C6EA5)); g.fillRoundRect(2, 3, 12, 4, 2, 2); g.fillRoundRect(2, 9, 12, 4, 2, 2); g.setColor(new Color(0x9FD0F0)); g.fillOval(4, 4, 2, 2); g.fillOval(4, 10, 2, 2); } private static void paintChannel(Graphics2D g, Color c, boolean lock) { g.setColor(c); g.setStroke(new BasicStroke(1.6f)); // simple speaker-cone glyph g.fillRect(2, 6, 3, 4); int[] xs = {5, 9, 9, 5}; int[] ys = {6, 3, 13, 10}; g.fillPolygon(xs, ys, 4); g.setStroke(new BasicStroke(1.4f)); g.drawArc(9, 5, 4, 6, -60, 120); if (lock) { g.setColor(new Color(0xB8860B)); g.fillRoundRect(10, 9, 5, 5, 1, 1); g.setColor(Color.WHITE); g.fillRect(12, 10, 1, 2); } } private static void paintPerson(Graphics2D g, Color c) { g.setColor(c); g.fillOval(5, 2, 6, 6); // head g.fillRoundRect(3, 9, 10, 6, 4, 4); // shoulders } private static void paintMicMuted(Graphics2D g) { g.setColor(Theme.MUTED); g.fillRoundRect(6, 2, 4, 7, 2, 2); g.setStroke(new BasicStroke(1.4f)); g.drawArc(4, 6, 8, 6, 200, 140); g.drawLine(8, 12, 8, 14); g.setStroke(new BasicStroke(2f)); g.drawLine(2, 2, 14, 14); // slash } private static void paintSpeakerMuted(Graphics2D g) { g.setColor(Theme.MUTED); g.fillRect(2, 6, 3, 4); int[] xs = {5, 9, 9, 5}; int[] ys = {6, 3, 13, 10}; g.fillPolygon(xs, ys, 4); g.setStroke(new BasicStroke(2f)); g.drawLine(2, 2, 14, 14); } private static void paintConnect(Graphics2D g) { g.setColor(new Color(0x2E8B57)); g.setStroke(new BasicStroke(2f)); g.drawLine(3, 8, 8, 8); g.fillRoundRect(8, 5, 5, 6, 2, 2); g.drawLine(10, 3, 10, 5); g.drawLine(12, 3, 12, 5); } private static void paintDisconnect(Graphics2D g) { g.setColor(Theme.MUTED); g.setStroke(new BasicStroke(2f)); g.drawLine(3, 8, 8, 8); g.fillRoundRect(8, 5, 5, 6, 2, 2); g.drawLine(2, 3, 6, 13); } private static void paintMic(Graphics2D g) { g.setColor(new Color(0x37474F)); g.fillRoundRect(6, 2, 4, 7, 2, 2); g.setStroke(new BasicStroke(1.4f)); g.drawArc(4, 6, 8, 6, 200, 140); g.drawLine(8, 12, 8, 14); g.drawLine(6, 14, 10, 14); } private static void paintSpeaker(Graphics2D g) { g.setColor(new Color(0x37474F)); g.fillRect(2, 6, 3, 4); int[] xs = {5, 9, 9, 5}; int[] ys = {6, 3, 13, 10}; g.fillPolygon(xs, ys, 4); g.setStroke(new BasicStroke(1.4f)); g.drawArc(9, 5, 4, 6, -60, 120); } private static void paintMicActive(Graphics2D g) { g.setColor(Theme.TALKING); g.fillOval(1, 1, 14, 14); g.setColor(Color.WHITE); g.fillRoundRect(6, 3, 4, 6, 2, 2); g.setStroke(new BasicStroke(1.4f)); g.drawArc(4, 6, 8, 6, 200, 140); g.drawLine(8, 11, 8, 13); } private static void paintSettings(Graphics2D g) { g.setColor(new Color(0x37474F)); g.setStroke(new BasicStroke(2f)); g.drawOval(5, 5, 6, 6); for (int a = 0; a < 360; a += 45) { double r = Math.toRadians(a); int x1 = (int) (8 + Math.cos(r) * 5); int y1 = (int) (8 + Math.sin(r) * 5); int x2 = (int) (8 + Math.cos(r) * 7); int y2 = (int) (8 + Math.sin(r) * 7); g.drawLine(x1, y1, x2, y2); } } private static void paintApp(Graphics2D g) { g.setColor(Theme.ACCENT); g.fillRoundRect(1, 1, 14, 14, 4, 4); g.setColor(Color.WHITE); g.setStroke(new BasicStroke(1.6f)); g.drawArc(4, 5, 8, 8, 30, 120); g.drawArc(2, 3, 12, 12, 30, 120); g.fillOval(7, 9, 2, 2); } }