/**
* Copyright (C) 1998-2000 by University of Maryland, College Park, MD 20742, USA
* All rights reserved.
*/
package edu.umd.cs.piccolox.pswing;
import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
import java.io.Serializable;
import java.util.Vector;
import javax.swing.ComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.plaf.basic.BasicComboBoxUI;
import javax.swing.plaf.basic.BasicComboPopup;
import javax.swing.plaf.basic.ComboPopup;
/**
* The <b>PComboBox</b> is used instead of a JComboBox in a Piccolo scene graph.
* This PComboBox won't work properly if it is located in an abnormal hierarchy of Cameras.
* Support is provided for only one (or zero) view transforms.
* <p/>
* A ComboBox for use in Piccolo. This still has an associated JPopupMenu
* (which is always potentially heavyweight depending on component location
* relative to containing window borders.) However, this ComboBox places
* the PopupMenu component of the ComboBox in the appropriate position
* relative to the permanent part of the ComboBox. The PopupMenu is never
* transformed.
* <p/>
* This class was not designed for subclassing. If different behavior
* is required, it seems more appropriate to subclass JComboBox directly
* using this class as a model.
* <p/>
* NOTE: There is currently a known bug, namely, if the ComboBox receives
* focus through 'tab' focus traversal and the keyboard is used to interact
* with the ComboBox, there may be unexpected results.
* <p/>
* <p/>
* <b>Warning:</b> Serialized objects of this class will not be
* compatible with future Piccolo releases. The current serialization support is
* appropriate for short term storage or RMI between applications running the
* same version of Piccolo. A future release of Piccolo will provide support for long
* term persistence.
*
* @author Lance Good
*/
public class PComboBox extends JComboBox implements Serializable {
private PSwing pSwing;
private PSwingCanvas canvas;
/**
* Creates a PComboBox that takes its items from an existing ComboBoxModel.
*
* @param model The ComboBoxModel from which the list will be created
*/
public PComboBox( ComboBoxModel model ) {
super( model );
init();
}
/**
* Creates a PComboBox that contains the elements in the specified array.
*
* @param items The items to populate the PComboBox list
*/
public PComboBox( final Object items[] ) {
super( items );
init();
}
/**
* Creates a PComboBox that contains the elements in the specified Vector.
*
* @param items The items to populate the PComboBox list
*/
public PComboBox( Vector items ) {
super( items );
init();
}
/**
* Create an empty PComboBox
*/
public PComboBox() {
super();
init();
}
/**
* Substitue our UI for the default
*/
private void init() {
setUI( new PBasicComboBoxUI() );
}
/**
* Clients must set the PSwing and PSwingCanvas environment for this PComboBox to work properly.
*
* @param pSwing
* @param canvas
*/
public void setEnvironment( PSwing pSwing, PSwingCanvas canvas ) {
this.pSwing = pSwing;
this.canvas = canvas;
}
/**
* The substitute look and feel - used to capture the mouse
* events on the arrowButton and the component itself and
* to create our PopupMenu rather than the default
*/
protected class PBasicComboBoxUI extends BasicComboBoxUI {
/**
* Create our Popup instead of theirs
*/
protected ComboPopup createPopup() {
PBasicComboPopup popup = new PBasicComboPopup( comboBox );
popup.getAccessibleContext().setAccessibleParent( comboBox );
return popup;
}
}
/**
* The substitute ComboPopupMenu that places itself correctly
* in Piccolo.
*/
protected class PBasicComboPopup extends BasicComboPopup {
/**
* @param combo The parent ComboBox
*/
public PBasicComboPopup( JComboBox combo ) {
super( combo );
}
/**
* Computes the bounds for the Popup in Piccolo if a
* PMouseEvent has been received. Otherwise, it uses the
* default algorithm for placing the popup.
*
* @param px corresponds to the x coordinate of the popup
* @param py corresponds to the y coordinate of the popup
* @param pw corresponds to the width of the popup
* @param ph corresponds to the height of the popup
* @return The bounds for the PopupMenu
*/
protected Rectangle computePopupBounds( int px, int py, int pw, int ph ) {
Rectangle2D r = getNodeBoundsInCanvas();
Rectangle sup = super.computePopupBounds( px, py, pw, ph );
return new Rectangle( (int)r.getX(), (int)r.getMaxY(), (int)sup.getWidth(), (int)sup.getHeight() );
}
}
private Rectangle2D getNodeBoundsInCanvas() {
if( pSwing == null || canvas == null ) {
throw new RuntimeException( "PComboBox.setEnvironment( swing, pCanvas );//has to be done manually at present" );
}
Rectangle2D r1c = pSwing.getBounds();
pSwing.localToGlobal( r1c );
canvas.getCamera().globalToLocal( r1c );
r1c = canvas.getCamera().getViewTransform().createTransformedShape( r1c ).getBounds2D();
return r1c;
}
}