/* CloseableTabIcon.java * * Created on Saturday June 20, 2009, 7:36 PM * * This file is a part of Shoddy Battle. * Copyright (C) 2009 Catherine Fitzpatrick and Benjamin Gwin * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program; if not, visit the Free Software Foundation, Inc. * online at http://gnu.org. */ package shoddybattleclient.utils; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Image; import java.awt.Insets; import java.awt.MediaTracker; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.util.ArrayList; import javax.swing.Icon; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.Timer; import shoddybattleclient.GameVisualisation; /** * * @author ben */ public class CloseableTabbedPane extends SlideTabbedPane { public interface CloseableTab { // Tell a tab that it is closing // Return whether it should continue to close public boolean informClosed(); } public interface TabCloseListener { public void tabClosed(Component c); } //These variables are used by the CloseableTabComponent private static final Image m_x = GameVisualisation.getImageFromResource("x.gif"); private static final Color COLOR_NORMAL = Color.BLACK; private static final Color COLOR_FLASH = Color.RED; /** * The component used to display the tabs */ private class CloseableTabComponent extends JPanel implements ActionListener { private class CloseButton extends JPanel implements MouseListener { private boolean m_highlight = false; public CloseButton() { MediaTracker tracker = new MediaTracker( CloseableTabComponent.this); tracker.addImage(m_x, 0); try { tracker.waitForAll(); } catch (InterruptedException e) { e.printStackTrace(); } this.setPreferredSize(new Dimension(13, 13)); addMouseListener(this); } @Override public void paint(Graphics g) { Graphics2D g2 = (Graphics2D)g.create(); if (m_highlight) { int r = 12; g2.setColor(new Color(235, 235, 235)); g2.fillOval(0, 0, r, r); g2.setColor(new Color(200, 200, 200)); g2.drawOval(0, 0, r, r); } g2.drawImage(m_x, (13 - m_x.getWidth(null))/2, (13 - m_x.getHeight(null))/2, this); } public void mouseClicked(MouseEvent e) { removeTabAt(indexOfTabComponent(CloseableTabComponent.this)); } public void mousePressed(MouseEvent e) {} @Override public void mouseEntered(MouseEvent e) { m_highlight = true; CloseableTabComponent.this.repaint(); } @Override public void mouseExited(MouseEvent e) { m_highlight = false; CloseableTabComponent.this.repaint(); } public void mouseReleased(MouseEvent e) { CloseableTabComponent.this.repaint(); } } private Timer m_timer = new Timer(500, this); private JLabel m_label; private CloseButton m_close; private boolean m_closeable; public CloseableTabComponent(String text, boolean closeable) { m_label = new JLabel(text); m_close = new CloseButton(); m_closeable = closeable; setOpaque(false); layoutCloseableTab(); } public void setCloseable(boolean closeable) { m_closeable = closeable; layoutCloseableTab(); } public void setFlashing(boolean flashing) { if (flashing) { m_label.setForeground(COLOR_FLASH); m_timer.start(); } else { m_label.setForeground(COLOR_NORMAL); m_timer.stop(); repaint(); } } public void setText(String text) { m_label.setText(text); } private void layoutCloseableTab() { this.removeAll(); GridBagLayout layout = new GridBagLayout(); setLayout(layout); add(m_label); if (m_closeable) { GridBagConstraints constraints = new GridBagConstraints(); constraints.anchor = GridBagConstraints.PAGE_END; constraints.insets = new Insets(0, 4, 0, 0); layout.setConstraints(m_close, constraints); add(m_close); } } public void actionPerformed(ActionEvent e) { Color colour = (m_label.getForeground() == COLOR_NORMAL) ? COLOR_FLASH : COLOR_NORMAL; m_label.setForeground(colour); } } private java.util.List<TabCloseListener> m_listeners = new ArrayList<TabCloseListener>(); public void addTabCloseListener(TabCloseListener lst) { m_listeners.add(lst); } public void removeTabCloseListener(TabCloseListener val) { for (TabCloseListener lst : m_listeners) { if (lst.equals(val)) m_listeners.remove(lst); } } public void addTab(String name, Component c, boolean closeable) { this.insertTab(name, null, c, null, true, closeable, getTabCount()); } public void insertTab(String title, Icon icon, Component component, String tip, boolean slidable, boolean closeable, int index) { super.insertTab(title, icon, component, tip, slidable, index); Component tabComponent = new CloseableTabComponent(title, closeable); setTabComponentAt(index, tabComponent); } @Override public void insertTab(String title, Icon icon, Component component, String tip, boolean slidable, int index) { this.insertTab(title, icon, component, tip, slidable, true, index); } // While setTitleAt works, getTitleAt doesn't. This is because Swing sucks. @Override public void setTitleAt(int idx, String text) { if ((idx < 0) || (idx >= getTabCount())) { return; } CloseableTabComponent tab = (CloseableTabComponent)getTabComponentAt(idx); if (tab != null) { tab.setText(text); repaint(); } } @Override public void remove(int idx) { if (idx >= getTabCount()) return; Component tab = getComponentAt(idx); if (tab instanceof CloseableTab) { CloseableTab cTab = (CloseableTab)tab; if (!cTab.informClosed()) return; } super.remove(idx); for (TabCloseListener lst : m_listeners) { lst.tabClosed(tab); } } public void setFlashingAt(int idx, boolean flashing) { Component component = getTabComponentAt(idx); if (!(component instanceof CloseableTabComponent)) return; CloseableTabComponent tab = (CloseableTabComponent)component; if (tab != null) { tab.setFlashing(flashing); } } }