/**
*
*/
package org.csstudio.ui.util.widgets;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.OwnerDrawLabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.wb.swt.ResourceManager;
import org.eclipse.wb.swt.SWTResourceManager;
/**
* A widget to display a set of Images
*
* @author shroffk
*
*/
public class ImageStackWidget extends Composite {
private boolean editable;
private String selectedImageName;
private boolean scrollBarVisble;
protected final PropertyChangeSupport changeSupport = new PropertyChangeSupport(
this);
private ImagePreview imagePreview;
private Table table;
private TableViewer tableViewer;
private Map<String, byte[]> imageInputStreamsMap = new HashMap<String, byte[]>();
private Button buttonRemove;
private TableViewerColumn tableViewerColumn;
private TableColumn tblclmnImage;
/**
* Adds a listener, notified a porperty has been changed.
*
* @param listener
* a new listener
*/
public void addPropertyChangeListener(PropertyChangeListener listener) {
changeSupport.addPropertyChangeListener(listener);
}
/**
* Removes a listener.
*
* @param listener
* listener to be removed
*/
public void removePropertyChangeListener(PropertyChangeListener listener) {
changeSupport.removePropertyChangeListener(listener);
}
public ImageStackWidget(final Composite parent, int style) {
super(parent, SWT.NONE);
setLayout(new FormLayout());
Label label = new Label(this, SWT.SEPARATOR | SWT.VERTICAL);
FormData fd_label = new FormData();
fd_label.bottom = new FormAttachment(100, 5);
fd_label.top = new FormAttachment(0, 5);
fd_label.right = new FormAttachment(100, 100, -125);
label.setLayoutData(fd_label);
Label lblImages = new Label(this, SWT.NONE);
FormData fd_lblImages = new FormData();
fd_lblImages.left = new FormAttachment(label, 5);
fd_lblImages.top = new FormAttachment(0, 5);
lblImages.setLayoutData(fd_lblImages);
lblImages.setText("Images:");
tableViewer = new TableViewer(this, SWT.DOUBLE_BUFFERED | SWT.NO_SCROLL
| SWT.V_SCROLL);
table = tableViewer.getTable();
FormData fd_table = new FormData();
fd_table.left = new FormAttachment(label, 5);
fd_table.right = new FormAttachment(100, -5);
fd_table.bottom = new FormAttachment(100, -5);
fd_table.top = new FormAttachment(0, 30);
table.setLayoutData(fd_table);
table.setBackground(SWTResourceManager
.getColor(SWT.COLOR_WIDGET_LIGHT_SHADOW));
tableViewerColumn = new TableViewerColumn(tableViewer, SWT.NONE);
tableViewerColumn.setLabelProvider(new OwnerDrawLabelProvider() {
@Override
protected void paint(Event event, Object element) {
String imageName = element == null ? "" : element.toString();
ImageData imageData = new ImageData(new ByteArrayInputStream(
imageInputStreamsMap.get(imageName)));
int width = scrollBarVisble ? 90 : 100;
double scale = determineImageScale(imageData, width, width);
Image img = new Image(getDisplay(), imageData.scaledTo(
(int) (imageData.width * scale),
(int) (imageData.height * scale)));
if (img != null) {
Rectangle bounds = ((TableItem) event.item)
.getBounds(event.index);
Rectangle imgBounds = img.getBounds();
bounds.width /= 2;
bounds.width -= imgBounds.width / 2;
bounds.height /= 2;
bounds.height -= imgBounds.height / 2;
int x = bounds.width > 0 ? bounds.x + bounds.width
: bounds.x;
int y = bounds.height > 0 ? bounds.y + bounds.height
: bounds.y;
event.gc.drawImage(img, x, y);
}
}
@Override
protected void measure(Event event, Object element) {
String imageName = element == null ? "" : element.toString();
ImageData imageData = new ImageData(new ByteArrayInputStream(
imageInputStreamsMap.get(imageName)));
double scale = determineImageScale(imageData, 85, 85);
event.height = (int) (scale * imageData.height) + 10;
}
private double determineImageScale(ImageData imgData,
int targetWidth, int targetHeight) {
if (imgData == null) {
return 1;
}
double scalex = (double) targetWidth / imgData.width;
double scaley = (double) targetHeight / imgData.height;
double ratio = Math.min(scalex, scaley);
if (ratio > 1) {
return 1;
}
return ratio;
}
});
table.addPaintListener(new PaintListener() {
@Override
public void paintControl(PaintEvent e) {
Rectangle rect = table.getClientArea();
int itemHeight = table.getItemHeight();
int headerHeight = table.getHeaderHeight();
int visibleCount = (rect.height - headerHeight + itemHeight - 1)
/ itemHeight;
setScrollBarVisble(table.getItemCount() >= visibleCount);
}
});
tblclmnImage = tableViewerColumn.getColumn();
tblclmnImage.setResizable(false);
tblclmnImage.setWidth(104);
tableViewer.setContentProvider(new IStructuredContentProvider() {
@Override
public void inputChanged(Viewer viewer, Object oldInput,
Object newInput) {
}
@Override
public void dispose() {
}
@Override
public Object[] getElements(Object inputElement) {
return (Object[]) inputElement;
}
});
tableViewer
.addSelectionChangedListener(new ISelectionChangedListener() {
@Override
public void selectionChanged(SelectionChangedEvent event) {
ISelection selection = event.getSelection();
if (selection != null
&& selection instanceof IStructuredSelection) {
IStructuredSelection sel = (IStructuredSelection) selection;
if (sel.size() == 1) {
setSelectedImageName((String) sel.iterator()
.next());
}
}
}
});
buttonRemove = new Button(this, SWT.NONE);
buttonRemove.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
try {
removeImage(getSelectedImageName());
} catch (IOException e1) {
}
}
});
buttonRemove.setImage(ResourceManager.getPluginImage(
"org.csstudio.ui.util", "icons/remove-16.gif"));
FormData fd_lblNewLabel = new FormData();
fd_lblNewLabel.right = new FormAttachment(label, -5);
fd_lblNewLabel.top = new FormAttachment(0, 5);
buttonRemove.setLayoutData(fd_lblNewLabel);
buttonRemove.setText("Remove");
buttonRemove.setVisible(false);
imagePreview = new ImagePreview(this);
FormData fd_imagePreview = new FormData();
fd_imagePreview.right = new FormAttachment(label, -5);
fd_imagePreview.bottom = new FormAttachment(100, -5);
fd_imagePreview.top = new FormAttachment(0, 5);
fd_imagePreview.left = new FormAttachment(0, 5);
imagePreview.setLayoutData(fd_imagePreview);
this.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
switch (evt.getPropertyName()) {
case "editable":
break;
case "imageInputStreamsMap":
if (imageInputStreamsMap != null
&& !imageInputStreamsMap.isEmpty()) {
// Populate the list on the side
tableViewer.setInput(imageInputStreamsMap.keySet()
.toArray(
new String[imageInputStreamsMap
.keySet().size()]));
if (imageInputStreamsMap.keySet().contains(
selectedImageName)) {
imagePreview
.setImage(new ByteArrayInputStream(
imageInputStreamsMap
.get(selectedImageName)));
} else {
Entry<String, byte[]> next = imageInputStreamsMap
.entrySet().iterator().next();
imagePreview.setImage(new ByteArrayInputStream(next
.getValue()));
selectedImageName = next.getKey();
buttonRemove.setVisible(true && editable);
table.setSelection(0);
}
} else {
tableViewer.setInput(null);
imagePreview.setImage((InputStream) null);
selectedImageName = null;
}
tableViewer.refresh();
table.redraw();
imagePreview.redraw();
break;
case "selectedImageName":
imagePreview.setImage(new ByteArrayInputStream(
imageInputStreamsMap.get(selectedImageName)));
buttonRemove.setVisible(true && editable);
imagePreview.redraw();
for (int index = 0; index < table.getItemCount(); index++) {
if(selectedImageName.equals(
table.getItem(index).getData())) {
table.select(index);
table.redraw();
break;
}
}
break;
case "scrollBarVisible":
tblclmnImage.setWidth(scrollBarVisble ? 94 : 104);
tableViewer.getTable().layout();
tableViewer.refresh();
default:
break;
}
}
});
}
public boolean isEditable() {
return editable;
}
public void setEditable(boolean editable) {
boolean oldValue = this.editable;
this.editable = editable;
changeSupport.firePropertyChange("editable", oldValue, this.editable);
}
/**
* Set multiple Images to the widget, this will remove all existing images.
* In the imageInputStreamMap - the key defines the imageName and the value
* is an inputStream to the Image
*
* @param imageInputStreamsMap
* - a map of image names and image input streams
* @throws IOException
*/
public void setImageInputStreamsMap(
Map<String, InputStream> imageInputStreamsMap) throws IOException {
Map<String, byte[]> oldValue = this.imageInputStreamsMap;
this.imageInputStreamsMap = new HashMap<String, byte[]>();
for (Entry<String, InputStream> test : imageInputStreamsMap.entrySet()) {
this.imageInputStreamsMap.put(test.getKey(),
read2byteArray(test.getValue()));
}
changeSupport.firePropertyChange("imageInputStreamsMap", oldValue,
this.imageInputStreamsMap);
}
/**
* Add a single Image to the stack
*
* @param name
* - the name to Identify the Image.
* @param imageInputStream
* - an inputStream for the Image to be added.
* @throws IOException
*/
public void addImage(String name, InputStream imageInputStream)
throws IOException {
Map<String, byte[]> oldValue = new HashMap<String, byte[]>(
this.imageInputStreamsMap);
this.imageInputStreamsMap.put(name, read2byteArray(imageInputStream));
changeSupport.firePropertyChange("imageInputStreamsMap", oldValue,
this.imageInputStreamsMap);
}
/**
* Remove the Image identified by name
*
* @param name
* - the name of the Image to be removed
* @throws IOException
*/
public void removeImage(String name) throws IOException {
if (imageInputStreamsMap.containsKey(name)) {
Map<String, byte[]> oldValue = new HashMap<String, byte[]>(
this.imageInputStreamsMap);
this.imageInputStreamsMap.remove(name);
changeSupport.firePropertyChange("imageInputStreamsMap", oldValue,
this.imageInputStreamsMap);
}
}
/**
* @param scrollBarVisble
* the scrollBarVisble to set
*/
private void setScrollBarVisble(boolean scrollBarVisble) {
boolean oldValue = this.scrollBarVisble;
this.scrollBarVisble = scrollBarVisble;
changeSupport.firePropertyChange("scrollBarVisible", oldValue,
this.scrollBarVisble);
}
/**
* Return an InputStream for the Image identified by name
*
* @param name
* - name of the Image
* @return InputStream - to the Image identified by name
*/
public InputStream getImage(String name) {
return new ByteArrayInputStream(imageInputStreamsMap.get(name));
}
/**
* get a set of all the image Names associated with the Images being
* displayed by this widget
*
* @return Set of strings containing the names of all Images
*/
public Set<String> getImageNames() {
return imageInputStreamsMap.keySet();
}
/**
* get the name of the current Image in focus
*
* @return String imageName of the Image in focus
*/
public String getSelectedImageName() {
return selectedImageName;
}
/**
* set the Image to be brought into focus using its imageName
*
* @param selectedImageName
*/
public void setSelectedImageName(String selectedImageName) {
String oldValue = this.selectedImageName;
this.selectedImageName = selectedImageName;
changeSupport.firePropertyChange("selectedImageName", oldValue,
this.selectedImageName);
}
private static byte[] read2byteArray(InputStream input) throws IOException {
byte[] buffer = new byte[8192];
int bytesRead;
ByteArrayOutputStream output = new ByteArrayOutputStream();
while ((bytesRead = input.read(buffer)) != -1) {
output.write(buffer, 0, bytesRead);
}
return output.toByteArray();
}
}