package ij.plugin.frame;
import ij.*;
import ij.gui.*;
import ij.measure.Calibration;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;
/** This class "synchronizes" mouse input in multiple windows. Once
several windows are synchronized, mouse events in any one of the
synchronized windows are propagated to the others.
Note, the notion of synchronization use by the SyncWindows class
here (i.e. multiple windows that all get the same mouse input) is
somewhat different than the use of the synchronize keyword in the
Java language. (In Java, synchronize has to do w/ critical section
access by multiple threads.)
Optionally passes on change of z-slice of a stack to other stacks;
Optionally translates positions to different windows via offscreen
coordinates, i.e. correctly translates coordinates to windows with a
different zoom;
Updates the list of windows by click of a button;
@author Patrick Kelly ;
Improved GUI, support of image coordinates and z-slices by Joachim Walter
*/
public class SyncWindows extends PlugInFrame implements
ActionListener, MouseMotionListener, MouseListener, DisplayChangeListener,
ItemListener, ImageListener {
/** Indices of synchronized image windows are maintained in this Vector. */
protected Vector vwins = null;
/* Manage mouse information.
The mouse coordinates x and y are only changed by the methods of the
MousMotionListener Interface. They are used by the MouseListener methods.
This way, the coordinates that were valid before a MouseListener event
(e.g. a Zoom) happened can be accessed. */
protected int oldX, oldY;
protected int x=0;
protected int y=0;
/** List of currently displayed windows retrieved from ImageJ
window manager. */
protected java.awt.List wList;
/** Panel for GUI */
protected java.awt.Panel panel;
/** Checkboxes for user control. */
protected Checkbox cCursor, cSlice, cChannel, cFrame, cCoords, cScaling;
/** Buttons for user control. */
protected Button bSyncAll, bUnsyncAll;
/** Hashtable to map list ids to image window ids. */
protected Vector vListMap;
/** reference to current instance of ImageJ (to avoid repeated IJ.getInstance() s) */
protected final ImageJ ijInstance;
/* Variables to store display values of current window.
* Translation by screenX/Y() and offScreenX/Y() does not work,
* because current window receives events (e.g. zooming) before this plugin.
*/
private double currentMag = 1;
private Rectangle currentSrcRect = new Rectangle(0,0,400,400);
// Control size of cursor box and clipping region. These could be
// changed to tune performance.
static final int RSZ = 16;
static final int SZ = RSZ/2;
static final int SCALE = 3;
private static SyncWindows instance;
private static Point location;
//--------------------------------------------------
/** Create window sync frame. Frame is shown via call to show() or
by invoking run method. */
public SyncWindows() {
this("Synchronize Windows");
}
public SyncWindows(String s) {
super(s);
ijInstance = IJ.getInstance();
if (instance!=null) {
WindowManager.toFront(instance);
return;
}
instance = this;
panel = controlPanel();
add(panel);
pack();
setResizable(false);
IJ.register(this.getClass());
if (location==null)
location = getLocation();
else
setLocation(location);
updateWindowList();
WindowManager.addWindow(this);
ImagePlus.addImageListener(this);
show();
}
public static void setC(ImageWindow source, int channel) {
SyncWindows syncWindows = instance;
if (syncWindows==null || !syncWindows.synced(source))
return;
DisplayChangeEvent event=new DisplayChangeEvent(source, DisplayChangeEvent.CHANNEL, channel);
syncWindows.displayChanged(event);
}
public static void setZ(ImageWindow source, int slice) {
SyncWindows syncWindows = instance;
if (syncWindows==null || !syncWindows.synced(source))
return;
DisplayChangeEvent event=new DisplayChangeEvent(source, DisplayChangeEvent.Z, slice);
syncWindows.displayChanged(event);
}
public static void setT(ImageWindow source, int frame) {
SyncWindows syncWindows = instance;
if (syncWindows==null || !syncWindows.synced(source))
return;
DisplayChangeEvent event=new DisplayChangeEvent(source, DisplayChangeEvent.T, frame);
syncWindows.displayChanged(event);
}
private boolean synced(ImageWindow source) {
if (source==null || vwins==null)
return false;
ImagePlus imp = source.getImagePlus();
if (imp==null)
return false;
return vwins.contains(new Integer(imp.getID()));
}
// --------------------------------------------------
/**
* Method to pass on changes of the z-slice of a stack.
*/
public void displayChanged(DisplayChangeEvent e) {
//if (e!=null) throw new IllegalArgumentException();
//IJ.log("displayChanged: "+e);
if (vwins == null) return;
Object source = e.getSource();
int type = e.getType();
int value = e.getValue();
ImagePlus imp;
ImageWindow iw;
// Current imagewindow
ImageWindow iwc = WindowManager.getCurrentImage().getWindow();
// pass on only if event comes from current window
if (!iwc.equals(source)) return;
// Change channel in other synchronized windows.
if (cChannel.getState() && type==DisplayChangeEvent.CHANNEL) {
for (int n=0; n1)
imp.setT(value);
else
imp.setZ(value);
}
}
}
}
// Change frame in other synchronized windows.
if (cFrame.getState() && type==DisplayChangeEvent.T) {
for(int n=0; n 0)
vwins = new Vector();
// Add all windows in vector to synchronized window list.
for(int n=0; n=0; i--) {
Roi roi2 = overlay2.get(i);
if (roi2.isCursor())
overlay2.remove(i);
}
if (cursor==null) {
imp.setOverlay(overlay2);
return;
}
} else
overlay2 = new Overlay();
if (cursor!=null) {
overlay2.add(cursor);
cursor.setStrokeColor(Color.red);
cursor.setStrokeWidth(2);
cursor.setNonScalable(true);
cursor.setIsCursor(true);
imp.setOverlay(overlay2);
}
}
/** Store srcRect and Magnification of the currently active ImageCanvas ic */
private void storeCanvasState(ImageCanvas ic) {
currentMag = ic.getMagnification();
currentSrcRect = new Rectangle(ic.getSrcRect());
}
// --------------------------------------------------
/** Get ImagePlus from Windows-Vector vwins. */
public ImagePlus getImageFromVector(int n) {
if (vwins == null || n<0 || vwins.size() < n+1) return null;
ImagePlus imp;
imp = WindowManager.getImage(((Integer)vwins.elementAt(n)).intValue());
return imp;
}
/** Get the title of image n from Windows-Vector vwins. If the image ends with
* .tif, the extension is removed. */
public String getImageTitleFromVector(int n) {
if (vwins == null || n<0 || vwins.size() < n+1) return "";
ImagePlus imp;
imp = WindowManager.getImage(((Integer)vwins.elementAt(n)).intValue());
String title = imp.getTitle();
if (title.length()>=4 && (title.substring(title.length()-4)).equalsIgnoreCase(".tif")) {
title = title.substring(0, title.length()-4);
} else if (title.length()>=5 && (title.substring(title.length()-5)).equalsIgnoreCase(".tiff")) {
title = title.substring(0, title.length()-5);
}
return title;
}
/** Get index of "image" in vector of synchronized windows, if image is in vector.
* Else return -1.
*/
public int getIndexOfImage(ImagePlus image) {
int index = -1;
ImagePlus imp;
if (vwins == null || vwins.size() == 0)
return index;
for (int n=0; n
* Example how to implement an object, which fires DisplayChangeEvents using the
* IJEventMulticaster:
*
*
* public mySpecialWindow extends StackWindow {
*
* DisplayChangeListener dclistener = null;
*
* public synchronized void addDisplayChangeListener(DisplayChangeListener l) {
* dclistener = IJEventMulticaster.add(dclistener, l);
* }
*
* public synchronized void removeDisplayChangeListener(DisplayChangeListener l) {
* dclistener = IJEventMulticaster.remove(dclistener, l);
* }
*
* public void myEventFiringMethod(arguments) {
* ... code ...
* if (dclistener != null) {
* DisplayChangeEvent dcEvent = new DisplayChangeEvent(this, DisplayChangeEvent.Z, zSlice);
* dclistener.displayChanged(dcEvent);
* }
* ... code ...
* }
*
* ... other methods ...
* }
*
*
* To put in a new event-listener (by changing this class or extending it):
*
* - Add the listener to the "implements" list.
*
* - Add the methods of this listener to pass on the events (like displayChanged).
*
* - Add the methods "add" and "remove" with the corresponding listener type.
*
*
* @author code taken from Sun's AWTEventMulticaster by J. Walter 2002-03-07
*/
class IJEventMulticaster extends AWTEventMulticaster implements DisplayChangeListener {
IJEventMulticaster(EventListener a, EventListener b) {
super(a,b);
}
/**
* Handles the DisplayChange event by invoking the
* displayChanged methods on listener-a and listener-b.
* @param e the DisplayChange event
*/
public void displayChanged(DisplayChangeEvent e) {
((DisplayChangeListener)a).displayChanged(e);
((DisplayChangeListener)b).displayChanged(e);
}
/**
* Adds DisplayChange-listener-a with DisplayChange-listener-b and
* returns the resulting multicast listener.
* @param a DisplayChange-listener-a
* @param b DisplayChange-listener-b
*/
public static DisplayChangeListener add(DisplayChangeListener a, DisplayChangeListener b) {
return (DisplayChangeListener)addInternal(a, b);
}
/**
* Removes the old DisplayChange-listener from DisplayChange-listener-l and
* returns the resulting multicast listener.
* @param l DisplayChange-listener-l
* @param oldl the DisplayChange-listener being removed
*/
public static DisplayChangeListener remove(DisplayChangeListener l, DisplayChangeListener oldl) {
return (DisplayChangeListener)removeInternal(l, oldl);
}
}
Ads help maintain this website.