Java tutorial
/******************************************************************************* * Copyright (c) 2008 The Bioclipse Project and others. * 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>. * * Contributors: * Arvid Berg * * ******************************************************************************/ package net.bioclipse.cdk.jchempaint.widgets; import static net.bioclipse.cdk.jchempaint.outline.StructureContentProvider.createCDKChemObject; import static org.openscience.cdk.geometry.GeometryTools.has2DCoordinatesNew; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import javax.vecmath.Point2d; import net.bioclipse.cdk.business.ICDKManager; import net.bioclipse.cdk.domain.CDKChemObject; import net.bioclipse.cdk.domain.ICDKMolecule; import net.bioclipse.cdk.jchempaint.Activator; import net.bioclipse.cdk.jchempaint.business.IJChemPaintGlobalPropertiesManager; import net.bioclipse.cdk.jchempaint.business.IJChemPaintManager; import net.bioclipse.cdk.jchempaint.editor.SWTMouseEventRelay; import net.bioclipse.cdk.jchempaint.undoredo.SWTUndoRedoFactory; import net.bioclipse.cdk.jchempaint.view.JChemPaintWidget; import net.bioclipse.core.business.BioclipseException; import net.bioclipse.core.util.LogUtils; import org.apache.log4j.Logger; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.commands.operations.IOperationHistory; import org.eclipse.core.commands.operations.IUndoContext; import org.eclipse.core.commands.operations.IUndoableOperation; import org.eclipse.core.commands.operations.OperationHistoryFactory; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.ListenerList; import org.eclipse.core.runtime.SafeRunner; import org.eclipse.jface.util.SafeRunnable; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.window.DefaultToolTip; import org.eclipse.jface.window.ToolTip; import org.eclipse.swt.SWT; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.DropTarget; import org.eclipse.swt.dnd.DropTargetEvent; import org.eclipse.swt.dnd.DropTargetListener; import org.eclipse.swt.dnd.TextTransfer; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.events.ControlAdapter; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.ScrollBar; import org.openscience.cdk.CDKConstants; import org.openscience.cdk.aromaticity.CDKHueckelAromaticityDetector; import org.openscience.cdk.atomtype.CDKAtomTypeMatcher; import org.openscience.cdk.controller.ControllerHub; import org.openscience.cdk.controller.ControllerModel; import org.openscience.cdk.controller.IChemModelEventRelayHandler; import org.openscience.cdk.controller.IControllerModule; import org.openscience.cdk.controller.IViewEventRelay; import org.openscience.cdk.controller.MoveModule; import org.openscience.cdk.controller.PhantomBondGenerator; import org.openscience.cdk.controller.undoredo.IUndoListener; import org.openscience.cdk.controller.undoredo.IUndoRedoable; import org.openscience.cdk.controller.undoredo.UndoRedoHandler; import org.openscience.cdk.exception.CDKException; import org.openscience.cdk.interfaces.IAtom; import org.openscience.cdk.interfaces.IAtomContainer; import org.openscience.cdk.interfaces.IAtomType; import org.openscience.cdk.interfaces.IBond; import org.openscience.cdk.interfaces.IChemModel; import org.openscience.cdk.interfaces.IReaction; import org.openscience.cdk.interfaces.IReactionSet; import org.openscience.cdk.nonotify.NoNotificationChemObjectBuilder; import org.openscience.cdk.renderer.Renderer; import org.openscience.cdk.renderer.RendererModel; import org.openscience.cdk.renderer.generators.ExternalHighlightGenerator; import org.openscience.cdk.renderer.generators.IGenerator; import org.openscience.cdk.renderer.generators.SelectAtomGenerator; import org.openscience.cdk.renderer.generators.SelectBondGenerator; import org.openscience.cdk.renderer.selection.IChemObjectSelection; import org.openscience.cdk.renderer.visitor.IDrawVisitor; import org.openscience.cdk.tools.CDKHydrogenAdder; import org.openscience.cdk.tools.manipulator.ChemModelManipulator; public class JChemPaintEditorWidget extends JChemPaintWidget implements ISelectionProvider, IViewEventRelay, IUndoListener { Logger logger = Logger.getLogger(JChemPaintEditorWidget.class); private ListenerList listeners = new ListenerList(); private IAtom prevHighlightedAtom; private IBond prevHighlightedBond; private ControllerHub hub; private ControllerModel c2dm; private SWTMouseEventRelay relay; private boolean new2Dcoordinates = false; private boolean isdirty = false; private boolean isScrolling = false; private IOperationHistory operationHistory = OperationHistoryFactory.getOperationHistory(); private final IUndoContext undoContext = new IUndoContext() { public final String label = "JChemPaintEditorWidget"; public String getLabel() { return label; } public boolean matches(IUndoContext context) { return context.getLabel().equals(label); } }; private PhantomBondGenerator phantomGenerator; public JChemPaintEditorWidget(Composite parent, int style) { super(parent, style | SWT.H_SCROLL | SWT.V_SCROLL | SWT.DOUBLE_BUFFERED); setupScrollbars(); // Commented becaus of bug 1100 selectioncolor not good on windows //java.awt.Color color = createFromSWT( SWT.COLOR_LIST_SELECTION ); java.awt.Color color = new java.awt.Color(0xc2deff); getRenderer().getRenderer2DModel().setSelectedPartColor(color); setupControllerHub(); addControlListener(new ControlAdapter() { public void controlResized(ControlEvent e) { resizeControl(); redraw(); } }); int ops = DND.DROP_COPY; final TextTransfer textTransfer = TextTransfer.getInstance(); Transfer[] transfers = new Transfer[] { textTransfer }; this.addDropSupport(ops, transfers, new DropTargetListener() { public void dragEnter(DropTargetEvent event) { event.detail = DND.DROP_COPY; } public void dragLeave(DropTargetEvent event) { // TODO Auto-generated method stub } public void dragOperationChanged(DropTargetEvent event) { event.detail = DND.DROP_COPY; } public void dragOver(DropTargetEvent event) { event.feedback = DND.FEEDBACK_SELECT | DND.FEEDBACK_SCROLL; if (textTransfer.isSupportedType(event.currentDataType)) { // NOTE: on unsupported platforms this will return null Object o = textTransfer.nativeToJava(event.currentDataType); String t = (String) o; if (t != null) System.out.println(t); } } public void drop(DropTargetEvent event) { if (textTransfer.isSupportedType(event.currentDataType)) { String text = (String) event.data; // ICDKManager cdk = net.bioclipse.cdk.business.Activator // .getDefault().getJavaCDKManager(); // try { // ICDKMolecule mol = cdk.fromSMILES( text ); // IAtomContainer ac = mol.getAtomContainer(); // if(ac.getAtomCount()==1) { // IJChemPaintManager jcp = Activator.getDefault() // .getJavaManager(); // Point2d point = new Point2d(event.x,event.y); // jcp.addAtom( ac.getAtom( 0 ), point); // } // } catch ( BioclipseException e ) { // logger.debug( "Could not create molecuel form text" ); // } IJChemPaintManager jcp = Activator.getDefault().getJavaManager(); Point p = JChemPaintEditorWidget.this.toControl(event.x, event.y); Point2d point = getRenderer().toModelCoordinates(p.x, p.y); jcp.addAtom(text, point); } } public void dropAccept(DropTargetEvent event) { // TODO Auto-generated method stub } }); // create a tooltip ToolTip tooltip = new DefaultToolTip(this) { @Override protected String getText(Event event) { RendererModel rmodel = getRenderer2DModel(); IAtom atom = rmodel.getHighlightedAtom(); if (atom != null && rmodel.getToolTipTextMap().isEmpty()) { List<IAtomContainer> acs = ChemModelManipulator .getAllAtomContainers(getControllerHub().getIChemModel()); int num = -1; for (IAtomContainer ac : acs) { num = ac.getAtomNumber(atom); if (num > -1) break; } if (num < 0) return ""; String atomType = atom.getAtomTypeName(); if (atomType != null) atomType = atomType.replaceFirst("^[^\\.]+\\.", ""); return String.format("%s%d, [%s]", atom.getSymbol(), num, atomType); } else { return rmodel.getToolTipText(atom); } } }; tooltip.setShift(new Point(10, 0)); tooltip.setPopupDelay(200); } private void setupControllerHub() { IChemModel chemModel = NoNotificationChemObjectBuilder.getInstance().newChemModel(); c2dm = new ControllerModel(); UndoRedoHandler undoRedoHandler = new UndoRedoHandler(); undoRedoHandler.addIUndoListener(this); hub = new ControllerHub(c2dm, getRenderer(), chemModel, this, undoRedoHandler, new SWTUndoRedoFactory(this.undoContext)); phantomGenerator.setControllerHub(hub); hub.setEventHandler(new IChemModelEventRelayHandler() { public void coordinatesChanged() { setDirty(true); setSelection(getSelection()); //TODO update selection => properties changed } public void selectionChanged() { setSelection(getSelection()); } public void structureChanged() { Display.getDefault().syncExec(new Runnable() { public void run() { JChemPaintEditorWidget.this.structureChanged(); } }); setDirty(true); } public void structurePropertiesChanged() { JChemPaintEditorWidget.this.structurePropertiesChanged(); setDirty(true); } public void zoomChanged() { resizeControl(); redraw(); } }); setupListeners(); // addListener( SWT.MouseHover, new Listener() { // public void handleEvent( Event event ) { // if(event.type == SWT.MouseHover) { // updateToolTip(); // } // } // }); hub.setActiveDrawModule(new MoveModule(hub)); applyGlobalProperties(); } private static final int[] EVENTS = new int[] { SWT.MouseDoubleClick, SWT.MouseDown, SWT.MouseEnter, SWT.MouseExit, // SWT.MouseHover, SWT.MouseMove, SWT.MouseUp, SWT.MouseWheel }; private void setupListeners() { relay = new SWTMouseEventRelay(hub); for (int event : EVENTS) { addListener(event, relay); } } private void setupScrollbars() { final ScrollBar hBar = getHorizontalBar(); hBar.setEnabled(true); hBar.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent event) { ScrollBar bar = (ScrollBar) event.widget; int d = bar.getSelection(); Rectangle rect = getDiagramBounds(); int dx = -d - rect.x; setIsScrolling(true); scroll(-d, rect.y, rect.x, rect.y, rect.width, rect.height, false); getRenderer().shiftDrawCenter(dx, 0); setIsScrolling(false); update(); } }); final ScrollBar vBar = getVerticalBar(); vBar.setEnabled(true); vBar.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent event) { ScrollBar bar = (ScrollBar) event.widget; int d = bar.getSelection(); Rectangle rect = getDiagramBounds(); int dy = -d - rect.y; setIsScrolling(true); scroll(rect.x, -d, rect.x, rect.y, rect.width, rect.height, false); getRenderer().shiftDrawCenter(0, dy); setIsScrolling(false); update(); } }); } @Override protected void paint(IDrawVisitor visitor) { Renderer renderer = getRenderer(); if (isScrolling) { renderer.repaint(visitor); } else { renderer.paintChemModel(model, visitor); } } private Rectangle getDiagramBounds() { java.awt.Rectangle r = getRenderer().calculateDiagramBounds(hub.getIChemModel()); return new Rectangle(r.x, r.y, r.width, r.height); } private void applyGlobalProperties() { // apply the global JCP properties IJChemPaintGlobalPropertiesManager jcpprop = Activator.getDefault().getJCPPropManager(); try { jcpprop.applyProperties(hub.getRenderer().getRenderer2DModel()); } catch (BioclipseException e) { LogUtils.debugTrace(logger, e); } } private void updateToolTip() { RendererModel rendererModel = getRenderer2DModel(); IAtom atom = rendererModel.getHighlightedAtom(); IBond bond = rendererModel.getHighlightedBond(); if (atom != prevHighlightedAtom || bond != prevHighlightedBond) { prevHighlightedAtom = atom; prevHighlightedBond = bond; if (prevHighlightedAtom != null) { setToolTipText(rendererModel.getToolTipText(prevHighlightedAtom)); } else if (prevHighlightedBond != null) { // put getToolTipText(prevHighlightedBond) here setToolTipText(null); } else { setToolTipText(""); } } } public void setIsScrolling(boolean isScrolling) { this.isScrolling = isScrolling; } public void reset() { if (hub != null) { Rectangle clientRect = getClientArea(); java.awt.Rectangle rect = new java.awt.Rectangle(clientRect.x, clientRect.y, clientRect.width, clientRect.height); getRenderer().setup(hub.getIChemModel(), rect); resizeControl(); } } public void updateView() { redraw(); } @Override protected List<IGenerator> createGenerators() { List<IGenerator> generatorList = new ArrayList<IGenerator>(); generatorList.add(new ExternalHighlightGenerator()); generatorList.addAll(super.createGenerators()); generatorList.add(phantomGenerator = new PhantomBondGenerator()); generatorList.add(new SelectAtomGenerator()); generatorList.add(new SelectBondGenerator()); return generatorList; } @Override public void setModel(IChemModel model) { hub.setChemModel(model); this.applyGlobalProperties(); reset(); super.setModel(model); } public void setAtomContainer(IAtomContainer atomContainer) { if (atomContainer != null) { IChemModel model = ChemModelManipulator.newChemModel(atomContainer); setModel(model); } else { setModel(null); } } public void setReaction(IReaction reaction) { if (reaction != null) { IChemModel model = reaction.getBuilder().newChemModel(); IReactionSet reactionSet = reaction.getBuilder().newReactionSet(); reactionSet.addReaction(reaction); model.setReactionSet(reactionSet); setModel(model); } else { setModel(null); } } public void setReactionSet(IReactionSet reactionSet) { if (reactionSet != null) { IChemModel model = reactionSet.getBuilder().newChemModel(); model.setReactionSet(reactionSet); setModel(model); } else { setModel(null); } } public void setInput(IAdaptable element) { new2Dcoordinates = false; ICDKMolecule molecule = null; if (element != null) molecule = (ICDKMolecule) ((IAdaptable) element).getAdapter(ICDKMolecule.class); if (molecule != null) { source = molecule; IAtomContainer atomContainer = molecule.getAtomContainer(); if (atomContainer.getAtomCount() > 0 && has2DCoordinatesNew(atomContainer) < 2) { molecule = generate2Dfrom3D(molecule); if (molecule != null) // FIXME do what else dose empty chem model atomContainer = molecule.getAtomContainer(); else atomContainer = null; new2Dcoordinates = true; // Editor not dirty when generated coordinates see bug 1372 //setDirty( true ); add(Message.GENERATED); } else { IAtomContainer oldAC = atomContainer; atomContainer = atomContainer.getBuilder().newAtomContainer(atomContainer); atomContainer.setProperties(new HashMap<Object, Object>(oldAC.getProperties())); setDirty(false); } setAtomContainer(atomContainer); } else { IChemModel model = NoNotificationChemObjectBuilder.getInstance().newChemModel(); source = null; setModel(model); setDirty(false); } setSelection(getSelection()); } /* * Utility method for copying 3D x,y to 2D coordinates */ public static ICDKMolecule generate2Dfrom3D(ICDKMolecule cdkMolecule) { ICDKManager cdk = net.bioclipse.cdk.business.Activator.getDefault().getJavaCDKManager(); ICDKMolecule newMolecule; try { newMolecule = cdk.generate2dCoordinates(cdkMolecule); } catch (Exception e) { // FIXME make non static return null; } newMolecule.getAtomContainer() .setProperties(new HashMap<Object, Object>(cdkMolecule.getAtomContainer().getProperties())); return newMolecule; } public ControllerHub getControllerHub() { return hub; } public void addSelectionChangedListener(ISelectionChangedListener listener) { listeners.add(listener); } public ISelection getSelection() { RendererModel rendererModel = getRenderer2DModel(); if (rendererModel == null) if (source != null) return new StructuredSelection(source); else return StructuredSelection.EMPTY; List<CDKChemObject<?>> selection = new LinkedList<CDKChemObject<?>>(); IChemObjectSelection sel = rendererModel.getSelection(); IAtomContainer modelSelection = sel.getConnectedAtomContainer(); if (modelSelection != null) { for (IAtom atom : modelSelection.atoms()) { selection.add(createCDKChemObject(atom)); } for (IBond bond : modelSelection.bonds()) { selection.add(createCDKChemObject(bond)); } } if (selection.isEmpty() && source != null) { return new StructuredSelection(source); } return new StructuredSelection(selection); } public void removeSelectionChangedListener(ISelectionChangedListener listener) { listeners.remove(listener); } public void setSelection(ISelection selection) { final SelectionChangedEvent e = new SelectionChangedEvent(this, selection); Object[] listenersArray = listeners.getListeners(); for (int i = 0; i < listenersArray.length; i++) { final ISelectionChangedListener l = (ISelectionChangedListener) listenersArray[i]; SafeRunner.run(new SafeRunnable() { public void run() { l.selectionChanged(e); } }); } } public void setActiveDrawModule(IControllerModule activeDrawModule) { hub.setActiveDrawModule(activeDrawModule); } protected void structureChanged() { IChemModel model = hub.getIChemModel(); updateAtomTypesAndHCounts(model); if (!this.isDisposed()) resizeControl(); } private void updateAtomTypesAndHCounts(IChemModel model) { CDKHydrogenAdder hAdder = CDKHydrogenAdder.getInstance(model.getBuilder()); CDKAtomTypeMatcher matcher = CDKAtomTypeMatcher.getInstance(model.getBuilder()); for (IAtomContainer container : ChemModelManipulator.getAllAtomContainers(model)) { // erase old information for (IAtom atom : container.atoms()) { atom.setAtomTypeName(null); atom.setHybridization(null); atom.setHydrogenCount(0); atom.setFlag(CDKConstants.ISAROMATIC, false); } for (IBond bond : container.bonds()) bond.setFlag(CDKConstants.ISAROMATIC, false); // determine new information try { IAtomType[] types = matcher.findMatchingAtomType(container); for (int i = 0; i < container.getAtomCount(); i++) { IAtom atom = container.getAtom(i); if (types[i] != null) { atom.setAtomTypeName(types[i].getAtomTypeName()); atom.setHybridization(types[i].getHybridization()); hAdder.addImplicitHydrogens(container, atom); } } CDKHueckelAromaticityDetector.detectAromaticity(container); } catch (CDKException e) { e.printStackTrace(); } } } protected void structurePropertiesChanged() { IChemModel model = hub.getIChemModel(); updateAtomTypesAndHCounts(model); } public void setDirty(boolean dirty) { if (!dirty) { new2Dcoordinates = false; } this.isdirty = dirty; if (!this.isDisposed()) { Display.getDefault().asyncExec(new Runnable() { public void run() { if (isdirty) { add(Message.DIRTY); remove(Message.GENERATED); } else { remove(Message.DIRTY); remove(Message.GENERATED); } redraw(); } }); } } public boolean is2Dnew() { return new2Dcoordinates; } public boolean getDirty() { return isdirty; } private java.awt.Color createFromSWT(org.eclipse.swt.graphics.Color color) { return new java.awt.Color(color.getRed(), color.getGreen(), color.getBlue()); } private java.awt.Color createFromSWT(int swt_color_constant) { return createFromSWT(getDisplay().getSystemColor(swt_color_constant)); } public void undo() throws ExecutionException { if (this.operationHistory.canUndo(this.undoContext)) { this.operationHistory.undo(undoContext, null, null); if (!this.operationHistory.canUndo(this.undoContext)) { setDirty(false); } structureChanged(); } } public void redo() throws ExecutionException { if (this.operationHistory.canRedo(this.undoContext)) { this.operationHistory.redo(undoContext, null, null); if (!getDirty()) { setDirty(true); } structureChanged(); } } public void doUndo(IUndoRedoable undoredo) { operationHistory.add((IUndoableOperation) undoredo); } private void resizeControl() { final ScrollBar hBar = getHorizontalBar(); final ScrollBar vBar = getVerticalBar(); Integer xVal = null, yVal = null; Rectangle rect = getDiagramBounds(); 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;// negative width fits in should center xVal = -hSelection + client.width / 2 - rect.width / 2; } if (vSelection >= vPage) { if (vPage <= 0) vSelection = 0; yVal = -vSelection + client.height / 2 - rect.height / 2; } int dx = xVal != null ? xVal - rect.x : 0; int dy = yVal != null ? yVal - rect.y : 0; if (dx != 0 || dy != 0) getRenderer().shiftDrawCenter(dx, dy); } public void addDropSupport(int operations, Transfer[] transferTypes, final DropTargetListener listener) { Control control = this; DropTarget dropTarget = new DropTarget(control, operations); dropTarget.setTransfer(transferTypes); dropTarget.addDropListener(listener); } }