/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is NetBeans. The Initial Developer of the Original
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
* Microsystems, Inc. All Rights Reserved.
*/
package org.freehep.application.mdi;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import javax.swing.JPopupMenu;
import javax.swing.JTabbedPane;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.freehep.swing.popup.HasPopupItems;
import org.freehep.util.images.ImageHandler;
/** <code>JTabbedPane</code> which also optionally uses close button on tab.
*
* @author Tran Duc Trung (freehep mods by tonyj)
* @version $Id: CloseButtonTabbedPane.java 8584 2006-08-10 23:06:37Z duns $
*
*/
class CloseButtonTabbedPane extends JTabbedPane implements HasPopupItems
{
public static final boolean CLOSE_BUTTON_ENABLED = true;
/**
* this string is appended to the tab name to make room for the small close
* button. Ideally one should be able to set the insets for the tab
* handles.
*/
static final String TAB_NAME_TRAILING_SPACE;
static
{
if (CLOSE_BUTTON_ENABLED)
{
//if(Dependency.JAVA_SPEC.compareTo(
//new SpecificationVersion("1.4")) < 0) { // NOI18N
// TAB_NAME_TRAILING_SPACE = " "; // NOI18N
//} else {
TAB_NAME_TRAILING_SPACE = " "; // NOI18N
//}
}
else
{
TAB_NAME_TRAILING_SPACE = ""; // NOI18N
}
}
private static Image closeTabImage = ImageHandler.getImage("tabclose", CloseButtonTabbedPane.class);
private static Image closeTabInactiveImage = ImageHandler.getImage("tabcloseinactive", CloseButtonTabbedPane.class);
private boolean draggedOut = false;
private int mouseOverCloseButtonIndex = -1;
private int pressedCloseButtonIndex = -1;
CloseButtonTabbedPane()
{
if (CLOSE_BUTTON_ENABLED)
{
CloseButtonListener cl = new CloseButtonListener();
addMouseListener(cl);
addMouseMotionListener(cl);
addChangeListener(cl);
}
}
// #24033. Needles insets in tabbed panes (making difference between single).
/** Overrides superclass method.
* @return [0, 0, 0, 0] insets */
public Insets getInsets()
{
return new Insets(0, 0, 0, 0);
}
public void paint(Graphics g)
{
super.paint(g);
if (!CLOSE_BUTTON_ENABLED)
{
return;
}
// Have a look at
// http://ui.netbeans.org/docs/ui/closeButton/closeButtonUISpec.html
// to see how the buttons are specified to be drawn.
int selectedIndex = getSelectedIndex();
for (int i = 0, n = getTabCount(); i < n; i++)
{
Rectangle r = getCloseButtonBoundsAt(i);
if (r == null)
{
continue;
}
if ((i == pressedCloseButtonIndex) && !draggedOut)
{
g.setColor(new Color(153, 153, 153));
g.fillRect(r.x, r.y, r.width, r.height);
}
if (i != selectedIndex) // && i != mouseOverCloseButtonIndex && i != pressedCloseButtonIndex)
{
g.drawImage(closeTabInactiveImage, r.x + 2, r.y + 2, this);
}
else
{
g.drawImage(closeTabImage, r.x + 2, r.y + 2, this);
}
if ((i == mouseOverCloseButtonIndex) || (i == pressedCloseButtonIndex && draggedOut))
{
g.setColor(new Color(102, 102, 102));
g.drawRect(r.x, r.y, r.width, r.height);
g.setColor((i == selectedIndex) ? new Color(255, 255, 255) : new Color(204, 204, 204));
g.drawRect(r.x + 1, r.y + 1, r.width, r.height);
// Draw the dots.
g.setColor(new Color(162, 162, 162));
g.drawLine(r.x + r.width, r.y + 1, r.x + r.width, r.y + 1);
g.drawLine(r.x + 1, r.y + r.height, r.x + 1, r.y + r.height);
}
else if (i == pressedCloseButtonIndex)
{
g.setColor(new Color(102, 102, 102));
g.drawRect(r.x, r.y, r.width, r.height);
g.setColor((i == selectedIndex) ? new Color(255, 255, 255) : new Color(204, 204, 204));
g.drawLine(r.x + 1, r.y + r.height + 1, r.x + r.width + 1, r.y + r.height + 1);
g.drawLine(r.x + r.width + 1, r.y + 1, r.x + r.width + 1, r.y + r.height + 1);
// Draw the lines.
g.setColor(new Color(153, 153, 153));
g.drawLine(r.x + 1, r.y + 1, r.x + r.width, r.y + 1);
g.drawLine(r.x + 1, r.y + 1, r.x + 1, r.y + r.height);
}
}
}
protected void fireCloseTabAt(int index)
{
removeTabAt(index);
}
private Rectangle getCloseButtonBoundsAt(int i)
{
Rectangle b = getBoundsAt(i);
if (b == null)
{
return null;
}
else
{
b = new Rectangle(b);
//JdkBug4620540Hack.fixGetBoundsAt(b);
Dimension tabsz = getSize();
if (((b.x + b.width) >= tabsz.width) || ((b.y + b.height) >= tabsz.height))
{
return null;
}
return new Rectangle((b.x + b.width) - 13, (b.y + (b.height / 2)) - 5, 8, 8);
}
}
private void setMouseOverCloseButtonIndex(int index)
{
if (mouseOverCloseButtonIndex == index)
{
return;
}
if (mouseOverCloseButtonIndex >= 0)
{
Rectangle r = getCloseButtonBoundsAt(mouseOverCloseButtonIndex);
repaint(r.x, r.y, r.width + 2, r.height + 2);
//TopComponent tc =
// (TopComponent) getComponentAt(mouseOverCloseButtonIndex);
//setToolTipTextAt(mouseOverCloseButtonIndex, tc.getToolTipText());
}
mouseOverCloseButtonIndex = index;
if (mouseOverCloseButtonIndex >= 0)
{
Rectangle r = getCloseButtonBoundsAt(mouseOverCloseButtonIndex);
repaint(r.x, r.y, r.width + 2, r.height + 2);
setPressedCloseButtonIndex(-1);
setToolTipTextAt(mouseOverCloseButtonIndex, null);
}
}
private void setPressedCloseButtonIndex(int index)
{
if (pressedCloseButtonIndex == index)
{
return;
}
if (pressedCloseButtonIndex >= 0)
{
Rectangle r = getCloseButtonBoundsAt(pressedCloseButtonIndex);
repaint(r.x, r.y, r.width + 2, r.height + 2);
//TopComponent tc =
// (TopComponent) getComponentAt(pressedCloseButtonIndex);
//setToolTipTextAt(pressedCloseButtonIndex, tc.getToolTipText());
}
pressedCloseButtonIndex = index;
if (pressedCloseButtonIndex >= 0)
{
Rectangle r = getCloseButtonBoundsAt(pressedCloseButtonIndex);
repaint(r.x, r.y, r.width + 2, r.height + 2);
setMouseOverCloseButtonIndex(-1);
setToolTipTextAt(pressedCloseButtonIndex, null);
}
}
private void reset()
{
setMouseOverCloseButtonIndex(-1);
setPressedCloseButtonIndex(-1);
draggedOut = false;
}
public JPopupMenu modifyPopupMenu(JPopupMenu menu,Component source,Point p) {
return menu;
}
private class CloseButtonListener extends MouseAdapter implements MouseMotionListener, ChangeListener
{
public void mouseDragged(MouseEvent e)
{
if (pressedCloseButtonIndex >= 0)
{
Rectangle r = getCloseButtonBoundsAt(pressedCloseButtonIndex);
if (r != null &&
draggedOut != !r.contains(e.getPoint()))
{
draggedOut = !r.contains(e.getPoint());
repaint(r.x, r.y, r.width + 2, r.height + 2);
}
e.consume();
}
}
public void mouseMoved(MouseEvent e)
{
int index = indexAtLocation(e.getX(), e.getY());
if (index >= 0)
{
Rectangle r = getCloseButtonBoundsAt(index);
if (r != null && r.contains(e.getPoint()))
{
setMouseOverCloseButtonIndex(index);
draggedOut = false;
e.consume();
}
else if (mouseOverCloseButtonIndex >= 0)
{
setMouseOverCloseButtonIndex(-1);
draggedOut = false;
e.consume();
}
}
}
public void mousePressed(MouseEvent e)
{
int index = indexAtLocation(e.getX(), e.getY());
if (index >= 0)
{
Rectangle r = getCloseButtonBoundsAt(index);
if (r != null && r.contains(e.getPoint()))
{
setPressedCloseButtonIndex(index);
draggedOut = false;
e.consume();
}
}
}
public void mouseReleased(MouseEvent e)
{
int tabIndex = pressedCloseButtonIndex;
if (tabIndex >= 0)
{
Rectangle r = getCloseButtonBoundsAt(tabIndex);
if (r != null && r.contains(e.getPoint()) && (tabIndex >= 0))
{
reset();
// A bug in the XP look and feel, under JDK 1.5 beta causes an NPE
// after this. No current workaround.
fireCloseTabAt(tabIndex);
e.consume();
return;
}
}
reset();
}
public void stateChanged(ChangeEvent e)
{
reset();
}
}
}