home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 1998 November
/
Chip_1998-11_cd.bin
/
tema
/
Cafe
/
jfc.bin
/
MacPopupMenuUI.java
< prev
next >
Wrap
Text File
|
1998-02-26
|
24KB
|
816 lines
/*
* @(#)MacPopupMenuUI.java 1.6 98/02/02
*
* Copyright (c) 1998 Sun Microsystems, Inc. All Rights Reserved.
*
* This software is the confidential and proprietary information of Sun
* Microsystems, Inc. ("Confidential Information"). You shall not
* disclose such Confidential Information and shall use it only in
* accordance with the terms of the license agreement you entered into
* with Sun.
*
* SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
* SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
* SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
* THIS SOFTWARE OR ITS DERIVATIVES.
*
*/
package com.sun.java.swing.plaf.mac;
import com.sun.java.swing.*;
import com.sun.java.swing.plaf.*;
import com.sun.java.swing.event.PopupMenuListener;
import com.sun.java.swing.event.PopupMenuEvent;
import com.sun.java.swing.plaf.basic.BasicPopupMenuUI;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
/**
* A Mac L&F implementation of PopupMenuUI.
* <p>
* Warning: serialized objects of this class will not be compatible with
* future swing releases. The current serialization support is appropriate
* for short term storage or RMI between Swing1.0 applications. It will
* not be possible to load serialized Swing1.0 objects with future releases
* of Swing. The JDK1.2 release of Swing will be the compatibility
* baseline for the serialized form of Swing objects.
*
* @version @(#)MacPopupMenuUI.java 1.0 01/20/98
* @author Symantec
*/
public class MacPopupMenuUI extends BasicPopupMenuUI implements PopupMenuListener {
LayoutManager2 originalLayoutManager;
MacPopupMenuLayout scrollerLayoutManager = new MacPopupMenuLayout();
PopupUpScrollerMenuItem upScroller = new PopupUpScrollerMenuItem();
PopupDownScrollerMenuItem downScroller = new PopupDownScrollerMenuItem();
//
// Variables for determining what items are visible within the scrollable popup menu
//
Rectangle constrainedRect = null;
int minY = 0;
int maxY = 0;
int firstRealMenuItem = 0;
int lastRealMenuItem = 0;
int nonVisibleXAdjustment = 500;
int yAdjustment = 0;
int topVisibleMenuItem = 0;
int bottomVisibleMenuItem = -1;
int selectedMenuItem = -1;
public static ComponentUI createUI(JComponent c) {
return new MacPopupMenuUI();
}
public void installUI(JComponent c) {
super.installUI(c);
/*
* Install a layout manager that knows about up and down arrows in
* the popup menu, and sizing it to fit.
*/
LayoutManager layout = popupMenu.getLayout();
if (layout instanceof LayoutManager2) {
originalLayoutManager = (LayoutManager2) layout;
popupMenu.setLayout(scrollerLayoutManager);
}
popupMenu.addPopupMenuListener(this);
}
public void uninstallUI(JComponent c) {
/*
* Remove all of our extras
*/
upScroller.stopScroller();
downScroller.stopScroller();
popupMenu.remove(upScroller);
popupMenu.remove(downScroller);
popupMenu.removePopupMenuListener(this);
/*
* Re-install the regular layout manager
*/
popupMenu.setLayout(originalLayoutManager);
super.uninstallUI(c);
}
public Dimension getMinimumSize(JComponent c) {
return getPreferredSize(c);
}
public Dimension getPreferredSize(JComponent c) {
if (constrainedRect != null)
return constrainedRect.getSize();
Dimension superPreferredSize = c.preferredSize();
return superPreferredSize;
}
public Dimension getMaximumSize(JComponent c) {
return getPreferredSize(c);
}
void getPopupConstraints(Point preferredLocation, Rectangle maxBounds) {
Component invoker = popupMenu.getInvoker();
if (invoker == null) return;
Container parent;
for (parent = invoker.getParent(); parent != null ; parent = parent.getParent()) {
if (parent instanceof JFrame || parent instanceof JDialog) {
maxBounds.setBounds(parent.getBounds());
break;
} else if (parent instanceof JApplet) {
Rectangle r = parent.getBounds();
Point p = parent.getLocationOnScreen();
maxBounds.setBounds(p.x, p.y, r.width, r.height);
break;
} else if (parent instanceof java.awt.Frame) {
maxBounds.setBounds(parent.getBounds());
break;
}
}
parent = invoker.getParent();
preferredLocation.setLocation(invoker.getLocationOnScreen());
if (invoker instanceof JMenu)
{
Dimension s = invoker.getSize();
if (parent instanceof JPopupMenu)
{
preferredLocation.x += s.width; // (Pull Right)
}
else if (parent instanceof JMenuBar)
{
preferredLocation.y += s.height; // (Pull Down)
// Constrain the maxBounds so the menu won't go above the menubar
maxBounds.height -= (preferredLocation.y - maxBounds.y);
maxBounds.y = preferredLocation.y;
}
}
else if (invoker instanceof JComboBox)
{ // (Pop Up)
}
}
void setTopVisibleMenuItem(int menuItem) {
if (menuItem < 0)
topVisibleMenuItem = 0;
else if (menuItem > popupMenu.getComponents().length - 1)
topVisibleMenuItem = popupMenu.getComponents().length - 1;
else
topVisibleMenuItem = menuItem;
bottomVisibleMenuItem = -1;
selectedMenuItem = -1;
}
void setBottomVisibleMenuItem(int menuItem) {
topVisibleMenuItem = -1;
if (menuItem < 0)
bottomVisibleMenuItem = 0;
else if (bottomVisibleMenuItem > popupMenu.getComponents().length - 1)
bottomVisibleMenuItem = popupMenu.getComponents().length - 1;
else
bottomVisibleMenuItem = menuItem;
selectedMenuItem = -1;
}
void setSelectedVisibleMenuItem(int menuItem) {
topVisibleMenuItem = -1;
bottomVisibleMenuItem = -1;
selectedMenuItem = ((menuItem >= 0) && (menuItem <= popupMenu.getComponents().length - 1)) ? menuItem : 0;
}
//
// Locate the first Fully Visible menu item below yPos
//
private int findFirstItemBelow(Component components[], int yPos)
{
int compindex;
for (compindex = firstRealMenuItem; compindex < lastRealMenuItem; compindex++) {
if (components[compindex].getLocation().y >= yPos)
break;
}
return compindex;
}
//
// Locate the last Fully Visible menu item above yPos
//
private int findLastItemAbove(Component components[], int yPos)
{
int compindex;
for (compindex = lastRealMenuItem; compindex > firstRealMenuItem; compindex--) {
if ((components[compindex].getLocation().y + components[compindex].getSize().height) <= yPos)
break;
}
return compindex;
}
void adjustVisibleMenuItems(int lastTopVisibleMenuItem, int lastBottomVisibleMenuItem)
{
Component components[] = popupMenu.getComponents();
Component comp;
yAdjustment = 0;
//
// Calculate adjustments for the outofrange components
//
//
// We have defined a top visible menu item. Make sure it is in view at the top.
//
if (topVisibleMenuItem >= 0)
{
if (topVisibleMenuItem > lastRealMenuItem) // Force it into range
topVisibleMenuItem = lastRealMenuItem;
comp = components[topVisibleMenuItem];
// If this component isn't the first visible item,
// Need to adjust components upward/downward so it is brought into view
yAdjustment = minY - comp.getLocation().y;
if (topVisibleMenuItem > firstRealMenuItem) {
yAdjustment += upScroller.getSize().height;
// Adjusting for the scroller may have made enough room for the rest
if (findFirstItemBelow(components, minY - yAdjustment) == firstRealMenuItem)
topVisibleMenuItem = firstRealMenuItem;
}
// determine which component is the bottom-most fully visible
bottomVisibleMenuItem = findLastItemAbove(components, maxY - yAdjustment);
// If there are items below the bottom-most visible component, leave room for the scroller
if (bottomVisibleMenuItem < lastRealMenuItem)
{
bottomVisibleMenuItem--;
}
// Otherwise, if the bottom-most visible component is the scroller (or after), don't bother with it
else if (bottomVisibleMenuItem > lastRealMenuItem)
{
bottomVisibleMenuItem = lastRealMenuItem;
}
if (bottomVisibleMenuItem < topVisibleMenuItem)
bottomVisibleMenuItem = topVisibleMenuItem;
}
//
// We have defined a bottom visible menu item. Make sure it is in view at the bottom.
//
else if (bottomVisibleMenuItem >= 0)
{
if (bottomVisibleMenuItem > lastRealMenuItem) // Force it into range
bottomVisibleMenuItem = lastRealMenuItem;
comp = components[bottomVisibleMenuItem];
// If this component isn't the last visible item,
// Need to adjust components upward/downward so it is brought into view
yAdjustment = maxY - (comp.getLocation().y + comp.getSize().height);
if (bottomVisibleMenuItem < lastRealMenuItem) {
yAdjustment -= downScroller.getSize().height;
// Adjusting for the scroller may have made enough room for the rest
if (findLastItemAbove(components, maxY - yAdjustment) == lastRealMenuItem)
bottomVisibleMenuItem = lastRealMenuItem;
}
// determine which component is the top-most visible
topVisibleMenuItem = findFirstItemBelow(components, minY - yAdjustment);
// If there are items above the top-most visible component, leave room for the scroller
if (topVisibleMenuItem > firstRealMenuItem)
{
topVisibleMenuItem++;
}
// Otherwise, if the top-most visible component is the scroller (or before), don't bother with it
else if (topVisibleMenuItem < firstRealMenuItem)
{
topVisibleMenuItem = firstRealMenuItem;
}
if (topVisibleMenuItem > bottomVisibleMenuItem)
topVisibleMenuItem = bottomVisibleMenuItem;
}
//
// We have defined a selected visible menu item. Make sure it is in view.
// It just has to be in view, it doesn't have to be first or last.
//
else if (selectedMenuItem >= 0)
{
comp = components[topVisibleMenuItem];
int topVisibleMenuItemY = comp.getLocation().y;
if (topVisibleMenuItemY < minY)
{
// Selected component is too high.
// Need to adjust components downward so the selected component is brought into view
yAdjustment = minY - topVisibleMenuItemY;
// If there are other selected items above this one, allow room for the UP arrow scroller
if (topVisibleMenuItem > 1)
yAdjustment += upScroller.getSize().height;
}
else if (topVisibleMenuItemY + comp.getSize().height > maxY)
{
// Selected component is too low.
// Need to adjust components upward so the selected component is brought into view
yAdjustment = maxY - (topVisibleMenuItemY + comp.getSize().height);
// If there are other selected items below this one, allow for a DOWN arrow scroller
if (topVisibleMenuItem < (components.length - 2))
yAdjustment -= downScroller.getSize().height;
}
}
//
// Adjust all items vertically, and adjust the newly out-of-view / newly in-view ones horizontally
//
Point compLocation;
for (int compindex = firstRealMenuItem; compindex <= lastRealMenuItem; compindex++) {
comp = components[compindex];
compLocation = comp.getLocation();
compLocation.y += yAdjustment;
// If the item is not in view now, but was, hide it
if (compindex < topVisibleMenuItem || compindex > bottomVisibleMenuItem) {
if (compindex >= lastTopVisibleMenuItem && compindex <= lastBottomVisibleMenuItem) {
compLocation.x += nonVisibleXAdjustment;
}
}
// If the item is in view now, but wasn't, show it
else if (compindex < lastTopVisibleMenuItem || compindex > lastBottomVisibleMenuItem) {
compLocation.x -= nonVisibleXAdjustment;
}
comp.setLocation(compLocation);
}
// Are there more items above the topmost visible menu item
if (topVisibleMenuItem > firstRealMenuItem) {
if (lastTopVisibleMenuItem == firstRealMenuItem)
upScroller.activateScroller();
}
// No longer need the up scroller
else if (lastTopVisibleMenuItem > firstRealMenuItem) {
upScroller.deactivateScroller();
}
// Are there more items below the bottommost visible menu item
if (bottomVisibleMenuItem < lastRealMenuItem) {
if (lastBottomVisibleMenuItem == lastRealMenuItem)
downScroller.activateScroller();
}
// No longer need the down scroller
else if (lastBottomVisibleMenuItem < lastRealMenuItem) {
downScroller.deactivateScroller();
}
}
void AdjustLayout() {
if (constrainedRect != null) {
// Allow for the up and down scrollers
lastRealMenuItem = popupMenu.getComponents().length - 3;
Insets insets = popupMenu.getInsets();
minY = insets.top;
maxY = constrainedRect.height - (insets.top + insets.bottom);
//
// Adjust all items vertically, and adjust the out-of-view / in-view ones horizontally
//
adjustVisibleMenuItems(firstRealMenuItem, lastRealMenuItem);
}
}
class MacPopupMenuLayout implements LayoutManager2 {
//
// LayoutManager2 overrides
//
/**
* Adds the specified component to the layout, using the specified
* constraint object.
* @param comp the component to be added
* @param constraints where/how the component is added to the layout.
*/
public void addLayoutComponent(Component comp, Object constraints) {
originalLayoutManager.addLayoutComponent(comp, constraints);
}
/**
* Returns the maximum size of this component.
* @see java.awt.Component#getMinimumSize()
* @see java.awt.Component#getPreferredSize()
* @see LayoutManager
*/
public Dimension maximumLayoutSize(Container target) {
return originalLayoutManager.maximumLayoutSize(target);
}
/**
* Returns the alignment along the x axis. This specifies how
* the component would like to be aligned relative to other
* components. The value should be a number between 0 and 1
* where 0 represents alignment along the origin, 1 is aligned
* the furthest away from the origin, 0.5 is centered, etc.
*/
public float getLayoutAlignmentX(Container target) {
return originalLayoutManager.getLayoutAlignmentX(target);
}
/**
* Returns the alignment along the y axis. This specifies how
* the component would like to be aligned relative to other
* components. The value should be a number between 0 and 1
* where 0 represents alignment along the origin, 1 is aligned
* the furthest away from the origin, 0.5 is centered, etc.
*/
public float getLayoutAlignmentY(Container target) {
return originalLayoutManager.getLayoutAlignmentY(target);
}
/**
* Invalidates the layout, indicating that if the layout manager
* has cached information it should be discarded.
*/
public void invalidateLayout(Container target) {
originalLayoutManager.invalidateLayout(target);
}
//
// LayoutManager2 overrides
//
/**
* Adds the specified component with the specified name to
* the layout.
* @param name the component name
* @param comp the component to be added
*/
public void addLayoutComponent(String name, Component comp) {
originalLayoutManager.addLayoutComponent(name, comp);
}
/**
* Removes the specified component from the layout.
* @param comp the component ot be removed
*/
public void removeLayoutComponent(Component comp) {
originalLayoutManager.removeLayoutComponent(comp);
}
/**
* Calculates the preferred size dimensions for the specified
* panel given the components in the specified parent container.
* @param parent the component to be laid out
*
* @see #minimumLayoutSize
*/
public Dimension preferredLayoutSize(Container parent) {
return originalLayoutManager.preferredLayoutSize(parent);
}
/**
* Calculates the minimum size dimensions for the specified
* panel given the components in the specified parent container.
* @param parent the component to be laid out
* @see #preferredLayoutSize
*/
public Dimension minimumLayoutSize(Container parent) {
return originalLayoutManager.minimumLayoutSize(parent);
}
/**
* Lays out the container in the specified panel.
* @param parent the component which needs to be laid out
*/
public void layoutContainer(Container parent) {
originalLayoutManager.layoutContainer(parent);
AdjustLayout();
}
}
//===========================
//
// Popup Menu scroller item methods
//
//===========================
abstract class PopupScrollerMenuItem extends JMenuItem implements ActionListener {
Timer scrollTimer = null;
public PopupScrollerMenuItem(Icon scrollerIcon) {
super(" ", scrollerIcon);
setHorizontalTextPosition(JMenuItem.RIGHT);
setEnabled(false);
}
public void menuSelectionChanged(boolean isIncluded) {
if (isIncluded) {
actionPerformed(null);
}
else {
stopScroller();
}
}
public void processMouseEvent(MouseEvent event,MenuElement path[],MenuSelectionManager manager) {
if (scrollTimer != null &&
(event.getID() == MouseEvent.MOUSE_RELEASED || event.getID() == MouseEvent.MOUSE_EXITED)) {
stopScroller();
if(path.length > 0 && path[path.length-1] == this) {
MenuElement newPath[] = new MenuElement[path.length-1];
System.arraycopy(path,0,newPath,0,path.length - 1);
manager.setSelectedPath(newPath);
}
}
super.processMouseEvent(event, path, manager);
}
void startScroller() {
if (scrollTimer == null) {
scrollTimer = new Timer(250, this);
scrollTimer.setRepeats(false);
scrollTimer.start();
} else {
//
// The VM runs slow in MRJ. Post a thread to restart the scrolling when we
// are done the current scroll.
//
Thread restartScroller = new Thread() {
public void run() {
if (scrollTimer != null) // Might be null by now.
scrollTimer.restart();
}
};
restartScroller.setPriority(Thread.MIN_PRIORITY);
restartScroller.start();
// scrollTimer.restart();
}
}
void stopScroller() {
if (scrollTimer != null) {
scrollTimer.stop();
scrollTimer = null;
}
}
void activateScroller()
{
this.setLocation(getLocation().x, this.activeYLocation());
setEnabled(true);
}
void deactivateScroller()
{
stopScroller();
this.setLocation(getLocation().x, -500);
setEnabled(false);
}
/**
* Invoked when an action occurs.
*/
public void actionPerformed(ActionEvent e) {
Scroll();
startScroller(); // Force the timer to restart when the scroll is done.
}
abstract void Scroll();
abstract int activeYLocation();
}
class PopupDownScrollerMenuItem extends PopupScrollerMenuItem {
public PopupDownScrollerMenuItem() {
super(MacMenuUtilities.getPopupMenuDownScrollerArrowIcon());
}
void Scroll() {
if (bottomVisibleMenuItem < lastRealMenuItem) {
int lastTopVisibleMenuItem = topVisibleMenuItem;
int lastBottomVisibleMenuItem = bottomVisibleMenuItem;
// Adjust the bottom visible menu item down by one
topVisibleMenuItem = -1;
bottomVisibleMenuItem++;
//
// Calculate what items are now in range
// Adjust all items vertically, and adjust the newly out-of-view / newly in-view ones horizontally
//
adjustVisibleMenuItems(lastTopVisibleMenuItem, lastBottomVisibleMenuItem);
}
}
int activeYLocation() {
return maxY - getSize().height;
}
}
class PopupUpScrollerMenuItem extends PopupScrollerMenuItem {
public PopupUpScrollerMenuItem() {
super(MacMenuUtilities.getPopupMenuUpScrollerArrowIcon());
}
void Scroll() {
if (topVisibleMenuItem > firstRealMenuItem) {
int lastTopVisibleMenuItem = topVisibleMenuItem;
int lastBottomVisibleMenuItem = bottomVisibleMenuItem;
// Adjust the top visible menu item up by one
topVisibleMenuItem--;
//
// Calculate what items are now in range
// Adjust all items vertically, and adjust the newly out-of-view / newly in-view ones horizontally
//
adjustVisibleMenuItems(lastTopVisibleMenuItem, lastBottomVisibleMenuItem);
}
}
int activeYLocation() {
return minY;
}
}
//===========================
//
// PopupMenuListener methods
//
//===========================
//
// popupMenuWillBecomeVisible
//
// Just before the popup menu becomes visible, recalculate its location
// and size, so that it fits into the maximum bounds for its location
//
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
constrainedRect = null;
//
// Get the constraints for where the popup menu can/should appear
//
Rectangle maxBounds = new Rectangle();
Point preferredLocation = new Point();
getPopupConstraints(preferredLocation, maxBounds);
//
// Determine if the menu fits within the maxbounds:
//
Rectangle preferredRect = new Rectangle(preferredLocation,
scrollerLayoutManager.preferredLayoutSize(popupMenu));
if (!SwingUtilities.isRectangleContainingRectangle(maxBounds, preferredRect))
{
// Simple adjustments: make the left and right sides within range
if (preferredRect.y < maxBounds.y) // Too far up?
preferredRect.y = maxBounds.y;
if (preferredRect.x < maxBounds.x) // Too far left?
preferredRect.x = maxBounds.x;
//
// The Popup menu doesn't fit. See if we can adjust the location of the menu
// We can do this if the width and height are within bounds
//
if (maxBounds.width >= preferredRect.width &&
maxBounds.height >= preferredRect.height)
{
// If the menu doesn't fit vertically, adjust it
int preferredBottom = preferredRect.y + preferredRect.height;
int maxBottom = maxBounds.y + maxBounds.height;
if (preferredBottom > maxBottom) // Too far down?
preferredRect.y -= (preferredBottom - maxBottom);
// If the menu doesn't fit horizontally, adjust it
int preferredRight = preferredRect.x + preferredRect.width;
int maxRight = maxBounds.x + maxBounds.width;
if (preferredRight > maxRight) // Too far right?
preferredRect.x -= (preferredRight - maxRight);
}
//
// Can't adjust location of the menu. Adjust the size of the menu, and add scroller arrows.
//
else
{
//
// Calculate the viewable area of the popup menu
//
if (maxBounds.height < preferredRect.height)
{
// Make sure the scroller menu items appear at the correct locations
popupMenu.add(upScroller);
popupMenu.add(downScroller);
// Make the menu fill the available area vertically
preferredRect.y = maxBounds.y;
preferredRect.height = maxBounds.height;
/*
// Adjust the menu item selection to account for the scrollers
if (topVisibleMenuItem >= 0)
topVisibleMenuItem++;
else if (bottomVisibleMenuItem >= 0)
bottomVisibleMenuItem++;
else if (selectedMenuItem >= 0)
selectedMenuItem++;
*/
}
if (maxBounds.width < preferredRect.width)
{
// Make the menu fill the available area horizontally
preferredRect.x = maxBounds.x;
preferredRect.width = maxBounds.width;
}
popupMenu.invalidate();
constrainedRect = preferredRect;
//
// Now, run through the menu items, to see which ones fit within the viewable area
//
}
popupMenu.setLocation(preferredRect.x, preferredRect.y);
}
popupMenu.validate();
}
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
constrainedRect = null;
// Remove the scroller menu items
popupMenu.remove(upScroller);
popupMenu.remove(downScroller);
setTopVisibleMenuItem(0);
}
public void popupMenuCanceled(PopupMenuEvent e) {
}
}