mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-06 17:16:12 +00:00
518 lines
14 KiB
Java
518 lines
14 KiB
Java
/* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public
|
|
* License Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code is the Grendel mail/news client.
|
|
*
|
|
* The Initial Developer of the Original Code is Netscape Communications
|
|
* Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1997 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* Created: Will Scullin <scullin@netscape.com>, 26 Aug 1997.
|
|
*/
|
|
|
|
package grendel.widgets;
|
|
|
|
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.Insets;
|
|
import java.awt.LayoutManager;
|
|
import java.awt.LayoutManager2;
|
|
import java.awt.Point;
|
|
import java.awt.Rectangle;
|
|
import java.awt.event.MouseAdapter;
|
|
import java.awt.event.MouseEvent;
|
|
import java.awt.event.MouseMotionListener;
|
|
|
|
import java.util.Enumeration;
|
|
import java.util.Hashtable;
|
|
import java.util.Vector;
|
|
|
|
import javax.swing.JComponent;
|
|
import javax.swing.UIManager;
|
|
|
|
/**
|
|
* This class is used to create a divided pane with moveable dividers (NYI)
|
|
* Components can be added with numeric weights that will determine how
|
|
* much of the pane they will occupy:
|
|
* <code>add(aComponent, new Float(.5))</code>
|
|
* If no weight is given the default is 1.
|
|
* <p>
|
|
* (Soon to be) Moveable separators can be created by the
|
|
* <code>createSeparator()</code>
|
|
* method. The width of the separator is fixed in pixels.
|
|
*
|
|
* @author Will Scullin
|
|
*/
|
|
|
|
//
|
|
// Splitter Class
|
|
//
|
|
|
|
public class Splitter extends JComponent {
|
|
Hashtable fWeights = new Hashtable();
|
|
float fTotalWeight = 0.0f;
|
|
int fTotalSeparators = 0;
|
|
|
|
int fOrientation;
|
|
|
|
int fMinSize = 16;
|
|
|
|
SplitterSeparatorListener fListener;
|
|
|
|
/**
|
|
* Value to pass for a splitter that stacks components
|
|
* vertically.
|
|
*/
|
|
|
|
public static final int VERTICAL = 0;
|
|
|
|
/**
|
|
* Value to pass for a splitter that lays out components
|
|
* horizontally.
|
|
*/
|
|
|
|
public static final int HORIZONTAL = 1;
|
|
|
|
//
|
|
// Constructor
|
|
//
|
|
|
|
/**
|
|
* Contructs a splitter container with the given orientation.
|
|
*/
|
|
|
|
public Splitter(int aOrientation) {
|
|
super();
|
|
|
|
setLayout(new SplitterLayout());
|
|
fOrientation = aOrientation;
|
|
|
|
fListener = new SplitterSeparatorListener();
|
|
|
|
updateUI();
|
|
}
|
|
|
|
public void updateUI() {
|
|
super.updateUI();
|
|
|
|
setBackground(UIManager.getColor("control"));
|
|
}
|
|
|
|
/**
|
|
* Creates a separator of a given pixel size.
|
|
*/
|
|
|
|
public Component createSeparator(int aSize) {
|
|
return new Separator(fOrientation, aSize, false);
|
|
}
|
|
|
|
/**
|
|
* Creates an separator of a given pixel size. If aFixed is
|
|
* true, the separator is not user movable.
|
|
*/
|
|
|
|
public Component createSeparator(int aSize, boolean aFixed) {
|
|
return new Separator(fOrientation, aSize, aFixed);
|
|
}
|
|
|
|
/**
|
|
* Returns the weight associated with a given component. This
|
|
* number can be used to save and restore layout preferences.
|
|
*/
|
|
|
|
public Float getWeight(Component aComponent) {
|
|
return (Float) fWeights.get(aComponent);
|
|
}
|
|
|
|
/**
|
|
* Sets the minimum size a component can be sized to.
|
|
* Should be greater than zero for best results.
|
|
*/
|
|
|
|
public void setMinSize(int aMinSize) {
|
|
fMinSize = aMinSize;
|
|
}
|
|
|
|
/**
|
|
* Returns the minimum size a component can be sized to.
|
|
*/
|
|
|
|
public int getMinSize() {
|
|
return fMinSize;
|
|
}
|
|
|
|
//
|
|
// SplitterLayout
|
|
//
|
|
|
|
class SplitterLayout implements LayoutManager2 {
|
|
|
|
public void addLayoutComponent(String aString, Component aComponent) {
|
|
addLayoutComponent(aComponent, new Float(1.0));
|
|
}
|
|
|
|
public void addLayoutComponent(Component aComponent, Object aObject) {
|
|
if (aComponent instanceof Separator) {
|
|
Separator sep = (Separator) aComponent;
|
|
sep.addSeparatorListener(fListener);
|
|
fTotalSeparators += sep.getSeparatorSize();
|
|
} else {
|
|
Number weight = null;
|
|
if (aObject instanceof Number) {
|
|
weight = new Float(((Number) aObject).floatValue());
|
|
} else {
|
|
weight = new Float(1.0f);
|
|
}
|
|
fTotalWeight += weight.floatValue();
|
|
fWeights.put(aComponent, weight);
|
|
}
|
|
}
|
|
|
|
public void removeLayoutComponent(Component aComponent) {
|
|
if (aComponent instanceof Separator) {
|
|
Separator sep = (Separator) aComponent;
|
|
sep.removeSeparatorListener(fListener);
|
|
fTotalSeparators -= sep.getSeparatorSize();
|
|
} else {
|
|
Float weight = (Float) fWeights.get(aComponent);
|
|
fWeights.remove(aComponent);
|
|
fTotalWeight -= weight.floatValue();
|
|
}
|
|
}
|
|
|
|
public void layoutContainer(Container aParent) {
|
|
int i, count = aParent.getComponentCount();
|
|
Component components[] = aParent.getComponents();
|
|
|
|
Dimension parentSize = aParent.getSize();
|
|
Insets insets = aParent.getInsets();
|
|
parentSize.width -= insets.left + insets.right;
|
|
parentSize.height -= insets.top + insets.bottom;
|
|
|
|
Dimension availableSize = new Dimension(parentSize);
|
|
|
|
if (fOrientation == Splitter.VERTICAL) {
|
|
availableSize.height -= fTotalSeparators;
|
|
} else {
|
|
availableSize.width -= fTotalSeparators;
|
|
}
|
|
|
|
Point pos = new Point(insets.left, insets.right);
|
|
Dimension size = new Dimension(availableSize);
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
if (i == count - 1) {
|
|
if (fOrientation == Splitter.VERTICAL) {
|
|
size.height = parentSize.height - pos.y;
|
|
} else {
|
|
size.width = parentSize.width - pos.x;
|
|
}
|
|
} else {
|
|
if (components[i] instanceof Separator) {
|
|
if (fOrientation == Splitter.VERTICAL) {
|
|
size.height = ((Separator) components[i]).getSeparatorSize();
|
|
} else {
|
|
size.width = ((Separator) components[i]).getSeparatorSize();
|
|
}
|
|
} else {
|
|
float weight = ((Number) fWeights.get(components[i])).floatValue();
|
|
if (fOrientation == Splitter.VERTICAL) {
|
|
size.height = (int)(parentSize.height * (weight / fTotalWeight));
|
|
} else {
|
|
size.width = (int)(parentSize.width * (weight / fTotalWeight));
|
|
}
|
|
}
|
|
}
|
|
|
|
components[i].setLocation(pos);
|
|
components[i].setSize(size);
|
|
|
|
if (fOrientation == Splitter.VERTICAL) {
|
|
pos.y += size.height;
|
|
} else {
|
|
pos.x += size.width;
|
|
}
|
|
}
|
|
}
|
|
|
|
public float getLayoutAlignmentX(Container aParent) {
|
|
return 0.5f;
|
|
}
|
|
|
|
public float getLayoutAlignmentY(Container aParent) {
|
|
return 0.5f;
|
|
}
|
|
|
|
public void invalidateLayout(Container aParent) {
|
|
}
|
|
|
|
public Dimension preferredLayoutSize(Container aParent) {
|
|
return aParent.getSize();
|
|
}
|
|
|
|
public Dimension minimumLayoutSize(Container aParent) {
|
|
return preferredLayoutSize(aParent);
|
|
}
|
|
|
|
public Dimension maximumLayoutSize(Container aParent) {
|
|
return preferredLayoutSize(aParent);
|
|
}
|
|
}
|
|
|
|
class SplitterSeparatorListener implements SeparatorListener {
|
|
int fOldPos = -1;
|
|
|
|
public boolean separatorMoving(Separator aSep, int aDelta) {
|
|
Dimension size = aSep.getParent().getSize();
|
|
|
|
Graphics g = getGraphics();
|
|
int pos;
|
|
if (fOrientation == VERTICAL) {
|
|
pos = aSep.getY() + aDelta;
|
|
if (pos < fMinSize)
|
|
pos = fMinSize;
|
|
else if (pos > (size.height - aSep.getHeight() - fMinSize))
|
|
pos = size.height - aSep.getHeight() - fMinSize;
|
|
} else {
|
|
pos = aSep.getX() + aDelta;
|
|
if (pos < fMinSize)
|
|
pos = fMinSize;
|
|
else if (pos > (size.width - aSep.getWidth() - fMinSize))
|
|
pos = size.width - aSep.getWidth() - fMinSize;
|
|
}
|
|
drawDividerLine(g, pos, aSep.getSeparatorSize());
|
|
return true;
|
|
}
|
|
|
|
public void separatorMoved(Separator aSep, int aDelta) {
|
|
Graphics g = getGraphics();
|
|
drawDividerLine(g, -1, aSep.getSeparatorSize());
|
|
|
|
Component components[] = getComponents();
|
|
int count = getComponentCount();
|
|
|
|
float beforeValue = 0, afterValue = 0;
|
|
int beforeSize = 0, afterSize = 0;
|
|
boolean before = true;
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
if (components[i] instanceof Separator) {
|
|
if (components[i] == aSep) {
|
|
before = false;
|
|
}
|
|
} else {
|
|
Dimension dim = components[i].getSize();
|
|
int size = fOrientation == VERTICAL ?
|
|
dim.height :
|
|
dim.width;
|
|
float value = ((Number) fWeights.get(components[i])).floatValue();
|
|
if (before) {
|
|
beforeValue += value;
|
|
beforeSize += size;
|
|
} else {
|
|
afterValue += value;
|
|
afterSize += size;
|
|
}
|
|
}
|
|
}
|
|
|
|
Dimension size = aSep.getParent().getSize();
|
|
|
|
int pos;
|
|
if (fOrientation == VERTICAL) {
|
|
pos = aSep.getY() + aDelta;
|
|
if (pos < fMinSize)
|
|
pos = fMinSize;
|
|
else if (pos > (size.height - aSep.getHeight() - fMinSize))
|
|
pos = size.height - aSep.getHeight() - fMinSize;
|
|
|
|
aDelta = pos - aSep.getY();
|
|
} else {
|
|
pos = aSep.getX() + aDelta;
|
|
if (pos < fMinSize)
|
|
pos = fMinSize;
|
|
else if (pos > (size.width - aSep.getWidth() - fMinSize))
|
|
pos = size.width - aSep.getWidth() - fMinSize;
|
|
|
|
aDelta = pos - aSep.getX();
|
|
}
|
|
|
|
float beforeDelta = (float) (beforeSize + aDelta) / (float) beforeSize;
|
|
float afterDelta = (float) (afterSize - aDelta) / (float) afterSize;
|
|
|
|
before = true;
|
|
fTotalWeight = 0;
|
|
for (int i = 0; i < count; i++) {
|
|
if (components[i] instanceof Separator) {
|
|
if (components[i] == aSep) {
|
|
before = false;
|
|
}
|
|
} else {
|
|
float value = ((Number) fWeights.get(components[i])).floatValue();
|
|
if (before) {
|
|
value *= beforeDelta;
|
|
} else {
|
|
value *= afterDelta;
|
|
}
|
|
fWeights.put(components[i], new Float(value));
|
|
fTotalWeight += value;
|
|
}
|
|
}
|
|
|
|
invalidate();
|
|
validate();
|
|
}
|
|
|
|
void drawDividerLine(Graphics g, int aPos, int aSize) {
|
|
g.setXORMode(Color.white);
|
|
g.setColor(Color.black);
|
|
if (fOldPos != -1) {
|
|
if (fOrientation == VERTICAL) {
|
|
g.fillRect(0, fOldPos, getWidth(), aSize);
|
|
} else {
|
|
g.fillRect(fOldPos, 0, aSize, getHeight());
|
|
}
|
|
}
|
|
if (aPos != -1) {
|
|
if (fOrientation == VERTICAL) {
|
|
g.fillRect(0, aPos, getWidth(), aSize);
|
|
} else {
|
|
g.fillRect(aPos, 0, aSize, getHeight());
|
|
}
|
|
}
|
|
|
|
fOldPos = aPos;
|
|
g.setPaintMode();
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// SeparatorListener
|
|
//
|
|
|
|
interface SeparatorListener {
|
|
public boolean separatorMoving(Separator aSep, int aDelta);
|
|
public void separatorMoved(Separator aSep, int aDelta);
|
|
}
|
|
|
|
//
|
|
// Separator Class
|
|
//
|
|
|
|
class Separator extends JComponent {
|
|
int fOrientation;
|
|
int fSize;
|
|
SplitterMouseListener fMouseListener;
|
|
SeparatorListener fListener;
|
|
|
|
class SplitterMouseListener extends MouseAdapter implements MouseMotionListener {
|
|
int hitX, hitY;
|
|
boolean fDragging;
|
|
int fDelta;
|
|
|
|
public void mouseEntered(MouseEvent aEvent) {
|
|
setCursor(Cursor.getPredefinedCursor(fOrientation == Splitter.VERTICAL ?
|
|
Cursor.N_RESIZE_CURSOR :
|
|
Cursor.E_RESIZE_CURSOR));
|
|
}
|
|
|
|
public void mousePressed(MouseEvent aEvent) {
|
|
// XXX AWT bug work around
|
|
// mousePressed gets called again if the cursor leaves the
|
|
// frame. grr.
|
|
if (!fDragging) {
|
|
hitX = aEvent.getX();
|
|
hitY = aEvent.getY();
|
|
fDelta = 0;
|
|
|
|
fDragging = true;
|
|
}
|
|
}
|
|
|
|
public void mouseReleased(MouseEvent aEvent) {
|
|
notifyMoved(fDelta);
|
|
|
|
fDragging = false;
|
|
}
|
|
|
|
public void mouseMoved(MouseEvent aEvent) {
|
|
}
|
|
|
|
public void mouseDragged(MouseEvent aEvent) {
|
|
int delta = fOrientation == Splitter.VERTICAL ?
|
|
aEvent.getY() - hitY :
|
|
aEvent.getX() - hitX;
|
|
|
|
if (notifyMoving(delta)) {
|
|
fDelta = delta;
|
|
}
|
|
}
|
|
}
|
|
|
|
public Separator(int aOrientation, int aSize, boolean aFixed) {
|
|
fOrientation = aOrientation;
|
|
fSize = aSize;
|
|
|
|
if (!aFixed) {
|
|
fMouseListener = new SplitterMouseListener();
|
|
addMouseListener(fMouseListener);
|
|
addMouseMotionListener(fMouseListener);
|
|
}
|
|
}
|
|
|
|
public int getSeparatorSize() {
|
|
return fSize;
|
|
}
|
|
|
|
public void paint(Graphics g) {
|
|
Color bg = getBackground();
|
|
if (bg != null) {
|
|
Rectangle rect = getVisibleRect();
|
|
|
|
g.setColor(bg);
|
|
g.fillRect(rect.x, rect.y, rect.width, rect.height);
|
|
}
|
|
}
|
|
|
|
public void addSeparatorListener(SeparatorListener aListener) {
|
|
fListener = aListener;
|
|
}
|
|
|
|
public void removeSeparatorListener(SeparatorListener aListener) {
|
|
if (aListener == fListener) {
|
|
aListener = null;
|
|
}
|
|
}
|
|
|
|
boolean notifyMoving(int aDelta) {
|
|
if (fListener != null) {
|
|
return fListener.separatorMoving(this, aDelta);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void notifyMoved(int aDelta) {
|
|
if (fListener != null) {
|
|
fListener.separatorMoved(this, aDelta);
|
|
}
|
|
}
|
|
}
|