/**
* This file is part of HIDB2.
*
* HIDB2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* HIDB2 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 Lesser Public License for more details.
*
* You should have received a copy of the GNU Lesser Public License
* along with HIDB2. If not, see <http://www.gnu.org/licenses/>.
*/
package hidb2.gui.util;
import hidb2.gui.ressources.RscMan;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.ScrollBar;
import org.eclipse.swt.widgets.Shell;
/**
* Canvas specialization for Image display with scroll bars and zoom actions.
*
*/
public class ImageCanvas extends Canvas
{
public final static int OPTIMAL_ZOOM = -1;
public final static int SCROLLBAR_WIDTH = 17;
private int _minZoom = 10;
private int _maxZoom = 400;
private int _defaultWidth = 512;
private int _defaultHeight = 512;
private ScrollBar _vBar;
private ScrollBar _hBar;
private ImageData _imgData = null;
private Image _displayedImage = null;
private Point _origin = new Point(0, 0);
/** Zoom Factor in percent for the displayed Image. if set to OPTIMAL_ZOOM, the image size fits the dialog */
private int _zoomFactor = OPTIMAL_ZOOM;
private String _curPath = null;
private boolean _sizeComputed = false;
/**
* Ugly preference setter to have an roughtly independant object.
* @param minZoom
* @param maxZoom
* @param DefaultWidth
* @param DefaultHeight
*/
public void setPref(int minZoom, int maxZoom, int DefaultWidth, int DefaultHeight)
{
_minZoom = minZoom;
_maxZoom = maxZoom;
_defaultWidth = DefaultWidth;
_defaultHeight = DefaultHeight;
}
/**
* Create a new image canvas for image browsing/zooming.
*
* @param parent
* @param style
*/
public ImageCanvas(Composite parent, int style)
{
super(parent, SWT.NO_BACKGROUND | SWT.NO_REDRAW_RESIZE | SWT.V_SCROLL | SWT.H_SCROLL);
_hBar = getHorizontalBar();
_hBar.addListener(SWT.Selection, new Listener()
{
public void handleEvent(Event e)
{
int hSelection = _hBar.getSelection();
int destX = -hSelection - _origin.x;
Rectangle rect = _displayedImage.getBounds();
scroll(destX, 0, 0, 0, rect.width, rect.height, false);
_origin.x = -hSelection;
}
});
_vBar = getVerticalBar();
_vBar.addListener(SWT.Selection, new Listener()
{
public void handleEvent(Event e)
{
int vSelection = _vBar.getSelection();
int destY = -vSelection - _origin.y;
Rectangle rect = _displayedImage.getBounds();
scroll(0, destY, 0, 0, rect.width, rect.height, false);
_origin.y = -vSelection;
}
});
addListener(SWT.Resize, new Listener()
{
public void handleEvent(Event e)
{
if (_displayedImage != null)
{
computeScroll();
redraw();
}
}
});
addListener(SWT.Paint, new Listener()
{
public void handleEvent(Event e)
{
if (_displayedImage != null)
{
GC gc = e.gc;
gc.drawImage(_displayedImage, _origin.x, _origin.y);
Rectangle rect = _displayedImage.getBounds();
Rectangle client = getClientArea();
int marginWidth = client.width - rect.width;
if (marginWidth > 0)
{
gc.fillRectangle(rect.width, 0, marginWidth, client.height);
}
int marginHeight = client.height - rect.height;
if (marginHeight > 0)
{
gc.fillRectangle(0, rect.height, client.width, marginHeight);
}
}
}
});
}
private Menu initMenu()
{
// Setup a popup menu (if needed)
Menu menu = getMenu();
if (menu == null)
{
menu = new Menu(this); // Implicit : SWT.POP_UP
setMenu(menu);
}
return menu;
}
/**
* Add a menu item at the end of the current popup menu.
* The popup menu is created if needed.
* @param label Label of the menu item
* @param s Callback
*/
public void addMenuItem(String label, SelectionListener s)
{
Menu menu = initMenu();
MenuItem item = new MenuItem(menu, SWT.PUSH);
item.setText(label);
item.addSelectionListener(s);
}
public void initZoomMenu()
{
Menu menu = initMenu();
MenuItem item3 = new MenuItem(menu, SWT.CASCADE);
item3.setText("Zoom");
Menu subMenu = new Menu(menu);
item3.setMenu(subMenu);
MenuItem subItem1 = new MenuItem(subMenu, SWT.PUSH);
subItem1.setText("Optimal");
subItem1.addSelectionListener(new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
setZoom(OPTIMAL_ZOOM);
}
});
MenuItem subItem2 = new MenuItem(subMenu, SWT.PUSH);
subItem2.setText("100%");
subItem2.addSelectionListener(new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
setZoom(100);
}
});
MenuItem subItem3 = new MenuItem(subMenu, SWT.PUSH);
subItem3.setText("200%");
subItem3.addSelectionListener(new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
setZoom(200);
}
});
MenuItem subItem4 = new MenuItem(subMenu, SWT.PUSH);
subItem4.setText("300%");
subItem4.addSelectionListener(new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
setZoom(300);
}
});
MenuItem subItem5 = new MenuItem(subMenu, SWT.PUSH);
subItem5.setText("400%");
subItem5.addSelectionListener(new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
setZoom(400);
}
});
}
/**
* Manage Zoom bounds and apply it. (generates a redraw on the canvas)
* @param zoomLevel
*/
public void setZoom(int zoomLevel)
{
_zoomFactor = (zoomLevel == OPTIMAL_ZOOM) ? OPTIMAL_ZOOM : (zoomLevel < _minZoom) ? _minZoom
: ((zoomLevel > _maxZoom) ? _maxZoom : zoomLevel);
zoom();
redraw();
}
public int getZoom()
{
return _zoomFactor;
}
public void freeImage()
{
// Release Image (if needed)
if (_displayedImage != null)
{
_displayedImage.dispose();
_displayedImage = null;
_imgData = null;
}
}
public void setImagePath(String path)
{
_curPath = path;
freeImage();
// Load the image
try
{
_imgData = new ImageData(path);
}
catch (SWTException swex)
{
// Manage bad image format or missing image file
_imgData = RscMan.getImage(RscMan.IN_NOFILE).getImageData();
}
zoom();
}
public void setImage(Image img)
{
_curPath = "--internal--";
freeImage();
// Load the image
_imgData = img.getImageData();
zoom();
}
public String getImagePath()
{
return _curPath;
}
private void zoom()
{
ImageData isrcData = null;
// Take into account the current zoom factor
if (_zoomFactor == 100)
{
isrcData = _imgData;
}
else
{
int width = _imgData.width;
int height = _imgData.height;
int zoom = _zoomFactor;
if (_zoomFactor == OPTIMAL_ZOOM)
{
Rectangle client = _sizeComputed ? getClientArea() : new Rectangle(0, 0, _defaultWidth, _defaultHeight);
zoom = Math.min((100 * client.width) / width, (100 * client.height) / height);
}
int nWidth = (width * zoom) / 100;
int nHeight = (height * zoom) / 100;
isrcData = _imgData.scaledTo(nWidth, nHeight);
}
// Dispose OS Ressources
if (_displayedImage != null)
{
_displayedImage.dispose();
_displayedImage = null;
}
_displayedImage = new Image(getParent().getDisplay(), isrcData);
computeScroll();
}
private void computeScroll()
{
Rectangle rect = _displayedImage.getBounds();
Rectangle client = getClientArea();
_hBar.setMaximum(rect.width);
_vBar.setMaximum(rect.height);
_hBar.setThumb(Math.min(rect.width, client.width));
_vBar.setThumb(Math.min(rect.height, client.height));
int hPage = rect.width - client.width;
int vPage = rect.height - client.height;
int hSelection = _hBar.getSelection();
int vSelection = _vBar.getSelection();
if (hSelection >= hPage)
{
if (hPage <= 0)
hSelection = 0;
_origin.x = -hSelection;
}
if (vSelection >= vPage)
{
if (vPage <= 0)
vSelection = 0;
_origin.y = -vSelection;
}
}
/**
* Convient function to adapt the size of the (toplevel or dialog) shell to the Canvas area.
*
*/
public void computeDialogSize(Shell dlg)
{
Rectangle winRect = dlg.getDisplay().getBounds();
Rectangle rect = _displayedImage.getBounds();
// On WinXP : scrollbar are 17 pixels wide
int width = Math.min(winRect.width, Math.max(_defaultWidth, rect.width + _vBar.getSize().x + SCROLLBAR_WIDTH));
int height = Math.min(winRect.height, Math.max(_defaultHeight, rect.height + _hBar.getSize().y));
dlg.setSize(width, height);
dlg.setLocation((winRect.width - width) / 2, (winRect.height - height) / 2);
_sizeComputed = true;
}
@Override
public void dispose()
{
freeImage();
super.dispose();
}
}