All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openide.awt.ActionState Maven / Gradle / Ivy

There is a newer version: RELEASE240
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.openide.awt;

import java.beans.PropertyChangeListener;
import javax.swing.event.ChangeListener;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.EventListener;
import javax.swing.Action;
import org.openide.util.actions.Presenter;

/**
 * Specifies that the action behaviour is conditional and how the action should obtain the
 * state for its presentation. The annotation is used as value for {@link ActionRegistration#enabledOn}
 * and {@link ActionRegistration#checkedOn} to control action's enabled or checked state. The annotation
 * can be only applied on context actions, which have a single parameter constructor,
 * which accept the model object - see {@link Actions#context(java.lang.Class, boolean, boolean, org.openide.util.ContextAwareAction, java.lang.String, java.lang.String, java.lang.String, boolean)}.
 * 

* When used as {@link ActionRegistration#checkedOn} value, the annotated action will change * to toggle on/off action, represented by a checkbox (menu) or a toggle button (toolbar). * The action state will track the model property specified by this * annotation. Toggle actions become enabled when the model object is * found in the Lookup, and checked (or toggled on) when the model property * is set to a defined value (usually {@code true}) *

* The {@link #type} specifies type which is searched for in the {@link org.openide.util.Lookup} and * if an instance is found, it is used as the model object. If the {@link #type} is not set, * the the type inferred from Action's * constructor (see {@link ActionRegistration}) will be used to find the model. *

* The {@link #property} specifies bean property whose value should be used to * determine checked state. The obtained value is compared using {@link #checkedValue} * as follows: *

    *
  • a boolean or Boolean value is compared to {@link Boolean#TRUE} or the {@link #checkedValue}, * if present. *
  • if {@link #checkedValue} is {@link #NULL_VALUE}, the action is checked if and only if * the value is {@code null}. *
  • if {@link #checkedValue} is {@link #NON_NULL_VALUE}, the action is checked if and only if * the value is not {@code null}. *
  • if the value type is an enum, its {@link Enum#name} is compared to {@link #checkedValue} *
  • if the value is a {@link java.util.Collection} or {@link java.util.Map}, state evaluates to the {@link java.util.Collection#isEmpty} or * {@link java.util.Map#isEmpty}, respectively. *
  • if the value is a {@link Number}, state will be true if value evaluates to a positive integer. *
  • the state will be {@code false} (unchecked) otherwise. *
*

* If {@link #type} is set to {@link Action}.class, the annotated element must * be an {@link Action} subclass. {@link Action#getValue} will be used to determine * the state. The Action delegate will not be instantiated eagerly, but only * after the necessary context type becomes available in Lookup. * This support minimizes from premature code loading for custom action implementations. * Important note: if your Action implements {@link org.openide.util.ContextAwareAction}, * or one of the {@link Presenter} interfaces, it is eager and will be loaded immediately ! *

* Changes to the model object will be tracked using event listener pattern. The annotation-supplied * delegate attempts to {@link PropertyChangeListener} and {@link ChangeListener} automatically; \ * other listener interfaces must be specified using {@link #listenOn} * value. Finally, {@link #listenOnMethod} specifies which listener method will trigger * state update; by default, all listener method calls will update the action state. *

* The {@link ActionState} annotation may be also used as a value of {@link ActionRegistration#enabledOn()} * and causes the annotated Action to be enabled not only on presence of object of the context type, * but also based on the model property. The property, enable value and listener is specified the * same way as for "checked" state. See the above text. *

* If a completely custom behaviour is desired, the system can finally delegate {@link Action#isEnabled} and * {@link Action#getValue getValue}({@link Action#SELECTED_KEY}) to the action implementation itself: use {@link #useActionInstance()} * value. *

* Here are several examples of {@code @ActionState} usage: *

* To define action, which enables on modified DataObjects do the following * registration: *


 * @ActionID(category = "Example", id = "example.SaveAction")
 * @ActionRegistration(displayName = "Save modified",
 *     enabledOn = @ActionState(property = "modified")
 * )
 * public class ExampleAction implements ActionListener {
 *     public ExampleAction(DataObject d) {
 *         // ...
 *     }
 *     
 *     public void actionPerformed(ActionEvent e) {
 *         // ...
 *     }
 * }
 * 
* The action will be instantiated and run only after: *
    *
  • DataObject becomes available, and *
  • its {@code modified} property becomes true *
* * To create "toggle" action in toolbar or a menu, which changes state based on some property, * you can code: *

 * enum SelectionMode {
 *     Rectangular,
 *     normal
 * }
 * @ActionID(category = "Example", id = "example.RectSelection")
 * @ActionRegistration(displayName = "Toggle rectangular selection", checkedOn = @ActionState(
 *     property = "selectionMode", checkedValue = "Rectangular", listenOn = EditorStateListener.class)
 * )
 * public class RectangularSelectionAction implements ActionListener {
 *     public RectangularSelectionAction(EditorInterface editor) {
 *         // ...
 *     }
 *     @Override
 *     public void actionPerformed(ActionEvent e) {
 *     }
 * }
 * 
* The action enables when {@code EditorInterface} appears in the action Lookup. Then, * its state will be derived from {@code EditorInterface.selectionMode} property. Since * there's a custom listener interface for this value, it must be specified using {@link #listenOn}. *

* Finally, if the action needs to perform its own special magic to check enable state, we * hand over final control to the action, but the annotation-introduced wrappers will still * create action instance for a new model object, attach and detach listeners on it and ensure * that UI will not be strongly referenced from the model for proper garbage collection: *


 * @ActionID(category = "Example", id = "example.SelectPrevious")
 * @ActionRegistration(displayName = "Selects previous item", checkedOn = @ActionState(
 *     listenOn = ListSelectionListener.class, useActionInstance = true)
 * )
 * public class SelectPreviousAction extends AbstractAction {
 *     private final ListSelectionModel model;
 *     
 *     public SelectPreviousAction(ListSelectionModel model) {
 *         this.model = model;
 *     }
 *     @Override
 *     public boolean isEnabled() {
 *         return model.getAnchorSelectionIndex() > 0;
 *     }
 *     @Override
 *     public void actionPerformed(ActionEvent e) {
 *     }
 * }
 * 
* The system will do the necessary bookkeeping, but the action decides using its * {@link Action#isEnabled} implementation. * * @author sdedic * @since 7.71 */ @Retention(RetentionPolicy.SOURCE) @Target({ElementType.FIELD}) public @interface ActionState { /** * The type which the action will look for in its context. The action * becomes checked (enabled) if and only if there's at least one instance of such type * present in the context. There are some special values that modify behaviour: *
    *
  • Object.class (the default) the context object will be used and the property will be read * from the context object. Only applicable if the action accepts single object. *
  • Action.class: the {@link #property} action value will be used, * as obtained by {@link Action#getValue}. *
* If {@code @ActionState} is used in {@link ActionRegistration#enabledOn()}, the * {@code type} can be left unspecified, defaulting to the context type for the action. * * @return type to work with. */ public Class type() default Object.class; /** * Property name whose value represents the state. The property must be a * property on the {@link #type()} class; read-only properties are * supported. If the target class supports attaching * {@link PropertyChangeListener} or {@link ChangeListener}, the action will * attach a listener ({@link PropertyChangeListener} takes precedence) and will fire * appropriate state events when the property property changes. *

* In the case that checked state is delegated to {@link Action}, the property * default is different depending on the context the annotation is used: *

    *
  • if used to specify enable state ({@link ActionRegistration#enabledOn()}, the property defaults to "enabled" *
  • if used as checked state ({@code @ActionState} directly annotates to element}, the property defaults to {@link Action#SELECTED_KEY}. *
  • if the model is {@link Action}, {@link Action#getValue} is also used * to obtain the value. *
* Note that although this value gives more flexibility than {@link #useActionInstance()} for Actions, * in the case where {@link #type}.{@link #property} is used to specify necessary guard condition, * {@link #useActionInstance()} is necessary to perform custom check. * @return property name. */ public String property() default ""; // NOI18N /** * The value which makes the action checked. Can be one of: *
    *
  • "true", "false" to represent boolean or Boolean values *
  • String representation of an enum value, as obtained by {@link Enum#name()} *
  • {@link #NULL_VALUE} to indicate null value *
  • {@link #NON_NULL_VALUE} to indicate any non-null value *
  • String representation of the value object, as obtained by {@link Object#toString} *
  • *
* @return value which indicates "set" state */ public String checkedValue() default ""; // NOI18N /** * Custom listener interface to monitor for changes. If undefined, then * either {@link PropertyChangeListener} or {@link ChangeListener} will be * auto-detected from {@link #type} class. *

* All listener methods will cause the system to re-evaluate enable and on/off * (if applicable) state for the action, unless {@link #listenOnMethod} is * also used. * * @return custom listener interface. */ public Class listenOn() default EventListener.class; /** * Allows to pick one listener method, which will trigger action state update. * The update will re-check both enable and on/off state (if applicable). * The action will however fire change events only if the state actually changes * from the previous one. *

* The default (empty) value means that all listener methods will cause * state update. The value can be only specified together with {@link #listenOn} value. * * @return listener method. */ public String listenOnMethod() default ""; // NOI18N /** * If true, the target system will delegate to the action instance itself. * The action instance will not be created until the context object (or {@link #type()} * becomes available and the guard {@link #property()} has the {@link #checkedValue() appropriate value}. *

* After that, the system will delegate to {@link Action#isEnabled()} for enablement, or * to {@link Action#getValue getValue}({@link Action#SELECTED_KEY}) for on/off state of the action. *

* The annotated element must implement {@link Action} interface in order to use * this value. * @return whether the action instance itself should be ultimately for enable/check status */ public boolean useActionInstance() default false; /** * An explicit {@code null} value for {@link #checkedValue}, represents {@code null} */ public static final String NULL_VALUE = "#null"; /** * An explicit {@code null} value for {@link #checkedValue}, represents {@code non-null} */ public static final String NON_NULL_VALUE = "#non-null"; }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy