package org.archstudio.bna;
import static org.archstudio.sysutils.SystemUtils.castOrNull;
import org.archstudio.bna.ui.IBNAUI;
import org.archstudio.bna.ui.utils.AutodetectBNAUI;
import org.archstudio.bna.utils.BNAUtils;
import org.archstudio.bna.utils.DefaultBNAView;
import org.archstudio.bna.utils.LinearCoordinateMapper;
import org.archstudio.swtutils.SWTWidgetUtils;
import org.archstudio.sysutils.Finally;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.ScrollBar;
public class BNACanvas extends Composite implements ControlListener, SelectionListener, MouseListener,
IBNAModelListener, ICoordinateMapperListener {
protected final int bnaUIStyle;
protected final IBNAView view;
protected IBNAUI bnaUI;
protected ScrollBar hBar;
protected ScrollBar vBar;
protected boolean disposeView = false;
public BNACanvas(Composite parent, int style, IBNAWorld world) {
this(parent, style, new DefaultBNAView(null, world, new LinearCoordinateMapper()));
disposeView = true;
}
public BNACanvas(Composite parent, int style, final IBNAView view) {
super(parent, style & (SWT.H_SCROLL | SWT.V_SCROLL));
try (Finally lock = BNAUtils.lock()) {
parent.setLayout(new FillLayout());
setLayout(new FillLayout());
this.bnaUIStyle = style & ~(SWT.H_SCROLL | SWT.V_SCROLL);
this.view = view;
setBNAUI(new AutodetectBNAUI(view));
updateScrollBars();
updateCoordinateMapper();
view.getCoordinateMapper().addCoordinateMapperListener(this);
view.getBNAWorld().getBNAModel().addBNAModelListener(this);
addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent e) {
try (Finally lock = BNAUtils.lock()) {
view.getCoordinateMapper().removeCoordinateMapperListener(BNACanvas.this);
view.getBNAWorld().getBNAModel().removeBNAModelListener(BNACanvas.this);
if (bnaUI != null) {
bnaUI.getComposite().removeControlListener(BNACanvas.this);
bnaUI.getComposite().removeMouseListener(BNACanvas.this);
bnaUI.dispose();
bnaUI = null;
if (disposeView) {
view.dispose();
}
}
}
}
});
}
}
public void setBNAUI(IBNAUI bnaUI) {
if (hBar != null && !hBar.isDisposed()) {
hBar.removeSelectionListener(this);
}
if (vBar != null && !vBar.isDisposed()) {
vBar.removeSelectionListener(this);
}
try (Finally lock = BNAUtils.lock()) {
view.disposePeers();
if (this.bnaUI != null) {
this.bnaUI.getComposite().removeControlListener(this);
this.bnaUI.getComposite().removeMouseListener(this);
this.bnaUI.dispose();
}
this.bnaUI = bnaUI;
bnaUI.init(this, bnaUIStyle);
}
layout(true, true);
bnaUI.getComposite().addControlListener(this);
bnaUI.getComposite().addMouseListener(this);
hBar = getHorizontalBar();
vBar = getVerticalBar();
updateScrollBars();
if (hBar != null) {
hBar.addSelectionListener(this);
}
if (vBar != null) {
vBar.addSelectionListener(this);
}
}
@Override
public void controlMoved(ControlEvent e) {
}
@Override
public void controlResized(ControlEvent e) {
updateScrollBars();
bnaUI.paint();
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {
updateCoordinateMapper();
}
@Override
public void widgetSelected(SelectionEvent e) {
updateCoordinateMapper();
}
@Override
public void mouseDoubleClick(MouseEvent e) {
}
@Override
public void mouseUp(MouseEvent e) {
}
@Override
public void mouseDown(MouseEvent e) {
bnaUI.forceFocus();
}
@Override
public void bnaModelChanged(BNAModelEvent evt) {
bnaUI.paint();
}
@Override
public void coordinateMappingsChanged(CoordinateMapperEvent evt) {
SWTWidgetUtils.async(bnaUI.getComposite(), new Runnable() {
@Override
public void run() {
updateScrollBars();
}
});
bnaUI.paint();
}
boolean isUpdatingScrollBars = false;
boolean isUpdatingCoordinateMapper = false;
protected void updateScrollBars() {
if (isUpdatingCoordinateMapper) {
return;
}
isUpdatingScrollBars = true;
try (Finally lock = BNAUtils.lock()) {
ICoordinateMapper cm = view.getCoordinateMapper();
Rectangle client = getClientArea();
Rectangle localBounds = cm.getLocalBounds();
Point localOrigin = cm.getLocalOrigin();
if (hBar != null) {
updateScrollBar(hBar, localOrigin.x - localBounds.x, client.width, localBounds.width);
}
if (vBar != null) {
updateScrollBar(vBar, localOrigin.y - localBounds.y, client.height, localBounds.height);
}
}
finally {
isUpdatingScrollBars = false;
}
}
protected void updateScrollBar(ScrollBar bar, int selection, int thumb, int total) {
// ScrollBar setValues(...) silently fails when certain constraints are violated
thumb = Math.min(thumb, total);
bar.setValues(Math.max(0, selection), 0, Math.max(1, total - thumb), Math.max(1, thumb),
Math.max(1, thumb / 10), Math.max(1, thumb / 3));
}
protected void updateCoordinateMapper() {
if (!isUpdatingScrollBars) {
isUpdatingCoordinateMapper = true;
try (Finally lock = BNAUtils.lock()) {
IMutableCoordinateMapper mcm = castOrNull(view.getCoordinateMapper(), IMutableCoordinateMapper.class);
if (mcm != null) {
Rectangle localBounds = mcm.getLocalBounds();
Point oldLocalOrigin = mcm.getLocalOrigin();
Point newLocalOrigin = new Point(hBar != null ? hBar.getSelection() + localBounds.x
: oldLocalOrigin.x, vBar != null ? vBar.getSelection() + localBounds.y : oldLocalOrigin.y);
mcm.setLocalOrigin(newLocalOrigin);
}
}
finally {
isUpdatingCoordinateMapper = false;
}
}
}
public IBNAView getBNAView() {
return view;
}
}