Java tutorial
/******************************************************************************* * Caleydo - Visualization for Molecular Biology - http://caleydo.org * Copyright (c) The Caleydo Team. All rights reserved. * Licensed under the new BSD license, available at http://caleydo.org/license ******************************************************************************/ package org.caleydo.core.view.opengl.canvas; import gleem.linalg.Vec2f; import java.awt.Point; import java.awt.Rectangle; import java.awt.geom.Rectangle2D; import java.util.Iterator; import javax.media.opengl.GL; import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.GLEventListener; import javax.media.opengl.GLRunnable; import org.apache.commons.lang.SystemUtils; import org.caleydo.core.view.internal.MyPreferences; import org.caleydo.core.view.opengl.canvas.internal.DnDAdapter; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.swt.SWT; import org.eclipse.swt.dnd.DragSourceListener; import org.eclipse.swt.dnd.DropTargetListener; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import com.google.common.base.Function; /** * @author Samuel Gratzl * */ public abstract class AGLCanvas implements IGLCanvas { private float scale; private final DnDAdapter dnd; /** * volatile for better multi thread access */ protected volatile boolean visible = true; private final Runnable updateVisibility = new Runnable() { @Override public void run() { Composite c = asComposite(); boolean r = !c.isDisposed() && c.isVisible(); if (r != visible) { visible = r; if (visible) triggerResize(); } } }; private final Listener showHide = new Listener() { @Override public void handleEvent(Event event) { switch (event.type) { case SWT.Hide: visible = false; break; case SWT.Show: visible = true; triggerResize(); break; } } }; public AGLCanvas(GLAutoDrawable drawable) { float s = MyPreferences.getViewZoomFactor(); if (s <= 0) s = 1; scale = s; drawable.addGLEventListener(new ReshapeOnScaleChange()); this.dnd = new DnDAdapter(this, new Iterable<IGLMouseListener>() { @Override public Iterator<IGLMouseListener> iterator() { return mouseListeners(); } }); } @Override public void addDragListener(DragSourceListener l) { dnd.addDragListener(l); } @Override public void addDropListener(DropTargetListener l) { dnd.addDropListener(l); } @Override public boolean removeDragListener(DragSourceListener l) { return dnd.removeDragListener(l); } @Override public boolean removeDropListener(DropTargetListener l) { return dnd.removeDropListener(l); } /** * @return */ protected abstract Iterator<IGLMouseListener> mouseListeners(); /** * */ protected void triggerResize() { if (!SystemUtils.IS_OS_MAC_OSX) // mac special return; Composite c = asComposite(); c.layout(true); org.eclipse.swt.graphics.Point size = c.getSize(); // change the size of composite to force some surface updates, see # c.setSize(size.x - 1, size.y); } protected void init(final Composite canvas) { Composite c = canvas; // add a listener to the whole chain to the top as setVisible doesn't propagate down c.addListener(SWT.Show, showHide); c.addListener(SWT.Hide, showHide); dnd.init(); } /** * @return the visible, see {@link #visible} */ @Override public boolean isVisible() { Composite c = asComposite(); Display display = c.isDisposed() ? null : c.getDisplay(); if (display == null) return false; // async not perfect as we have old state but better than blocking display.asyncExec(updateVisibility); if (!visible) return false; //in addition check if the frame buffer is ok GL gl = asGLAutoDrawAble().getGL(); if (gl != null && !isFrameBufferComplete(gl)) return false; return true; } private static boolean isFrameBufferComplete(GL gl) { if (!gl.isFunctionAvailable("glCheckFramebufferStatus")) return true; int frameBuffer = gl.glCheckFramebufferStatus(GL.GL_FRAMEBUFFER); if (frameBuffer != GL.GL_FRAMEBUFFER_COMPLETE) { return false; } return true; } @Override public final float getDIPWidth() { return dip(asGLAutoDrawAble().getSurfaceWidth()); } @Override public final float getDIPHeight() { return dip(asGLAutoDrawAble().getSurfaceHeight()); } private float dip(int px) { return Units.px(px) / scale; } public final float toDIP(int value_px) { return dip(value_px); } @Override public final Vec2f toDIP(Point point) { return new Vec2f(dip(point.x), dip(point.y)); } @Override public Rectangle2D.Float toDIP(Rectangle viewArea_raw) { return new Rectangle2D.Float(toDIP(viewArea_raw.x), toDIP(viewArea_raw.y), toDIP(viewArea_raw.width), toDIP(viewArea_raw.height)); } @Override public final int toRawPixel(float value_dip) { return Units.dip2px(value_dip, scale); } @Override public Function<Float, Float> toRawPixelFunction() { return new Function<Float, Float>() { @Override public Float apply(Float dip) { return (float) toRawPixel(dip); } }; } @Override public final float getWidth(Units unit) { return unit.unapply(getDIPWidth()); } @Override public final float getHeight(Units unit) { return unit.unapply(getDIPHeight()); } @Override public final Rectangle toRawPixel(Rectangle2D.Float viewArea_dip) { return new Rectangle(toRawPixel(viewArea_dip.x), toRawPixel(viewArea_dip.y), toRawPixel(viewArea_dip.width), toRawPixel(viewArea_dip.height)); } protected final void updateScale() { scale = MyPreferences.getViewZoomFactor(); GLAutoDrawable d = asGLAutoDrawAble(); // fire a reshape too all registered listeners d.invoke(false, new GLRunnable() { @Override public boolean run(GLAutoDrawable drawable) { final int count = drawable.getGLEventListenerCount(); for (int i = 0; i < count; ++i) { GLEventListener l = drawable.getGLEventListener(i); l.reshape(drawable, 0, 0, drawable.getSurfaceWidth(), drawable.getSurfaceHeight()); } return true; } }); } private class ReshapeOnScaleChange implements GLEventListener, IPropertyChangeListener { @Override public void init(GLAutoDrawable drawable) { MyPreferences.prefs().addPropertyChangeListener(this); } @Override public void display(GLAutoDrawable drawable) { } @Override public void dispose(GLAutoDrawable drawable) { MyPreferences.prefs().removePropertyChangeListener(this); } @Override public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { } @Override public void propertyChange(PropertyChangeEvent event) { if (MyPreferences.VIEW_ZOOM_FACTOR.equals(event.getProperty())) { updateScale(); } } } }