package chatty.gui.components; import chatty.Chatty; import chatty.Helper; import chatty.gui.GuiUtil; import chatty.gui.MainGui; import chatty.gui.components.menus.ContextMenuListener; import chatty.gui.components.menus.EmoteContextMenu; import chatty.util.StringUtil; import chatty.util.api.CheerEmoticon; import chatty.util.api.Emoticon; import chatty.util.api.Emoticon.EmoticonImage; import chatty.util.api.Emoticon.EmoticonUser; import chatty.util.api.Emoticons; import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.Color; import java.awt.Cursor; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.swing.BorderFactory; import javax.swing.ButtonGroup; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JToggleButton; import javax.swing.SwingUtilities; import javax.swing.border.Border; /** * Dialog showing emoticons that can be clicked on to insert them in the last * active channel inputbox. Also allows to open the emote context menu. * * @author tduva */ public class EmotesDialog extends JDialog { public final int UPDATE_EMOTESET_CHANGED = 1; public final int UPDATE_CHANNEL_CHANGED = 2; public final int UPDATE_FAVORITES = 4; private static final Insets TITLE_INSETS = new Insets(5,8,0,8); private static final Insets SUBTITLE_INSETS = new Insets(6,4,2,4); private static final Insets SUBTITLE_INSETS_SMALLER_MARGIN = new Insets(1,2,0,2); private static final Insets EMOTE_INSETS = new Insets(4,10,4,10); private static final String FAVORITE_EMOTES = "Favorites"; private static final String MY_EMOTES = "My Emotes"; private static final String CHANNEL_EMOTES = "Channel"; private static final String TWITCH_EMOTES = "Twitch"; private static final String OTHER_EMOTES = "Other"; private static final String EMOJI = "Emoji"; private static final String EMOTE_DETAILS = "Emote Details"; private static final String BITS = "B"; private final JPanel emotesPanel; private final Emoticons emoteManager; private final EmoticonUser emoteUser; private final CardLayout cardLayout = new CardLayout(); private final MouseAdapter mouseListener; private final ContextMenuListener contextMenuListener; private final List<EmotesPanel> panels = new ArrayList<>(); private final Map<JToggleButton, EmotesPanel> buttons = new HashMap<>(); private final EmotesPanel defaultPanel; /** * GridBagConstraints for adding titles/emotes. */ private final GridBagConstraints gbc; private Set<Integer> emotesets = new HashSet<>(); private String stream; private Emoticon detailsEmote; private boolean repaint; private float scale; private boolean closeOnDoubleClick = true; public EmotesDialog(Window owner, Emoticons emotes, final MainGui main, ContextMenuListener contextMenuListener) { super(owner); emoteUser = new Emoticon.EmoticonUser() { @Override public void iconLoaded() { // repaint = true; repaint(); } }; // Timer timer = new Timer(100, new ActionListener() { // // @Override // public void actionPerformed(ActionEvent e) { // if (repaint) { // repaint(); // repaint = false; // } // } // }); // timer.setRepeats(true); // timer.start(); // TODO: Focusable or maybe just when clicked on emote to insert code? this.setFocusable(false); this.setFocusableWindowState(false); this.contextMenuListener = contextMenuListener; this.emoteManager = emotes; setResizable(true); panels.add(new FavoritesPanel(FAVORITE_EMOTES, UPDATE_CHANNEL_CHANGED | UPDATE_EMOTESET_CHANGED | UPDATE_FAVORITES)); panels.add(new SubemotesPanel(MY_EMOTES, UPDATE_EMOTESET_CHANGED)); panels.add(new ChannelEmotesPanel(CHANNEL_EMOTES, UPDATE_CHANNEL_CHANGED | UPDATE_EMOTESET_CHANGED)); panels.add(new TwitchEmotesPanel(TWITCH_EMOTES,0)); panels.add(new OtherEmotesPanel(OTHER_EMOTES,0)); if (Chatty.DEBUG) { panels.add(new BitsPanel(BITS, 0)); } // Not quite ready yet //panels.add(new EmojiPanel(EMOJI, 0)); defaultPanel = panels.get(1); // Buttons/Button Panel ActionListener buttonAction = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { EmotesPanel emotesPanel = buttons.get(e.getSource()); if (emotesPanel != null) { showPanel(emotesPanel); } } }; ButtonGroup buttonGroup = new ButtonGroup(); JPanel buttonPanel = new JPanel(); for (EmotesPanel p : panels) { JToggleButton button = new JToggleButton(p.label); buttons.put(button, p); buttonGroup.add(button); button.setMargin(GuiUtil.SMALL_BUTTON_INSETS); buttonPanel.add(button); button.addActionListener(buttonAction); } add(buttonPanel, BorderLayout.NORTH); panels.add(new EmoteDetailPanel(EMOTE_DETAILS, 0)); mouseListener = new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (SwingUtilities.isLeftMouseButton(e)) { if (e.getClickCount() == 2 && closeOnDoubleClick) { setVisible(false); } else { Emote label = (Emote) e.getSource(); if (!label.noInsert) { main.insert(Emoticons.toWriteable(label.code), true); } } } } @Override public void mousePressed(MouseEvent e) { openContextMenu(e); } @Override public void mouseReleased(MouseEvent e) { openContextMenu(e); } }; // Emotes emotesPanel = new JPanel(); emotesPanel.setBackground(Color.WHITE); emotesPanel.setLayout(cardLayout); for (EmotesPanel panel : panels) { emotesPanel.add(wrapPanel(panel), panel.label); } //emotesPanel.setSize(0, 0); add(emotesPanel, BorderLayout.CENTER); gbc = new GridBagConstraints(); gbc.weightx = 1; gbc.weighty = 0; pack(); setMinimumSize(getPreferredSize()); setSize(320,300); } public void setCloseOnDoubleClick(boolean enabled) { this.closeOnDoubleClick = enabled; } /** * Wrap the given component into a JPanel, which aligns it at the top. There * may be an easier/more direct way of doing this. Also add it to a scroll * pane. * * @param panel * @return */ private static JComponent wrapPanel(JComponent panel) { panel.setBackground(Color.WHITE); JPanel outer = new JPanel(); outer.setLayout(new GridBagLayout()); outer.setBackground(Color.WHITE); GridBagConstraints gbcTest = new GridBagConstraints(); gbcTest.fill = GridBagConstraints.HORIZONTAL; gbcTest.weightx = 1; gbcTest.weighty = 1; gbcTest.anchor = GridBagConstraints.NORTH; outer.add(panel, gbcTest); //outer.setSize(0, 0); // Add and configure scroll pane JScrollPane scroll = new JScrollPane(outer); scroll.getVerticalScrollBar().setUnitIncrement(20); return scroll; } /** * On right-click on an emote, open the appropriate context menu. * * @param e */ private void openContextMenu(MouseEvent e) { if (e.isPopupTrigger()) { EmoticonImage emote = ((Emote)e.getSource()).emote; JPopupMenu m = new EmoteContextMenu(emote, contextMenuListener); m.show(e.getComponent(), e.getX(), e.getY()); } } /** * Opens the dialog, using the given emotesets and stream. * * @param emotesets * @param stream */ public void showDialog(Set<Integer> emotesets, String stream) { if (stream != null && !stream.equals(this.stream)) { setUpdated(UPDATE_CHANNEL_CHANGED); } if (stream != null) { this.stream = stream; } if (emotesets != null && !emotesets.equals(this.emotesets)) { setUpdated(UPDATE_EMOTESET_CHANGED); } this.emotesets = new HashSet<>(emotesets); updateTitle(); showEmotes(); setVisible(true); //update(); // Only for testing if the layouting still works if updated } public void showChannelEmotes() { showPanelByName(CHANNEL_EMOTES); } public void showEmoteDetails(Emoticon emote) { detailsEmote = emote; getPanelByName(EMOTE_DETAILS).updateEmotes(); showPanelByName(EMOTE_DETAILS); } /** * Reloads the current emotes if visible. This can be used if e.g. new * emotes have been added. */ public void update() { setEmotesUpdated(); if (isVisible()) { showEmotes(); } } /** * Changes the current stream and updates the channel-specific emotes if * necessary. * * @param stream The name of the stream */ public void updateStream(String stream) { if (!isVisible()) { return; } if (stream != null && stream.equals(this.stream)) { return; } this.stream = stream; updateTitle(); setUpdated(UPDATE_CHANNEL_CHANGED); showEmotes(); } private void setEmotesUpdated() { for (EmotesPanel p : panels) { p.setUpdated(); } } private void setUpdated(int what) { for (EmotesPanel p : panels) { p.setUpdated(what); } } /** * Updates the emotesets that are used to display the correct subemotes and * refreshes the subscriber emotes if necessary. * * @param emotesets The Set of emotesets */ public void updateEmotesets(Set<Integer> emotesets) { if (!isVisible() || emotesets == null || emotesets.equals(this.emotesets)) { return; } this.emotesets = new HashSet<>(emotesets); setUpdated(UPDATE_EMOTESET_CHANGED); showEmotes(); } public void favoritesUpdated() { setUpdated(UPDATE_FAVORITES); if (isVisible()) { showEmotes(); } } /** * Sets the scale to show the emoticons at. * * @param percentage */ public void setEmoteScale(int percentage) { float newScale = (float)(percentage / 100.0); if (newScale != scale) { scale = newScale; update(); } } /** * Sets the title according to the current stream. */ private void updateTitle() { String base = "Global/Subscriber/Turbo"; if (stream == null) { setTitle("Emoticons ("+base+")"); } else { setTitle("Emoticons ("+base+"/#"+stream+")"); } } private void showPanel(EmotesPanel panel) { panel.update(); cardLayout.show(emotesPanel, panel.label); for (JToggleButton button : buttons.keySet()) { if (buttons.get(button) == panel) { button.setSelected(true); } } } private void showPanelByName(String name) { EmotesPanel panel = getPanelByName(name); if (panel != null) { showPanel(panel); } } private EmotesPanel getPanelByName(String name) { for (EmotesPanel panel : panels) { if (panel.label.equals(name)) { return panel; } } return null; } /** * Shows the selected emotes page (depending on the pressed button), * subemotes by default. */ private void showEmotes() { for (JToggleButton button : buttons.keySet()) { if (button.isSelected()) { showPanel(buttons.get(button)); return; } } // No button selected, so set default showPanel(defaultPanel); } /** * A single emote displayed in a JLabel. Saves a reference to the actual * Emoticon object, so it can be retrieved when opening the context menu. */ private static class Emote extends JLabel { private static final Border BORDER = BorderFactory.createEmptyBorder(2, 2, 2, 2); public final String code; public final EmoticonImage emote; public final boolean noInsert; public Emote(Emoticon emote, MouseListener mouseListener, float scale, EmoticonUser emoteUser) { setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); addMouseListener(mouseListener); EmoticonImage emoteImage = emote.getIcon(scale, 0, emoteUser); this.code = emote.code; this.emote = emoteImage; setIcon(emoteImage.getImageIcon()); setToolTipText(emote.code); if (emote.type == Emoticon.Type.EMOJI) { setToolTipText(emote.getInfos().toString()); } if (emote.subType == Emoticon.SubType.CHEER) { setToolTipText(emote.getInfos().toString()); } if (emote.subType == Emoticon.SubType.CHEER) { noInsert = true; } else { noInsert = false; } setBorder(BORDER); } } private static class SortEmotesByEmoteset implements Comparator<Emoticon> { @Override public int compare(Emoticon o1, Emoticon o2) { return o1.emoteSet - o2.emoteSet; } } private static class SortEmotesByTypeAndName implements Comparator<Emoticon> { @Override public int compare(Emoticon o1, Emoticon o2) { int compareType = o1.type.compareTo(o2.type); if (compareType == 0) { return o1.code.compareToIgnoreCase(o2.code); } return compareType; } } private abstract class EmotesPanel extends JPanel { private boolean shouldUpdate; private final String label; private final int updateOn; EmotesPanel(String name, int updateOn) { super(new GridBagLayout()); this.label = name; this.updateOn = updateOn; } public void setUpdated(int reason) { if ((updateOn & reason) == reason) { shouldUpdate = true; } } public void setUpdated() { shouldUpdate = true; } public void update() { if (shouldUpdate) { updateEmotes(); } shouldUpdate = false; } protected abstract void updateEmotes(); /** * Clears everything to get ready to add emotes. */ void reset() { removeAll(); //panel.setSize(1,1); //panel.revalidate(); gbc.gridy = 0; } /** * Adds the emotes of the given emoteset. Includes the name of the * stream if available. * * @param emoteset The emoteset */ void addEmotes(int emoteset) { String stream = emoteManager.getStreamFromEmoteset(emoteset); if (stream == null) { stream = "-"; } Set<Emoticon> emotes = emoteManager.getEmoticons(emoteset); List<Emoticon> sorted = new ArrayList<>(emotes); Collections.sort(sorted, new SortEmotesByTypeAndName()); addTitle(stream + " [" + emoteset + "] (" + emotes.size() + " emotes)"); addEmotesPanel(sorted); } /** * Adds a title (label with seperating line). * * @param title The text of the title */ void addTitle(String title) { JLabel titleLabel = new JLabel(StringUtil.shortenTo(title, 48, 34)); titleLabel.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.BLACK)); gbc.fill = GridBagConstraints.HORIZONTAL; gbc.insets = TITLE_INSETS; gbc.anchor = GridBagConstraints.WEST; gbc.weightx = 1; add(titleLabel, gbc); gbc.gridy++; } /** * Adds some centered text without a seperating line and with a slightly * subdued color. * * @param title The text to add * @param smallMargin If true, uses smaller margins (for use below * emotes) */ void addSubtitle(String title, boolean smallMargin) { JLabel titleLabel = new JLabel(title); titleLabel.setForeground(Color.GRAY); gbc.fill = GridBagConstraints.NONE; gbc.weightx = 0; gbc.insets = smallMargin ? SUBTITLE_INSETS_SMALLER_MARGIN : SUBTITLE_INSETS; gbc.anchor = GridBagConstraints.CENTER; add(titleLabel, gbc); gbc.gridy++; } /** * Adds the given emotes to a new panel. * * @param emotes The emotes to add */ void addEmotesPanel(Collection<Emoticon> emotes) { JPanel panel = new JPanel(); panel.setBackground(new Color(250, 250, 250)); panel.setLayout(new WrapLayout()); /** * Using getParent() twice to get to JScrollPane viewport width, * however it still doesn't always seem to work, depending on the * width of the dialog. Substracting too much increases the gap in * between panels (probably because it layouts the emotes in a * narrower but higher panel). Manually resizing the dialog fixes * the layout. */ panel.setSize(getParent().getParent().getWidth() - 20, 1); //System.out.println(targetPanel.getParent().getParent()); for (Emoticon emote : emotes) { final JLabel label = new Emote(emote, mouseListener, scale, emoteUser); panel.add(label); } gbc.fill = GridBagConstraints.HORIZONTAL; gbc.insets = EMOTE_INSETS; gbc.anchor = GridBagConstraints.WEST; gbc.weightx = 1; add(panel, gbc); gbc.gridy++; } void addEmotes(Collection<Emoticon> emotes, String title) { if (!emotes.isEmpty()) { addTitle(title+" ("+emotes.size()+" emotes)"); addEmotesPanel(sortEmotes(emotes)); } } Collection<Emoticon> sortEmotes(Collection<Emoticon> emotes) { // Copy so the original doesn't get changed List<Emoticon> sorted = new ArrayList<>(emotes); Collections.sort(sorted, new SortEmotesByTypeAndName()); return sorted; } /** * Show layout changes after adding emotes. */ void relayout() { revalidate(); repaint(); } } private class FavoritesPanel extends EmotesPanel { public FavoritesPanel(String name, int updateOn) { super(name, updateOn); } @Override protected void updateEmotes() { reset(); Set<Emoticon> emotes = emoteManager.getFavorites(); if (emotes.isEmpty()) { addTitle("You haven't added any favorite emotes"); if (emoteManager.getNumNotFoundFavorites() > 0) { addSubtitle("(Emotes may not have been loaded yet.)", false); } } // Sort emotes by emoteset List<Emoticon> sorted = new ArrayList<>(emotes); Collections.sort(sorted, new SortEmotesByEmoteset()); // Sort out emotes that the user probably doesn't have access to List<Emoticon> subEmotesNotSubbedTo = new ArrayList<>(); for (Emoticon emote : sorted) { if (emote.emoteSet != Emoticon.SET_UNDEFINED && !emotesets.contains(emote.emoteSet)) { subEmotesNotSubbedTo.add(emote); } } sorted.removeAll(subEmotesNotSubbedTo); // Add emotes addEmotesPanel(sorted); if (!subEmotesNotSubbedTo.isEmpty()) { addTitle("You need to subscribe to use these emotes:"); addEmotesPanel(subEmotesNotSubbedTo); } relayout(); } } private class SubemotesPanel extends EmotesPanel { public SubemotesPanel(String name, int updateOn) { super(name, updateOn); } @Override protected void updateEmotes() { reset(); if (emotesets.isEmpty() || (emotesets.size() == 1 && emotesets.iterator().next().equals(Emoticon.SET_GLOBAL))) { addTitle("You don't seem to have any sub or turbo emotes"); if (stream == null) { addSubtitle("(Must join a channel for them to be recognized.)", false); } } // Put turbo emotes at the end Set<Integer> turboEmotes = new HashSet<>(); for (Integer emoteset : emotesets) { if (Emoticons.isTurboEmoteset(emoteset)) { turboEmotes.add(emoteset); } else if (emoteset != Emoticon.SET_GLOBAL) { addEmotes(emoteset); } } int turboSetA = 793; int turboSetB = 19194; for (Integer emoteset : turboEmotes) { if (emoteset == turboSetB && turboEmotes.contains(turboSetA) && emoteManager.equalsByCode(turboSetA, turboSetB)) { // Don't show these Turbo/Prime emotes if the user has the // other set as well, and the emotes are equal continue; } addEmotes(emoteset); } relayout(); } } private class ChannelEmotesPanel extends EmotesPanel { public ChannelEmotesPanel(String name, int updateOn) { super(name, updateOn); } @Override protected void updateEmotes() { reset(); if (stream == null) { addTitle("No Channel."); } else { // FFZ/BTTV Set<Emoticon> channelEmotes = emoteManager.getEmoticons(stream); // Split Event/Regular emotes into separate structures Set<Emoticon> regular = new HashSet<>(); Map<String, Set<Emoticon>> event = new HashMap<>(); for (Emoticon emote : channelEmotes) { if (emote.type == Emoticon.Type.FFZ && emote.subType == Emoticon.SubType.EVENT) { for (String info : emote.getInfos()) { if (!event.containsKey(info)) { event.put(info, new HashSet<Emoticon>()); } event.get(info).add(emote); } } else { regular.add(emote); } } if (channelEmotes.isEmpty()) { addTitle("No emotes found for #" + stream); addSubtitle("No FFZ or BTTV emotes found.", false); } else { addEmotes(regular, "Emotes specific to #" + stream); for (String info : event.keySet()) { addEmotes(event.get(info), "Featured " + info); } } // Subscriber Emotes int emoteset = emoteManager.getEmotesetFromStream(stream); if (emoteset != -1) { Set<Emoticon> subEmotes = emoteManager.getEmoticons(emoteset); if (!subEmotes.isEmpty()) { addTitle("Subscriber emotes of " + stream + " (" + subEmotes.size() + ")"); addEmotesPanel(sortEmotes(subEmotes)); if (!emotesets.contains(emoteset)) { addSubtitle("(Need to be subscribed to use these.)", true); } } } } relayout(); } } private class TwitchEmotesPanel extends EmotesPanel { public TwitchEmotesPanel(String name, int updateOn) { super(name, updateOn); } @Override protected void updateEmotes() { reset(); Set<Emoticon> emotes = emoteManager.getGlobalTwitchEmotes(); addEmotes(emotes, "Global Twitch Emotes"); relayout(); } } private class OtherEmotesPanel extends EmotesPanel { public OtherEmotesPanel(String name, int updateOn) { super(name, updateOn); } @Override protected void updateEmotes() { reset(); Set<Emoticon> ffz = Emoticons.filterByType(emoteManager.getOtherGlobalEmotes(), Emoticon.Type.FFZ); Set<Emoticon> ffzRegular = new HashSet<>(); Set<Emoticon> ffzFeatured = new HashSet<>(); for (Emoticon emote : ffz) { if (emote.subType == Emoticon.SubType.FEATURE_FRIDAY) { ffzFeatured.add(emote); } else { ffzRegular.add(emote); } } Set<Emoticon> bttv = Emoticons.filterByType(emoteManager.getOtherGlobalEmotes(), Emoticon.Type.BTTV); addEmotes(ffzRegular, "Global FFZ Emotes"); addEmotes(ffzFeatured, "Global FFZ Emotes [Featured]"); addEmotes(bttv, "Global BTTV Emotes"); relayout(); } } private class BitsPanel extends EmotesPanel { public BitsPanel(String name, int updateOn) { super(name, updateOn); } @Override protected void updateEmotes() { reset(); Collection<CheerEmoticon> cheerEmotes = emoteManager.getCheerEmotes(); Collection<Emoticon> emotes = new ArrayList<>(); for (CheerEmoticon emote : cheerEmotes) { emotes.add((Emoticon)emote); } addEmotesPanel(emotes); relayout(); } } private class EmojiPanel extends EmotesPanel { public EmojiPanel(String name, int updateOn) { super(name, updateOn); } @Override protected void updateEmotes() { reset(); Map<String, Set<Emoticon>> categories = new HashMap<>(); Set<Emoticon> emotes = emoteManager.getEmoji(); for (Emoticon emote : emotes) { for (String info : emote.getInfos()) { if (!info.startsWith("Category: ")) { continue; } if (!categories.containsKey(info)) { categories.put(info, new HashSet<Emoticon>()); } categories.get(info).add(emote); } } for (String category : categories.keySet()) { addEmotes(categories.get(category), category); } //addEmotes(emoteManager.getEmoji(), "Emoji"); relayout(); } } private class EmoteDetailPanel extends EmotesPanel { public EmoteDetailPanel(String name, int updateOn) { super(name, updateOn); } private GridBagConstraints lgbc = new GridBagConstraints(); @Override protected void updateEmotes() { reset(); Emoticon emote = detailsEmote; addTitle("Emote Details: "+emote.code); lgbc.insets = new Insets(5, 7, 5, 7); JPanel panel = new JPanel(); panel.setBackground(new Color(250, 250, 250)); panel.setLayout(new GridBagLayout()); addScaledEmote(emote, panel, 1, "100%"); if (emote.getWidth()*3+200 < EmotesDialog.this.getWidth() && !emote.isAnimated) { /** * Don't show middle one if emote is too wide (this won't be too * exact, but should work well enough in this case). */ addScaledEmote(emote, panel, (float)1.5, "150%"); } addScaledEmote(emote, panel, 2, "200%"); JPanel panel2 = new JPanel(); panel2.setLayout(new GridBagLayout()); lgbc.gridy = 2; lgbc.gridx = 0; lgbc.insets = new Insets(4, 4, 4, 4); if (emote.subType == Emoticon.SubType.CHEER) { addInfo(panel2, "", "Cheering Emote"); } else { addInfo(panel2, "Code:", emote.code); } String featured = emote.subType == Emoticon.SubType.EVENT ? " (Featured)" : ""; addInfo(panel2, "Type:", emote.type.toString()+featured); if (emote.numericId > Emoticon.ID_UNDEFINED) { addInfo(panel2, "Emote ID:", ""+emote.numericId); } if (emote.emoteSet > Emoticon.SET_UNDEFINED) { String emoteSetInfo = String.valueOf(emote.emoteSet); if (Emoticons.isTurboEmoteset(emote.emoteSet)) { emoteSetInfo += " (Turbo)"; } addInfo(panel2, "Twitch Emoteset:", emoteSetInfo); } if (emote.emoteSet == Emoticon.SET_UNKNOWN) { addInfo(panel2, "Twitch Emoteset:", "unknown"); } if (emote.hasStreamSet() && Helper.validateStream(emote.getStream())) { addInfo(panel2, "Channel:", emote.getStream()); } addInfo(panel2, "Usability:", emote.hasStreamRestrictions() ? "Local" : "Global"); addInfo(panel2, "Regular Size: ", emote.getWidth()+"x"+emote.getHeight()); if (emote.creator != null) { addInfo(panel2, "Emote by:", emote.creator); } // Info featured = emote.subType == Emoticon.SubType.EVENT ? "Featured " : ""; for (String info : emote.getInfos()) { addInfo(panel2, featured+info); } gbc.fill = GridBagConstraints.HORIZONTAL; gbc.insets = EMOTE_INSETS; gbc.anchor = GridBagConstraints.WEST; gbc.weightx = 1; add(panel, gbc); gbc.gridy++; add(panel2, gbc); gbc.gridy++; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.CENTER; add(new JLabel("<html><body style='width:200px;text-align:center;color:#888888'>Right-click on " + "emotes here or in chat to open context-menu with info/options."), gbc); relayout(); } private void addScaledEmote(Emoticon emote, JPanel panel, float scale, String label) { lgbc.anchor = GridBagConstraints.CENTER; lgbc.gridy = 0; panel.add(new Emote(emote, mouseListener, scale, emoteUser), lgbc); lgbc.gridy = 1; panel.add(new JLabel(label), lgbc); lgbc.gridx++; } /** * Adds a info line with separated key and value. * * @param panel * @param key * @param value */ private void addInfo(JPanel panel, String key, String value) { lgbc.gridx = 0; lgbc.anchor = GridBagConstraints.WEST; panel.add(new JLabel(key), lgbc); lgbc.gridx = 1; lgbc.anchor = GridBagConstraints.EAST; panel.add(new JLabel(StringUtil.shortenTo(value, 35, 20)), lgbc); lgbc.gridy++; } /** * Adds a full-width info line. * * @param panel * @param value */ private void addInfo(JPanel panel, String value) { lgbc.gridx = 0; lgbc.gridwidth = 2; lgbc.anchor = GridBagConstraints.CENTER; panel.add(new JLabel(StringUtil.shortenTo(value, 35, 20)), lgbc); lgbc.gridwidth = 1; lgbc.gridy++; } } }