| /*=============================================================================# |
| # Copyright (c) 2009, 2021 Stephan Wahlbrink and others. |
| # |
| # This program and the accompanying materials are made available under the |
| # terms of the Eclipse Public License 2.0 which is available at |
| # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 |
| # which is available at https://www.apache.org/licenses/LICENSE-2.0. |
| # |
| # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 |
| # |
| # Contributors: |
| # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation |
| #=============================================================================*/ |
| |
| package org.eclipse.statet.rj.eclient.graphics; |
| |
| import static org.eclipse.statet.ecommons.ui.actions.UIActions.ADDITIONS_GROUP_ID; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| |
| import org.eclipse.core.commands.AbstractHandler; |
| import org.eclipse.core.commands.ExecutionEvent; |
| import org.eclipse.core.commands.ExecutionException; |
| import org.eclipse.core.commands.IHandler2; |
| import org.eclipse.jface.action.IToolBarManager; |
| import org.eclipse.jface.action.Separator; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.jface.resource.ImageRegistry; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.Event; |
| import org.eclipse.swt.widgets.Listener; |
| import org.eclipse.ui.IActionBars; |
| import org.eclipse.ui.menus.CommandContributionItemParameter; |
| import org.eclipse.ui.services.IServiceLocator; |
| |
| import org.eclipse.statet.jcommons.collections.CopyOnWriteIdentityListSet; |
| import org.eclipse.statet.jcommons.status.ProgressMonitor; |
| import org.eclipse.statet.jcommons.status.Status; |
| import org.eclipse.statet.jcommons.status.StatusException; |
| import org.eclipse.statet.jcommons.ts.core.SystemRunnable; |
| import org.eclipse.statet.jcommons.ts.core.Tool; |
| |
| import org.eclipse.statet.ecommons.commands.core.BasicHandlerCollection; |
| import org.eclipse.statet.ecommons.commands.core.HandlerCollection; |
| import org.eclipse.statet.ecommons.ui.SharedUIResources; |
| import org.eclipse.statet.ecommons.ui.actions.HandlerContributionItem; |
| |
| import org.eclipse.statet.internal.rj.eclient.graphics.RGraphicsPlugin; |
| import org.eclipse.statet.rj.eclient.graphics.util.AbstractLocalLocator; |
| import org.eclipse.statet.rj.ts.core.AbstractRToolRunnable; |
| import org.eclipse.statet.rj.ts.core.RToolService; |
| |
| |
| /** |
| * Actions for R graphics in an {@link RGraphicComposite}. |
| */ |
| public class RGraphicCompositeActionSet implements ERGraphic.ListenerLocatorExtension { |
| |
| |
| public static final String POSITION_STATUSLINE_ITEM_ID= "position"; //$NON-NLS-1$ |
| |
| public static final String CONTEXT_MENU_GROUP_ID= "context"; //$NON-NLS-1$ |
| public static final String SIZE_MENU_GROUP_ID= "size"; //$NON-NLS-1$ |
| |
| private static final String LOCATOR_DONE_COMMAND_ID= ".locator.done"; //$NON-NLS-1$ |
| private static final String LOCATOR_CANCEL_COMMAND_ID= ".locator.cancel"; //$NON-NLS-1$ |
| private static final String RESIZE_FIT_COMMAND_ID= ".resize.fit"; //$NON-NLS-1$ |
| |
| |
| public static interface LocationListener { |
| |
| void loading(); |
| |
| void located(double x, double y); |
| |
| } |
| |
| |
| private class ResizeFitRHandler extends AbstractHandler { |
| |
| public ResizeFitRHandler() { |
| } |
| |
| @Override |
| public void setEnabled(final Object evaluationContext) { |
| setBaseEnabled(RGraphicCompositeActionSet.this.graphic != null); |
| } |
| |
| @Override |
| public Object execute(final ExecutionEvent event) throws ExecutionException { |
| if (RGraphicCompositeActionSet.this.graphic == null) { |
| return null; |
| } |
| final double[] size= RGraphicCompositeActionSet.this.graphicComposite.getGraphicFitSize(); |
| final Status status= RGraphicCompositeActionSet.this.graphic.resize(size[0], size[1]); |
| if (status == null || status.getSeverity() != Status.OK) { |
| // TODO: Status message |
| Display.getCurrent().beep(); |
| } |
| return null; |
| } |
| |
| } |
| |
| protected class StopLocatorHandler extends AbstractHandler { |
| |
| private final String type; |
| |
| public StopLocatorHandler(final String type) { |
| this.type= type; |
| } |
| |
| @Override |
| public void setEnabled(final Object evaluationContext) { |
| setBaseEnabled(RGraphicCompositeActionSet.this.graphic != null && RGraphicCompositeActionSet.this.graphic.isLocatorStarted() |
| && RGraphicCompositeActionSet.this.graphic.getLocatorStopTypes().contains(this.type) ); |
| } |
| |
| @Override |
| public Object execute(final ExecutionEvent event) throws ExecutionException { |
| if (RGraphicCompositeActionSet.this.graphic == null) { |
| return null; |
| } |
| RGraphicCompositeActionSet.this.graphic.stopLocator(this.type); |
| return null; |
| } |
| |
| } |
| |
| private static abstract class ConversionRunnable extends AbstractRToolRunnable implements SystemRunnable { |
| |
| private final ERGraphic graphic; |
| |
| private boolean scheduled; |
| |
| private double[] todoSource; |
| |
| private double[] convertedSource; |
| private double[] convertedTarget; |
| |
| |
| public ConversionRunnable(final ERGraphic graphic) { |
| super("r/rjgd/position", "Converting graphic coordinates"); //$NON-NLS-1$ |
| this.graphic= graphic; |
| } |
| |
| |
| public boolean schedule(final double[] source) { |
| synchronized (this) { |
| this.todoSource= source; |
| if (this.scheduled) { |
| return true; |
| } |
| final Status status= this.graphic.getRHandle().getQueue().addHot(this); |
| if (status.getSeverity() == Status.OK) { |
| this.scheduled= true; |
| return true; |
| } |
| return false; |
| } |
| } |
| |
| public void cancel() { |
| synchronized (this) { |
| if (this.scheduled) { |
| this.scheduled= false; |
| this.todoSource= null; |
| this.graphic.getRHandle().getQueue().removeHot(this); |
| } |
| } |
| } |
| |
| |
| @Override |
| public boolean canRunIn(final Tool tool) { |
| return (tool == this.graphic.getRHandle() && super.canRunIn(tool)); |
| } |
| |
| @Override |
| public boolean changed(final int event, final Tool tool) { |
| switch (event) { |
| case MOVING_FROM: |
| return false; |
| case REMOVING_FROM: |
| case BEING_ABANDONED: |
| case FINISHING_ERROR: |
| case FINISHING_CANCEL: |
| synchronized (this) { |
| this.scheduled= false; |
| break; |
| } |
| case FINISHING_OK: |
| converted(this.graphic, this.convertedSource, this.convertedTarget); |
| break; |
| } |
| return true; |
| } |
| |
| @Override |
| protected void run(final RToolService service, |
| final ProgressMonitor m) throws StatusException { |
| double[] source= null; |
| double[] target= null; |
| while (true) { |
| synchronized (this) { |
| this.convertedSource= source; |
| this.convertedTarget= target; |
| |
| source= this.todoSource; |
| if (source == null) { |
| this.scheduled= false; |
| return; |
| } |
| this.todoSource= null; |
| } |
| target= this.graphic.convertGraphic2User(source, m); |
| } |
| } |
| |
| protected abstract void converted(ERGraphic graphic, double[] source, double[] target); |
| |
| } |
| |
| private class MouseLocationListener implements Listener { |
| |
| private double[] currentGraphic; |
| private double[] currentTarget; |
| |
| |
| @Override |
| public void handleEvent(final Event event) { |
| switch (event.type) { |
| case SWT.MouseDown: |
| if (event.button == 1) { |
| final double[] request= this.currentGraphic= new double[] { |
| RGraphicCompositeActionSet.this.graphicComposite.convertWidget2GraphicX(event.x), |
| RGraphicCompositeActionSet.this.graphicComposite.convertWidget2GraphicY(event.y) }; |
| this.currentTarget= null; |
| event.display.timerExec(1000, new Runnable() { |
| @Override |
| public void run() { |
| if (MouseLocationListener.this.currentTarget == null && MouseLocationListener.this.currentGraphic == request |
| && !RGraphicCompositeActionSet.this.graphicComposite.isDisposed()) { |
| notifyMouseLocationListeners(null); |
| } |
| } |
| }); |
| if (RGraphicCompositeActionSet.this.mouseLocationRunnable == null) { |
| RGraphicCompositeActionSet.this.mouseLocationRunnable= new ConversionRunnable( |
| RGraphicCompositeActionSet.this.graphic) { |
| @Override |
| protected void converted(final ERGraphic graphic, |
| final double[] source, final double[] target) { |
| if (RGraphicCompositeActionSet.this.graphic == graphic) { |
| RGraphicCompositeActionSet.this.display.asyncExec(new Runnable() { |
| @Override |
| public void run() { |
| if (RGraphicCompositeActionSet.this.graphic == graphic |
| && source != null && target != null && MouseLocationListener.this.currentGraphic != null |
| && source[0] == MouseLocationListener.this.currentGraphic[0] && source[1] == MouseLocationListener.this.currentGraphic[1]) { |
| MouseLocationListener.this.currentTarget= target; |
| notifyMouseLocationListeners(target); |
| } |
| } |
| }); |
| } |
| } |
| }; |
| } |
| RGraphicCompositeActionSet.this.mouseLocationRunnable.schedule(this.currentGraphic); |
| } |
| break; |
| } |
| } |
| |
| } |
| |
| |
| private final List<IActionBars> actionBars= new ArrayList<>(4); |
| |
| private ERGraphic graphic; |
| private final RGraphicComposite graphicComposite; |
| private final Display display; |
| |
| private HandlerCollection handlerCollection; |
| |
| private final CopyOnWriteIdentityListSet<LocationListener> mouseLocationListeners= new CopyOnWriteIdentityListSet<>(); |
| private MouseLocationListener mouseListenerListener; |
| private ConversionRunnable mouseLocationRunnable; |
| |
| |
| public RGraphicCompositeActionSet(final RGraphicComposite composite) { |
| this.graphicComposite= composite; |
| this.display= this.graphicComposite.getDisplay(); |
| } |
| |
| |
| public void setGraphic(final ERGraphic graphic) { |
| if (this.graphic != null) { |
| this.graphic.removeListener(this); |
| if (this.mouseLocationRunnable != null) { |
| this.mouseLocationRunnable.cancel(); |
| this.mouseLocationRunnable= null; |
| } |
| } |
| this.graphic= graphic; |
| if (this.graphic != null) { |
| this.graphic.addListener(this); |
| } |
| |
| update(); |
| } |
| |
| public void initActions(final IServiceLocator serviceLocator) { |
| this.handlerCollection= new BasicHandlerCollection(); |
| this.handlerCollection.add(LOCATOR_DONE_COMMAND_ID, |
| new StopLocatorHandler(ERGraphic.LOCATOR_DONE) ); |
| this.handlerCollection.add(LOCATOR_CANCEL_COMMAND_ID, |
| new StopLocatorHandler(ERGraphic.LOCATOR_CANCEL) ); |
| this.handlerCollection.add(RESIZE_FIT_COMMAND_ID, |
| new ResizeFitRHandler() ); |
| } |
| |
| public void contributeToActionsBars(final IServiceLocator serviceLocator, |
| final IActionBars actionBars) { |
| this.actionBars.add(actionBars); |
| |
| final IToolBarManager toolBar= actionBars.getToolBarManager(); |
| if (toolBar.find(CONTEXT_MENU_GROUP_ID) == null) { |
| toolBar.insertBefore(ADDITIONS_GROUP_ID, new Separator(CONTEXT_MENU_GROUP_ID)); |
| } |
| if (toolBar.find(SIZE_MENU_GROUP_ID) == null) { |
| toolBar.insertBefore(ADDITIONS_GROUP_ID, new Separator(SIZE_MENU_GROUP_ID)); |
| } |
| |
| final ImageRegistry rGraphicsImageRegistry= RGraphicsPlugin.getInstance().getImageRegistry(); |
| |
| toolBar.appendToGroup(CONTEXT_MENU_GROUP_ID, new HandlerContributionItem(new CommandContributionItemParameter( |
| serviceLocator, null, HandlerContributionItem.NO_COMMAND_ID, null, |
| rGraphicsImageRegistry.getDescriptor(RGraphicsPlugin.IMG_LOCTOOL_LOCATOR_DONE), null, null, |
| "Stop Locator", null, null, HandlerContributionItem.STYLE_PUSH, null, true), |
| this.handlerCollection.get(LOCATOR_DONE_COMMAND_ID) )); |
| toolBar.appendToGroup(CONTEXT_MENU_GROUP_ID, new HandlerContributionItem(new CommandContributionItemParameter( |
| serviceLocator, null, HandlerContributionItem.NO_COMMAND_ID, null, |
| rGraphicsImageRegistry.getDescriptor(RGraphicsPlugin.IMG_LOCTOOL_LOCATOR_CANCEL), null, null, |
| "Cancel Locator", null, null, HandlerContributionItem.STYLE_PUSH, null, true), |
| this.handlerCollection.get(LOCATOR_CANCEL_COMMAND_ID) )); |
| } |
| |
| protected void addTestLocator(final IServiceLocator serviceLocator, final IActionBars actionBars) { |
| final IToolBarManager toolBar= actionBars.getToolBarManager(); |
| |
| final IHandler2 handler= new AbstractHandler() { |
| @Override |
| public void setEnabled(final Object evaluationContext) { |
| setBaseEnabled(RGraphicCompositeActionSet.this.graphic != null && !RGraphicCompositeActionSet.this.graphic.isLocatorStarted()); |
| } |
| @Override |
| public Object execute(final ExecutionEvent event) throws ExecutionException { |
| if (RGraphicCompositeActionSet.this.graphic == null || RGraphicCompositeActionSet.this.graphic.isLocatorStarted()) { |
| return null; |
| } |
| final AbstractLocalLocator locator= new AbstractLocalLocator(RGraphicCompositeActionSet.this.graphic) { |
| @Override |
| protected void finished(final List<double[]> graphic, final List<double[]> user) { |
| final StringBuilder sb= new StringBuilder(); |
| for (int i= 0; i < user.size(); i++) { |
| sb.append(Arrays.toString(user.get(i))).append("\n"); |
| } |
| Display.getDefault().asyncExec(new Runnable() { |
| @Override |
| public void run() { |
| MessageDialog.openInformation(null, "Locator Result", |
| sb.toString()); |
| } |
| }); |
| } |
| @Override |
| protected void canceled() { |
| } |
| }; |
| locator.start(); |
| return null; |
| } |
| }; |
| this.handlerCollection.add(".locator.startTest", handler); |
| toolBar.appendToGroup(CONTEXT_MENU_GROUP_ID, new HandlerContributionItem(new CommandContributionItemParameter( |
| serviceLocator, null, HandlerContributionItem.NO_COMMAND_ID, null, |
| SharedUIResources.getImages().getDescriptor(SharedUIResources.LOCTOOL_SORT_SCORE_IMAGE_ID), null, null, |
| "Test Locator", null, null, HandlerContributionItem.STYLE_PUSH, null, false), |
| handler )); |
| } |
| |
| protected void addSizeActions(final IServiceLocator serviceLocator, final IActionBars actionBars) { |
| final IToolBarManager toolBar= actionBars.getToolBarManager(); |
| |
| final ImageRegistry rGraphicsImageRegistry= RGraphicsPlugin.getInstance().getImageRegistry(); |
| |
| toolBar.appendToGroup(SIZE_MENU_GROUP_ID, new HandlerContributionItem(new CommandContributionItemParameter( |
| serviceLocator, null, HandlerContributionItem.NO_COMMAND_ID, null, |
| rGraphicsImageRegistry.getDescriptor(RGraphicsPlugin.IMG_LOCTOOL_RESIZE_FIT_R), null, null, |
| "Resize Fit in R", null, null, HandlerContributionItem.STYLE_PUSH, null, false ), |
| this.handlerCollection.get(RESIZE_FIT_COMMAND_ID) )); |
| |
| update(); |
| } |
| |
| protected void update() { |
| if (this.actionBars.isEmpty()) { |
| return; |
| } |
| |
| this.handlerCollection.update(null); |
| |
| for (final IActionBars actionBars : this.actionBars) { |
| actionBars.getToolBarManager().update(true); |
| } |
| } |
| |
| |
| @Override |
| public void activated() { |
| } |
| |
| @Override |
| public void deactivated() { |
| } |
| |
| @Override |
| public void drawingStarted() { |
| } |
| |
| @Override |
| public void drawingStopped() { |
| } |
| |
| @Override |
| public void locatorStarted() { |
| update(); |
| } |
| |
| @Override |
| public void locatorStopped() { |
| update(); |
| } |
| |
| |
| public void addMouseClickLocationListener(final LocationListener listener) { |
| this.mouseLocationListeners.add(listener); |
| if (this.mouseListenerListener == null) { |
| this.mouseListenerListener= new MouseLocationListener(); |
| final Control widget= this.graphicComposite.getGraphicWidget(); |
| widget.addListener(SWT.MouseDown, this.mouseListenerListener); |
| } |
| } |
| |
| public void removeMouseLocationListener(final LocationListener listener) { |
| this.mouseLocationListeners.remove(listener); |
| } |
| |
| private void notifyMouseLocationListeners(final double[] xy) { |
| if (xy != null) { |
| for (final LocationListener listener : this.mouseLocationListeners) { |
| listener.located(xy[0], xy[1]); |
| } |
| } |
| else { |
| for (final LocationListener listener : this.mouseLocationListeners) { |
| listener.loading(); |
| } |
| } |
| } |
| |
| |
| public void dispose(final IActionBars actionBars) { |
| this.actionBars.remove(actionBars); |
| } |
| |
| public void dispose() { |
| setGraphic(null); |
| } |
| |
| } |