/*
JMeld is a visual diff and merge tool.
Copyright (C) 2007 Kees Kuip
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301 USA
*/
package org.jmeld.ui.util;
import org.jmeld.settings.JMeldSettings;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
/** Very ugly hack to make possible a close button on a
* tabbedpane (use it for jdk's before 1.6).
*/
public class TabIcon
implements Icon
{
// class variables:
private static int CLOSE_ICON_HEIGHT = 7;
private static int CLOSE_ICON_WIDTH = 7;
private static int SPACE_WIDTH = 5;
// instance variables:
private Icon icon;
private String text;
private int width;
private int height;
private JLabel label;
private int stringWidth;
private Rectangle closeBounds;
private JTabbedPane tabbedPane;
private Icon currentIcon;
private Icon closeIcon;
private Icon closeIcon_rollover;
private Icon closeIcon_pressed;
private Icon closeIcon_disabled;
private boolean pressed;
private boolean ignoreNextMousePressed;
private ChangeListener changeListener;
private MouseListener mouseListener;
private MouseMotionListener mouseMotionListener;
private ArrayList<TabExitListenerIF> tabExitListeners;
public TabIcon(Icon icon, String text)
{
this.icon = icon;
this.text = text;
init();
tabExitListeners = new ArrayList<TabExitListenerIF>();
}
private void init()
{
Font font;
FontMetrics fm;
height = 0;
width = 0;
closeIcon = ImageUtil.getImageIcon("jmeld_close");
closeIcon_rollover = ImageUtil.getImageIcon("jmeld_close-rollover");
closeIcon_pressed = ImageUtil.getImageIcon("jmeld_close-pressed");
closeIcon_disabled = ImageUtil.getImageIcon("jmeld_close-disabled");
if (closeIcon != null)
{
CLOSE_ICON_WIDTH = closeIcon.getIconWidth();
CLOSE_ICON_HEIGHT = closeIcon.getIconHeight();
}
if (icon != null)
{
height = height < icon.getIconHeight() ? icon.getIconHeight() : height;
width = width + icon.getIconWidth();
}
if (text != null)
{
label = new JLabel(text);
font = label.getFont();
fm = label.getFontMetrics(font);
height = height < fm.getHeight() ? fm.getHeight() : height;
stringWidth = fm.stringWidth(text);
width += stringWidth;
}
height = height < CLOSE_ICON_HEIGHT ? CLOSE_ICON_HEIGHT : height;
width += CLOSE_ICON_WIDTH;
if (icon != null)
{
width += SPACE_WIDTH;
}
if (text != null)
{
width += SPACE_WIDTH;
}
}
public void addExitListener(TabExitListenerIF listener)
{
tabExitListeners.add(listener);
}
public void removeExitListener(TabExitListenerIF listener)
{
tabExitListeners.remove(listener);
}
public void exit()
{
// This exit can be called in 2 different ways:
// 1. From within this Icon itself (pressing the close-button on the tab)
// 2. From within the application (for instance when pressing ESCAPE)
// This method was primarily made 'public' for the second way.
// Don't create memory leaks!
tabbedPane.removeMouseListener(getMouseListener());
tabbedPane.removeMouseMotionListener(getMouseMotionListener());
tabbedPane.removeChangeListener(getChangeListener());
}
public int getIconWidth()
{
return width;
}
public int getIconHeight()
{
return height;
}
public void paintIcon(Component c, Graphics g, int x, int y)
{
FontMetrics fm;
int x0;
int y0;
Rectangle b;
Icon cIcon;
Graphics2D g2;
g2 = (Graphics2D) g;
x0 = x;
if (tabbedPane == null)
{
tabbedPane = (JTabbedPane) c;
tabbedPane.addMouseListener(getMouseListener());
tabbedPane.addMouseMotionListener(getMouseMotionListener());
tabbedPane.addChangeListener(getChangeListener());
}
if (icon != null)
{
icon.paintIcon(c, g, x0, y + ((height - icon.getIconHeight()) / 2));
x0 += icon.getIconWidth();
x0 += SPACE_WIDTH;
}
if (text != null)
{
if (JMeldSettings.getInstance().getEditor().isAntialiasEnabled())
{
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
}
fm = label.getFontMetrics(label.getFont());
y0 = y + fm.getAscent() + ((height - fm.getHeight()) / 2);
g.setFont(label.getFont());
g.drawString(text, x0, y0);
x0 += stringWidth;
x0 += SPACE_WIDTH;
}
y0 = y + (height - CLOSE_ICON_HEIGHT) / 2;
if (closeIcon != null)
{
cIcon = currentIcon;
if (!isSelected())
{
cIcon = closeIcon_disabled;
}
if (cIcon == null)
{
cIcon = closeIcon;
}
cIcon.paintIcon(c, g, x0, y0);
closeBounds = new Rectangle(x0, y0, CLOSE_ICON_WIDTH, CLOSE_ICON_HEIGHT);
}
else
{
g.drawLine(x0, y0, x0 + CLOSE_ICON_HEIGHT, y0 + CLOSE_ICON_WIDTH);
g.drawLine(x0 + CLOSE_ICON_HEIGHT, y0, x0, y0 + CLOSE_ICON_WIDTH);
closeBounds = new Rectangle(x0, y0, CLOSE_ICON_WIDTH, CLOSE_ICON_HEIGHT);
}
x0 += CLOSE_ICON_WIDTH;
}
private MouseListener getMouseListener()
{
if (mouseListener == null)
{
mouseListener = new MouseAdapter()
{
public void mousePressed(MouseEvent me)
{
Icon icon;
if (ignoreNextMousePressed)
{
ignoreNextMousePressed = false;
return;
}
if (isCloseHit(me))
{
pressed = true;
icon = closeIcon_pressed;
if (currentIcon != icon)
{
currentIcon = icon;
tabbedPane.repaint();
}
}
else
{
pressed = false;
}
}
public void mouseReleased(MouseEvent me)
{
int index;
if (pressed && isCloseHit(me))
{
index = tabbedPane.indexOfTab(TabIcon.this);
// Only allow selected tabs to be closed.
if (index != tabbedPane.getSelectedIndex())
{
return;
}
if (index != -1)
{
for (TabExitListenerIF listener : tabExitListeners)
{
listener.doExit(new TabExitEvent(TabIcon.this, index));
}
me.consume();
}
}
if (currentIcon != closeIcon)
{
currentIcon = closeIcon;
tabbedPane.repaint();
}
}
};
}
return mouseListener;
}
private MouseMotionListener getMouseMotionListener()
{
if (mouseMotionListener == null)
{
mouseMotionListener = new MouseMotionAdapter()
{
public void mouseMoved(MouseEvent me)
{
Icon icon;
if (isSelected())
{
ignoreNextMousePressed = false;
}
if (isCloseHit(me))
{
icon = closeIcon_rollover;
}
else
{
pressed = false;
icon = closeIcon;
}
if (icon != currentIcon)
{
currentIcon = icon;
tabbedPane.repaint();
}
}
};
}
return mouseMotionListener;
}
private ChangeListener getChangeListener()
{
if (changeListener == null)
{
changeListener = new ChangeListener()
{
public void stateChanged(ChangeEvent ce)
{
ignoreNextMousePressed = true;
}
};
}
return changeListener;
}
private boolean isCloseHit(MouseEvent me)
{
return (!me.isConsumed() && closeBounds != null
&& closeBounds.contains(me.getX(), me.getY()) && isSelected());
}
private boolean isSelected()
{
int index;
index = tabbedPane.indexOfTab(this);
return (index != -1 && tabbedPane.getSelectedIndex() == index);
}
}