/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Liquid Look and Feel *
* *
* Author, Miroslav Lazarevic *
* *
* For licensing information and credits, please refer to the *
* comment in file com.birosoft.liquid.LiquidLookAndFeel *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
package com.birosoft.liquid;
import com.birosoft.liquid.util.LiquidUtilities;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Event;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.font.FontRenderContext;
import java.awt.font.LineMetrics;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JInternalFrame;
import javax.swing.UIManager;
import javax.swing.plaf.basic.BasicInternalFrameTitlePane;
/**
* This class represents the title pane for the JInternalFrame components.
*
* @author Taoufik Romdhane
*/
public class LiquidInternalFrameTitlePane extends BasicInternalFrameTitlePane
implements LayoutManager {
/**
*
*/
private static final long serialVersionUID = 2216160519095598985L;
static LiquidWindowButtonUI iconButtonUI;
static LiquidWindowButtonUI maxButtonUI;
static LiquidWindowButtonUI closeButtonUI;
protected boolean isPalette = false;
protected Icon paletteCloseIcon;
protected int paletteTitleHeight;
/**
* Color for the title in a normal sized internal frame
*/
Color normalTitleColor = Color.white;
/**
* Color for the shadow of the title in a normal sized internal frame
*/
Color shadowColor = new Color(10, 24, 131);
/**
* Color for the title in a normal sized internal frame that is not enabled
*/
Color disabledTitleColor = new Color(64, 63, 63);
/**
* The frame's title height, read from the UIDefaults table.
*/
protected int frameTitleHeight;
/**
* This constructor creates a title pane for the given internal frame
* instance.
*
* @param frame The internal frame that needs a title pane.
*/
public LiquidInternalFrameTitlePane(JInternalFrame frame) {
super(frame);
//if (LiquidInternalFrameUI.allowRoundedWindows)
//setOpaque(false);
}
/**
* Installs some default values.
* Reads the internalframe title height from the ui defaults table.
*/
protected void installDefaults() {
super.installDefaults();
frameTitleHeight = UIManager.getInt("InternalFrame.frameTitleHeight");
Font font = isPalette ? UIManager.getFont("InternalFrame.paletteTitleFont")//super.getFont()
: (LiquidLookAndFeel.winDecoPanther ? UIManager.getFont("InternalFrame.pantherTitleFont")
: UIManager.getFont("InternalFrame.titleFont"));
// setFont(UIManager.getFont("InternalFrame.titleFont"));
setFont(font);
paletteTitleHeight = UIManager.getInt(
"InternalFrame.paletteTitleHeight");
paletteCloseIcon = UIManager.getIcon("InternalFrame.paletteCloseIcon");
//wasClosable = frame.isClosable();
}
protected void uninstallDefaults() {
super.uninstallDefaults();
//if (wasClosable != frame.isClosable()) {
// frame.setClosable(wasClosable);
//}
}
protected void paintTitleBackground(Graphics g) {
return;
}
/**
* Paints this component.
*
* @param g The graphics context to use.
*/
public void paintComponent(Graphics g) {
// if (isPalette)
// {
// paintPalette(g);
// return;
// }
boolean leftToRight = frame.getComponentOrientation().isLeftToRight();
boolean isSelected = frame.isSelected();
Insets insets = frame.getInsets();
int width = getWidth();
int height = getHeight();
Graphics2D g2 = (Graphics2D) g;
Object oldAntiAliasingValue = g2.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
if (LiquidLookAndFeel.winDecoPanther) {
drawPantherCaption(g, isSelected, width, height);
} else {
drawLiquidCaption(g, isSelected, width, height);
}
int xOffset = leftToRight && !LiquidLookAndFeel.winDecoPanther ? 2 : (width - 2);
int xOvalOffset = 8;
// 20060212 MEV - Correct InternalFrame for Mac decorations
Icon icon = null;
if (LiquidLookAndFeel.winDecoPanther) {
icon = UIManager.getIcon("InternalFrame.pantherFrameIcon");
} else {
icon = frame.getFrameIcon();
}
if (icon != null) {
if (!leftToRight || LiquidLookAndFeel.winDecoPanther) {
xOffset -= icon.getIconWidth();
}
int iconY = ((height / 2) - (icon.getIconHeight() / 2));
icon.paintIcon(frame, g, xOffset, iconY);
xOffset += (leftToRight && !LiquidLookAndFeel.winDecoPanther ? (icon.getIconWidth() + 2) : (-2));
}
// Add in the oval offset
xOffset += (leftToRight && !LiquidLookAndFeel.winDecoPanther) ? xOvalOffset : 0;
String frameTitle = frame.getTitle();
int titleLength = 0;
if (frameTitle != null) {
Font f = getFont();
g.setFont(f);
int osFontOffset = f.getFamily().equals(LiquidLookAndFeel.fontName) ? 2 : 0;
FontMetrics fm = g.getFontMetrics();
int titleW = 0;
Rectangle r = new Rectangle(0, 0, 0, 0);
if( leftToRight && !LiquidLookAndFeel.winDecoPanther ) {
if (frame.isIconifiable()) {
r = iconButton.getBounds();
} else if (frame.isMaximizable()) {
r = maxButton.getBounds();
} else if (frame.isClosable()) {
r = closeButton.getBounds();
}
if( r.x == 0 ) {
r.x = frame.getWidth()-frame.getInsets().right;
}
xOffset += menuBar != null ? (menuBar.getX() + menuBar.getWidth() + 2) : 2;
titleW = r.x - xOffset - (!isPalette ? xOvalOffset : 0);
} else {
if (frame.isMaximizable()) {
r = maxButton.getBounds();
} else if (frame.isIconifiable()) {
r = iconButton.getBounds();
} else if (frame.isClosable()) {
r = closeButton.getBounds();
}
Rectangle menu = new Rectangle(0, 0, 0, 0);
menu.x = frame.getWidth() - insets.right;
xOffset = !isPalette ? (r.x + r.width + 2) : 10;
titleW = menu.x - xOffset;
}
int startTitleLength = fm.stringWidth(frameTitle);
frameTitle = LiquidUtilities.clipStringIfNecessary(fm, frameTitle, titleW);
titleLength = fm.stringWidth(frameTitle);
if( titleLength == startTitleLength && !isPalette ) {
xOffset += (titleW - titleLength) / 2;
}
FontRenderContext frc = g2.getFontRenderContext();
LineMetrics lm = f.getLineMetrics(frameTitle, frc);
// System.out.println("Internal: Title Height = " + height);
//
// System.out.println("Internal: osFontOffset = " + osFontOffset);
// System.out.println("Internal: Font = " + f);
// System.out.println("Internal: Font.size = " + f.getSize());
// System.out.println("Internal: FontMetrics.getAscent = " + fm.getAscent());
// System.out.println("Internal: FontMetrics.getDescent = " + fm.getDescent());
// System.out.println("Internal: FontMetrics.getHeight = " + fm.getHeight());
//
// System.out.println("Internal: LineMetrics.getAscent = " + lm.getAscent());
// System.out.println("Internal: LineMetrics.getDescent = " + lm.getDescent());
// System.out.println("Internal: LineMetrics.getHeight = " + lm.getHeight());
// int yOffset = ((height - f.getSize()) / 2) + fm.getAscent() + osFontOffset - 1;
int yOffset = ((height - Math.round(lm.getHeight())) / 2) + Math.round(lm.getAscent()) + osFontOffset;
int endOffset = 19;//yOffset + Math.round(lm.getDescent());//height - (yOffset - 15) - Math.round(lm.getDescent());
// System.out.println("Internal: yOffset = " + yOffset);
// System.out.println("Internal: endOffset = " + endOffset);
if (isPalette) {
if (isSelected) {
g.setColor(Color.white);
} else {
if (!LiquidLookAndFeel.winDecoPanther) {
g.setColor(Color.black);
} else {
g.setColor(Color.gray);
}
}
if (!leftToRight) {
xOffset -= titleLength;
}
// Take out the oval offset since the Palette doesn't get the oval
xOffset -= xOvalOffset;
g2.drawString(frameTitle, xOffset, yOffset);
xOffset += (leftToRight ? (titleLength + 2) : (-2));
} else {
// a shadow effect for the titles in normal sized internal frames
// int yOffset = ((height - f.getSize()) / 2) + fm.getAscent() + osFontOffset;
if (!leftToRight) {
xOffset -= titleLength;
}
//xOffset = (width / 2) - (titleLength / 2);
if (isSelected) {
// for an active window
if (!LiquidLookAndFeel.winDecoPanther) {
GradientPaint grad = new GradientPaint(xOffset +
(titleLength / 2), yOffset - 15,
new Color(60, 144, 233),
xOffset + (titleLength / 2),
endOffset, new Color(102, 186, 255));
g2.setPaint(grad);
g2.fillRoundRect(xOffset - 8, yOffset - 15,
titleLength + 15, endOffset, 18, 18);
g.setColor(new Color(0, 78, 167));
g2.drawRoundRect(xOffset - 8, yOffset - 15,
titleLength + 15, endOffset, 18, 18);
}
if (!LiquidLookAndFeel.winDecoPanther) {
g.setColor(shadowColor);//Color.black);
g.drawString(frameTitle, xOffset + 1, yOffset);
g.setColor(normalTitleColor);
} else {
g.setColor(Color.black);
}
g.drawString(frameTitle, xOffset, yOffset - 1);
xOffset += (leftToRight ? (titleLength + 2) : (-2));
} else {
// for an inactive window
// 20060212 MEV - Correct InternalFrame for Mac decorations
if (!LiquidLookAndFeel.winDecoPanther) {
GradientPaint grad = new GradientPaint(xOffset +
(titleLength / 2), yOffset - 15,
new Color(191, 211, 233),
xOffset + (titleLength / 2), endOffset,
new Color(233, 253, 255));
g2.setPaint(grad);
g2.fillRoundRect(xOffset - 8, yOffset - 15,
titleLength + 15, endOffset, 18, 18);
g.setColor(new Color(125, 145, 167));
g2.drawRoundRect(xOffset - 8, yOffset - 15,
titleLength + 15, endOffset, 18, 18);
}
// 20060212 MEV - Correct InternalFrame for Mac decorations to have inactive title gray
if (!LiquidLookAndFeel.winDecoPanther) {
g.setColor(Color.black);
} else {
g.setColor(Color.GRAY);
}
g.drawString(frameTitle, xOffset, yOffset - 1);
xOffset += (leftToRight ? (titleLength + 2) : (-2));
}
}
}
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldAntiAliasingValue);
}
private void drawLiquidCaption(Graphics g, boolean isSelected, int w, int h) {
Color c = isSelected ? new Color(62, 145, 235) : new Color(175, 214, 255);
g.setColor(c);
g.fillRect(0, 0, w, h - 1);
c = isSelected ? new Color(94, 172, 255) : new Color(226, 240, 255);
g.setColor(c);
g.drawLine(0, 0, w, 0);
c = isSelected ? new Color(60, 141, 228) : new Color(170, 207, 247);
g.setColor(c);
g.drawLine(0, 1, w, 1);
for (int i = 4; i < (h - 1); i += 4) {
c = isSelected ? new Color(59, 138, 223) : new Color(166, 203, 242);
g.setColor(c);
g.drawLine(0, i, w, i);
c = isSelected ? new Color(60, 141, 228) : new Color(170, 207, 247);
g.setColor(c);
g.drawLine(0, i + 1, w, i + 1);
}
c = isSelected ? new Color(47, 111, 180) : new Color(135, 164, 196);
g.setColor(c);
g.drawLine(0, h - 1, w, h - 1);
}
private void drawPantherCaption(Graphics g, boolean isSelected, int w, int h) {
Graphics2D g2 = (Graphics2D) g;
GradientPaint grad = isSelected
? new GradientPaint(0, 0, new Color(238, 238, 238), 0, h - 1,
new Color(192, 192, 192))
: new GradientPaint(0, 0, new Color(230, 230, 230), 0, h - 1,
new Color(202, 202, 202));
g2.setPaint(grad);
g2.fillRect(0, 0, w, h - 1);
g2.setColor(new Color(198, 198, 198));
g2.drawLine(0, 0, w - 1, 0);
g2.setColor(Color.WHITE);
g2.drawLine(0, 1, w - 1, 1);
g2.setColor(new Color(147, 147, 147));
g2.drawLine(0, h - 1, w, h - 1);
}
/**
* Creates the layout manager for the title pane.
*
* @return The layout manager for the title pane.
*/
protected LayoutManager createLayout() {
return this;
}
protected void addSubComponents() {
//super.addSubComponents();
add(iconButton);
add(maxButton);
add(closeButton);
if (menuBar != null) {
//menuBar.setOpaque(false);
}
}
protected void setButtonIcons() {
// System.err.println("setButtonIcons called!");
}
/**
* Creates the buttons of the title pane and initilizes their actions.
*/
protected void createButtons() {
// Boolean paintActive = frame.isSelected() ? Boolean.TRUE:Boolean.FALSE;
if (iconButtonUI == null) {
iconButtonUI = LiquidWindowButtonUI.createButtonUIForType(LiquidWindowButtonUI.MINIMIZE);
maxButtonUI = LiquidWindowButtonUI.createButtonUIForType(LiquidWindowButtonUI.MAXIMIZE);
closeButtonUI = LiquidWindowButtonUI.createButtonUIForType(LiquidWindowButtonUI.CLOSE);
}
iconButton = new SpecialUIButton(iconButtonUI, frame);
iconButton.addActionListener(iconifyAction);
iconButton.setRolloverEnabled(true);
iconButton.addMouseListener(new RolloverListener(iconButton,
iconifyAction));
maxButton = new SpecialUIButton(maxButtonUI, frame);
maxButton.addActionListener(maximizeAction);
maxButton.setRolloverEnabled(true);
maxButton.addMouseListener(new RolloverListener(maxButton,
maximizeAction));
closeButton = new SpecialUIButton(closeButtonUI, frame);
closeButton.addActionListener(closeAction);
closeButton.setRolloverEnabled(true);
closeButton.addMouseListener(new RolloverListener(closeButton,
closeAction));
iconButton.getAccessibleContext().setAccessibleName(UIManager.getString(
"InternalFrameTitlePane.iconifyButtonAccessibleName"));
maxButton.getAccessibleContext().setAccessibleName(UIManager.getString(
"InternalFrameTitlePane.maximizeButtonAccessibleName"));
closeButton.getAccessibleContext().setAccessibleName(UIManager.getString(
"InternalFrameTitlePane.closeButtonAccessibleName"));
if (frame.isSelected()) {
activate();
} else {
deactivate();
}
}
/**
* Paints the title pane for a palette.
*
* @param g The graphics context to use.
*/
public void paintPalette(Graphics g) {
return;
}
public void setPalette(boolean b) {
isPalette = b;
if (isPalette) {
closeButton.setIcon(paletteCloseIcon);
if (frame.isMaximizable()) {
remove(maxButton);
}
if (frame.isIconifiable()) {
remove(iconButton);
}
} else {
closeButton.setIcon(closeIcon);
if (frame.isMaximizable()) {
add(maxButton);
}
if (frame.isIconifiable()) {
add(iconButton);
}
}
revalidate();
repaint();
}
/**
* Adds the specified component with the specified name to the layout.
*
* @param name the component name
* @param c the component to be added
*/
public void addLayoutComponent(String name, Component c) {
}
/**
* Removes the specified component from the layout.
*
* @param c the component to be removed
*/
public void removeLayoutComponent(Component c) {
}
/**
* Calculates the preferred size dimensions for the specified
* panel given the components in the specified parent container.
*
* @param c the component to be laid out
*/
public Dimension preferredLayoutSize(Container c) {
return getPreferredSize(c);
}
/**
* Gets the preferred size of the given container.
*
* @param c The container to gets its preferred size.
* @return The preferred size of the given container.
*/
public Dimension getPreferredSize(Container c) {
return new Dimension(c.getSize().width,
(isPalette ? paletteTitleHeight : frameTitleHeight));
}
/**
* The minimum size of the frame.
* This is used, for example, during resizing to
* find the minimum allowable size.
* Providing at least some minimum size fixes a bug
* which breaks horizontal resizing.
* <b>Note</b>: the Motif plaf allows for a 0,0 min size,
* but we provide a reasonable minimum here.
* <b>Future</b>: calculate min size based upon contents.
*/
public Dimension getMinimumSize() {
return new Dimension(70, 25);
}
/**
* Calculates the minimum size dimensions for the specified
* panel given the components in the specified parent container.
*
* @param c the component to be laid out
*/
public Dimension minimumLayoutSize(Container c) {
return preferredLayoutSize(c);
}
/**
* Lays out the container in the specified panel.
*
* @param c the component which needs to be laid out
*/
public void layoutContainer(Container c) {
boolean leftToRight = frame.getComponentOrientation().isLeftToRight();
if( LiquidLookAndFeel.winDecoPanther ) {
leftToRight = !leftToRight;
}
int buttonHeight = closeButton.getPreferredSize().height;
int w = getWidth();
int x = leftToRight ? w : 0;
int y = ((getHeight() - buttonHeight) / 2) + 1;
int spacing;
// assumes all buttons have the same dimensions
// these dimensions include the borders
// int buttonWidth = 18; //closeButton.getIcon().getIconWidth();
int buttonWidth = closeButton.getPreferredSize().width;
if (frame.isClosable()) {
if (isPalette) {
spacing = LiquidLookAndFeel.winDecoPanther ? 7 : 0;
x += (leftToRight ? (-spacing - (buttonWidth)) : spacing);
closeButton.setBounds(x, y, buttonWidth, buttonHeight);
if (!leftToRight) {
x += (buttonWidth);
}
} else {
spacing = LiquidLookAndFeel.winDecoPanther ? 7 : 0;
x += (leftToRight ? (-spacing - buttonWidth) : spacing);
closeButton.setBounds(x, y, buttonWidth, buttonHeight);
if (!leftToRight) {
x += buttonWidth;
}
}
}
if( LiquidLookAndFeel.winDecoPanther ) {
if (frame.isIconifiable() && !isPalette ) {//&& !frame.isIcon()) {
//spacing = 0; //frame.isMaximizable() ? 0 : (frame.isClosable() ? 0 : 2);
spacing = 7;//frame.isMaximizable() ? (LiquidLookAndFeel.winDecoPanther ? 7 : 0) : (frame.isClosable() ? (LiquidLookAndFeel.winDecoPanther ? 8 : 0) : 0);
x += (leftToRight ? (-spacing - buttonWidth) : spacing);
iconButton.setBounds(x, y, buttonWidth, buttonHeight);
if (!leftToRight) {
x += buttonWidth;
}
}
}
if (frame.isMaximizable() && !isPalette) {
if( !LiquidLookAndFeel.winDecoPanther || !frame.isIcon() ) {
spacing = (LiquidLookAndFeel.winDecoPanther ? 7 : 0);//frame.isClosable() ? (LiquidLookAndFeel.winDecoPanther ? 8 : 0) : 0;
x += (leftToRight ? (-spacing - buttonWidth) : spacing);
maxButton.setBounds(x, y, buttonWidth, buttonHeight);
if (!leftToRight) {
x += buttonWidth;
}
}
}
if( !LiquidLookAndFeel.winDecoPanther ) {
if (frame.isIconifiable() && !isPalette) {
//spacing = 0; //frame.isMaximizable() ? 0 : (frame.isClosable() ? 0 : 2);
spacing = 0;//frame.isMaximizable() ? (LiquidLookAndFeel.winDecoPanther ? 7 : 0) : (frame.isClosable() ? (LiquidLookAndFeel.winDecoPanther ? 8 : 0) : 0);
x += (leftToRight ? (-spacing - buttonWidth) : spacing);
iconButton.setBounds(x, y, buttonWidth, buttonHeight);
if (!leftToRight) {
x += buttonWidth;
}
}
}
}
public void activate() {
closeButton.setEnabled(true);
iconButton.setEnabled(true);
maxButton.setEnabled(true);
}
public void deactivate() {
closeButton.setEnabled(false);
iconButton.setEnabled(false);
maxButton.setEnabled(false);
}
/**
* This listener is added to the maximize, minimize and close button to
* manage the rollover status of the buttons
*
*/
class RolloverListener implements MouseListener {
JButton button;
Action action;
public RolloverListener(JButton b, Action a) {
button = b;
action = a;
}
public void mouseClicked(MouseEvent e) {
action.actionPerformed(new ActionEvent(this, Event.ACTION_EVENT,
button.getText()));
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
button.getModel().setRollover(true);
if (!button.isEnabled()) {
button.setEnabled(true);
}
button.repaint();
}
public void mouseExited(MouseEvent e) {
button.getModel().setRollover(false);
if (!frame.isSelected()) {
button.setEnabled(false);
}
button.repaint();
}
}
}