/* * Copyright (C) 2010 Brockmann Consult GmbH (info@brockmann-consult.de) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) * any later version. * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, see http://www.gnu.org/licenses/ */ package org.esa.snap.rcp.actions.interactors; import com.bc.ceres.core.Assert; import com.bc.ceres.swing.figure.AbstractInteractorListener; import com.bc.ceres.swing.figure.Interactor; import com.bc.ceres.swing.figure.InteractorListener; import com.bc.ceres.swing.figure.interactions.NullInteractor; import org.esa.snap.core.util.Guardian; import org.esa.snap.ui.product.ProductSceneView; import org.openide.util.ContextAwareAction; import org.openide.util.HelpCtx; import org.openide.util.Lookup; import org.openide.util.LookupEvent; import org.openide.util.LookupListener; import org.openide.util.Utilities; import org.openide.util.WeakListeners; import org.openide.util.actions.Presenter; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JCheckBoxMenuItem; import javax.swing.JMenuItem; import javax.swing.JToggleButton; import java.awt.Component; import java.awt.event.ActionEvent; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.logging.Logger; /** * Tool actions are used to interact with a {@link com.bc.ceres.swing.figure.FigureEditor FigureEditor}, * such as the VISAT product scene view. * <p> * Derived tool actions must at least provide two public constructors: * <ol> * <li>a default constructor</li> * <li>a constructor that takes a single {@code Lookup} argument</li> * </ol> * <p> * Derived actions must also tell the system to load the actions eagerly, that is, use the action registration * as follows: {@code @ActionRegistration(displayName = "not-used", lazy = false)}. * * @author Norman Fomferra */ public abstract class ToolAction extends AbstractAction implements ContextAwareAction, LookupListener, Presenter.Toolbar, Presenter.Menu, Presenter.Popup, HelpCtx.Provider { public static final String INTERACTOR_KEY = "interactor"; private static final Logger LOG = Logger.getLogger(ToolAction.class.getName()); private final InteractorListener interactorListener; private final Lookup lookup; private final Lookup.Result<ProductSceneView> viewResult; protected ToolAction() { this(null); } protected ToolAction(Lookup lookup) { this(lookup, NullInteractor.INSTANCE); } protected ToolAction(Lookup lookup, Interactor interactor) { putValue(ACTION_COMMAND_KEY, getClass().getName()); putValue(SELECTED_KEY, false); interactorListener = new InternalInteractorListener(); setInteractor(interactor); this.lookup = lookup != null ? lookup : Utilities.actionsGlobalContext(); this.viewResult = this.lookup.lookupResult(ProductSceneView.class); this.viewResult.addLookupListener(WeakListeners.create(LookupListener.class, this, viewResult)); updateEnabledState(); } public ProductSceneView getProductSceneView() { return lookup.lookup(ProductSceneView.class); } @Override public Component getToolbarPresenter() { JToggleButton toggleButton = new JToggleButton(this); toggleButton.setText(null); return toggleButton; } @Override public JMenuItem getMenuPresenter() { return new JCheckBoxMenuItem(this); } @Override public JMenuItem getPopupPresenter() { return getMenuPresenter(); } @Override public Action createContextAwareInstance(Lookup actionContext) { try { Constructor<? extends ToolAction> constructor = getClass().getConstructor(Lookup.class); return constructor.newInstance(actionContext); } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { throw new IllegalStateException(e); } } @Override public void resultChanged(LookupEvent ignored) { updateEnabledState(); if (isEnabled() && isSelected()) { ProductSceneView productSceneView = getProductSceneView(); if (productSceneView != null) { Interactor oldInteractor = productSceneView.getFigureEditor().getInteractor(); Interactor newInteractor = getInteractor(); if (oldInteractor != newInteractor) { oldInteractor.deactivate(); newInteractor.activate(); productSceneView.getFigureEditor().setInteractor(newInteractor); } } } } @Override public void actionPerformed(ActionEvent actionEvent) { onSelectionStateChanged(); } public Lookup getLookup() { return lookup; } public boolean isSelected() { return Boolean.TRUE.equals(getValue(SELECTED_KEY)); } public void setSelected(boolean selected) { putValue(SELECTED_KEY, selected); } public Interactor getInteractor() { return (Interactor) getValue(INTERACTOR_KEY); } public final void setInteractor(Interactor interactor) { Guardian.assertNotNull("interactor", interactor); Interactor oldInteractor = getInteractor(); if (interactor == oldInteractor) { return; } if (oldInteractor != null) { oldInteractor.removeListener(interactorListener); } interactor.addListener(interactorListener); putValue(INTERACTOR_KEY, interactor); } protected void updateEnabledState() { super.setEnabled(!viewResult.allInstances().isEmpty()); } private void onSelectionStateChanged() { LOG.fine(String.format(">>> %s#onSelectionStateChanged: selected = %s, interactor = %s%n", getClass().getName(), isSelected(), getInteractor())); ProductSceneView productSceneView = getProductSceneView(); if (productSceneView != null && isSelected()) { Interactor oldInteractor = productSceneView.getFigureEditor().getInteractor(); Interactor newInteractor = getInteractor(); Assert.notNull(newInteractor, "No interactor set on action " + getClass()); if (oldInteractor != newInteractor && oldInteractor.isActive()) { oldInteractor.deactivate(); } if (!newInteractor.isActive()) { newInteractor.activate(); } productSceneView.getFigureEditor().setInteractor(newInteractor); } } private class InternalInteractorListener extends AbstractInteractorListener { @Override public void interactorActivated(Interactor interactor) { LOG.fine(String.format(">>> %s#interactorActivated: interactor = %s%n", getClass().getName(), interactor)); if (interactor == getInteractor() && !isSelected()) { setSelected(true); } } @Override public void interactorDeactivated(Interactor interactor) { LOG.fine(String.format(">>> %s#interactorDeactivated: interactor = %s%n", getClass().getName(), interactor)); if (interactor == getInteractor() && isSelected()) { setSelected(false); } } } }