org.jdesktop.swingx.plaf.basic.BasicStatusBarUI Maven / Gradle / Ivy
Show all versions of swingx-all Show documentation
/*
* $Id: BasicStatusBarUI.java 3927 2011-02-22 16:34:11Z kleopatra $
*
* 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 org.jdesktop.swingx.JXStatusBar;
import org.jdesktop.swingx.JXStatusBar.Constraint;
import org.jdesktop.swingx.plaf.StatusBarUI;
import org.jdesktop.swingx.plaf.UIManagerExt;
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 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;
/**
* @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 = "auto-add-separator";
/**
* Used to help reduce the amount of trash being generated
*/
private static final Insets TEMP_INSETS = new Insets(0, 0, 0, 0);
/**
* 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
getSeparatorInsets(TEMP_INSETS);
for (int i = 0; i < statusBar.getComponentCount() - 1; i++) {
Component comp = statusBar.getComponent(i);
int x = comp.getX() + comp.getWidth() + TEMP_INSETS.left;
int y = TEMP_INSETS.top;
int w = getSeparatorWidth() - TEMP_INSETS.left - TEMP_INSETS.right;
int h = c.getHeight() - TEMP_INSETS.top - TEMP_INSETS.bottom;
paintSeparator((Graphics2D) g, statusBar, x, y, w, h);
}
}
}
//----------------------------------------------------- Extension Points
protected void paintBackground(Graphics2D g, JXStatusBar bar) {
if (bar.isOpaque()) {
g.setColor(bar.getBackground());
g.fillRect(0, 0, bar.getWidth(), bar.getHeight());
}
}
protected void paintSeparator(Graphics2D g, JXStatusBar bar, int x, int y, int w, int h) {
Color fg = UIManagerExt.getSafeColor("Separator.foreground", Color.BLACK);
Color bg = UIManagerExt.getSafeColor("Separator.background", Color.WHITE);
x += w / 2;
g.setColor(fg);
g.drawLine(x, y, x, h);
g.setColor(bg);
g.drawLine(x + 1, y, x + 1, h);
}
protected Insets getSeparatorInsets(Insets insets) {
if (insets == null) {
insets = new Insets(0, 0, 0, 0);
}
insets.top = 4;
insets.left = 4;
insets.bottom = 2;
insets.right = 4;
return insets;
}
protected int getSeparatorWidth() {
return 10;
}
protected boolean includeSeparators() {
Boolean b = (Boolean) statusBar.getClientProperty(AUTO_ADD_SEPARATOR);
return b == null || b.booleanValue();
}
protected BorderUIResource createBorder() {
return new BorderUIResource(BorderFactory.createEmptyBorder(4, 5, 4, 22));
}
protected LayoutManager createLayout() {
//This is in the UI delegate because the layout
//manager takes into account spacing for the separators between components
return new LayoutManager2() {
private final Map 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 (Map.Entry entry : constraints.entrySet()) {
Constraint c = entry.getValue();
Dimension d = entry.getKey().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 < preferredWidths.length; i++) {
preferredWidths[i] = getPreferredWidth(parent.getComponent(i));
sumPreferredWidths += preferredWidths[i];
}
//if the availableWidth is greater than the sum of preferred
//sizes, then adjust the preferred width of each component that
//has a FILL constraint, to evenly use up the extra space.
if (availableWidth > 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 < parent.getComponentCount(); i++) {
Component comp = parent.getComponent(i);
Constraint c = constraints.get(comp);
if (c != null && c.getResizeBehavior() == Constraint.ResizeBehavior.FILL) {
if (remaining > 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 < parent.getComponentCount(); i++) {
Component comp = parent.getComponent(i);
Constraint c = constraints.get(comp);
Insets insets = c == null ? new Insets(0, 0, 0, 0) : c.getInsets();
int width = preferredWidths[i] - (insets.left + insets.right);
int x = nextX + insets.left;
int y = parentInsets.top + insets.top;
comp.setSize(width, height);
comp.setLocation(x, y);
nextX = x + width + insets.right;
//If this is not the last component, add extra space
//for the separator
if (includeSeparators() && i < parent.getComponentCount() - 1) {
nextX += getSeparatorWidth();
}
}
}
/**
* @return the "preferred" width, where that means either
* comp.getPreferredSize().width + constraintInsets, or
* constraint.fixedWidth + constraintInsets.
*/
private int getPreferredWidth(Component comp) {
Constraint c = constraints.get(comp);
if (c == null) {
return comp.getPreferredSize().width;
} else {
Insets insets = c.getInsets();
assert insets != null;
if (c.getFixedWidth() <= 0) {
return comp.getPreferredSize().width + insets.left + insets.right;
} else {
return c.getFixedWidth() + insets.left + insets.right;
}
}
}
};
}
}