org.jdesktop.swingx.plaf.basic.BasicStatusBarUI Maven / Gradle / Ivy
Show all versions of swingx-all Show documentation
/*
* $Id$
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jdesktop.swingx.plaf.basic;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.LayoutManager2;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.HashMap;
import java.util.Map;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.LookAndFeel;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import javax.swing.plaf.BorderUIResource;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import org.jdesktop.swingx.JXStatusBar;
import org.jdesktop.swingx.JXStatusBar.Constraint;
import org.jdesktop.swingx.plaf.StatusBarUI;
import org.jdesktop.swingx.plaf.UIManagerExt;
/**
*
* @author rbair
* @author Karl Schaefer
*/
public class BasicStatusBarUI extends StatusBarUI {
private class Handler implements MouseListener, MouseMotionListener, PropertyChangeListener {
private Window window = SwingUtilities.getWindowAncestor(statusBar);
private int handleBoundary = getHandleBoundary();
private boolean validPress = false;
private Point startingPoint;
private int getHandleBoundary() {
Border border = statusBar.getBorder();
if (border == null || !statusBar.isResizeHandleEnabled()) {
return 0;
}
if (statusBar.getComponentOrientation().isLeftToRight()) {
return border.getBorderInsets(statusBar).right;
} else {
return border.getBorderInsets(statusBar).left;
}
}
private boolean isHandleAreaPoint(Point point) {
if (window == null || window.isMaximumSizeSet()) {
return false;
}
if (statusBar.getComponentOrientation().isLeftToRight()) {
return point.x >= statusBar.getWidth() - handleBoundary;
} else {
return point.x <= handleBoundary;
}
}
/**
* {@inheritDoc}
*/
@Override
public void mouseClicked(MouseEvent e) {
//does nothing
}
/**
* {@inheritDoc}
*/
@Override
public void mouseEntered(MouseEvent e) {
if (isHandleAreaPoint(e.getPoint())) {
if (statusBar.getComponentOrientation().isLeftToRight()) {
window.setCursor(Cursor.getPredefinedCursor(
Cursor.SE_RESIZE_CURSOR));
} else {
window.setCursor(Cursor.getPredefinedCursor(
Cursor.SW_RESIZE_CURSOR));
}
} else {
window.setCursor(null);
}
}
/**
* {@inheritDoc}
*/
@Override
public void mouseExited(MouseEvent e) {
if (!validPress) {
window.setCursor(null);
}
}
/**
* {@inheritDoc}
*/
@Override
public void mousePressed(MouseEvent e) {
validPress = SwingUtilities.isLeftMouseButton(e) && isHandleAreaPoint(e.getPoint());
startingPoint = e.getPoint();
SwingUtilities.convertPointToScreen(startingPoint, statusBar);
}
/**
* {@inheritDoc}
*/
@Override
public void mouseReleased(MouseEvent e) {
validPress = !SwingUtilities.isLeftMouseButton(e);
window.validate();
window.setCursor(null);
}
/**
* {@inheritDoc}
*/
@Override
public void mouseDragged(MouseEvent e) {
if (validPress) {
Rectangle wb = window.getBounds();
Point p = e.getPoint();
SwingUtilities.convertPointToScreen(p, statusBar);
wb.height += (p.y - startingPoint.y);
if (statusBar.getComponentOrientation().isLeftToRight()) {
wb.width += (p.x - startingPoint.x);
} else {
wb.x += (p.x - startingPoint.x);
wb.width += (startingPoint.x - p.x);
}
window.setBounds(wb);
window.validate();
startingPoint = p;
}
}
/**
* {@inheritDoc}
*/
@Override
public void mouseMoved(MouseEvent e) {
if (isHandleAreaPoint(e.getPoint())) {
if (statusBar.getComponentOrientation().isLeftToRight()) {
window.setCursor(Cursor.getPredefinedCursor(
Cursor.SE_RESIZE_CURSOR));
} else {
window.setCursor(Cursor.getPredefinedCursor(
Cursor.SW_RESIZE_CURSOR));
}
} else {
window.setCursor(null);
}
}
/**
* {@inheritDoc}
*/
@Override
public void propertyChange(PropertyChangeEvent evt) {
if ("ancestor".equals(evt.getPropertyName())) {
window = SwingUtilities.getWindowAncestor(statusBar);
boolean useResizeHandle = statusBar.getParent() != null
&& statusBar.getRootPane() != null
&& (statusBar.getParent() == statusBar.getRootPane()
|| statusBar.getParent() == statusBar.getRootPane().getContentPane());
statusBar.setResizeHandleEnabled(useResizeHandle);
} else if ("border".equals(evt.getPropertyName())) {
handleBoundary = getHandleBoundary();
} else if ("componentOrientation".equals(evt.getPropertyName())) {
handleBoundary = getHandleBoundary();
} else if ("resizeHandleEnabled".equals(evt.getPropertyName())) {
//TODO disable handle display
handleBoundary = getHandleBoundary();
}
}
}
public static final String AUTO_ADD_SEPARATOR = new StringBuffer("auto-add-separator").toString();
/**
* Used to help reduce the amount of trash being generated
*/
private static Insets TEMP_INSETS;
/**
* The one and only JXStatusBar for this UI delegate
*/
protected JXStatusBar statusBar;
protected MouseListener mouseListener;
protected MouseMotionListener mouseMotionListener;
protected PropertyChangeListener propertyChangeListener;
private Handler handler;
/** Creates a new instance of BasicStatusBarUI */
public BasicStatusBarUI() {
}
/**
* Returns an instance of the UI delegate for the specified component.
* Each subclass must provide its own static createUI
* method that returns an instance of that UI delegate subclass.
* If the UI delegate subclass is stateless, it may return an instance
* that is shared by multiple components. If the UI delegate is
* stateful, then it should return a new instance per component.
* The default implementation of this method throws an error, as it
* should never be invoked.
*/
public static ComponentUI createUI(JComponent c) {
return new BasicStatusBarUI();
}
/**
* {@inheritDoc}
*/
@Override
public void installUI(JComponent c) {
assert c instanceof JXStatusBar;
statusBar = (JXStatusBar)c;
installDefaults(statusBar);
installListeners(statusBar);
// only set the layout manager if the layout manager of the component is
// null or a UIResource. Do not replace custom layout managers.
LayoutManager m = statusBar.getLayout();
if (m == null || m instanceof UIResource) {
statusBar.setLayout(createLayout());
}
}
protected void installDefaults(JXStatusBar sb) {
//only set the border if it is an instanceof UIResource
//In other words, only replace the border if it has not been
//set by the developer. UIResource is the flag we use to indicate whether
//the value was set by the UIDelegate, or by the developer.
Border b = statusBar.getBorder();
if (b == null || b instanceof UIResource) {
statusBar.setBorder(createBorder());
}
LookAndFeel.installProperty(sb, "opaque", Boolean.TRUE);
}
private Handler getHandler() {
if (handler == null) {
handler = new Handler();
}
return handler;
}
/**
* Creates a {@code MouseListener} which will be added to the
* status bar. If this method returns null then it will not
* be added to the status bar.
*
* Subclasses may override this method to return instances of their own
* MouseEvent handlers.
*
* @return an instance of a {@code MouseListener} or null
*/
protected MouseListener createMouseListener() {
return getHandler();
}
/**
* Creates a {@code MouseMotionListener} which will be added to the
* status bar. If this method returns null then it will not
* be added to the status bar.
*
* Subclasses may override this method to return instances of their own
* MouseEvent handlers.
*
* @return an instance of a {@code MouseMotionListener} or null
*/
protected MouseMotionListener createMouseMotionListener() {
return getHandler();
}
/**
* Creates a {@code PropertyChangeListener} which will be added to the
* status bar. If this method returns null then it will not
* be added to the status bar.
*
* Subclasses may override this method to return instances of their own
* PropertyChangeEvent handlers.
*
* @return an instance of a {@code PropertyChangeListener} or null
*/
protected PropertyChangeListener createPropertyChangeListener() {
return getHandler();
}
/**
* Create and install the listeners for the status bar.
* This method is called when the UI is installed.
*/
protected void installListeners(JXStatusBar sb) {
if ((mouseListener = createMouseListener()) != null) {
statusBar.addMouseListener(mouseListener);
}
if ((mouseMotionListener = createMouseMotionListener()) != null) {
statusBar.addMouseMotionListener(mouseMotionListener);
}
if ((propertyChangeListener = createPropertyChangeListener()) != null) {
statusBar.addPropertyChangeListener(propertyChangeListener);
}
}
/**
* {@inheritDoc}
*/
@Override
public void uninstallUI(JComponent c) {
assert c instanceof JXStatusBar;
uninstallDefaults(statusBar);
uninstallListeners(statusBar);
if (statusBar.getLayout() instanceof UIResource) {
statusBar.setLayout(null);
}
}
protected void uninstallDefaults(JXStatusBar sb) {
if (sb.getBorder() instanceof UIResource) {
sb.setBorder(null);
}
}
/**
* Remove the installed listeners from the status bar.
* The number and types of listeners removed in this method should be
* the same that were added in installListeners
*/
protected void uninstallListeners(JXStatusBar sb) {
if (mouseListener != null) {
statusBar.removeMouseListener(mouseListener);
}
if (mouseMotionListener != null) {
statusBar.removeMouseMotionListener(mouseMotionListener);
}
if (propertyChangeListener != null) {
statusBar.removePropertyChangeListener(propertyChangeListener);
}
}
@Override
public void paint(Graphics g, JComponent c) {
//paint the background if opaque
if (statusBar.isOpaque()) {
Graphics2D g2 = (Graphics2D)g;
paintBackground(g2, statusBar);
}
if (includeSeparators()) {
//now paint the separators
TEMP_INSETS = getSeparatorInsets(TEMP_INSETS);
for (int i=0; i constraints = new HashMap();
@Override
public void addLayoutComponent(String name, Component comp) {addLayoutComponent(comp, null);}
@Override
public void removeLayoutComponent(Component comp) {constraints.remove(comp);}
@Override
public Dimension minimumLayoutSize(Container parent) {return preferredLayoutSize(parent);}
@Override
public Dimension maximumLayoutSize(Container target) {return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);}
@Override
public float getLayoutAlignmentX(Container target) {return .5f;}
@Override
public float getLayoutAlignmentY(Container target) {return .5f;}
@Override
public void invalidateLayout(Container target) {}
@Override
public void addLayoutComponent(Component comp, Object constraint) {
//we accept an Insets, a ResizeBehavior, or a Constraint.
if (constraint instanceof Insets) {
constraint = new Constraint((Insets)constraint);
} else if (constraint instanceof Constraint.ResizeBehavior) {
constraint = new Constraint((Constraint.ResizeBehavior)constraint);
}
constraints.put(comp, (Constraint)constraint);
}
@Override
public Dimension preferredLayoutSize(Container parent) {
Dimension prefSize = new Dimension();
int count = 0;
for (Component comp : constraints.keySet()) {
Constraint c = constraints.get(comp);
Dimension d = comp.getPreferredSize();
int prefWidth = 0;
if (c != null) {
Insets i = c.getInsets();
d.width += i.left + i.right;
d.height += i.top + i.bottom;
prefWidth = c.getFixedWidth();
}
prefSize.height = Math.max(prefSize.height, d.height);
prefSize.width += Math.max(d.width, prefWidth);
//If this is not the last component, add extra space between each
//component (for the separator).
count++;
if (includeSeparators() && constraints.size() < count) {
prefSize.width += getSeparatorWidth();
}
}
Insets insets = parent.getInsets();
prefSize.height += insets.top + insets.bottom;
prefSize.width += insets.left + insets.right;
return prefSize;
}
@Override
public void layoutContainer(Container parent) {
/*
* Layout algorithm:
* If the parent width is less than the sum of the preferred
* widths of the components (including separators), where
* preferred width means either the component preferred width +
* constraint insets, or fixed width + constraint insets, then
* simply layout the container from left to right and let the
* right hand components flow off the parent.
*
* Otherwise, lay out each component according to its preferred
* width except for components with a FILL constraint. For these,
* resize them evenly for each FILL constraint.
*/
//the insets of the parent component.
Insets parentInsets = parent.getInsets();
//the available width for putting components.
int availableWidth = parent.getWidth() - parentInsets.left - parentInsets.right;
if (includeSeparators()) {
//remove from availableWidth the amount of space the separators will take
availableWidth -= (parent.getComponentCount() - 1) * getSeparatorWidth();
}
//the preferred widths of all of the components -- where preferred
//width mean the preferred width after calculating fixed widths and
//constraint insets
int[] preferredWidths = new int[parent.getComponentCount()];
int sumPreferredWidths = 0;
for (int i=0; i sumPreferredWidths) {
//the number of components with a fill constraint
int numFilledComponents = 0;
for (Component comp : parent.getComponents()) {
Constraint c = constraints.get(comp);
if (c != null && c.getResizeBehavior() == Constraint.ResizeBehavior.FILL) {
numFilledComponents++;
}
}
if (numFilledComponents > 0) {
//calculate the share of free space each FILL component will take
availableWidth -= sumPreferredWidths;
double weight = 1.0 / (double)numFilledComponents;
int share = (int)(availableWidth * weight);
int remaining = numFilledComponents;
for (int i=0; i 1) {
preferredWidths[i] += share;
availableWidth -= share;
} else {
preferredWidths[i] += availableWidth;
}
remaining--;
}
}
}
}
//now lay out the components
int nextX = parentInsets.left;
int height = parent.getHeight() - parentInsets.top - parentInsets.bottom;
for (int i=0; i