1
/* $Revision: 8753 $ $Author: shk3 $ $Date: 2007-08-28 23:40:30 +0200 (Tue, 28 Aug 2007) $
3
* Copyright (C) 2005-2007 Christoph Steinbeck <steinbeck@users.sf.net>
5
* Contact: cdk-devel@lists.sourceforge.net
7
* This program is free software; you can redistribute it and/or
8
* modify it under the terms of the GNU Lesser General Public License
9
* as published by the Free Software Foundation; either version 2.1
10
* of the License, or (at your option) any later version.
11
* All I ask is that proper credit is given for my work, which includes
12
* - but is not limited to - adding the above copyright notice to the beginning
13
* of your source code files, and to any copyright notice that you may distribute
14
* with programs based on this work.
16
* This program is distributed in the hope that it will be useful,
17
* but WITHOUT ANY WARRANTY; without even the implied warranty of
18
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
* GNU Lesser General Public License for more details.
21
* You should have received a copy of the GNU Lesser General Public License
22
* along with this program; if not, write to the Free Software
23
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
25
package org.openscience.cdk.controller;
27
import java.awt.BorderLayout;
28
import java.awt.Color;
29
import java.awt.Component;
30
import java.awt.Container;
31
import java.awt.Dimension;
32
import java.awt.Frame;
33
import java.awt.Point;
34
import java.awt.Polygon;
35
import java.awt.Shape;
36
import java.awt.event.ActionEvent;
37
import java.awt.event.ActionListener;
38
import java.awt.event.KeyEvent;
39
import java.awt.event.KeyListener;
40
import java.awt.event.MouseEvent;
41
import java.awt.event.MouseListener;
42
import java.awt.event.MouseMotionListener;
43
import java.awt.geom.AffineTransform;
44
import java.awt.geom.PathIterator;
45
import java.util.ArrayList;
46
import java.util.EventObject;
47
import java.util.HashMap;
48
import java.util.Iterator;
49
import java.util.List;
50
import java.util.Vector;
52
import javax.swing.BorderFactory;
53
import javax.swing.Box;
54
import javax.swing.BoxLayout;
55
import javax.swing.JButton;
56
import javax.swing.JComboBox;
57
import javax.swing.JDialog;
58
import javax.swing.JLabel;
59
import javax.swing.JOptionPane;
60
import javax.swing.JPanel;
61
import javax.swing.undo.UndoableEdit;
62
import javax.vecmath.Point2d;
63
import javax.vecmath.Vector2d;
65
import org.openscience.cdk.CDKConstants;
66
import org.openscience.cdk.applications.undoredo.AddAtomsAndBondsEdit;
67
import org.openscience.cdk.applications.undoredo.AddFuncGroupEdit;
68
import org.openscience.cdk.applications.undoredo.AdjustBondOrdersEdit;
69
import org.openscience.cdk.applications.undoredo.ChangeAtomSymbolEdit;
70
import org.openscience.cdk.applications.undoredo.ChangeChargeEdit;
71
import org.openscience.cdk.applications.undoredo.IUndoRedoHandler;
72
import org.openscience.cdk.applications.undoredo.MergeMoleculesEdit;
73
import org.openscience.cdk.applications.undoredo.MoveAtomEdit;
74
import org.openscience.cdk.applications.undoredo.RemoveAtomsAndBondsEdit;
75
import org.openscience.cdk.config.IsotopeFactory;
76
import org.openscience.cdk.event.ICDKChangeListener;
77
import org.openscience.cdk.geometry.BondTools;
78
import org.openscience.cdk.geometry.GeometryTools;
79
import org.openscience.cdk.graph.ConnectivityChecker;
80
import org.openscience.cdk.interfaces.IAtom;
81
import org.openscience.cdk.interfaces.IAtomContainer;
82
import org.openscience.cdk.interfaces.IBond;
83
import org.openscience.cdk.interfaces.IChemModel;
84
import org.openscience.cdk.interfaces.IChemObject;
85
import org.openscience.cdk.interfaces.IElectronContainer;
86
import org.openscience.cdk.interfaces.IIsotope;
87
import org.openscience.cdk.interfaces.IMapping;
88
import org.openscience.cdk.interfaces.IMolecule;
89
import org.openscience.cdk.interfaces.IMoleculeSet;
90
import org.openscience.cdk.interfaces.IReaction;
91
import org.openscience.cdk.interfaces.IRing;
92
import org.openscience.cdk.layout.AtomPlacer;
93
import org.openscience.cdk.layout.RingPlacer;
94
import org.openscience.cdk.renderer.Renderer2DModel;
95
import org.openscience.cdk.tools.HydrogenAdder;
96
import org.openscience.cdk.tools.LoggingTool;
97
import org.openscience.cdk.tools.manipulator.AtomContainerManipulator;
98
import org.openscience.cdk.tools.manipulator.ChemModelManipulator;
101
* Class that acts on MouseEvents and KeyEvents.
105
* @cdk.created 2005-05-02
106
* @cdk.keyword mouse events
107
* @cdk.require java1.4+
108
* @cdk.module control
111
abstract class AbstractController2D implements MouseMotionListener, MouseListener, KeyListener
114
private final static int DRAG_UNSET = 0;
115
private final static int DRAG_MOVING_SELECTED = 1;
116
private final static int DRAG_DRAWING_PROPOSED_BOND = 2;
117
private final static int DRAG_DRAWING_PROPOSED_RING = 3;
118
private final static int DRAG_MAKING_SQUARE_SELECTION = 4;
119
private final static int DRAG_MAKING_LASSO_SELECTION = 5;
120
private final static int DRAG_DRAWING_PROPOSED_ATOMATOMMAP = 6;
121
private final static int DRAG_ROTATE = 7;
123
protected Vector lastAction=null;
124
protected JButton moveButton=null;
126
protected IChemModel chemModel;
128
Renderer2DModel r2dm;
129
Controller2DModel c2dm;
130
boolean wasDragged = false;
131
boolean isUndoableChange = false;
133
private Vector listeners = new Vector();
135
private LoggingTool logger;
137
private int prevDragCoordX = 0;
138
private int prevDragCoordY = 0;
139
private boolean draggingSelected = true;
141
private int dragMode = DRAG_UNSET;
143
private Vector commonElements;
144
private HashMap currentCommonElement = new HashMap();
145
IAtom lastAtomInRange = null;
146
private double shiftX = 0;
147
private double shiftY = 0;
150
private IUndoRedoHandler undoRedoHandler;
152
private HashMap funcgroupsmap=new HashMap();
156
HydrogenAdder hydrogenAdder = new HydrogenAdder("org.openscience.cdk.tools.ValencyChecker");
159
AbstractController2D()
161
logger = new LoggingTool(this);
165
AbstractController2D(Controller2DModel c2dm)
169
commonElements = new Vector();
170
String[] elements = c2dm.getCommonElements();
171
for (int i = 0; i < elements.length; i++)
173
commonElements.add(elements[i]);
178
AbstractController2D(Renderer2DModel r2dm, Controller2DModel c2dm)
188
* Gets the controller2DModel attribute of the Controller2D object
190
*@return The controller2DModel value
192
public Controller2DModel getController2DModel()
199
* Gets the undoableChange attribute of the Controller2D object
201
*@return The undoableChange value
203
public boolean isUndoableChange()
205
return isUndoableChange;
210
* Sets the undoableChange attribute of the Controller2D object
212
*@param isUndoable The new undoableChange value
214
public void setUndoableChange(boolean isUndoable)
216
this.isUndoableChange = isUndoable;
221
* Sets the controller2DModel attribute of the Controller2D object
223
*@param model The new controller2DModel value
225
public void setController2DModel(Controller2DModel model)
232
* Manages all actions that will be invoked when the mouse is moved.
234
*@param event MouseEvent object
236
public void mouseMoved(MouseEvent event)
238
int[] screenCoords = {event.getX(), event.getY()};
239
int[] mouseCoords = getWorldCoordinates(screenCoords);
240
int mouseX = mouseCoords[0];
241
int mouseY = mouseCoords[1];
242
highlightNearestChemObject(mouseX, mouseY);
243
//this is the rotate feature
244
if(c2dm.isMovingAllowed() && r2dm.getSelectedPart()!=null && r2dm.getHighlightedAtom()==null && r2dm.getHighlightedBond()==null && c2dm.getDrawMode() == Controller2DModel.LASSO){
245
double xmin=Double.MAX_VALUE;
246
double xmax=Double.MIN_VALUE;
247
double ymin=Double.MAX_VALUE;
248
double ymax=Double.MIN_VALUE;
249
for(int i=0;i<r2dm.getSelectedPart().getAtomCount();i++){
250
if(((Point2d)r2dm.getRenderingCoordinate(r2dm.getSelectedPart().getAtom(i))).x>xmax)
251
xmax=((Point2d)r2dm.getRenderingCoordinate(r2dm.getSelectedPart().getAtom(i))).x;
252
if(((Point2d)r2dm.getRenderingCoordinate(r2dm.getSelectedPart().getAtom(i))).y>ymax)
253
ymax=((Point2d)r2dm.getRenderingCoordinate(r2dm.getSelectedPart().getAtom(i))).y;
254
if(((Point2d)r2dm.getRenderingCoordinate(r2dm.getSelectedPart().getAtom(i))).x<xmin)
255
xmin=((Point2d)r2dm.getRenderingCoordinate(r2dm.getSelectedPart().getAtom(i))).x;
256
if(((Point2d)r2dm.getRenderingCoordinate(r2dm.getSelectedPart().getAtom(i))).y<ymin)
257
ymin=((Point2d)r2dm.getRenderingCoordinate(r2dm.getSelectedPart().getAtom(i))).y;
259
if(mouseCoords[0]>xmin && mouseCoords[0]<xmax && mouseCoords[1]>ymin && mouseCoords[1]<ymax){
260
//ok, we want to rotate
261
r2dm.setRotateCenter(xmin+(xmax-xmin)/2,ymin+(ymax-ymin)/2);
262
r2dm.setRotateRadius(Math.min((xmax-xmin)/4,(ymax-ymin)/4));
266
r2dm.setRotateRadius(0);
272
* Manages all actions that will be invoked when the mouse is dragged.
274
*@param event MouseEvent object
276
public void mouseDragged(MouseEvent event)
278
isUndoableChange = false;
279
logger.debug("MouseDragged Event Props: mode=", c2dm.getDrawModeString());
280
if (logger.isDebugEnabled())
282
logger.debug(" trigger=" + event.isPopupTrigger() +
284
* ", Button number: " + event.getButton() +
286
", Click count: " + event.getClickCount());
289
int[] screenCoords = {event.getX(), event.getY()};
290
int[] mouseCoords = getWorldCoordinates(screenCoords);
291
int mouseX = mouseCoords[0];
292
int mouseY = mouseCoords[1];
296
prevDragCoordX = mouseX;
297
prevDragCoordY = mouseY;
301
if (dragMode == DRAG_DRAWING_PROPOSED_BOND)
303
int startX = r2dm.getPointerVectorStart().x;
304
int startY = r2dm.getPointerVectorStart().y;
305
highlightNearestChemObject(mouseX, mouseY);
306
drawProposedBond(startX, startY, mouseX, mouseY);
307
} else if (dragMode == DRAG_MAKING_SQUARE_SELECTION)
309
int startX = r2dm.getPointerVectorStart().x;
310
int startY = r2dm.getPointerVectorStart().y;
311
selectRectangularArea(startX, startY, mouseX, mouseY);
312
} else if (dragMode == DRAG_DRAWING_PROPOSED_RING)
317
double pointerVectorLength = c2dm.getRingPointerLength();
318
Point2d center = GeometryTools.get2DCenter(getHighlighted(),r2dm.getRenderingCoordinates());
319
r2dm.setPointerVectorStart(new Point((int) center.x, (int) center.y));
320
angle = GeometryTools.getAngle(center.x - mouseX, center.y - mouseY);
321
endX = (int) center.x - (int) (Math.cos(angle) * pointerVectorLength);
322
endY = (int) center.y - (int) (Math.sin(angle) * pointerVectorLength);
323
r2dm.setPointerVectorEnd(new Point(endX, endY));
324
} else if (dragMode == DRAG_MAKING_LASSO_SELECTION)
327
* Draw polygon in screencoordinates, convert them
328
* to world coordinates when mouse released
330
r2dm.addLassoPoint(new Point(event.getX(), event.getY()));
331
} else if (dragMode == DRAG_MOVING_SELECTED)
333
// all these are in model coordinates
334
logger.debug("Dragging selected atoms");
335
int deltaX = mouseX - prevDragCoordX;
336
int deltaY = mouseY - prevDragCoordY;
337
moveSelectedAtomsWith(deltaX, deltaY);
338
IAtomContainer selected=r2dm.getSelectedPart();
339
r2dm.getMerge().clear();
340
for(int i=0;i<selected.getAtomCount();i++){
341
IAtom inrange=getAtomInRange((int)((Point2d)r2dm.getRenderingCoordinate(selected.getAtom(i))).x, (int)((Point2d)r2dm.getRenderingCoordinate(selected.getAtom(i))).y, selected.getAtom(i));
342
if(inrange!=null && inrange!=selected.getAtom(i)){
343
r2dm.getMerge().put(selected.getAtom(i),inrange);
347
* PRESERVE THIS. This notifies the
348
* the listener responsible for
349
* undo and redo storage that it
350
* should not store this change
352
isUndoableChange = false;
354
} else if(dragMode==DRAG_ROTATE){
355
double angle=BondTools.giveAngleBothMethods(new Point2d(r2dm.getRotateCenter()[0],r2dm.getRotateCenter()[1]),new Point2d(prevDragCoordX,prevDragCoordY),new Point2d(mouseX, mouseY),true);
356
Polygon polygon=new Polygon();
357
for(int i=0;i<r2dm.getSelectedPart().getAtomCount();i++) {
358
polygon.addPoint((int)(((Point2d)r2dm.getRenderingCoordinate(r2dm.getSelectedPart().getAtom(i))).x*1000),(int)(((Point2d)r2dm.getRenderingCoordinate(r2dm.getSelectedPart().getAtom(i))).y*1000));
360
polygon.addPoint((int)(((Point2d)r2dm.getRenderingCoordinate(r2dm.getSelectedPart().getAtom(0))).x*1000),(int)(((Point2d)r2dm.getRenderingCoordinate(r2dm.getSelectedPart().getAtom(0))).y*1000));
361
AffineTransform at=AffineTransform.getRotateInstance(angle,r2dm.getRotateCenter()[0]*1000,r2dm.getRotateCenter()[1]*1000);
362
Shape transformedpolygon=at.createTransformedShape(polygon);
363
PathIterator pa=transformedpolygon.getPathIterator(null);
365
for(int i=0;i<r2dm.getSelectedPart().getAtomCount();i++) {
366
double[] d=new double[6];
367
pa.currentSegment(d);
368
r2dm.setRenderingCoordinate(r2dm.getSelectedPart().getAtom(i),new Point2d(d[0]/1000,d[1]/1000));
374
// make note of current coordinates for next DraggedEvent
375
prevDragCoordX = mouseX;
376
prevDragCoordY = mouseY;
381
* manages all actions that will be invoked when a mouse button is pressed
383
*@param event MouseEvent object
385
public void mousePressed(MouseEvent event)
387
isUndoableChange = false;
388
int[] screenCoords = {event.getX(), event.getY()};
389
int[] mouseCoords = getWorldCoordinates(screenCoords);
390
int mouseX = mouseCoords[0];
391
int mouseY = mouseCoords[1];
393
logger.debug("MousePressed Event Props: mode=", c2dm.getDrawModeString());
394
if (logger.isDebugEnabled())
396
logger.debug(" trigger=" + event.isPopupTrigger() +
398
* ", Button number: " + event.getButton() +
400
", Click count: " + event.getClickCount());
405
r2dm.setPointerVectorStart(null);
406
r2dm.setPointerVectorEnd(null);
407
IAtom atomInRange = getAtomInRange(mouseX, mouseY);
408
IBond bondInRange = getBondInRange(mouseX, mouseY);
409
if (atomInRange != null)
411
startX = (int) ((Point2d)r2dm.getRenderingCoordinate(atomInRange)).x;
412
startY = (int) ((Point2d)r2dm.getRenderingCoordinate(atomInRange)).y;
413
r2dm.setPointerVectorStart(new Point(startX, startY));
416
r2dm.setPointerVectorStart(new Point(mouseX, mouseY));
419
if(r2dm.getSelectedPart()!=null &&
420
!((atomInRange == null) || (atomInRange == null)) &&
421
!(r2dm.getSelectedPart().contains(atomInRange) ||
422
r2dm.getSelectedPart().contains(bondInRange)) &&
423
r2dm.getRotateRadius()==0){
424
r2dm.setSelectedPart(atomInRange.getBuilder().newAtomContainer());
427
if (c2dm.getDrawMode() == Controller2DModel.MOVE)
429
selectNearestChemObjectIfNoneSelected(mouseX, mouseY);
430
dragMode = DRAG_MOVING_SELECTED;
431
} else if (c2dm.getDrawMode() == Controller2DModel.DRAWBOND || c2dm.getDrawMode() == Controller2DModel.DOWN_BOND || c2dm.getDrawMode() == Controller2DModel.UP_BOND)
433
if (bondInRange != null && atomInRange == null)
435
// make sure we are not dragging a bond
438
dragMode = DRAG_DRAWING_PROPOSED_BOND;
439
lastAtomInRange = atomInRange;
441
} else if (c2dm.getDrawMode() == Controller2DModel.MAPATOMATOM)
443
dragMode = DRAG_DRAWING_PROPOSED_ATOMATOMMAP;
444
} else if (c2dm.getDrawMode() == Controller2DModel.SELECT)
446
dragMode = DRAG_MAKING_SQUARE_SELECTION;
447
} else if (c2dm.getDrawMode() == Controller2DModel.LASSO && r2dm.getRotateRadius()==0)
449
if(c2dm.isMovingAllowed() && r2dm.getSelectedPart()!=null && (r2dm.getSelectedPart().contains(r2dm.getHighlightedAtom()) || r2dm.getSelectedPart().contains(r2dm.getHighlightedBond()))){
450
if(r2dm.getSelectedPart().getAtomCount()>0)
451
c2dm.setDrawMode(Controller2DModel.MOVE);
452
if(lastAction!=null){
453
((JButton)lastAction.get(0)).setBackground(Color.LIGHT_GRAY);
454
lastAction.set(0,moveButton);
455
moveButton.setBackground(Color.GRAY);
457
dragMode = DRAG_MOVING_SELECTED;
460
dragMode = DRAG_MAKING_LASSO_SELECTION;
462
} else if (c2dm.getDrawMode() == Controller2DModel.RING ||
463
c2dm.getDrawMode() == Controller2DModel.BENZENERING)
465
dragMode = DRAG_DRAWING_PROPOSED_RING;
466
} else if(r2dm.getRotateRadius()!=0){
467
dragMode = DRAG_ROTATE;
470
if(dragMode==DRAG_MOVING_SELECTED){
471
if (r2dm.getSelectedPart() != null && r2dm.getSelectedPart().getAtom(0) != null) {
472
moveoldX=((Point2d)r2dm.getRenderingCoordinate(r2dm.getSelectedPart().getAtom(0))).x;
473
moveoldY=((Point2d)r2dm.getRenderingCoordinate(r2dm.getSelectedPart().getAtom(0))).y;
480
* manages all actions that will be invoked when a mouse button is released
482
*@param event MouseEvent object
484
public void mouseReleased(MouseEvent event)
486
isUndoableChange = false;
487
logger.debug("MouseReleased Event Props: mode=", c2dm.getDrawModeString());
488
if (logger.isDebugEnabled())
490
logger.debug(" trigger=" + event.isPopupTrigger(),
491
", Click count: " + event.getClickCount());
493
if ((event.getModifiers() & MouseEvent.BUTTON1_MASK) != 0)
495
int[] screenCoords = {event.getX(), event.getY()};
496
int[] mouseCoords = getWorldCoordinates(screenCoords);
497
int mouseX = mouseCoords[0];
498
int mouseY = mouseCoords[1];
499
if (c2dm.getDrawMode() == Controller2DModel.SYMBOL)
503
if (c2dm.getDrawMode() == Controller2DModel.ELEMENT)
508
if (c2dm.getDrawMode() == Controller2DModel.INCCHARGE)
512
if (c2dm.getDrawMode() == Controller2DModel.ENTERELEMENT)
516
if (c2dm.getDrawMode() == Controller2DModel.DECCHARGE)
521
if (c2dm.getDrawMode() == Controller2DModel.MAPATOMATOM)
523
handleMapping(wasDragged, r2dm);
526
if (c2dm.getDrawMode() == Controller2DModel.DRAWBOND || c2dm.getDrawMode() == Controller2DModel.DOWN_BOND || c2dm.getDrawMode() == Controller2DModel.UP_BOND)
528
drawBond(mouseX, mouseY);
531
if (c2dm.getDrawMode() == Controller2DModel.SELECT && wasDragged)
533
logger.info("User asks to selected atoms");
534
IAtomContainer selectedPart = chemModel.getBuilder().newAtomContainer();
535
r2dm.setSelectedPart(selectedPart);
536
r2dm.setSelectedPart(getContainedAtoms(r2dm.getSelectRect()));
537
r2dm.setSelectRect(null);
538
logger.debug("selected stuff ", selectedPart);
541
if (c2dm.getDrawMode() == Controller2DModel.ERASER)
546
if (c2dm.getDrawMode() == Controller2DModel.RING || c2dm.getDrawMode() == Controller2DModel.BENZENERING)
548
drawRing(mouseX, mouseY);
551
if (c2dm.getDrawMode() == Controller2DModel.LASSO && r2dm.getRotateRadius()==0)
553
// first deselect all atoms
554
r2dm.setSelectedPart(chemModel.getBuilder().newAtomContainer());
555
// now select new atoms
562
singleObjectSelected(mouseX, mouseY);
573
if (dragMode==DRAG_MOVING_SELECTED){
574
dragAndDropSelection();
577
if (c2dm.getDrawMode() == Controller2DModel.MOVE)
579
if (draggingSelected == false)
581
// then it was dragging nearest Bond or Atom
582
r2dm.setSelectedPart(chemModel.getBuilder().newAtomContainer());
584
if(r2dm.getMerge().size()>0){
586
this.updateMoleculeCoordinates();
590
dragMode = DRAG_UNSET;
591
r2dm.setPointerVectorStart(null);
592
r2dm.setPointerVectorEnd(null);
594
if (shiftX != 0 || shiftY != 0)
603
private void shiftMolecule() {
604
for (int i = 0; i < chemModel.getMoleculeSet().getMoleculeCount(); i++)
606
IMolecule mol = chemModel.getMoleculeSet().getMolecule(i);
607
for (int k = 0; k < mol.getAtomCount(); k++)
609
((Point2d)r2dm.getRenderingCoordinate(mol.getAtom(k))).x=((Point2d)r2dm.getRenderingCoordinate(mol.getAtom(k))).x -shiftX;
610
((Point2d)r2dm.getRenderingCoordinate(mol.getAtom(k))).y=((Point2d)r2dm.getRenderingCoordinate(mol.getAtom(k))).y -shiftY;
617
private void dragAndDropSelection() {
620
IAtomContainer undoredoContainer = chemModel.getBuilder().newAtomContainer();
621
if(r2dm.getSelectedPart()!=null && r2dm.getSelectedPart().getAtomCount()>0){
622
undoredoContainer.add(r2dm.getSelectedPart());
623
deltaX=((Point2d)r2dm.getRenderingCoordinate(r2dm.getSelectedPart().getAtom(0))).x-moveoldX;
624
deltaY=((Point2d)r2dm.getRenderingCoordinate(r2dm.getSelectedPart().getAtom(0))).y-moveoldY;
626
}else if(r2dm.getHighlightedAtom()!=null){
627
deltaX=((Point2d)r2dm.getRenderingCoordinate(r2dm.getHighlightedAtom())).x-moveoldX;
628
deltaY=((Point2d)r2dm.getRenderingCoordinate(r2dm.getHighlightedAtom())).y-moveoldY;
629
undoredoContainer.addAtom(r2dm.getHighlightedAtom());
630
}else if (r2dm.getHighlightedBond()!=null){
631
deltaX=((Point2d)r2dm.getRenderingCoordinate(r2dm.getHighlightedBond().getAtom(0))).x-moveoldX;
632
deltaY=((Point2d)r2dm.getRenderingCoordinate(r2dm.getHighlightedBond().getAtom(0))).y-moveoldY;
634
UndoableEdit edit = new MoveAtomEdit(undoredoContainer, (int)deltaX, (int)deltaY, r2dm.getRenderingCoordinates());
635
undoRedoHandler.postEdit(edit);
638
private void mergeMolecules() {
639
Iterator it=r2dm.getMerge().keySet().iterator();
640
IBond[] bondson2 = null;
643
ArrayList undoredoContainer = new ArrayList();
645
Object[] undoObject = new Object[3];
646
atom1=(IAtom)it.next();
647
atom2=(IAtom)r2dm.getMerge().get(atom1);
648
undoObject[0] = atom1;
649
undoObject[1] = atom2;
650
IMoleculeSet som=chemModel.getMoleculeSet();
651
IAtomContainer container1 = ChemModelManipulator.getRelevantAtomContainer(chemModel, atom1);
652
IAtomContainer container2 = ChemModelManipulator.getRelevantAtomContainer(chemModel, atom2);
653
if (container1 != container2) {
654
container1.add(container2);
655
som.removeAtomContainer(container2);
657
bondson2=AtomContainerManipulator.getBondArray(container1.getConnectedBondsList(atom2));
658
undoObject[2] = bondson2;
659
undoredoContainer.add(undoObject);
660
for(int i=0;i<bondson2.length;i++){
661
if(bondson2[i].getAtom(0)==atom2)
662
bondson2[i].setAtom(atom1,0);
663
if(bondson2[i].getAtom(1)==atom2)
664
bondson2[i].setAtom(atom1,1);
665
if(bondson2[i].getAtom(0)==bondson2[i].getAtom(1)){
666
container1.removeBond(bondson2[i]);
669
container1.removeAtom(atom2);
671
UndoableEdit edit = new MergeMoleculesEdit(chemModel, undoredoContainer, "Molecules merged");
672
undoRedoHandler.postEdit(edit);
673
r2dm.getMerge().clear();
676
private void singleObjectSelected(int mouseX, int mouseY) {
677
// one atom clicked or one bond clicked
678
IChemObject chemObj = getChemObjectInRange(mouseX, mouseY);
679
IAtomContainer container = chemObj.getBuilder().newAtomContainer();
680
if (chemObj instanceof IAtom)
682
container.addAtom((IAtom) chemObj);
683
logger.debug("selected one atom in lasso mode");
684
r2dm.setSelectedPart(container);
685
} else if (chemObj instanceof IBond)
687
IBond bond = (IBond) chemObj;
688
container.addBond(bond);
689
logger.debug("selected one bond in lasso mode");
690
for (int i = 0; i < bond.getAtomCount(); i++)
692
container.addAtom(bond.getAtom(i));
694
r2dm.setSelectedPart(container);
698
private void lassoSelection() {
699
Vector lassoPoints = r2dm.getLassoPoints();
700
r2dm.addLassoPoint(new Point((Point) lassoPoints.elementAt(0)));
701
int number = lassoPoints.size();
702
logger.debug("# lasso points: ", number);
703
int[] screenLassoCoords = new int[number * 2];
705
for (int i = 0; i < number; i++)
707
currentPoint = (Point) lassoPoints.elementAt(i);
708
screenLassoCoords[i * 2] = currentPoint.x;
709
screenLassoCoords[i * 2 + 1] = currentPoint.y;
710
logger.debug("ScreenLasso.x = ", screenLassoCoords[i * 2]);
711
logger.debug("ScreenLasso.y = ", screenLassoCoords[i * 2 + 1]);
714
* Convert points to world coordinates as they are
715
* in screen coordinates in the vector
717
int[] worldCoords = getWorldCoordinates(screenLassoCoords);
718
logger.debug("Returned coords: ", worldCoords.length);
719
logger.debug(" # points: ", number);
720
int[] xPoints = new int[number];
721
int[] yPoints = new int[number];
722
for (int i = 0; i < number; i++)
724
xPoints[i] = worldCoords[i * 2];
725
yPoints[i] = worldCoords[i * 2 + 1];
726
logger.debug("WorldCoords.x = ", worldCoords[i * 2]);
727
logger.debug("WorldCoords.y = ", worldCoords[i * 2 + 1]);
728
logger.debug("Polygon.x = ", xPoints[i]);
729
logger.debug("Polygon.y = ", yPoints[i]);
731
Polygon polygon = new Polygon(xPoints, yPoints, number);
732
r2dm.setSelectedPart(getContainedAtoms(polygon));
733
r2dm.getLassoPoints().removeAllElements();
737
private void drawRing(int mouseX, int mouseY) {
738
this.updateMoleculeCoordinates();
739
IAtomContainer undoRedoContainer = chemModel.getBuilder().newAtomContainer();
740
IRing newRing = null;
741
Point2d sharedAtomsCenter;
742
Vector2d ringCenterVector;
755
double distance1 = 0;
757
double distance2 = 0;
761
Point2d conAtomsCenter = null;
765
RingPlacer ringPlacer = new RingPlacer();
766
int ringSize = c2dm.getRingSize();
767
String symbol = c2dm.getDrawElement();
768
IAtomContainer sharedAtoms = getHighlighted();
770
if (sharedAtoms.getAtomCount() == 0)
772
sharedAtoms = sharedAtoms.getBuilder().newAtomContainer();
773
newRing = sharedAtoms.getBuilder().newRing(ringSize, symbol);
774
if (c2dm.getDrawMode() == Controller2DModel.BENZENERING)
776
// make newRing a benzene ring
777
newRing.getBond(0).setOrder(2.0);
778
newRing.getBond(2).setOrder(2.0);
779
newRing.getBond(4).setOrder(2.0);
780
makeRingAromatic(newRing);
782
bondLength = r2dm.getBondLength();
783
ringRadius = (bondLength / 2) / Math.sin(Math.PI / c2dm.getRingSize());
784
sharedAtomsCenter = new Point2d(mouseX, mouseY - ringRadius);
785
firstAtom = newRing.getAtom(0);
786
firstAtom.setPoint2d(sharedAtomsCenter);
787
sharedAtoms.addAtom(firstAtom);
788
ringCenterVector = new Vector2d(new Point2d(mouseX, mouseY));
789
ringCenterVector.sub(sharedAtomsCenter);
790
ringPlacer.placeSpiroRing(newRing, sharedAtoms, sharedAtomsCenter, ringCenterVector, bondLength);
791
IAtomContainer atomCon = ChemModelManipulator.createNewMolecule(chemModel);
792
atomCon.add(newRing);
793
undoRedoContainer.add(newRing);
794
} else if (sharedAtoms.getAtomCount() == 1) {
795
spiroAtom = sharedAtoms.getAtom(0);
796
sharedAtomsCenter = GeometryTools.get2DCenter(sharedAtoms,r2dm.getRenderingCoordinates());
797
newRing = createAttachRing(sharedAtoms, ringSize, symbol);
798
if (c2dm.getDrawMode() == Controller2DModel.BENZENERING)
800
// make newRing a benzene ring
801
newRing.getBond(0).setOrder(2.0);
802
newRing.getBond(2).setOrder(2.0);
803
newRing.getBond(4).setOrder(2.0);
804
makeRingAromatic(newRing);
806
bondLength = r2dm.getBondLength();
807
conAtomsCenter = getConnectedAtomsCenter(sharedAtoms);
808
if (conAtomsCenter.equals(((Point2d)r2dm.getRenderingCoordinate(spiroAtom))))
810
ringCenterVector = new Vector2d(0, 1);
813
ringCenterVector = new Vector2d(sharedAtomsCenter);
814
ringCenterVector.sub(conAtomsCenter);
816
ringPlacer.placeSpiroRing(newRing, sharedAtoms, sharedAtomsCenter, ringCenterVector, bondLength);
817
// removes the highlighed atom from the ring to add only the new placed
818
// atoms to the AtomContainer.
821
newRing.removeAtom(spiroAtom);
822
} catch (Exception exc)
824
logger.error("Could not remove atom from ring");
827
IAtomContainer atomCon = ChemModelManipulator.getRelevantAtomContainer(chemModel, spiroAtom);
828
atomCon.add(newRing);
829
undoRedoContainer.add(newRing);
830
} else if (sharedAtoms.getAtomCount() == 2) {
831
sharedAtomsCenter = GeometryTools.get2DCenter(sharedAtoms,r2dm.getRenderingCoordinates());
833
// calculate two points that are perpendicular to the highlighted bond
834
// and have a certain distance from the bondcenter
835
firstAtom = sharedAtoms.getAtom(0);
836
secondAtom = sharedAtoms.getAtom(1);
837
xDiff = ((Point2d)r2dm.getRenderingCoordinate(secondAtom)).x - ((Point2d)r2dm.getRenderingCoordinate(firstAtom)).x;
838
yDiff = ((Point2d)r2dm.getRenderingCoordinate(secondAtom)).y - ((Point2d)r2dm.getRenderingCoordinate(firstAtom)).y;
839
bondLength = Math.sqrt(Math.pow(xDiff, 2) + Math.pow(yDiff, 2));
840
angle = GeometryTools.getAngle(xDiff, yDiff);
841
newPoint1 = new Point2d((Math.cos(angle + (Math.PI / 2)) * bondLength / 4) + sharedAtomsCenter.x, (Math.sin(angle + (Math.PI / 2)) * bondLength / 4) + sharedAtomsCenter.y);
842
newPoint2 = new Point2d((Math.cos(angle - (Math.PI / 2)) * bondLength / 4) + sharedAtomsCenter.x, (Math.sin(angle - (Math.PI / 2)) * bondLength / 4) + sharedAtomsCenter.y);
846
// check which one of the two points is nearest to the endpoint of the pointer
847
// vector that was dragged to make the ringCenterVector point into the right direction.
848
pointerMarkX = r2dm.getPointerVectorEnd().x;
849
pointerMarkY = r2dm.getPointerVectorEnd().y;
850
distance1 = -1 * (Math.sqrt(Math.pow(newPoint1.x - pointerMarkX, 2) + Math.pow(newPoint1.y - pointerMarkY, 2)));
851
distance2 = -1 * (Math.sqrt(Math.pow(newPoint2.x - pointerMarkX, 2) + Math.pow(newPoint2.y - pointerMarkY, 2)));
852
r2dm.setPointerVectorStart(null);
853
r2dm.setPointerVectorEnd(null);
855
// check which one of the two points is nearest to the center of the
856
// connected atoms to make the ringCenterVector point into the right direction.
857
conAtomsCenter = getConnectedAtomsCenter(sharedAtoms);
858
distance1 = Math.sqrt(Math.pow(newPoint1.x - conAtomsCenter.x, 2) + Math.pow(newPoint1.y - conAtomsCenter.y, 2));
859
distance2 = Math.sqrt(Math.pow(newPoint2.x - conAtomsCenter.x, 2) + Math.pow(newPoint2.y - conAtomsCenter.y, 2));
861
ringCenterVector = new Vector2d(sharedAtomsCenter);
862
// no ring is attached if the two ditances are equal
863
//shk3: I removed this condition since it leads to npe and I cannot see why it is necessary.
864
//the npe is if you draw a single bond and want to attach a ring to it. Now the ring is in an arbitrary direction, but this is ok
865
/*if (distance1 == distance2)
867
logger.warn("don't know where to draw the new Ring");
870
if (distance1 < distance2)
872
ringCenterVector.sub(newPoint1);
873
} else if (distance2 < distance1)
875
ringCenterVector.sub(newPoint2);
877
ringCenterVector.sub(newPoint2);
880
IAtomContainer atomCon = ChemModelManipulator.getRelevantAtomContainer(chemModel, firstAtom);
882
// construct a new Ring that contains the highlighted bond an its two atoms
883
newRing = createAttachRing(sharedAtoms, ringSize, symbol);
884
if (c2dm.getDrawMode() == Controller2DModel.BENZENERING)
886
// make newRing a benzene ring
887
IBond existingBond = atomCon.getBond(firstAtom, secondAtom);
888
if (existingBond.getOrder() == 1.0)
890
if (existingBond.getFlag(CDKConstants.ISAROMATIC))
892
newRing.getBond(2).setOrder(2.0);
893
newRing.getBond(4).setOrder(2.0);
896
newRing.getBond(1).setOrder(2.0);
897
newRing.getBond(3).setOrder(2.0);
898
newRing.getBond(5).setOrder(2.0);
902
newRing.getBond(2).setOrder(2.0);
903
newRing.getBond(4).setOrder(2.0);
905
makeRingAromatic(newRing);
908
// place the new atoms of the new ring to the right position
909
ringPlacer.placeFusedRing(newRing, sharedAtoms, sharedAtomsCenter, ringCenterVector, bondLength);
911
// removes the highlighed bond and its atoms from the ring to add only
912
// the new placed atoms to the AtomContainer.
915
newRing.remove(sharedAtoms);
916
} catch (Exception exc)
918
logger.error("Could not remove atom from ring");
921
atomCon.add(newRing);
924
double highlightRadius = r2dm.getHighlightRadius();
925
for (int i = 0; i < newRing.getAtomCount(); i++)
927
IAtom atom=newRing.getAtom(i);
928
r2dm.setRenderingCoordinate(atom,new Point2d(atom.getPoint2d()));
930
for (int i = 0; i < newRing.getAtomCount(); i++)
932
IAtom atom=newRing.getAtom(i);
933
r2dm.setRenderingCoordinate(atom,new Point2d(atom.getPoint2d()));
934
centerAtom(atom,chemModel);
935
//We make sure atoms don't overlap
936
//Solution is a bit crude, we would need to find an unoccupied place (and even the bond display should be optimized)
937
IAtom inrange=getAtomInRange((int)((Point2d)r2dm.getRenderingCoordinate(atom)).x, (int)((Point2d)r2dm.getRenderingCoordinate(atom)).y, atom);
938
if(inrange!=null && Math.sqrt(Math.pow(((Point2d)r2dm.getRenderingCoordinate(inrange)).x - ((Point2d)r2dm.getRenderingCoordinate(atom)).x, 2) + Math.pow(((Point2d)r2dm.getRenderingCoordinate(inrange)).y - ((Point2d)r2dm.getRenderingCoordinate(atom)).y, 2)) < highlightRadius/4){
939
((Point2d)r2dm.getRenderingCoordinate(atom)).x-=highlightRadius/4;
940
((Point2d)r2dm.getRenderingCoordinate(atom)).y-=highlightRadius/4;
943
this.updateMoleculeCoordinates();
944
this.updateAtoms(ChemModelManipulator.getRelevantAtomContainer(chemModel, newRing.getAtom(0)), sharedAtoms.atoms());
945
this.updateAtoms(ChemModelManipulator.getRelevantAtomContainer(chemModel, newRing.getAtom(0)), newRing.atoms());
946
undoRedoContainer.add(newRing);
947
UndoableEdit edit = new AddAtomsAndBondsEdit(chemModel, undoRedoContainer, "Added Ring",c2dm);
948
undoRedoHandler.postEdit(edit);
953
private void eraseSelection() {
954
IAtomContainer undoRedoContainer = chemModel.getBuilder().newAtomContainer();
956
IAtom highlightedAtom = r2dm.getHighlightedAtom();
957
IBond highlightedBond = r2dm.getHighlightedBond();
958
if (highlightedAtom != null && (r2dm.getSelectedPart()==null || !r2dm.getSelectedPart().contains(highlightedAtom)))
960
logger.info("User asks to delete an Atom");
961
IAtomContainer container = ChemModelManipulator.getRelevantAtomContainer(chemModel, highlightedAtom);
962
IElectronContainer[] eContainer = AtomContainerManipulator.getElectronContainerArray(container.getConnectedElectronContainersList(highlightedAtom));
963
for (int i=0; i<eContainer.length; i++) {
964
undoRedoContainer.addBond((IBond) eContainer[i]);
966
logger.debug("Atoms before delete: ", container.getAtomCount());
967
Iterator atoms = container.getConnectedAtomsList(highlightedAtom).iterator();
968
ChemModelManipulator.removeAtomAndConnectedElectronContainers(chemModel, highlightedAtom);
969
updateAtoms(container, atoms);
970
undoRedoContainer.addAtom(highlightedAtom);
972
type = "Remove Atom";
975
type = "Remove Substructure";
977
logger.debug("Atoms before delete: ", container.getAtomCount());
978
} else if (highlightedBond != null && (r2dm.getSelectedPart()==null || !r2dm.getSelectedPart().contains(highlightedBond))){
979
logger.info("User asks to delete a Bond");
980
ChemModelManipulator.removeElectronContainer(chemModel, highlightedBond);
981
undoRedoContainer.addBond(highlightedBond);
983
type = "Remove Bond";
986
type = "Remove Substructure";
989
java.util.Iterator atoms = highlightedBond.atoms();
990
IAtomContainer container = ChemModelManipulator.getRelevantAtomContainer(chemModel, highlightedBond.getAtom(0));
991
updateAtoms(container, atoms);
992
} else if(r2dm.getSelectedPart()!=null && (r2dm.getSelectedPart().getAtomCount()>0 || r2dm.getSelectedPart().getBondCount()>0)){
993
logger.info("User asks to delete selected part");
994
IAtomContainer containerToUpdate = ChemModelManipulator.getRelevantAtomContainer(chemModel, r2dm.getSelectedPart().getAtom(0));
995
for(int i=0;i<r2dm.getSelectedPart().getAtomCount();i++){
996
undoRedoContainer.addAtom(r2dm.getSelectedPart().getAtom(i));
997
for(int k=0;k<ChemModelManipulator.getRelevantAtomContainer(chemModel,r2dm.getSelectedPart().getAtom(i)).getConnectedBondsCount(r2dm.getSelectedPart().getAtom(i));k++){
998
undoRedoContainer.addBond((IBond)ChemModelManipulator.getRelevantAtomContainer(chemModel,r2dm.getSelectedPart().getAtom(i)).getConnectedBondsList(r2dm.getSelectedPart().getAtom(i)).get(k));
1000
ChemModelManipulator.removeAtomAndConnectedElectronContainers(chemModel,r2dm.getSelectedPart().getAtom(i));
1002
type = "Remove Substructure";
1004
updateAtoms(containerToUpdate, containerToUpdate.atoms());
1006
logger.warn("Cannot deleted if nothing is highlighted");
1010
* PRESERVE THIS. This notifies the
1011
* the listener responsible for
1012
* undo and redo storage that it
1013
* should store this change of an atom symbol
1015
isUndoableChange = true;
1019
UndoableEdit edit = new RemoveAtomsAndBondsEdit(chemModel, undoRedoContainer, type);
1020
undoRedoHandler.postEdit(edit);
1025
private void drawBond(int mouseX, int mouseY) {
1026
this.updateMoleculeCoordinates();
1027
logger.debug("mouseReleased->drawbond");
1029
IAtom newAtom1 = null;
1030
IAtom newAtom2 = null;
1031
IBond newBond = null;
1032
int startX = r2dm.getPointerVectorStart().x;
1033
int startY = r2dm.getPointerVectorStart().y;
1034
IBond bondInRange = r2dm.getHighlightedBond();
1038
//atomInRange = r2dm.getHighlightedAtom();
1039
//Bond bondInRange = getBondInRange(mouseX, mouseY);
1041
* IMPORTANT: I don't use getHighlighteAtom()
1042
* here because of the special case of
1043
* only one atom on the screen.
1044
* In this case, this atom will not detected
1045
* if the mouse hasn't moved after it's creation
1047
atomInRange = getAtomInRange(mouseX, mouseY);
1048
if (bondInRange != null)
1051
IAtomContainer container = ChemModelManipulator.getRelevantAtomContainer(chemModel, bondInRange.getAtom(0));
1052
updateAtoms(container, bondInRange.atoms());
1053
HashMap changedBonds = new HashMap();
1054
double formerBondOrder = bondInRange.getOrder();
1055
if (c2dm.getDrawMode() == Controller2DModel.DRAWBOND){
1056
if(bondInRange.getStereo()!=CDKConstants.STEREO_BOND_NONE){
1057
bondInRange.setStereo(CDKConstants.STEREO_BOND_NONE);
1059
// increase Bond order
1060
double order = bondInRange.getOrder();
1061
if (order >= CDKConstants.BONDORDER_TRIPLE)
1063
bondInRange.setOrder(CDKConstants.BONDORDER_SINGLE);
1065
bondInRange.setOrder(order + 1.0);
1066
// this is tricky as it depends on the fact that the
1067
// constants are unidistant, i.e. {1.0, 2.0, 3.0}.
1070
}else if(c2dm.getDrawMode() == Controller2DModel.DOWN_BOND){
1071
// toggle bond stereo
1072
double stereo = bondInRange.getStereo();
1073
if (stereo == CDKConstants.STEREO_BOND_DOWN)
1075
bondInRange.setStereo(CDKConstants.STEREO_BOND_DOWN_INV);
1076
} else if (stereo == CDKConstants.STEREO_BOND_DOWN_INV)
1078
bondInRange.setStereo(CDKConstants.STEREO_BOND_NONE);
1081
bondInRange.setStereo(CDKConstants.STEREO_BOND_DOWN);
1083
bondInRange.setOrder(1);
1085
// toggle bond stereo
1086
double stereo = bondInRange.getStereo();
1087
if (stereo == CDKConstants.STEREO_BOND_UP)
1089
bondInRange.setStereo(CDKConstants.STEREO_BOND_UP_INV);
1090
} else if (stereo == CDKConstants.STEREO_BOND_UP_INV)
1092
bondInRange.setStereo(CDKConstants.STEREO_BOND_NONE);
1095
bondInRange.setStereo(CDKConstants.STEREO_BOND_UP);
1097
bondInRange.setOrder(1);
1100
* PRESERVE THIS. This notifies the
1101
* the listener responsible for
1102
* undo and redo storage that it
1103
* should store this change of an atom symbol
1105
if (bondInRange.getOrder() != formerBondOrder) {
1106
double[] bondOrders = new double[2];
1107
bondOrders[0] = bondInRange.getOrder();
1108
bondOrders[1] = formerBondOrder;
1109
changedBonds.put(bondInRange, bondOrders);
1111
isUndoableChange = true;
1115
UndoableEdit edit = new AdjustBondOrdersEdit(changedBonds);
1116
undoRedoHandler.postEdit(edit);
1117
updateAtoms(container, bondInRange.atoms());
1120
IAtomContainer undoRedoContainer = chemModel.getBuilder().newAtomContainer();
1121
if (atomInRange != null)
1123
logger.debug("We had an atom in range");
1124
newAtom1 = atomInRange;
1125
} else if (!wasDragged)
1127
// create a new molecule
1128
logger.debug("We make a new molecule");
1129
newAtom1 = undoRedoContainer.getBuilder().newAtom(c2dm.getDrawElement(), new Point2d(startX, startY));
1130
IAtomContainer atomCon = ChemModelManipulator.createNewMolecule(chemModel);
1131
atomCon.addAtom(newAtom1);
1132
r2dm.setRenderingCoordinate(newAtom1,new Point2d(startX, startY));
1134
updateAtom(atomCon, newAtom1);
1135
undoRedoContainer.add(atomCon);
1140
if (dragMode == DRAG_DRAWING_PROPOSED_BOND)
1142
int endX = r2dm.getPointerVectorEnd().x;
1143
int endY = r2dm.getPointerVectorEnd().y;
1144
atomInRange = getAtomInRange(endX, endY);
1145
// OK, there is some repartitioning done on atomCon later, so put everything in the first AC
1146
IAtomContainer atomCon = null;
1147
Iterator atomCons = ChemModelManipulator.getAllAtomContainers(chemModel).iterator();
1148
while (atomCons.hasNext()) {
1149
if (atomCon == null) {
1150
atomCon = (IAtomContainer)atomCons.next();
1152
atomCon.add((IAtomContainer)atomCons.next());
1155
if (atomCon == null) atomCon = chemModel.getBuilder().newAtomContainer();
1156
if (atomInRange != null)
1158
logger.debug("*** atom in range");
1160
newAtom2 = atomInRange;
1161
logger.debug("atomCon.getAtomCount() " + atomCon.getAtomCount());
1164
logger.debug("*** new atom");
1165
newAtom2 = atomCon.getBuilder().newAtom(c2dm.getDrawElement(), new Point2d(endX, endY));
1166
atomCon.addAtom(newAtom2);
1167
r2dm.setRenderingCoordinate(newAtom2,new Point2d(endX, endY));
1168
undoRedoContainer.addAtom(newAtom2);
1170
newAtom1 = lastAtomInRange;
1171
if (newAtom1 == null)
1173
newAtom1 = atomCon.getBuilder().newAtom(c2dm.getDrawElement(), new Point2d(r2dm.getPointerVectorStart().x, r2dm.getPointerVectorStart().y));
1174
undoRedoContainer.addAtom(newAtom1);
1176
if (newAtom1 != newAtom2)
1178
newBond = atomCon.getBuilder().newBond(newAtom1, newAtom2, 1);
1179
if(c2dm.getDrawMode() == Controller2DModel.UP_BOND)
1180
newBond.setStereo(CDKConstants.STEREO_BOND_UP);
1181
if(c2dm.getDrawMode() == Controller2DModel.DOWN_BOND)
1182
newBond.setStereo(CDKConstants.STEREO_BOND_DOWN);
1183
logger.debug(newAtom1 + " - " + newAtom2);
1184
atomCon.addBond(newBond);
1185
undoRedoContainer.addBond(newBond);
1190
IMoleculeSet setOfMolecules = ConnectivityChecker.partitionIntoMolecules(atomCon);
1191
chemModel.setMoleculeSet(setOfMolecules);
1192
logger.debug("We have " + setOfMolecules.getAtomContainerCount() + " molecules on screen");
1193
} catch (Exception exception)
1195
logger.warn("Could not partition molecule: ", exception.getMessage());
1196
logger.debug(exception);
1201
updateAtom(atomCon, newAtom2);
1204
* PRESERVE THIS. This notifies the
1205
* the listener responsible for
1206
* undo and redo storage that it
1207
* should store this change of an atom symbol
1209
isUndoableChange = true;
1214
} else if (atomInRange != null)
1216
// add a new atom to the current atom in some random
1218
IAtomContainer atomCon = ChemModelManipulator.getRelevantAtomContainer(chemModel, atomInRange);
1219
newAtom2 = atomCon.getBuilder().newAtom(c2dm.getDrawElement(), ((Point2d)r2dm.getRenderingCoordinate(atomInRange)));
1221
// now create 2D coords for new atom
1222
double bondLength = r2dm.getBondLength();
1223
java.util.List connectedAtoms = atomCon.getConnectedAtomsList(atomInRange);
1224
logger.debug("connectedAtoms.length: " + connectedAtoms.size());
1225
IAtomContainer placedAtoms = atomCon.getBuilder().newAtomContainer();
1226
//placedAtoms.addAtom(atomInRange);
1227
for (int i = 0; i < connectedAtoms.size(); i++)
1229
placedAtoms.addAtom((IAtom)connectedAtoms.get(i));
1231
IAtomContainer unplacedAtoms = atomCon.getBuilder().newAtomContainer();
1232
unplacedAtoms.addAtom(newAtom2);
1233
AtomPlacer atomPlacer = new AtomPlacer();
1234
atomPlacer.setMolecule(atomCon.getBuilder().newMolecule(atomCon));
1235
Point2d center2D = GeometryTools.get2DCenter(placedAtoms,r2dm.getRenderingCoordinates());
1236
logger.debug("placedAtoms.getAtomCount(): " + placedAtoms.getAtomCount());
1237
logger.debug("unplacedAtoms.getAtomCount(): " + unplacedAtoms.getAtomCount());
1238
if (placedAtoms.getAtomCount() == 1)
1240
Vector2d bondVector = atomPlacer.getNextBondVector(
1241
atomInRange, placedAtoms.getAtom(0),
1242
GeometryTools.get2DCenter(atomCon.getBuilder().newMolecule(atomCon),r2dm.getRenderingCoordinates()),
1243
true // FIXME: is this correct? (see SF bug #1367002)
1245
Point2d atomPoint = new Point2d(((Point2d)r2dm.getRenderingCoordinate(atomInRange)));
1246
bondVector.normalize();
1247
bondVector.scale(bondLength);
1248
atomPoint.add(bondVector);
1249
newAtom2.setPoint2d(atomPoint);
1250
r2dm.setRenderingCoordinate(newAtom2,atomPoint);
1254
atomPlacer.distributePartners(atomInRange, placedAtoms, center2D,
1255
unplacedAtoms, bondLength);
1258
// now add the new atom
1259
atomCon.addAtom(newAtom2);
1260
undoRedoContainer.addAtom(newAtom2);
1261
r2dm.setRenderingCoordinate(newAtom2,new Point2d(newAtom2.getPoint2d()));
1262
newBond= undoRedoContainer.getBuilder().newBond(atomInRange, newAtom2, 1.0);
1263
atomCon.addBond(newBond);
1264
undoRedoContainer.addBond(newBond);
1265
if(c2dm.getDrawMode() == Controller2DModel.UP_BOND)
1266
newBond.setStereo(CDKConstants.STEREO_BOND_UP);
1267
if(c2dm.getDrawMode() == Controller2DModel.DOWN_BOND)
1268
newBond.setStereo(CDKConstants.STEREO_BOND_DOWN);
1270
updateAtom(atomCon, atomInRange);
1271
updateAtom(atomCon, newAtom2);
1273
UndoableEdit edit = new AddAtomsAndBondsEdit(chemModel, undoRedoContainer, "Add Bond",c2dm);
1274
undoRedoHandler.postEdit(edit);
1278
if(newAtom1!=null && r2dm.getRenderingCoordinate(newAtom1)==null)
1279
r2dm.setRenderingCoordinate(newAtom1,new Point2d(newAtom1.getPoint2d()));
1281
r2dm.setRenderingCoordinate(newAtom2,new Point2d(newAtom2.getPoint2d()));
1282
centerAtom(newAtom1,chemModel);
1283
centerAtom(newAtom2,chemModel);
1284
this.updateMoleculeCoordinates();
1287
private void decreaseCharge() {
1288
IAtom atomInRange = r2dm.getHighlightedAtom();
1289
if (atomInRange != null)
1291
int formerCharge = atomInRange.getFormalCharge();
1292
atomInRange.setFormalCharge(atomInRange.getFormalCharge() - 1);
1294
IAtomContainer container = ChemModelManipulator.getRelevantAtomContainer(chemModel, atomInRange);
1295
updateAtom(container, atomInRange);
1297
UndoableEdit edit = new ChangeChargeEdit(atomInRange, formerCharge, atomInRange.getFormalCharge());
1298
undoRedoHandler.postEdit(edit);
1304
private void enterElement() {
1305
IAtom atomInRange = r2dm.getHighlightedAtom();
1306
if (atomInRange != null)
1308
String[] funcGroupsKeys=new String[funcgroupsmap.keySet().size()+1];
1309
Iterator it=funcgroupsmap.keySet().iterator();
1311
funcGroupsKeys[0]="";
1312
while(it.hasNext()){
1313
funcGroupsKeys[h]=(String)it.next();
1316
String x=EnterElementOrGroupDialog.showDialog(null,null, "Enter an element symbol or choose/enter a functional group abbrivation:", "Enter element", funcGroupsKeys, "","");
1318
IAtomContainer ac=(IAtomContainer)funcgroupsmap.get(x.toLowerCase());
1319
//this means a functional group was entered
1321
if(ac!=null && !x.equals("")){
1322
ac=(IAtomContainer)((IAtomContainer)funcgroupsmap.get(x)).clone();
1323
IAtomContainer container = ChemModelManipulator.getRelevantAtomContainer(chemModel, atomInRange);
1324
IAtom lastplaced=null;
1326
//this is the starting point for placing
1327
ac.getAtom(0).setPoint2d((Point2d)r2dm.getRenderingCoordinate(atomInRange));
1328
r2dm.setRenderingCoordinate(ac.getAtom(0), (Point2d)r2dm.getRenderingCoordinate(atomInRange));
1329
lastplaced=ac.getAtom(0);
1332
List connbonds=container.getConnectedBondsList(atomInRange);
1333
//this is needed for undo redo
1334
IAtomContainer undoredocontainer=ac.getBuilder().newAtomContainer();
1335
undoredocontainer.addAtom(atomInRange);
1336
for(int i=0;i<connbonds.size();i++){
1337
IBond bond=(IBond)connbonds.get(i);
1338
if(bond.getAtom(0)==atomInRange){
1339
bond.setAtom(ac.getAtom(0), 0);
1340
undoredocontainer.addBond(bond);
1342
bond.setAtom(ac.getAtom(0), 1);
1343
undoredocontainer.addBond(bond);
1346
container.removeAtomAndConnectedElectronContainers(atomInRange);
1347
AtomPlacer ap=new AtomPlacer();
1348
while(lastplaced!=null){
1349
IAtomContainer placedNeighbours=container.getBuilder().newAtomContainer();
1350
IAtomContainer unplacedNeighbours=container.getBuilder().newAtomContainer();
1351
List l=container.getConnectedAtomsList(lastplaced);
1352
for(int i=0;i<l.size();i++){
1353
if(r2dm.getRenderingCoordinate((IAtom)l.get(i))!=null)
1354
placedNeighbours.addAtom((IAtom)l.get(i));
1356
unplacedNeighbours.addAtom((IAtom)l.get(i));
1358
ap.distributePartners(lastplaced, placedNeighbours, GeometryTools.get2DCenter(placedNeighbours,r2dm.getRenderingCoordinates()), unplacedNeighbours, r2dm.getBondLength(), r2dm.getRenderingCoordinates());
1359
lastplaced=ac.getAtom(counter);
1361
if(counter==ac.getAtomCount())
1364
it=container.atoms();
1365
while(it.hasNext()){
1366
IAtom atom=(IAtom)it.next();
1367
if(r2dm.getRenderingCoordinate(atom)==null)
1368
r2dm.setRenderingCoordinate(atom, atom.getPoint2d());
1371
* PRESERVE THIS. This notifies the
1372
* the listener responsible for
1373
* undo and redo storage that it
1374
* should store this change of an atom symbol
1376
isUndoableChange = true;
1381
UndoableEdit edit = new AddFuncGroupEdit(chemModel, undoredocontainer ,ac, "add "+x);
1382
undoRedoHandler.postEdit(edit);
1383
//undoRedoHandler.postEdit(edit);
1386
}else if(x!=null && x.length()>0){
1387
String formerSymbol="";
1388
if(Character.isLowerCase(x.toCharArray()[0]))
1389
x=Character.toUpperCase(x.charAt(0))+x.substring(1);
1390
IsotopeFactory ifa=IsotopeFactory.getInstance(r2dm.getHighlightedAtom().getBuilder());
1391
IIsotope iso=ifa.getMajorIsotope(x);
1392
formerSymbol=r2dm.getHighlightedAtom().getSymbol();
1394
r2dm.getHighlightedAtom().setSymbol(x);
1396
IAtomContainer container = ChemModelManipulator.getRelevantAtomContainer(chemModel, atomInRange);
1397
updateAtom(container, atomInRange);
1399
* PRESERVE THIS. This notifies the
1400
* the listener responsible for
1401
* undo and redo storage that it
1402
* should store this change of an atom symbol
1404
isUndoableChange = true;
1409
UndoableEdit edit = new ChangeAtomSymbolEdit(atomInRange, formerSymbol, x);
1410
undoRedoHandler.postEdit(edit);
1414
}catch(Exception ex){
1415
ex.printStackTrace();
1416
logger.debug(ex.getMessage()+" in SELECTELEMENT");
1421
private void increaseCharge() {
1422
IAtom atomInRange = r2dm.getHighlightedAtom();
1423
if (atomInRange != null)
1425
int formerCharge = atomInRange.getFormalCharge();
1426
atomInRange.setFormalCharge(atomInRange.getFormalCharge() + 1);
1429
IAtomContainer container = ChemModelManipulator.getRelevantAtomContainer(chemModel, atomInRange);
1430
updateAtom(container, atomInRange);
1432
UndoableEdit edit = new ChangeChargeEdit(atomInRange, formerCharge, atomInRange.getFormalCharge());
1433
undoRedoHandler.postEdit(edit);
1439
private void changeElement() {
1440
IAtom atomInRange = r2dm.getHighlightedAtom();
1441
if (atomInRange != null)
1443
String symbol = c2dm.getDrawElement();
1444
if (!(atomInRange.getSymbol().equals(symbol)))
1446
// only change symbol if needed
1447
String formerSymbol = atomInRange.getSymbol();
1448
atomInRange.setSymbol(symbol);
1449
// configure the atom, so that the atomic number matches the symbol
1452
IsotopeFactory.getInstance(atomInRange.getBuilder()).configure(atomInRange);
1453
} catch (Exception exception)
1455
logger.error("Error while configuring atom");
1456
logger.debug(exception);
1459
IAtomContainer container = ChemModelManipulator.getRelevantAtomContainer(chemModel, atomInRange);
1460
updateAtom(container, atomInRange);
1463
* PRESERVE THIS. This notifies the
1464
* the listener responsible for
1465
* undo and redo storage that it
1466
* should store this change of an atom symbol
1468
// isUndoableChange = true;
1473
UndoableEdit edit = new ChangeAtomSymbolEdit(atomInRange, formerSymbol, symbol);
1474
undoRedoHandler.postEdit(edit);
1479
int startX = r2dm.getPointerVectorStart().x;
1480
int startY = r2dm.getPointerVectorStart().y;
1481
IAtom newAtom1 = chemModel.getBuilder().newAtom(c2dm.getDrawElement(), new Point2d(startX, startY));
1482
r2dm.setRenderingCoordinate(newAtom1,new Point2d(startX, startY));
1483
IAtomContainer atomCon = ChemModelManipulator.createNewMolecule(chemModel);
1484
atomCon.addAtom(newAtom1);
1486
updateAtom(atomCon, newAtom1);
1487
chemModel.getMoleculeSet().addAtomContainer(atomCon);
1489
IAtomContainer undoRedoContainer= chemModel.getBuilder().newAtomContainer();
1490
undoRedoContainer.addAtom(newAtom1);
1491
UndoableEdit edit = new AddAtomsAndBondsEdit(chemModel, undoRedoContainer, "Add Atom",c2dm);
1492
undoRedoHandler.postEdit(edit);
1493
this.updateMoleculeCoordinates();
1499
private void changeSymbol() {
1500
IAtom atomInRange = r2dm.getHighlightedAtom();
1501
if (atomInRange != null)
1503
if (currentCommonElement.get(atomInRange) == null)
1505
currentCommonElement.put(atomInRange, new Integer(1));
1507
int oldCommonElement = ((Integer) currentCommonElement.get(atomInRange)).intValue();
1508
String symbol = (String) commonElements.elementAt(oldCommonElement);
1509
if (!(atomInRange.getSymbol().equals(symbol)))
1511
// only change symbol if needed
1512
String formerSymbol = atomInRange.getSymbol();
1513
atomInRange.setSymbol(symbol);
1514
// configure the atom, so that the atomic number matches the symbol
1517
IsotopeFactory.getInstance(atomInRange.getBuilder()).configure(atomInRange);
1518
} catch (Exception exception)
1520
logger.error("Error while configuring atom");
1521
logger.debug(exception);
1524
IAtomContainer container = ChemModelManipulator.getRelevantAtomContainer(chemModel, atomInRange);
1525
updateAtom(container, atomInRange);
1528
* PRESERVE THIS. This notifies the
1529
* the listener responsible for
1530
* undo and redo storage that it
1531
* should store this change of an atom symbol
1533
isUndoableChange = true;
1537
UndoableEdit edit = new ChangeAtomSymbolEdit(atomInRange, formerSymbol, symbol);
1538
undoRedoHandler.postEdit(edit);
1543
if (oldCommonElement == commonElements.size())
1545
oldCommonElement = 0;
1547
currentCommonElement.put(atomInRange, new Integer(oldCommonElement));
1552
* Makes a ring aromatic
1554
*@param ring The ring to be made aromatic
1556
private void makeRingAromatic(IRing ring)
1558
Iterator atoms = ring.atoms();
1559
while (atoms.hasNext()) {
1560
((IAtom)atoms.next()).setFlag(CDKConstants.ISAROMATIC, true);
1562
Iterator bonds = ring.bonds();
1563
while (bonds.hasNext()) {
1564
((IBond)bonds.next()).setFlag(CDKConstants.ISAROMATIC, true);
1570
* manages all actions that will be invoked when a mouse button is clicked
1572
*@param e MouseEvent object
1574
public void mouseClicked(MouseEvent e)
1576
// logger.debug("Mouse clicked");
1581
* manages all actions that will be invoked when a mouse enters a component
1583
*@param e MouseEvent object
1585
public void mouseEntered(MouseEvent e)
1587
// logger.debug("Mouse entered");
1592
* manages all actions that will be invoked when a mouse exits a component
1594
*@param e MouseEvent object
1596
public void mouseExited(MouseEvent e)
1598
// logger.debug("Mouse exited");
1603
* manages all actions that will be invoked when a key is released
1605
*@param e MouseEvent object
1607
public void keyReleased(KeyEvent e)
1609
logger.debug("Key released");
1614
* manages all actions that will be invoked when a key is typed
1616
*@param e MouseEvent object
1618
public void keyTyped(KeyEvent e)
1621
logger.debug("Key typed");
1622
if(r2dm.getHighlightedAtom()!=null){
1623
IsotopeFactory ifa=IsotopeFactory.getInstance(r2dm.getHighlightedAtom().getBuilder());
1624
IIsotope iso=ifa.getMajorIsotope(e.getKeyChar());
1626
r2dm.getHighlightedAtom().setSymbol(e.getKeyChar()+"");
1628
}catch(Exception ex){
1629
logger.debug("Exception "+ex.getMessage()+" in keyPressed in AbstractController");
1635
* manages all actions that will be invoked when a key is pressed
1637
*@param e MouseEvent object
1639
public void keyPressed(KeyEvent e)
1641
logger.debug("Key pressed");
1646
* Start of private methods
1649
* Updates an array of atoms with respect to its hydrogen count
1651
*@param container The AtomContainer to work on
1652
*@param atoms The Atoms to update
1654
private void updateAtoms(IAtomContainer container, java.util.Iterator atoms)
1656
while (atoms.hasNext())
1658
updateAtom(container, (IAtom)atoms.next());
1664
* Updates an atom with respect to its hydrogen count
1666
*@param container The AtomContainer to work on
1667
*@param atom The Atom to update
1669
public void updateAtom(IAtomContainer container, IAtom atom)
1671
if (c2dm.getAutoUpdateImplicitHydrogens())
1673
atom.setHydrogenCount(0);
1676
hydrogenAdder.addImplicitHydrogensToSatisfyValency(container, atom);
1677
} catch (Exception exception)
1679
logger.error(exception.getMessage());
1680
logger.debug(exception);
1687
* No idea what this does
1689
*@param angle Some kind of angle
1690
*@return Don't know what
1692
private double snapAngle(double angle)
1694
double div = (Math.PI / 180) * c2dm.getSnapAngle();
1695
return (Math.rint(angle / div)) * div;
1700
* Gets the chemObjectInRange attribute of the Controller2D object
1702
*@param X Current mouse x
1703
*@param Y Current mouse x
1704
*@return The chemObjectInRange value
1706
public IChemObject getChemObjectInRange(int X, int Y)
1708
IChemObject objectInRange = getAtomInRange(X, Y);
1709
if (objectInRange != null)
1711
// logger.debug("Returning nearest Atom: " + objectInRange);
1712
return objectInRange;
1714
objectInRange = getBondInRange(X, Y);
1715
if (objectInRange != null)
1717
// logger.debug("Returning nearest Bond: " + objectInRange);
1718
return objectInRange;
1720
objectInRange = getReactionInRange(X, Y);
1721
if (objectInRange != null)
1723
// logger.debug("Returning nearest Reaction: " + objectInRange);
1724
return objectInRange;
1727
* chemModel covers whole of editing window, and if nothing
1728
* more interesting is near, then them model is in range.
1730
// logger.debug("Returning nearest ChemModel: " + chemModel);
1736
* Returns an Atom if it is in a certain range of the given point. Used to
1737
* highlight an atom that is near the cursor. <p>
1739
* <b>Important: the coordinates must be given in world coordinates and not in
1740
* screen coordinates!
1742
*@param X The x world coordinate of the point
1743
*@param Y The y world coordinate of the point
1744
*@return An Atom if it is in a certain range of the given point
1746
private IAtom getAtomInRange(int X, int Y)
1748
return getAtomInRange(X,Y,null);
1753
* Returns an Atom if it is in a certain range of the given point. Used to
1754
* highlight an atom that is near the cursor. <p>
1756
* <b>Important: the coordinates must be given in world coordinates and not in
1757
* screen coordinates!
1759
*@param X The x world coordinate of the point
1760
*@param Y The y world coordinate of the point
1761
*@return An Atom if it is in a certain range of the given point
1763
private IAtom getAtomInRange(int X, int Y, IAtom ignore)
1765
double highlightRadius = r2dm.getHighlightRadius();
1766
IAtom closestAtom = GeometryTools.getClosestAtom(X, Y, chemModel, ignore, r2dm.getRenderingCoordinates());
1767
if (closestAtom != null)
1769
//logger.debug("getAtomInRange(): An atom is near");
1770
if (!(Math.sqrt(Math.pow(r2dm.getRenderingCoordinate(closestAtom).x - X, 2) +
1771
Math.pow(r2dm.getRenderingCoordinate(closestAtom).y - Y, 2)) < highlightRadius))
1776
// set the associated AtomContainer, for use by JCP's Molecule Properties action
1777
// COMMENTED OUT: causes cloning trouble
1778
/* closestAtom.setProperty(
1779
SimpleController2D.MATCHING_ATOMCONTAINER,
1780
ChemModelManipulator.getRelevantAtomContainer(chemModel, closestAtom)
1787
private IAtomContainer getBondInRangeTemporaryAtomContainer = null;
1790
* Returns a Bond if it is in a certain range of the given point. Used to
1791
* highlight a bond that is near the cursor. <p>
1793
* <b>Important: the coordinates must be given in world coordinates and not in
1794
* screen coordinates!
1796
*@param X The x world coordinate of the point
1797
*@param Y The y world coordinate of the point
1798
*@return An Atom if it is in a certain range of the given point
1800
private IBond getBondInRange(int X, int Y) {
1801
if (getBondInRangeTemporaryAtomContainer == null) {
1802
getBondInRangeTemporaryAtomContainer = chemModel.getBuilder().newAtomContainer();
1804
getBondInRangeTemporaryAtomContainer.removeAllElements();
1806
double highlightRadius = r2dm.getHighlightRadius();
1807
Iterator atomCons = ChemModelManipulator.getAllAtomContainers(chemModel).iterator();
1808
while (atomCons.hasNext()) {
1809
getBondInRangeTemporaryAtomContainer.add((IAtomContainer)atomCons.next());
1811
if (getBondInRangeTemporaryAtomContainer.getBondCount() != 0) {
1812
IBond closestBond = GeometryTools.getClosestBond(X, Y, getBondInRangeTemporaryAtomContainer,r2dm.getRenderingCoordinates());
1813
if (closestBond == null)
1817
// logger.debug("closestBond "+ closestBond);
1818
int[] coords = GeometryTools.distanceCalculator(
1819
GeometryTools.getBondCoordinates(closestBond, r2dm.getRenderingCoordinates()), highlightRadius);
1820
int[] xCoords = {coords[0], coords[2], coords[4], coords[6]};
1821
int[] yCoords = {coords[1], coords[3], coords[5], coords[7]};
1822
if ((new Polygon(xCoords, yCoords, 4)).contains(new Point(X, Y)))
1830
abstract IReaction getReactionInRange(int X, int Y);
1833
* Returns an AtomContainer that contains the atom or the the bond with its
1834
* two atoms that are highlighted at the moment.
1836
*@return An AtomContainer containig the highlighted atom\atoms\bond
1838
IAtomContainer getHighlighted()
1840
IAtomContainer highlighted = chemModel.getBuilder().newAtomContainer();
1841
IAtom highlightedAtom = r2dm.getHighlightedAtom();
1842
IBond highlightedBond = r2dm.getHighlightedBond();
1843
if (highlightedAtom != null)
1845
highlighted.addAtom(highlightedAtom);
1846
} else if (highlightedBond != null)
1848
highlighted.addBond(highlightedBond);
1849
for (int i = 0; i < highlightedBond.getAtomCount(); i++)
1851
highlighted.addAtom(highlightedBond.getAtom(i));
1854
logger.debug("sharedAtoms ", highlighted);
1860
* Constructs a new Ring of a certain size that contains all the atoms and
1861
* bonds of the given AtomContainer and is filled up with new Atoms and Bonds.
1863
*@param sharedAtoms The AtomContainer containing the Atoms and bonds for the
1865
*@param ringSize The size (number of Atoms) the Ring will have
1866
*@param symbol The element symbol the new atoms will have
1867
*@return The constructed Ring
1869
IRing createAttachRing(IAtomContainer sharedAtoms, int ringSize, String symbol)
1871
IRing newRing = sharedAtoms.getBuilder().newRing(ringSize);
1872
IAtom[] ringAtoms = new IAtom[ringSize];
1873
for (int i = 0; i < sharedAtoms.getAtomCount(); i++)
1875
ringAtoms[i] = sharedAtoms.getAtom(i);
1877
for (int i = sharedAtoms.getAtomCount(); i < ringSize; i++)
1879
ringAtoms[i] = sharedAtoms.getBuilder().newAtom(symbol);
1881
Iterator bonds = sharedAtoms.bonds();
1882
while (bonds.hasNext())
1884
newRing.addBond((IBond)bonds.next());
1886
for (int i = sharedAtoms.getBondCount(); i < ringSize - 1; i++)
1888
newRing.addBond(sharedAtoms.getBuilder().newBond(ringAtoms[i], ringAtoms[i + 1], 1));
1890
newRing.addBond(sharedAtoms.getBuilder().newBond(ringAtoms[ringSize - 1], ringAtoms[0], 1));
1891
newRing.setAtoms(ringAtoms);
1897
* Searches all the atoms attached to the Atoms in the given AtomContainer and
1898
* calculates the center point of them.
1900
*@param sharedAtoms The Atoms the attached partners are searched of
1901
*@return The Center Point of all the atoms found
1903
Point2d getConnectedAtomsCenter(IAtomContainer sharedAtoms)
1906
//IAtom[] conAtomsArray;
1907
IAtomContainer conAtoms = sharedAtoms.getBuilder().newAtomContainer();
1908
for (int i = 0; i < sharedAtoms.getAtomCount(); i++)
1910
currentAtom = sharedAtoms.getAtom(i);
1911
conAtoms.addAtom(currentAtom);
1912
IAtomContainer atomCon = ChemModelManipulator.getRelevantAtomContainer(chemModel, currentAtom);
1913
java.util.Iterator atoms = atomCon.getConnectedAtomsList(currentAtom).iterator();
1914
while (atoms.hasNext())
1916
conAtoms.addAtom((IAtom)atoms.next());
1919
return GeometryTools.get2DCenter(conAtoms,r2dm.getRenderingCoordinates());
1924
* Returns an AtomContainer with all the atoms and bonds that are inside a
1927
*@param polygon The given Polygon
1928
*@return AtomContainer with all atoms and bonds inside the polygon
1930
IAtomContainer getContainedAtoms(Polygon polygon)
1934
IAtomContainer selectedPart = chemModel.getBuilder().newAtomContainer();
1935
Iterator atomCons = ChemModelManipulator.getAllAtomContainers(chemModel).iterator();
1936
while (atomCons.hasNext()) {
1937
IAtomContainer atomCon = (IAtomContainer)atomCons.next();
1938
for (int i = 0; i < atomCon.getAtomCount(); i++)
1940
currentAtom = atomCon.getAtom(i);
1941
logger.debug("Atom: ", currentAtom);
1942
if (polygon.contains(new Point((int) ((Point2d)r2dm.getRenderingCoordinate(currentAtom)).x, (int) ((Point2d)r2dm.getRenderingCoordinate(currentAtom)).y)))
1944
selectedPart.addAtom(currentAtom);
1947
Iterator bonds = atomCon.bonds();
1948
while (bonds.hasNext())
1950
currentBond = (IBond)bonds.next();
1951
for (int j = 0; j < selectedPart.getAtomCount(); j++)
1953
currentAtom = selectedPart.getAtom(j);
1954
if (selectedPart.contains(currentBond.getConnectedAtom(currentAtom)))
1956
selectedPart.addBond(currentBond);
1962
return selectedPart;
1967
* This methods corrects for the zoom factor, and thus transforms screen
1968
* coordinates back into world coordinates. IMPORTANT: even coords are taken
1969
* as y coordinates, uneven as x coordinates.
1971
*@param coords the array of coordinates to be used
1972
*@return The worldCoordinates value
1974
public int[] getWorldCoordinates(int[] coords)
1976
int[] worldCoords = new int[coords.length];
1977
int coordCount = coords.length / 2;
1978
//logger.debug("coord.length: ", coords.length);
1979
int height = (int) (r2dm.getBackgroundDimension()).getHeight();
1980
for (int i = 0; i < coordCount; i++)
1982
worldCoords[i * 2] = (int) ((double) coords[i * 2] / r2dm.getZoomFactor());
1983
worldCoords[i * 2 + 1] = (int) ((double) (height - coords[i * 2 + 1]) / r2dm.getZoomFactor());
1984
if (logger.isDebugEnabled())
1986
//logger.debug("getWorldCoord: " + coords[i * 2] + " -> " + worldCoords[i * 2]);
1987
//logger.debug("getWorldCoord: " + coords[i * 2 + 1] + " -> " + worldCoords[i * 2 + 1]);
1995
* Adds a change listener to the list of listeners
1997
*@param listener The listener added to the list
2000
public void addCDKChangeListener(ICDKChangeListener listener)
2002
listeners.add(listener);
2007
* Removes a change listener from the list of listeners
2009
*@param listener The listener removed from the list
2011
public void removeCDKChangeListener(ICDKChangeListener listener)
2013
listeners.remove(listener);
2018
* Notifies registered listeners of certain changes that have occurred in this
2021
public void fireChange()
2023
EventObject event = new EventObject(this);
2024
for (int i = 0; i < listeners.size(); i++)
2026
((ICDKChangeListener) listeners.get(i)).stateChanged(event);
2031
// ------------ CHEMICAL OPERATIONS -------------- //
2034
* Highlight the nearest Atom or Bond. <p>
2036
* FIXME: this needs to be extended for other ChemObjects.
2038
*@param mouseX x coordinate in world coordinates (not screen coordinates)
2039
*@param mouseY y coordinate in world coordinates (not screen coordinates)
2041
void highlightNearestChemObject(int mouseX, int mouseY)
2043
IChemObject objectInRange = getChemObjectInRange(mouseX, mouseY);
2044
if (objectInRange instanceof IAtom)
2046
r2dm.setHighlightedAtom((IAtom) objectInRange);
2047
r2dm.setHighlightedBond(null);
2048
} else if (objectInRange instanceof IBond)
2050
r2dm.setHighlightedBond((IBond) objectInRange);
2051
r2dm.setHighlightedAtom(null);
2054
r2dm.setHighlightedBond(null);
2055
r2dm.setHighlightedAtom(null);
2061
* Create a new bond. Possibly connecting the end point to the nearest Atom.
2064
* All coordinates are world coordinates.
2066
*@param startX Start X coordinate of the new bond
2067
*@param startY Start Y coordinate of the new bond
2068
*@param endX End X coordinate of the new bond
2069
*@param endY End Y coordinate of the new bond
2071
void createNewBond(int startX, int startY, int endX, int endY)
2078
* Draws a proposed bond, i.e. shows the bond direction
2079
* during dragging of the mouse
2081
*@param startX Start X coordinate of the proposed bond
2082
*@param startY Start Y coordinate of the proposed bond
2083
*@param mouseX Current X mouse coordinate
2084
*@param mouseY Current Y mouse coordinate
2086
void drawProposedBond(int startX, int startY, int mouseX, int mouseY)
2088
logger.debug("Drawing proposed bond...");
2091
double pointerVectorLength = c2dm.getBondPointerLength();
2095
angle = GeometryTools.getAngle(startX - mouseX, startY - mouseY);
2096
if (c2dm.getSnapToGridAngle())
2098
angle = snapAngle(angle);
2100
atomInRange = getAtomInRange(mouseX, mouseY);
2101
if (atomInRange != null)
2103
endX = (int) ((Point2d)r2dm.getRenderingCoordinate(atomInRange)).x;
2104
endY = (int) ((Point2d)r2dm.getRenderingCoordinate(atomInRange)).y;
2107
endX = startX - (int) (Math.cos(angle) * pointerVectorLength);
2108
endY = startY - (int) (Math.sin(angle) * pointerVectorLength);
2110
logger.debug("End point: " + endX + ", " + endY);
2111
logger.debug("Start point: " + startX + ", " + startY);
2112
r2dm.setPointerVectorEnd(new Point(endX, endY));
2117
* Selects a rectangular area on the screen
2119
*@param startX Start x coordinate
2120
*@param startY Start y coordinate
2121
*@param mouseX Current x mouse position
2122
*@param mouseY Current y mouse position
2124
void selectRectangularArea(int startX, int startY, int mouseX, int mouseY)
2126
int[] xPoints = {startX, startX, mouseX, mouseX};
2127
int[] yPoints = {startY, mouseY, mouseY, startY};
2128
r2dm.setSelectRect(new Polygon(xPoints, yPoints, 4));
2133
* Move an Atom by the specified change in coordinates.
2135
*@param deltaX change in x direction
2136
*@param deltaY change in y direction
2138
void moveSelectedAtomsWith(int deltaX, int deltaY)
2140
IAtomContainer container = r2dm.getSelectedPart();
2141
if (container != null)
2143
// only move selected atoms if count > 0
2144
java.util.Iterator atoms = container.atoms();
2145
while (atoms.hasNext())
2147
IAtom atom = (IAtom)atoms.next();
2148
atom.setNotification(false);
2149
atom.getPoint2d().x += deltaX;
2150
atom.getPoint2d().y += deltaY;
2151
atom.setNotification(true);
2152
((Point2d)r2dm.getRenderingCoordinate(atom)).x+=deltaX;
2153
((Point2d)r2dm.getRenderingCoordinate(atom)).y+=deltaY;
2155
r2dm.getSelectedPart().notifyChanged();
2160
* This updates the coordinates in the point2ds of the atoms with the renderingcoordinates in r2dm. This should be called
2161
* after a change of the structure (e. g. adding atoms), since we cannot and do not need to preserve original coordinates after this.
2163
private void updateMoleculeCoordinates(){
2164
Iterator atomCons = ChemModelManipulator.getAllAtomContainers(chemModel).iterator();
2165
while (atomCons.hasNext()) {
2166
IAtomContainer atomCon = (IAtomContainer)atomCons.next();
2167
for (int i = 0; i < atomCon.getAtomCount(); i++)
2169
IAtom currentAtom = atomCon.getAtom(i);
2170
if(r2dm.getRenderingCoordinate(currentAtom)!=null){
2171
currentAtom.setPoint2d(new Point2d((Point2d)r2dm.getRenderingCoordinate(currentAtom)));
2179
* Selects the nearest item on the screen next to the
2180
* mouse pointer, if none is selected yet
2182
* @param mouseX The current X coordinate of the mouse
2183
* @param mouseY The current Y coordinate of the mouse
2185
void selectNearestChemObjectIfNoneSelected(int mouseX, int mouseY)
2187
IAtomContainer container = r2dm.getSelectedPart();
2188
if (container == null || (container.getAtomCount() == 0))
2190
// if no atoms are selected, then temporarily select nearest
2191
// to make sure to original state is reached again when the
2192
// mouse is released, the draggingSelected boolean is set
2193
logger.warn("No atoms selected: temporarily selecting nearest atom/bond");
2194
draggingSelected = false;
2195
IAtomContainer selected = chemModel.getBuilder().newAtomContainer();
2196
IAtom atomInRange = getAtomInRange(mouseX, mouseY);
2197
if (atomInRange != null)
2199
selected.addAtom(atomInRange);
2200
r2dm.setSelectedPart(selected);
2203
IBond bondInRange = getBondInRange(mouseX, mouseY);
2204
// because only atoms are dragged, select the atoms
2205
// in the bond, instead of the bond itself
2206
if (bondInRange != null)
2208
java.util.Iterator atoms = bondInRange.atoms();
2209
while (atoms.hasNext())
2211
selected.addAtom((IAtom)atoms.next());
2213
r2dm.setSelectedPart(selected);
2216
logger.debug("Selected: ", selected);
2223
* Centers an atom on a given background dimension
2225
* @param atom The Atom to be centered
2227
void centerAtom(IAtom atom, IChemModel chemModel)
2229
double smallestx=Integer.MAX_VALUE;
2230
double largestx=Integer.MIN_VALUE;
2231
double smallesty=Integer.MAX_VALUE;
2232
double largesty=Integer.MIN_VALUE;
2233
Iterator atomCons = ChemModelManipulator.getAllAtomContainers(chemModel).iterator();
2234
while (atomCons.hasNext()) {
2235
IAtomContainer atomCon = (IAtomContainer)atomCons.next();
2236
for(int i=0;i<atomCon.getAtomCount();i++){
2237
if(((Point2d)r2dm.getRenderingCoordinate(atomCon.getAtom(i))).x<smallestx)
2238
smallestx=((Point2d)r2dm.getRenderingCoordinate(atomCon.getAtom(i))).x;
2239
if(((Point2d)r2dm.getRenderingCoordinate(atomCon.getAtom(i))).y<smallesty)
2240
smallesty=((Point2d)r2dm.getRenderingCoordinate(atomCon.getAtom(i))).y;
2241
if(((Point2d)r2dm.getRenderingCoordinate(atomCon.getAtom(i))).x>largestx)
2242
largestx=((Point2d)r2dm.getRenderingCoordinate(atomCon.getAtom(i))).x;
2243
if(((Point2d)r2dm.getRenderingCoordinate(atomCon.getAtom(i))).y>largesty)
2244
largesty=((Point2d)r2dm.getRenderingCoordinate(atomCon.getAtom(i))).y;
2247
int xstretch=((int)(largestx-smallestx))+20;
2248
int ystretch=((int)(largesty-smallesty))+20;
2249
if(xstretch<r2dm.getBackgroundDimension().width)
2250
xstretch=r2dm.getBackgroundDimension().width;
2251
if(ystretch<r2dm.getBackgroundDimension().height)
2252
ystretch=r2dm.getBackgroundDimension().height;
2253
r2dm.setBackgroundDimension(new Dimension(xstretch,ystretch));
2258
if (((Point2d)r2dm.getRenderingCoordinate(atom)).x < 0 && shiftX > ((Point2d)r2dm.getRenderingCoordinate(atom)).x - 10)
2260
shiftX = ((Point2d)r2dm.getRenderingCoordinate(atom)).x - 10;
2262
if (((Point2d)r2dm.getRenderingCoordinate(atom)).x > r2dm.getBackgroundDimension().getWidth() && shiftX < ((Point2d)r2dm.getRenderingCoordinate(atom)).x - r2dm.getBackgroundDimension().getWidth() + 10)
2264
shiftX = ((Point2d)r2dm.getRenderingCoordinate(atom)).x - r2dm.getBackgroundDimension().getWidth() + 10;
2266
if (((Point2d)r2dm.getRenderingCoordinate(atom)).y < 0 && shiftY > ((Point2d)r2dm.getRenderingCoordinate(atom)).y - 10)
2268
shiftY = ((Point2d)r2dm.getRenderingCoordinate(atom)).y - 10;
2270
if (((Point2d)r2dm.getRenderingCoordinate(atom)).y > r2dm.getBackgroundDimension().getHeight() && shiftY < ((Point2d)r2dm.getRenderingCoordinate(atom)).y - r2dm.getBackgroundDimension().getHeight() + 10)
2272
shiftY = ((Point2d)r2dm.getRenderingCoordinate(atom)).y - r2dm.getBackgroundDimension().getHeight() + 10;
2277
void handleMapping(boolean wasDragged, Renderer2DModel r2dm)
2279
logger.debug("Should make new mapping...");
2282
int endX = r2dm.getPointerVectorEnd().x;
2283
int endY = r2dm.getPointerVectorEnd().y;
2284
IAtom mappedToAtom = getAtomInRange(endX, endY);
2285
if (mappedToAtom != null)
2287
int startX = r2dm.getPointerVectorStart().x;
2288
int startY = r2dm.getPointerVectorStart().y;
2289
IAtom mappedFromAtom = getAtomInRange(startX, startY);
2290
if (mappedFromAtom != null)
2292
IMapping mapping = mappedFromAtom.getBuilder().newMapping(mappedFromAtom, mappedToAtom);
2293
logger.debug("Created mapping: ", mapping);
2294
logger.debug(" between ", mappedFromAtom);
2295
logger.debug(" and ", mappedToAtom);
2296
// ok, now figure out if they are in one reaction
2297
IReaction reaction1 = ChemModelManipulator.getRelevantReaction(chemModel, mappedFromAtom);
2298
IReaction reaction2 = ChemModelManipulator.getRelevantReaction(chemModel, mappedToAtom);
2299
if (reaction1 != null && reaction2 != null && reaction1 == reaction2)
2301
logger.debug("Adding mapping to reaction: ", reaction1.getID());
2302
((IReaction)reaction1).addMapping(mapping);
2305
logger.warn("Reactions do not match, or one or both are reactions are null");
2309
logger.debug("Dragging did not start in atom...");
2313
logger.debug("Dragging did not end in atom...");
2317
logger.debug("Not dragged in mapping mode");
2321
abstract IReaction getRelevantReaction(IChemModel model, IAtom atom);
2324
public IChemModel getChemModel() {
2328
public void setChemModel(IChemModel chemModel) {
2329
this.chemModel = chemModel;
2332
public void setUndoRedoHandler(IUndoRedoHandler undoRedoHandler) {
2333
this.undoRedoHandler = undoRedoHandler;
2336
public HashMap getFuncgroupsmap() {
2337
return funcgroupsmap;
2340
public void setFuncgroupsmap(HashMap funcgroupsmap) {
2341
this.funcgroupsmap = funcgroupsmap;
2344
static class EnterElementOrGroupDialog extends JDialog implements ActionListener {
2346
private static EnterElementOrGroupDialog dialog;
2347
private static String value = "";
2348
private JComboBox list;
2351
* Set up and show the dialog. The first Component argument
2352
* determines which frame the dialog depends on; it should be
2353
* a component in the dialog's controlling frame. The second
2354
* Component argument should be null if you want the dialog
2355
* to come up with its left corner in the center of the screen;
2356
* otherwise, it should be the component on top of which the
2357
* dialog should appear.
2359
public static String showDialog(Component frameComp,
2360
Component locationComp,
2363
String[] possibleValues,
2364
String initialValue,
2366
Frame frame = JOptionPane.getFrameForComponent(frameComp);
2367
dialog = new EnterElementOrGroupDialog(frame,
2374
dialog.setVisible(true);
2378
private EnterElementOrGroupDialog(Frame frame,
2379
Component locationComp,
2383
String initialValue,
2385
super(frame, title, true);
2387
//Create and initialize the buttons.
2388
JButton cancelButton = new JButton("Cancel");
2389
cancelButton.addActionListener(this);
2391
final JButton setButton = new JButton("Ok");
2392
setButton.setActionCommand("Set");
2393
setButton.addActionListener(this);
2394
getRootPane().setDefaultButton(setButton);
2396
//main part of the dialog
2397
list = new JComboBox(data);
2398
list.setEditable(true);
2399
JPanel listPane = new JPanel();
2400
listPane.setLayout(new BoxLayout(listPane, BoxLayout.PAGE_AXIS));
2401
JLabel label = new JLabel(labelText);
2402
label.setLabelFor(list);
2403
listPane.add(label);
2404
listPane.add(Box.createRigidArea(new Dimension(0,5)));
2406
listPane.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
2408
//Lay out the buttons from left to right.
2409
JPanel buttonPane = new JPanel();
2410
buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));
2411
buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
2412
buttonPane.add(Box.createHorizontalGlue());
2413
buttonPane.add(cancelButton);
2414
buttonPane.add(Box.createRigidArea(new Dimension(10, 0)));
2415
buttonPane.add(setButton);
2417
//Put everything together, using the content pane's BorderLayout.
2418
Container contentPane = getContentPane();
2419
contentPane.add(listPane, BorderLayout.CENTER);
2420
contentPane.add(buttonPane, BorderLayout.PAGE_END);
2422
//Initialize values.
2424
setLocationRelativeTo(locationComp);
2427
//Handle clicks on the Set and Cancel buttons.
2428
public void actionPerformed(ActionEvent e) {
2429
if ("Set".equals(e.getActionCommand())) {
2430
EnterElementOrGroupDialog.value = (String)(list.getSelectedItem());
2432
if(e.getActionCommand().equals("Cancel")){
2433
EnterElementOrGroupDialog.value="";
2435
EnterElementOrGroupDialog.dialog.setVisible(false);