/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is NetBeans. The Initial Developer of the Original
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2002 Sun
* Microsystems, Inc. All Rights Reserved.
*/
package org.netbeans.spi.looks;
import java.awt.Component;
import java.awt.datatransfer.Transferable;
import java.io.IOException;
import java.util.*;
import javax.swing.Action;
import org.openide.nodes.Node;
import org.openide.util.HelpCtx;
import org.openide.util.Lookup;
import org.openide.util.datatransfer.NewType;
import org.openide.util.datatransfer.PasteType;
import org.netbeans.modules.looks.LookListener;
import org.netbeans.modules.looks.LookEvent;
/**
* Base class for all Looks. All methods in the class provide
* neutral behavior (i.e. do nothing, rerurn null or false).
* Extending this class in order to implement
* a Look requires overriding all methods which should provide some
* functionality. You may also consider subclassing
* the adaptor class {@link org.netbeans.spi.looks.DefaultLook
* DefaultLook class}, which provides basic implementations for
* finding Icons and Actions.
*
* Most methods take a Lookup as parameter. This lookup represents environement
* associated with given represented object. This environement usually
* contains data from the {@link #getLookupItems} method. However if more
* looks cooperate on the same represented object it should contain union
* of items provided by all cooperating looks.
*
* @author Petr Hrebejk
* @see org.netbeans.spi.looks.DefaultLook
*/
public abstract class Look extends Object {
/** Internal mask for property change */
private static final long PROPERTY_CHANGE = -1;
/** Mask for enabling (unmasking) all methods */
public static final long ALL_METHODS = Long.MAX_VALUE;
/** Mask for disabling (masking) all methods */
public static final long NO_METHODS = 0;
/** Mask for the method {@link org.netbeans.spi.looks.Look#destroy}. */
public static final long DESTROY = 1;
/** Mask for the method {@link org.netbeans.spi.looks.Look#rename}. */
public static final long RENAME = DESTROY << 1;
/** Mask for firing Cookie events and providing lookup items */
public static final long GET_LOOKUP_ITEMS = RENAME << 1;
/** Mask for the method {@link org.netbeans.spi.looks.Look#getDisplayName}. */
public static final long GET_DISPLAY_NAME = GET_LOOKUP_ITEMS << 1;
/** Mask for the method {@link org.netbeans.spi.looks.Look#getName}. */
public static final long GET_NAME = GET_DISPLAY_NAME << 1;
/** Mask for the method {@link org.netbeans.spi.looks.Look#getShortDescription}. */
public static final long GET_SHORT_DESCRIPTION = GET_NAME << 1;
/** Mask for the method {@link org.netbeans.spi.looks.Look#getIcon}. */
public static final long GET_ICON = GET_SHORT_DESCRIPTION << 1;
/** Mask for the method {@link org.netbeans.spi.looks.Look#getOpenedIcon}. */
public static final long GET_OPENED_ICON = GET_ICON << 1;
/** Mask for the method {@link org.netbeans.spi.looks.Look#getHelpCtx}. */
public static final long GET_HELP_CTX = GET_OPENED_ICON << 1;
/** Mask for the method {@link org.netbeans.spi.looks.Look#getChildObjects}. */
public static final long GET_CHILD_OBJECTS = GET_HELP_CTX << 1;
/** Mask for the method {@link org.netbeans.spi.looks.Look#getNewTypes}. */
public static final long GET_NEW_TYPES = GET_CHILD_OBJECTS << 1;
/** Mask for the method {@link org.netbeans.spi.looks.Look#getActions}. */
public static final long GET_ACTIONS = GET_NEW_TYPES << 1;
/** Mask for the method {@link org.netbeans.spi.looks.Look#getContextActions}. */
public static final long GET_CONTEXT_ACTIONS = GET_ACTIONS << 1;
/** Mask for the method {@link org.netbeans.spi.looks.Look#getDefaultAction}. */
public static final long GET_DEFAULT_ACTION = GET_CONTEXT_ACTIONS << 1;
/** Mask for the method {@link org.netbeans.spi.looks.Look#getPropertySets}. */
public static final long GET_PROPERTY_SETS = GET_DEFAULT_ACTION << 1;
/** Mask for the method {@link org.netbeans.spi.looks.Look#getCustomizer}. */
public static final long GET_CUSTOMIZER = GET_PROPERTY_SETS << 1;
/** Mask for the method {@link org.netbeans.spi.looks.Look#canRename}. */
public static final long CAN_RENAME = GET_CUSTOMIZER << 1;
/** Mask for the method {@link org.netbeans.spi.looks.Look#canDestroy}. */
public static final long CAN_DESTROY = CAN_RENAME << 1;
/** Mask for the method {@link org.netbeans.spi.looks.Look#canCopy}. */
public static final long CAN_COPY = CAN_DESTROY << 1;
/** Mask for the method {@link org.netbeans.spi.looks.Look#canCut}. */
public static final long CAN_CUT = CAN_COPY << 1;
/** Mask for the method {@link org.netbeans.spi.looks.Look#getPasteTypes}. */
public static final long GET_PASTE_TYPES = CAN_CUT << 1;
/** Mask for the method {@link org.netbeans.spi.looks.Look#getDropType}. */
public static final long GET_DROP_TYPE = GET_PASTE_TYPES << 1;
/** Mask for the method {@link org.netbeans.spi.looks.Look#clipboardCopy}. */
public static final long CLIPBOARD_COPY = GET_DROP_TYPE << 1;
/** Mask for the method {@link org.netbeans.spi.looks.Look#clipboardCut}. */
public static final long CLIPBOARD_CUT = CLIPBOARD_COPY << 1;
/** Mask for the method {@link org.netbeans.spi.looks.Look#drag}. */
public static final long DRAG = CLIPBOARD_CUT << 1;
/** Mask for the method {@link org.netbeans.spi.looks.Look#hasCustomizer}. */
public static final long HAS_CUSTOMIZER = DRAG << 1;
private ListenerCache listenerCache;
// Programmatic name of the look
private String name;
static {
// initialize module private access to this package
org.netbeans.modules.looks.Accessor.DEFAULT = new AccessorImpl ();
}
// Constructors ------------------------------------------------------------
/** Creates new instance of look does no work.
* @param name the name to assign to the look.
*/
protected Look( String name ) {
this.name = name;
}
// Identification methods --------------------------------------------------
/** Returns name of the look. This name should identify the look.
* @return Name of the look.
*/
public final String getName() {
return name;
}
/** The human presentable name of the look. Default implementation
* calls getName();
* @return human presentable name
*/
public String getDisplayName() {
return getName();
}
public String toString() {
return getName();
}
// General methods ---------------------------------------------------------
/**
* Overriding this method permits registering listeners on represented objects.
*
* If given instance is used in many places (e.g. multiple views are using
* the look for representing the given object or when the look is used as a sublook
* in multiple composite looks) attachTo will only be called once per
* represented object.
*
* Implementors may not wait for any other threads at it may be potentially called
* from internal lock.
*
* @param representedObject Represented object the look should work with.
* @throws ClassCastException When the represented object is unacceptable
* for the look due to wrong class
* @throws IllegalArgumentException When the represented object is
* unacceptable for other reason than class cast.
*
*/
protected void attachTo( Object representedObject ) {
}
/**
* This method is called when listening on represeted object is no
* longer needed. Make sure to deregister all listeners registered
* in {@link #attachTo} method.
* If given instance is used in many places (e.g. more views are using
* the look for representing given object or when the look is used as a sublook
* in more composite look) the detachFrom method will only be called once per
* reprsented object.
*
* Implementors may not wait for any other threads at it may be potentially called
* from internal lock.
* @param representedObject Represented object to detach from.
*/
protected void detachFrom( Object representedObject ) {
}
// Methods for FUNCTIONALITY EXTENSIONS ------------------------------------
/** Allowes for adding new object into the object's environement passed
* to other methods as the env parameter.
* @param representedObject Parameter is ignored.
* @param oldEnv Content of previous environement when called after a change
* of environemnt or is empty.
* @return null
*/
public java.util.Collection getLookupItems( Object representedObject, Lookup oldEnv ) {
return null;
}
// Methods for STYLE -------------------------------------------------------
/** Gets the programmatic name of the object. This name shouldn't be
* localized.
* Notice that the env parameter may be empty in some cases (e.g. when
* call to this methods is performed during serialization)
* @param representedObject Represented object the look should work with.
* @param env Environement for the represented object.
* @return Programmatic name of the object.
*/
public String getName( Object representedObject, Lookup env ) {
return null;
}
/** Gets localized name of the object.
* @param representedObject Represented object the look should work with.
* @param env Environement for the represented object.
* @return Localized name of the object.
*/
public String getDisplayName( Object representedObject, Lookup env ) {
return null;
}
/** This method is called when the user renames the object.
* @param representedObject Represented object to be renamed.
* @param newName The new name set by the user.
* @param env Environement for the represented object.
*/
public void rename( Object representedObject, String newName, Lookup env ) throws IOException {
}
/** Gets short description for given object. The short description is usually
* visualized as a tooltip, but may have another forms as well.
* @param representedObject Represented object the look should work with.
* @param env Environement for the represented object.
* @return A localized short description of the object.
*/
public String getShortDescription( Object representedObject, Lookup env ) {
return null;
}
/** Find an icon for this object (in the closed state).
* @param representedObject Represented object the look should work with.
* @param type Constant from {@link java.beans.BeanInfo}
* @param env Environement for the represented object.
* @return Icon to use to represent the object in the closed state.
*/
public java.awt.Image getIcon( Object representedObject, int type, Lookup env ) {
return null;
}
/** Find an icon for this object (in the open state).
* This icon is used when the object may have children and is expanded.
* @param representedObject Represented object the look should work with.
* @param type Constant from {@link java.beans.BeanInfo}
* @param env Environement for the represented object.
* @return Icon to use to represent the object in the open state.
*/
public java.awt.Image getOpenedIcon( Object representedObject, int type, Lookup env ) {
return null;
}
/** Get context help associated with this object.
* @param representedObject Represented object the look should work with.
* @param env Environement for the represented object.
* @return The context help object (could be null or
* {@link org.openide.util.HelpCtx#DEFAULT_HELP})
*/
public HelpCtx getHelpCtx( Object representedObject, Lookup env ) {
return null;
}
// Methods for CHILDREN ----------------------------------------------------
/** Gets objects which are children of the represented object in the
* hierarchy represented by this Look.
* @param representedObject Represented object the look should work with.
* @param env Environement for the represented object.
* @return List of objects which should be represented as node children of
* the node, can return null.
*/
public List getChildObjects( Object representedObject, Lookup env ) {
return null;
}
/** Decides whether given object should be a leaf.
* I.e. if the visual
* representation of the object should be expandable.
*
* Notice : Implementation of
* this method must be consistent with implementation of
* {@link #getChildObjects}.
* Notice that you can switch the form LEAF to nonLEAF node and vice versa
* by calling {@link #fireChange} with parameter {@link #GET_CHILD_OBJECTS},
* which in turn will call
* isLeaf(...) and eventually getChildObjects(...)
* where you can return new value.
* @param representedObject Represented object the look should work with.
* @param env Environement for the represented object.
* @return true if the object should be unexpandable
* false otherwise.
*/
public boolean isLeaf( Object representedObject, Lookup env ) {
return false;
}
// Methods for ACTIONS & NEW TYPES -----------------------------------------
/** Get the new types that user can create from given object.
* For example, a a Java package will permit classes to be added.
* @param representedObject Represented object the look should work with.
* @param env Environement for the represented object.
* @return Array of new type operations that are allowed,
* can return null that is equivalent to empty array
*/
public NewType[] getNewTypes( Object representedObject, Lookup env ) {
return null;
}
/** Get the set of actions associated with the object.
* This may be used for in constructing popup menus etc.
*
* @param representedObject Represented object the look should work with.
* @param env Environement for the represented object.
* @return Array of the Actions applicable to the node or null
* if actions in {@link org.openide.nodes.NodeOp#getDefaultActions()} should be used.
*/
public Action[] getActions( Object representedObject, Lookup env ) {
return null;
}
/** Get a special set of actions for situations when this object is displayed
* as a context. (E.g. right clicking in the empty area of a tree)
* @param representedObject Represented object the look should work with.
* @param env Environement for the represented object.
* @return Actions for a context.
*/
public Action[] getContextActions( Object representedObject, Lookup env ) {
return null;
}
/** Get the default action for this object.
* This action can but need not be one from the list returned
* from {@link #getActions}.
* @param representedObject Represented object the look should work with.
* @param env Environement for the represented object.
* @return Default action, or null if there should be none.
*/
public Action getDefaultAction( Object representedObject, Lookup env ) {
return null;
}
// Methods for PROPERTIES AND CUSTOMIZER -----------------------------------
/** Get the list of property sets for object. E.g. typically there
* may be one for normal Bean properties, one for expert
* properties, and one for hidden properties.
* @param representedObject Represented object the look should work with.
* @param env Environement for the represented object.
* @return Property sets for the object, can return null that
* is equivalent to empty array
*/
public Node.PropertySet[] getPropertySets( Object representedObject, Lookup env ) {
return null;
}
/** Check whether the customizer for the represented object is available.
* If so, the method
* getCustomizer should return non-null value.
*
* @param representedObject Represented object the look should work with.
* @param env Environement for the represented object.
* @return true if the customizer is available, false otherwise
*/
public boolean hasCustomizer( Object representedObject, Lookup env ) {
return false;
}
/** Get the customizer for represented object if available.
* @param representedObject Represented object the look should work with.
* @param env Environement for the represented object.
* @return The component, or null if there is no customizer.
*/
public Component getCustomizer( Object representedObject, Lookup env ) {
return null;
}
// Methods for CLIPBOARD OPERATIONS ----------------------------------------
/** Test whether this object can be renamed.
* If true, {@link #rename} will be called when the user changes the name
* of the node.
* @param representedObject Represented object the look should work with.
* @param env Environement for the represented object.
* @return true if the node object be renamed.
*/
public boolean canRename( Object representedObject, Lookup env ) {
return false;
}
/** Test whether this object can be deleted.
* @param representedObject Represented object the look should work with.
* @param env Environement for the represented object.
* @return True if can be deleted.
*/
public boolean canDestroy( Object representedObject, Lookup env ) {
return false;
}
/** Test whether this object permits copying.
* @param representedObject Represented object the look should work with.
* @param env Environement for the represented object.
* @return True if so.
*/
public boolean canCopy( Object representedObject, Lookup env ) {
return false;
}
/** Test whether this object permits cutting.
* @param representedObject Represented object the look should work with.
* @param env Environement for the represented object.
* @return True if so.
*/
public boolean canCut( Object representedObject, Lookup env ) {
return false;
}
/** Determine which paste operations are allowed when a given
* transferable is in the clipboard. For example, a
* Java package will permit classes to be pasted into it.
* @param representedObject Represented object the look should work with.
* @param t The transferable in the clipboard.
* @param env Environement for the represented object.
* @return Array of operations that are allowed, can return null
* that is equivalent to empty array
*/
public PasteType[] getPasteTypes( Object representedObject, Transferable t, Lookup env ) {
return null;
}
/** Determine if there is a paste operation that can be performed
* on provided transferable. Used by drag'n'drop code to check
* whether the drop is possible.
* @param representedObject Represented object the look should work with.
* @param t The transferable.
* @param action The drag'n'drop action to do DnDConstants.ACTION_MOVE,
* ACTION_COPY, ACTION_LINK.
* @param index Index between children the drop occured at or -1 if not specified.
* @param env Environement for the represented object.
* @return Null if the transferable cannot be accepted or the paste type
* to execute when the drop occurs.
*/
public PasteType getDropType( Object representedObject, Transferable t, int action, int index, Lookup env ) {
return null;
}
/** Called when a object is to be copied to the clipboard.
* @param representedObject Represented object the look should work with.
* @param env Environement for the represented object.
* @return The transferable object representing the content of the clipboard.
* @throws java.io.IOException When the copy cannot be performed.
*/
public Transferable clipboardCopy( Object representedObject, Lookup env ) throws IOException {
return null;
}
/** Called when the object is to be cut to the clipboard.
* @param representedObject Represented object the look should work with.
* @param env Environement for the represented object.
* @return The transferable object representing the content of the clipboard.
* @throws java.io.IOException When the copy cannot be performed.
*/
public Transferable clipboardCut( Object representedObject, Lookup env ) throws IOException {
return null;
}
/** Called when a drag is started with this object.
* The object can attach a transfer listener to ExTransferable and
* will be then notified about progress of the drag (accept/reject).
* @param representedObject Represented object the look should work with.
* @param env Environement for the represented object.
* @return Transferable to represent this object during a drag.
* @throws java.io.IOException If a drag cannot be started.
*/
public Transferable drag( Object representedObject, Lookup env ) throws IOException {
return null;
}
/** Called when object was destroyed.
* @param representedObject Represented object the look should work with.
* @param env Environement for the represented object.
* @throws java.io.IOException If reflecting the destroy action in underlying data
* fails for some reason.
*/
public void destroy( Object representedObject, Lookup env ) throws IOException {
}
// Event firing ------------------------------------------------------------
/** Notifies all listeners attached to the representedObject that
* result(s) of some method(s) changed. The mask patameter contains
* bit mask of the methods.
*
* @param representedObject the object that has changed
* @param mask Bit mask of methods which's result changed. See {@link Look}
* for the constants
*
*/
protected final void fireChange( Object representedObject, long mask ) {
fireUniversal( mask, representedObject, null );
}
/** Notifies all listeners attached to the representedObject that
* a property in the PropertySets changed it's value.
*
* @param representedObject the object that has changed
* @param propertyName Name of the property
*/
protected final void firePropertyChange( Object representedObject, String propertyName ) {
fireUniversal( PROPERTY_CHANGE, representedObject, propertyName );
}
// Registering look listeners ----------------------------------------------
synchronized void addLookListener( Object representedObject, LookListener listener ) {
if ( representedObject != null &&
( listenerCache == null ||
listenerCache.getListenersCount( representedObject ) == 0 ) ) {
attachTo( representedObject );
}
if ( listenerCache == null ) {
listenerCache = new ListenerCache();
}
listenerCache.addListener( representedObject, listener );
}
synchronized void changeLookListener( Object representedObject, LookListener oldListener, LookListener newListener ) {
if ( listenerCache != null ) {
listenerCache.changeListener( representedObject, oldListener, newListener );
}
}
synchronized void removeLookListener( Object representedObject, LookListener listener ) {
if ( representedObject != null && listenerCache != null &&
listenerCache.getListenersCount( representedObject ) == 1 ) {
detachFrom( representedObject );
}
if ( listenerCache != null /* && listener != null */ ) {
listenerCache.removeListener( representedObject, listener );
}
}
// Package private methods -------------------------------------------------
/** Gets all objects which have some object registred. Used from look and
* to fire on all objects and from ProxyLook to fire when selector change
* occurs. It returns copy of the cache.
*/
synchronized Object[] getAllObjects() {
Collection allObjects = listenerCache.getAllObjects();
Object objects[] = allObjects.toArray();
return objects;
}
// Private methods ---------------------------------------------------------
private void fireUniversal( long mask, Object representedObject, String propertyName ) {
if ( listenerCache == null ) {
return;
}
if ( representedObject == null ) { // Fire on all objects
Object [] objects = getAllObjects();
if ( objects == null ) {
return;
}
for( int i = 0; i < objects.length; i++ ) {
fireUniversal( mask, objects[i], name );
}
}
else { // Fire on one object
LookEvent evt = mask == PROPERTY_CHANGE ?
new LookEvent( representedObject, propertyName ) :
new LookEvent( representedObject, mask );
List listeners;
synchronized( this ) {
listeners = listenerCache.getListeners( representedObject );
}
for( int i = 0; i < listeners.size(); i++ ) {
if ( mask == PROPERTY_CHANGE ) {
((LookListener)listeners.get( i )).propertyChange( evt );
}
else {
((LookListener)listeners.get( i )).change( evt );
}
}
}
}
// Innerclasses ------------------------------------------------------------
private static final Object PLACE_HOLDER = new Object();
// Cache of listeners
private class ListenerCache {
private HashSet allObjectListeners; // Listeners which should listen to all objects
private Map obj2l; // Standard listeners: Map