/*
* Copyright (c) 2012 European Synchrotron Radiation Facility,
* Diamond Light Source Ltd.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package fable.imageviewer.component;
import java.util.Vector;
import org.dawb.common.ui.image.PaletteFactory;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.graphics.PaletteData;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IReusableEditor;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IWorkbenchListener;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPart3;
import org.eclipse.ui.PlatformUI;
import org.embl.cca.utils.imageviewer.FableSelectionProvider;
import org.embl.cca.utils.imageviewer.Statistics;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import fable.framework.logging.FableLogger;
import fable.framework.navigator.controller.SampleController;
import fable.framework.navigator.toolBox.IVarKeys;
import fable.framework.toolbox.EclipseUtils;
import fable.framework.toolbox.FableUtils;
import fable.imageviewer.internal.Coordinates;
import fable.imageviewer.internal.IImagesVarKeys;
import fable.imageviewer.internal.ZoomSelection;
import fable.imageviewer.model.ImageModel;
import fable.imageviewer.model.ImageModelFactory;
import fable.imageviewer.preferences.PreferenceConstants;
import fable.imageviewer.rcp.Activator;
import fable.imageviewer.views.ImageView;
import fable.python.Peak;
import fable.python.PeakSearchSpt;
/**
* ImageComponent implements a view to display an image using the SWT Image widget.
* The image is automatically fitted to the window size. The main features are
* the display is fast, a variety of controls allow the user to zoom a box,
* line, integrated profile or 3d relief, change lookup tables, autoscale or
* manually scale the range. The image is treated as a floating point image so
* that images scaled between 0 and 1 are displayed correctly. Images can be
* loaded using loadFile(FabioFile) or via changeImageData().
* <p>
* ImageView allows multiple Views. These are distinguished by the secondary ID.
* The secondary ID is typically set by IWorkbenchPage.showView(String viewId,
* String secondaryId, int mode). The viewId to use is ImageView.ID. Any plug-in
* can call this method and is free to specify the secondary ID as it wishes.
* ImageView manages 5 secondary IDs, for the main, zoom, slice1D, slice2D, and
* copy views. These IDs are determined by the public fields SECONDARY_ID_xxx.
* If you wish to call these views, then you should use these fields by name.
* Except for the copy views, there is expected to be zero or one of the others.
* On workspace shutdown ImageView hides (i.e. removes) all of the views that it
* manages, except the main one with SECONDARY_ID_MAIN. This is to prevent
* workspace clutter on restarting the workspace. It does not remove others.
* Other plug-ins are responsible for removing ones they created if desired.
* When ImageView is first created and does not have an image, then the part
* name (name on the tab) is set to the secondary ID + "Image View". This should
* help to distinguish empty Image Views created by ImageView and other
* plug-ins. Eclipse can create ImageViews with a null secondary ID, e.g. via
* Window | Show View. ImageView hides these and creates a new one with
* SECONDARY_ID_MAIN to insure all views have a non-null secondary ID.
* <p>
* New images are typically loaded to the Main view. For example, only the Main
* view listens for property change events from the SampleController associated
* with the Image Navigator. Loading into the Main view will cause existing
* Zoom, Slice1, and Slice2 views to update, depending on if there is a
* selection and the current selection mode. That is, if there is a selection
* and the current mode is Area then the Zoom view will update, but not Slice1
* or Slice2. Drag and Drop supports dropping on any view.
* <p>
* ImageView is a large View and is implemented in several classes. The
* principal ones are (1) ImageView which manages the usual view things and
* holds the values of most of the settings, (2) ImageViewControls which manages
* the SWT controls except for the imageCanvas, and (3) ImageViewImage which
* manages the imageCanvas and things, such as selections, related to it.
* <p>
* <b>Dependencies</b> : <br>
* FabioFile - for loading FabioFiles <br>
* ZoomLineView - for plotting lines and integrated profiles<br>
* <br>
* The image can be viewed in 8 orientations. These orientations are associated
* with the TotalCrys o parameters (o11, o12, o21, o22). The parameters that
* result in an image oriented as it would be looking at the detector in the
* direction of the beam are the o parameters to specify in other Fable
* software, such as ImageD11. <br>
* <br>
* The coordinate origin can be selected as TL=(0,0) (typical image
* coordinates), TR=(0,0), BR=(0,0) (TotalCrys coordinates), and BL=(0,0) (usual
* xy coordinate system). They can also be specified as custom. The only place
* the coordinates appear is in the display of the mouse position. Note that the
* coordinate system and the image orientation may be specified independently. <br>
* <br>
* The TotalCryst project (cf.
* http://fable.wiki.sourceforge.net/space/showimage/Geometry_version_1.0.2.pdf)
* defines the coordinate system as follows: <br>
* (o) Horizontal axis as Y with zero on the right and positive to the left<br>
* (o) Vertical axis as Z with zero at the bottom and positive up.<br>
*
* @author Andy Gotz (ESRF), Ken Evans (APS)
*
*/
public class ImageComponent implements IPropertyChangeListener,
IImagesVarKeys,
ISelectionListener {
/**
* Secondary ID for the main ImageView. There should be zero or one of
* these.
*/
public static final String SECONDARY_ID_MAIN = "Main";
/**
* Secondary ID for the Zoom ImageView. There should be zero or one of
* these.
*
* Do not change the string is referenced in projects outside Fable
* (for reasons of avoiding direct connections).
*/
public static final String SECONDARY_ID_ZOOM = "Zoom";
/**
* Secondary ID for the slice 1D ImageView. There should be zero or one of
* these.
*/
public static final String SECONDARY_ID_SLICE1D = "Slice 1D";
/**
* Secondary ID for the slice 2D ImageView. There should be zero or one of
* these.
*/
public static final String SECONDARY_ID_SLICE2D = "Slice 2D";
/**
* Number to use for the secondary ID for the first copy ImageView. Do not
* use this explicitly as a secondary ID. Use zoomSecondaryID and increment
* it afterward.
*/
public static int SECONDARY_ID_COPY_START = 100;
/**
* Number to use for the secondary ID for the next copy ImageView. It should
* be incremented when used so each copy appears in a different view.
*/
public static int copySecondaryID = SECONDARY_ID_COPY_START;
private SampleController controller = SampleController.getController();
/**
* A reference to the class that manages the SWT controls for this view.
*/
private ImageComponentUI controls = null;
/**
* A reference to the class that manages the SWT image for this view.
*/
public ImageComponentImage image = null;
/**
* The Display for this view.
*/
private Display display;
/**
* The ImageModel. We have two choices: (1) keep one ImageModel and reset
* it, or (2) create new ones as needed. (1) allows the use of listeners for
* the RESET event. (2) allows an ImageModel to be used in two views, saving
* storage. Currently we are not using events, so we go with (2). We could
* use (2) anyway if we take care of adding and removing the listeners
* whenever an ImageModel is changed. This could be done in a
* changeImageModel method, for instance.
*/
private ImageModel imageModel = null;
private ImageModel imageDiffModel = null;
private ImageModel imageSavedModel = null;
// KE: TODO: This is not set anywhere
private String fileNameSaved = "";
private Vector<Float> peaks;
/**
* Coordinates representing the current coordinate system. Note that this
* system is related to the oriented image.
*/
private Coordinates coordinates = new Coordinates();
/**
* The value of x0 for custom coordinates last used.
*/
double x0Save = 0;
/**
* The value of y0 for custom coordinates last used.
*/
double y0Save = 0;
/**
* The value of pixelWidth for custom coordinates last used.
*/
double pixelWidthSave = 1;
/**
* The value of pixelHeight for custom coordinates last used.
*/
double pixelHeightSave = 1;
/**
* The value of xName for custom coordinates last used.
*/
String xNameSave = "x";
/**
* The value of yName for custom coordinates last used.
*/
String yNameSave = "y";
/**
* Parameter representing which of the eight orientations is used:
* <ul>
* <li>Original Image (1 0 0 1)</li>
* <li>Flip H (1 0 0 -1)</li>
* <li>Flip V (-1 0 0 1)</li>
* <li>Flip H and V (-1 0 0 -1)</li>
* <li>90 deg CW, Flip H (0 1 1 0)</li>
* <li>90 deg CW (0 1 -1 0)</li>
* <li>90 deg CWW (0 -1 1 0)</li>
* <li>90 deg CW, Flip V (0 -1 -1 0)</li>
* </ul>
*/
private int orientation = 0;
/**
* The current selection choice for zooming.
*/
private ZoomSelection zoomSelection = ZoomSelection.AREA;
private int coordOrigin = 0;
/**
* Flag that indicates whether peaks are to be shown.
*/
private boolean peaksOn = false;
/**
* Flag that indicates whether PSF is to be applied.
*/
private boolean psfOn = false;
/**
* Sets the size of the rectangle used to mark peaks, Should be odd.
*/
private int peakMarkerSize = ImageComponentUI.DEFAULT_MARKER_SIZE;
/**
* The statistics from the current file, for the zoomed area,
* e.g. minimum, maximum, mean intensity.
*/
private Statistics zoomStatistics = null;
/**
* The minimum intensity currently used. May be from the file or
* user-specified, depending on the setting of autoscale.
*/
private Float userMinimum = Float.NaN;
/**
* The maximum intensity currently used. May be from the file or
* user-specified, depending on the setting of autoscale.
*/
private Float userMaximum = Float.NaN;
/**
* The current palette.
*/
private PaletteData palette = null;
/**
* The index of the current palette in
*/
private int paletteIndex = 0;
/**
* Flag indicating if the image displayed is a single file image or a
* difference image.
*/
protected boolean imageDiffOn = false;
private boolean jobRunning = false;
/**
* Listener to listen for workspace shutdown so we can remove views with
* selected secondary IDs on shutdown to avoid clutter on restart.
*/
private IWorkbenchListener workbenchListener = null;
/**
* The parent part, either a view or an editor.
*/
private IWorkbenchPart3 parentPart;
private ActionsProvider provider;
private Text statusLabel;
public ImageComponent(final IWorkbenchPart3 parentPart) {
this(parentPart, (ActionsProvider)parentPart);
}
public ImageComponent(final IWorkbenchPart3 parentPart, final ActionsProvider provider) {
this.parentPart = parentPart;
this.provider = provider;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets
* .Composite)
*/
public void createPartControl(Composite parent) {
// Get the display
display = parent.getDisplay();
// Get the logger
// logger = FableLogger.getLogger(this.getClass());
controls = new ImageComponentUI(this);
controls.setStatusLabel(statusLabel);
getParentPart().getSite().setSelectionProvider(new FableSelectionProvider()); //Set this before createControls, because it adds listener to this
controls.createControls(parent);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.part.WorkbenchPart#setFocus()
*/
public void setFocus() {
if (controls!=null) controls.setFocus();
// Get the right side plot to show
if (getSecondaryId()!=ImageComponent.SECONDARY_ID_MAIN) return;
final ZoomSelection sel = getZoomSelection();
if (sel!=null) sel.bringToTop();
}
/**
* This method receives selectionEvents from the workbench. This is
* currently used for PeakSearch and its spt output file. When a user
* selects a peak in a TableViewer and if the selection is a instance of
* <code>PeakSearchSpt</code> or an instance of <code>Peak</code>, spots are
* displayed in the image.
*
* @see org.eclipse.ui.ISelectionListener#selectionChanged(org.eclipse.ui.IWorkbenchPart,
* org.eclipse.jface.viewers.ISelection)
*/
@Override
public void selectionChanged(IWorkbenchPart part, ISelection selection) {
// DEBUG
// System.out.println("\n>>>Entering selectionChanged");
if (selection instanceof IStructuredSelection) {
IStructuredSelection sSelection = (IStructuredSelection) selection;
Object first = sSelection.getFirstElement();
Object[] selections = sSelection.toArray();
// DEBUG
// System.out.println("Check selection");
if (first instanceof PeakSearchSpt) {
// Only do this for the main or zoom secondaryId
String id2 = getSecondaryId();
if (id2 == null
|| (!id2.equals(ImageComponent.SECONDARY_ID_MAIN) && !id2
.equals(SECONDARY_ID_ZOOM))) {
return;
}
PeakSearchSpt peakFile = ((PeakSearchSpt) first);
if (peakFile.getImageFile() != null) {
if (id2.equals(SECONDARY_ID_MAIN)) {
try {
this.loadModel(ImageModelFactory.getImageModel(peakFile.getImageFile()));
} catch (Throwable e) {
FableLogger.error("Cannot load file "+peakFile.getFabioFileName(), e);
}
}
setPeaksOn(true);
image.initAndDisplayImage();
// Show peaks if there are some peaks
Vector<Float> vals = peakFile.getTabChildren();
if (vals != null) {
setPeaks(vals);
image.showPeaks();
}
}
} else if (first instanceof Peak) {
// Only do this for the main or zoom secondaryId
String id2 = getSecondaryId();
if (id2 == null
|| (!id2.equals(SECONDARY_ID_MAIN) && !id2
.equals(SECONDARY_ID_ZOOM))) {
return;
}
PeakSearchSpt parent = ((Peak) first).getParent();
int nPeaksToDisplay = selections.length;
float[] coloredPeak = new float[nPeaksToDisplay * 2];
int j;
int k = 0;
for (int ip = 0; ip < selections.length; ip++) {
Peak peak = (Peak) selections[ip];
if (peak.isVisible()) {
j = k + 1;
coloredPeak[k] = Float.valueOf(peak.getS());
coloredPeak[j] = Float.valueOf(peak.getF());
k += 2;
}
}
Vector<Float> vals = parent.getTabChildren();
if (vals != null) {
setPeaks(vals);
}
setPeaksOn(true);
// Draw peaks in red with selected peaks in green
image.showSelectedPeaks(coloredPeak);
}
}
}
/**
*
* @param fileName
* @throws Throwable
*/
public void loadFile(String fileName) throws Throwable {
loadModel(ImageModelFactory.getImageModel(fileName));
}
/**
* Loads a new image from the specified image model.
*
* @param imageModel
* The image model to use.
*/
public void loadModel(ImageModel imageModel) {
final long start = System.currentTimeMillis();
try {
if (imageModel == null) {
image.clearCanvas();
FableUtils.errMsg(this, "Unable to load null image model");
return;
}
// DEBUG
// if (true) {
// System.out.println("loadFile: " + " [" + getSecondaryId() + "] "
// + getPartName());
// System.out.printf(" imageModel: %d %d\n", imageModel.getWidth(),
// imageModel.getHeight());
// System.out.printf(" imageRect (before): %d %d %d %d\n", image
// .getImageRect().x, image.getImageRect().y, image
// .getImageRect().width, image.getImageRect().height);
// }
this.imageModel = imageModel;
if (controls != null) {
controls.setStatusText("Loading... ");
}
peaks = null;
if (image.isDisposed()) return;
image.setImageChanged(true);
// To reduce flashing only clear the image background if the size
// changes
if (image.getImageRect().width != imageModel.getWidth()
|| image.getImageRect().height != imageModel.getHeight()) {
image.clearCanvas();
}
// Reset the image rect. In the case of a zoomed image, the old zoom
// area may or may not be appropriate to the new image. We choose to
// make the new zoom area be in the same proportions to the new
// image as the old one was to the old image. Other choices could be
// made.
int newWidth = imageModel.getWidth();
int newHeight = imageModel.getHeight();
int newX = 0;
int newY = 0;
double widthFactor = 1;
double heightFactor = 1;
if (image != null && image.getOrigRect() != null
&& image.getImageRect() != null) {
if (image.getOrigRect().width != 0) {
widthFactor = (double) newWidth / image.getOrigRect().width;
}
if (image.getOrigRect().height != 0) {
heightFactor = (double) newHeight / image.getOrigRect().height;
}
newX = (int) (widthFactor * image.getImageRect().x);
newY = (int) (heightFactor * image.getImageRect().y);
// If the width or height is 0 this indicates it has not been
// initialized, (or something else is wrong) so don't scale
if (image.getImageRect().width > 0) {
newWidth = (int) (widthFactor * image.getImageRect().width);
}
if (image.getImageRect().height > 0) {
newHeight = (int) (heightFactor * image.getImageRect().height);
}
}
// Insure it is in bounds
if (newWidth > imageModel.getWidth()) {
newWidth = imageModel.getWidth();
}
if (newHeight > imageModel.getHeight()) {
newHeight = imageModel.getHeight();
}
if (newX + newWidth > imageModel.getWidth()) {
newX = 0;
}
if (newY + newHeight > imageModel.getHeight()) {
newY = 0;
}
image.setImageRect(new Rectangle(newX, newY, newWidth, newHeight));
// DEBUG
// if (true) {
// System.out.printf(" imageRect (after set): %d %d %d %d\n", image
// .getImageRect().x, image.getImageRect().y, image
// .getImageRect().width, image.getImageRect().height);
// }
image.calculateMainRectangles();
// DEBUG
// if (true) {
// System.out.printf(" imageRect (after calc): %d %d %d %d\n", image
// .getImageRect().x, image.getImageRect().y, image
// .getImageRect().width, image.getImageRect().height);
// System.out.println();
// }
resetCoordinates();
if (imageDiffOn) {
calcImageDiff();
} else {
setPartName(getSecondaryId() + " " + getFileName());
}
// Use the original rect here, not the image rect. They are the same
// for the main view but not for the a zoom view.
// setStatistics(imageModel.getStatistics(image.getOrigRect()));
setStatistics( image.getOrigRect() );
} finally {
if (imageModel.getTimeToReadImage()>0) {
updateStatusLabel("Loaded in "
+ imageModel.getTimeToReadImage() + " ms");
} else {
final long end = System.currentTimeMillis();
updateStatusLabel("Loaded in "
+ (end-start) + " ms");
}
}
}
/**
* Calculate / image
*/
public void calcImageDiff() {
long start = System.currentTimeMillis();
if (imageSavedModel == null) {
setImageDiffOn(false);
updateStatusLabel("Cannot create difference image - no difference image set");
FableUtils.errMsg(this, "Cannot create difference: "
+ "no difference image set\n");
setPartName(null);
return;
}
// Take difference of image
FableLogger.debug("Calculate difference of " + imageModel.getFileName()
+ " and background image " + imageSavedModel.getFileName());
float[] newArray = imageModel.getData();
float[] savedArray = imageSavedModel.getData();
int newLen = newArray.length;
int savedLen = savedArray.length;
if (newLen != savedLen) {
FableUtils.errMsg(this, "Cannot create difference:\n"
+ "newWidth=%d savedWidth=%d\n"
+ "newHeight=%d savedHeight=%d");
setPartName(getSecondaryId() + " " + getFileName());
return;
}
float[] imageDiffArray = new float[savedLen];
for (int i = 0; i < savedLen; i++) {
imageDiffArray[i] = newArray[i] - savedArray[i];
}
imageDiffModel = ImageModelFactory.getImageModel(imageModel.getFileName() + " - "
+ imageSavedModel.getFileName(), imageModel.getWidth(),
imageModel.getHeight(), imageDiffArray);
imageDiffModel.reset(imageModel.getFileName() + " - "
+ imageSavedModel.getFileName(), imageModel.getWidth(),
imageModel.getHeight(), imageDiffArray);
// TODO: KE: Consider keeping the full statistics so the zoomed
// image looks the same as the unzoomed
// TODO: GN: Also not calculating the statistics each time would
// make it a bit faster
zoomStatistics = imageDiffModel.getStatistics(image.getImageRect());
long elapsed = System.currentTimeMillis() - start;
updateStatusLabel(getFileName() + " - " + fileNameSaved + " took "
+ elapsed + " ms");
setPartName("Difference " + getFileName());
// Put the min and max in the text boxes only if Autoscale
}
private void setPartName(String name) {
if (parentPart instanceof ImageView) {
if (name == null) name = getSecondaryId() + " " + getFileName();
((ImageView)parentPart).setPartName(name);
}
}
/**
* update status label asynchronously
*
* @param _status
* - status to display
*/
public void updateStatusLabel(String _status) {
final String status = _status;
display.asyncExec(new Runnable() {
public void run() {
if (controls != null)
controls.setStatusText(status);
}
});
}
/**
* set the list of peaks to display
*
* @param vals
* - list of peaks as pairs of [y,z] coordinates
*/
public void setPeaks(Vector<Float> vals) {
peaks = vals;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse
* .jface.util.PropertyChangeEvent)
*/
@Override
public void propertyChange(PropertyChangeEvent evt) {
// These events come from the SampleController and the names are defined
// in fable.framework.navigator.toolBox.IVarKeys
// DEBUG
// System.out.println("ImageView.propertyChange: " + getSecondaryId());
if (evt.getProperty().equals(IVarKeys.SET_CURRENTFILE_EVENT)) {
// DEBUG
// System.out.println(" SET_CURRENTFILE_EVENT");
Object val = evt.getNewValue();
/*
* first load current file and display it
*/
try {
loadModel(ImageModelFactory.getImageModel(val));
} catch (Throwable e) {
FableLogger.error("Cannot load image "+val, e);
}
image.initAndDisplayImage();
controls.setFileNumberText(Integer.toString(controller
.getCurrentFileIndex()));
/*
* gain time by trying to read the files around the current
* selection +-3 into memory already as a system job (i.e. invisible
* to the user) I don't know if this is responsible for a memory
* leak ... there seems to be one
*/
final int sampleIndex = controller.getCurrentsample()
.getFilteredfiles().indexOf(val);
int sampleSize = controller.getCurrentsample().getFilteredfiles()
.size();
if (sampleIndex >= 0 && sampleIndex < sampleSize && !jobRunning) {
// logger.debug("read files ahead");
final int fileReadFrom = Math.max(0, sampleIndex - 3);
final int fileReadTo = Math
.min(sampleIndex + 3, sampleSize - 1);
jobRunning = true;
Job job = new Job("Read files ahead ") {
protected IStatus run(IProgressMonitor monitor) {
monitor.beginTask("Read files ahead", 7);
// logger.debug("start job to read file " + fileReadFrom
// + " to " + fileReadTo + " ahead");
/*
* start off with a sleep to give the main thread time
* to read the file and display the image
*/
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
FableUtils.excNoTraceMsg(this,
"Reading files ahead interrupted", ex);
}
// FableJep fableJep;
try {
// fableJep = FableJep.getFableJep();
for (int i = fileReadFrom; i <= fileReadTo; i++) {
/*
* do not read the current file in the
* imageview, it could be in the process of
* being read anyway it should be loaded by the
* main thread.
*/
if (i >= 0
&& i < (controller.getCurrentsample()
.getFilteredfiles().size())
&& i != sampleIndex) {
controller.getCurrentsample()
.getFilteredfiles().get(i)
.readImageAsFloat();
}
monitor.worked(1);
}
// fableJep.close();
} catch (Throwable ex) {
/*
* FableUtils.excNoTraceMsg(this,
* "Unable to access fabioFile while " +
* "reading files ahead", ex);
*/
}
monitor.done();
jobRunning = false;
return Status.OK_STATUS;
}
};
job.setSystem(true);
job.schedule();
}
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.part.WorkbenchPart#dispose()
*/
public void dispose() {
if (image != null) {
image.dispose();
}
getParentPart().getSite().setSelectionProvider(null);
// Remove this instance from the controller's listener list. It doesn't
// matter if it is not there for this instance.
controller.removePropertyChangeListener(this);
if (workbenchListener != null) {
PlatformUI.getWorkbench()
.removeWorkbenchListener(workbenchListener);
workbenchListener = null;
}
if (parentPart.getSite() != null) {
parentPart.getSite().getWorkbenchWindow().getSelectionService().removeSelectionListener(this);
}
if (controls!=null) controls.dispose();
}
/**
* Transfer some of settings from another instance to this one. Typically
* used when zooming, copying, doing slices, etc. The values transferred
* are:
* <ul>
* <li>orientation</li>
* <li>coordinates</li>
* <li>palette</li>
* <li>statistics (minimum, maximum, mean)</li>
* <li>autoScale</li>
* <li>userMinimum, userMaximum</li>
* <li>peaks and peaksOn</li>
* </ul>
*
* @param source
* The source instance.
*/
public void transferSelectedSettings(ImageComponent src) {
this.setOrientation(src.getOrientation());
this.setCoordOrigin(src.getCoordOrigin());
if (src.getCoordOrigin() == COORD_CUSTOM) {
this.getCoordinates().reset(src.getCoordOrigin(),
src.getCoordinates().getX0(), src.getCoordinates().getY0(),
src.getCoordinates().getPixelWidth(),
src.getCoordinates().getPixelHeight(),
src.getCoordinates().getXName(),
src.getCoordinates().getYName());
} else {
this.resetCoordinates();
}
this.setPalette(src.getPalette());
// This breaks the min and max in the side plot,
// since this method runs after the side plot it updated.
//this.setUserMinimum(src.getUserMinimum());
//this.setUserMaximum(src.getUserMaximum());
this.setPeaks(src.getPeaks());
this.setPeaksOn(src.isPeaksOn());
// This must be done last
//this.setStatistics(src.getStatistics(), src.getAutoscale());
}
/**
* @author SUCHET
* @date Feb, 11 2008
* @description : called from peaksearch to show peaks and to force button
* peaks to be selected
*/
public void initWithShowPeaks() {
if (controls == null)
return;
setPeaksOn(true);
controls.firePeaksUpate();
}
/**
* Reset the coordinates to reflect the current image and orientation. Does
* not reset custom coordinates.
*/
public void resetCoordinates() {
if (coordOrigin != COORD_CUSTOM) {
coordinates.reset(coordOrigin, image.getOrientedOrigRect().width,
image.getOrientedOrigRect().height);
}
}
/**
* @return the coordOrigin
*/
public int getCoordOrigin() {
return coordOrigin;
}
/**
* @param coordOrigin
* the coordOrigin to set
*/
public void setCoordOrigin(int coordOrigin) {
if (coordOrigin < 0 || coordOrigin >= coordNameValues.length) {
return;
}
if (this.coordOrigin != coordOrigin) {
this.coordOrigin = coordOrigin;
if (controls != null) {
controls.setCoordinate(coordOrigin);
}
}
}
/**
* @return the orientation
*/
public int getOrientation() {
return orientation;
}
/**
* @param orientation
* the orientation to set
*/
public void setOrientation(int orientation) {
if (orientation < 0 || orientation >= 8) return;
if (orientation == this.orientation) return;
this.orientation = orientation;
if (controls != null) {
controls.setOrientation(orientation);
}
// Reset the coordinates
resetCoordinates();
// Clear canvas because image dimensions could change
image.clearCanvas();
image.initAndDisplayImage();
final IPreferenceStore prefs = Activator.getDefault().getPreferenceStore();
prefs.setValue(PreferenceConstants.P_ORIENT, orientation);
}
/**
* @return the palette
*/
public PaletteData getPalette() {
return palette;
}
private static final Logger logger = LoggerFactory.getLogger(ImageComponent.class);
/**
* Creates a palette in the palettes array if it has not been created yet,
* sets that palette, and redisplays the image. New palettes must be added
* here, in IImagesVarKeys, and in PaletteUtils.
*
* @see fable.imageviewer.internal#IImagesVarKeys for possible indices.
* @param index
* The index of the palette in the palettes array.
*/
public void setPalette(int index) {
try {
this.palette = PaletteFactory.getPalette(index);
} catch (Exception e) {
logger.error("Cannot initiate palette!", e);
}
if (controls != null) {
controls.setImageScheme(index);
}
paletteIndex = index;
// clearCanvas();
image.initAndDisplayImage(); //Using palette is in init, so not enough to display image
final IPreferenceStore prefs = Activator.getDefault().getPreferenceStore();
prefs.setValue(PreferenceConstants.P_PALETTE, index);
}
/**
* Gets the statistics.
*
* @return The statistics.
* @remark as float[] {min, max, mean}.
*/
public Statistics getStatistics() {
return zoomStatistics;
}
/**
* Sets the statistics and autoScale.
*
* @param statistics
* The new values.
* @remark as float[] {min, max, mean}.
*/
public void setStatistics(Rectangle imageRect, boolean autoScale) {
if (controls != null) {
controls.setAutoScale(autoScale);
}
setStatistics( imageRect );
}
/**
* Sets the statistics.
*
* @param statistics
* The new values.
* @remark as float[] {min, max, mean}.
*/
public void setStatistics(Rectangle imageRect) {
zoomStatistics = getImageModel().getStatistics( imageRect );
// Set the UI controls
if (controls != null) {
controls.setMinMaxValueText(zoomStatistics);
}
if (image != null) {
image.initAndDisplayImage();
}
}
/**
* @return the filename
*/
public String getFileName() {
String fileName = null;
if (imageModel != null) {
fileName = imageModel.getFileName();
}
return (fileName == null) ? "" : fileName;
}
/**
* @return the imageModel
*/
public ImageModel getImageModel() {
return imageModel;
}
/**
* @param imageModel
* The imageModel to set.
*/
public void setImageModel(ImageModel imageModel) {
this.imageModel = imageModel;
}
/**
* @return the imageDiffModel
*/
public ImageModel getImageDiffModel() {
return imageDiffModel;
}
/**
* @param imageDiffModel
*/
public void setImageDiffModel(ImageModel imageDiffModel) {
this.imageDiffModel = imageDiffModel;
}
/**
* @return the imageSavedModel
*/
public ImageModel getImageSavedModel() {
return imageSavedModel;
}
/**
* @param imageSavedModel
* the imageSavedModel to set
*/
public void setImageSavedModel(ImageModel imageSavedModel) {
this.imageSavedModel = imageSavedModel;
}
/**
* @return the coordinates
*/
public Coordinates getCoordinates() {
return coordinates;
}
/**
* Gets the short name of the current coordinate origin.
*
* @return the short name.
*/
public String getCoordinatesName() {
if (coordOrigin < 0 || coordOrigin > 4) {
return "Unknown";
}
// Take the part up to the first whitespace or .
return coordNameValues[coordOrigin][0].split("[\\s\\.]", 2)[0];
}
/**
* @return the controller
*/
public SampleController getController() {
return controller;
}
/**
* @return the peaksOn
*/
public boolean isPeaksOn() {
return peaksOn;
}
/**
* @param peaksOn
* the peaksOn to set
*/
public void setPeaksOn(boolean peaksOn) {
this.peaksOn = peaksOn;
// Set the button
controls.setPeaks(peaksOn);
}
/**
* @return the psfOn
*/
public boolean isPSFOn() {
return psfOn;
}
/**
* @param psfOn
* the psfOn to set
*/
public void setPSFOn(boolean psfOn) {
this.psfOn = psfOn;
// Set the button
controls.setPSF(psfOn);
}
/**
* @return the peakMarkerSize
*/
public int getPeakMarkerSize() {
return peakMarkerSize;
}
/**
* @param peakMarkerSize
* the peakMarkerSize to set
*/
public void setPeakMarkerSize(int peakMarkerSize) {
this.peakMarkerSize = peakMarkerSize;
}
/**
* @return the controls
*/
public ImageComponentUI getControls() {
return controls;
}
/**
* @param controls
* the controls to set
*/
public void setControls(ImageComponentUI controls) {
this.controls = controls;
}
/**
* @return the image
*/
public ImageComponentImage getImage() {
return image;
}
/**
* @param image
* the image to set
*/
public void setImage(ImageComponentImage image) {
this.image = image;
}
/**
* Sets the zoomSelection.
*
* @param zoomSelection
*/
public void setZoomSelection(ZoomSelection zoomSelection) {
this.zoomSelection = zoomSelection;
}
/**
* @return the zoomSelection.
*/
public ZoomSelection getZoomSelection() {
return zoomSelection;
}
/**
* @return the peaks
*/
public Vector<Float> getPeaks() {
return peaks;
}
/**
* @return the imageDiffOn
*/
public boolean isImageDiffOn() {
return imageDiffOn;
}
/**
* @param imageDiffOn
* the imageDiffOn to set
*/
public void setImageDiffOn(boolean imageDiffOn) {
this.imageDiffOn = imageDiffOn;
if (imageDiffOn)
calcImageDiff();
else if (parentPart instanceof ImageView ){
setPartName(getSecondaryId() + " " + getFileName());
}
image.setImageChanged(true);
image.initAndDisplayImage();
}
/**
* @return the minimum
*/
public float getMinimum() {
return zoomStatistics.getMinimum();
}
/**
* @return the maximum
*/
public float getMaximum() {
return zoomStatistics.getMaximum();
}
/**
* @return the mean
*/
public float getMean() {
return zoomStatistics.getMean();
}
/**
* @return the userMinimum
*/
public Float getUserMinimum() {
return userMinimum;
}
/**
* @param userMinimum
* the userMinimum to set
* @return false if value is not changed
*/
public boolean setUserMinimum(float userMinimum) {
if( this.userMinimum == userMinimum )
return false;
this.userMinimum = userMinimum;
if (controls != null) {
controls.setUserMinimumText(userMinimum);
controls.setUserMinimumScale(userMinimum);
}
return true;
}
/**
* @return the userMaximum
*/
public Float getUserMaximum() {
return userMaximum;
}
/**
* @param userMaximum
* the userMaximum to set
* @return false if value is not changed
*/
public boolean setUserMaximum(float userMaximum) {
if( this.userMaximum == userMaximum )
return false;
this.userMaximum = userMaximum;
if (controls != null) {
controls.setUserMaximumText(userMaximum);
controls.setUserMaximumScale(userMaximum);
}
return true;
}
/**
* @return the x0Save
*/
public double getX0Save() {
return x0Save;
}
/**
* @param save
* the x0Save to set
*/
public void setX0Save(double save) {
x0Save = save;
}
/**
* @return the y0Save
*/
public double getY0Save() {
return y0Save;
}
/**
* @param save
* the y0Save to set
*/
public void setY0Save(double save) {
y0Save = save;
}
/**
* @return the pixelWidthSave
*/
public double getPixelWidthSave() {
return pixelWidthSave;
}
/**
* @param pixelWidthSave
* the pixelWidthSave to set
*/
public void setPixelWidthSave(double pixelWidthSave) {
this.pixelWidthSave = pixelWidthSave;
}
/**
* @return the pixelHeightSave
*/
public double getPixelHeightSave() {
return pixelHeightSave;
}
/**
* @param pixelHeightSave
* the pixelHeightSave to set
*/
public void setPixelHeightSave(double pixelHeightSave) {
this.pixelHeightSave = pixelHeightSave;
}
/**
* @return the xNameSave
*/
public String getXNameSave() {
return xNameSave;
}
/**
* @param nameSave
* the xNameSave to set
*/
public void setXNameSave(String nameSave) {
xNameSave = nameSave;
}
/**
* @return the yNameSave
*/
public String getYNameSave() {
return yNameSave;
}
/**
* @param nameSave
* the yNameSave to set
*/
public void setYNameSave(String nameSave) {
yNameSave = nameSave;
}
/**
* @return the paletteIndex
*/
public int getPaletteIndex() {
return paletteIndex;
}
/**
* @param paletteIndex
* the paletteIndex to set
*/
public void setPaletteIndex(int paletteIndex) {
this.paletteIndex = paletteIndex;
}
/**
* @param palette
* the palette to set
*/
public void setPalette(PaletteData palette) {
this.palette = palette;
}
/**
* @return the display
*/
public Display getDisplay() {
return display;
}
/**
* @return the secondary ID of this instance.
*/
public String getSecondaryId() {
String id2 = null;
try {
if (parentPart instanceof IViewPart) {
id2 = ((IViewPart)parentPart).getViewSite().getSecondaryId();
} else {
id2 = ImageComponent.SECONDARY_ID_MAIN;
}
} catch (Exception ex) {
// Do nothing
}
return id2;
}
public Object getPartName() {
return parentPart.getPartName();
}
public IActionBars getActionBars() {
return provider.getActionBars();
}
public String toString() {
return "Image Component for "+this.parentPart.getPartName();
}
public IWorkbenchPart3 getParentPart() {
return this.parentPart;
}
public void setStatusLabel(Text statusLabel) {
this.statusLabel = statusLabel;
}
public void setPlotTitle(final String title) {
controls.setTitle(title);
}
public void setEditorInput(String filePath) throws Throwable {
if (parentPart instanceof IReusableEditor) {
final IEditorInput in = EclipseUtils.getEditorInput(filePath);
IReusableEditor ed = (IReusableEditor) parentPart;
ed.setInput(in);
} else {
ImageModel model = ImageModelFactory.getImageModel(filePath);
loadModel(model);
}
}
}