com.actelion.research.gui.JDrawArea Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of openchemlib Show documentation
Show all versions of openchemlib Show documentation
Open Source Chemistry Library
/*
* Copyright (c) 1997 - 2016
* Actelion Pharmaceuticals Ltd.
* Gewerbestrasse 16
* CH-4123 Allschwil, Switzerland
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package com.actelion.research.gui;
import com.actelion.research.chem.*;
import com.actelion.research.chem.reaction.IReactionMapper;
import com.actelion.research.chem.reaction.Reaction;
import com.actelion.research.chem.reaction.ReactionArrow;
import com.actelion.research.gui.clipboard.IClipboardHandler;
import com.actelion.research.gui.dnd.MoleculeDropAdapter;
import com.actelion.research.gui.hidpi.HiDPIHelper;
import com.actelion.research.gui.hidpi.ScaledEditorKit;
import com.actelion.research.util.ColorHelper;
import com.actelion.research.util.CursorHelper;
import javax.swing.*;
import javax.swing.text.html.HTMLEditorKit;
import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.event.*;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
public class JDrawArea extends JPanel
implements ActionListener, KeyListener, MouseListener, MouseMotionListener
{
static final long serialVersionUID = 0x20061019;
public static final int MODE_MULTIPLE_FRAGMENTS = 1;
public static final int MODE_MARKUSH_STRUCTURE = 2;
public static final int MODE_REACTION = 4;
public static final int MODE_DRAWING_OBJECTS = 8;
private static final int MAX_CONNATOMS = 8;
private static final int MIN_BOND_LENGTH_SQUARE = 100;
private static final int KEY_IS_ATOM_LABEL = 1;
private static final int KEY_IS_SUBSTITUENT = 2;
private static final int KEY_IS_VALID_START = 3;
private static final int KEY_IS_INVALID = 4;
private static final float FRAGMENT_MAX_CLICK_DISTANCE = 24.0f;
private static final float FRAGMENT_GROUPING_DISTANCE = 1.4f; // in average bond lengths
private static final float FRAGMENT_CLEANUP_DISTANCE = 1.5f; // in average bond lengths
private static final float DEFAULT_ARROW_LENGTH = 0.08f; // relative to panel width
private static final boolean USE_GRAPHICS_2D = true;
protected static final int UPDATE_NONE = 0;
protected static final int UPDATE_REDRAW = 1;
// redraw molecules and drawing objects with their current coordinates
protected static final int UPDATE_CHECK_VIEW = 2;
// redraw with on-the-fly coordinate transformation only if current coords do not fit within view area
// (new coords are generated for one draw() only; the original coords are not changed)
protected static final int UPDATE_CHECK_COORDS = 3;
// redraw with in-place coordinate transformation only if current coords do not fit within view area
// (the original atom and object coords are replaced by the new ones)
protected static final int UPDATE_SCALE_COORDS = 4;
// redraw with in-place coordinate transformation; molecules and objects are scaled to fill
// the view unless the maximum average bond length reaches the optimum.
protected static final int UPDATE_SCALE_COORDS_USE_FRAGMENTS = 5;
// as UPDATE_SCALE_COORDS but uses fragments from mFragment rather than creating a
// fresh mFragment list from mMol. Used for setting a reaction or fragment list from outside.
protected static final int UPDATE_INVENT_COORDS = 6;
// redraw with in-place coordinate transformation; first all molecules' coordinates
// are generated from scratch, then molecules and objects are scaled to fill
// the view unless the maximum average bond length reaches the optimum.
private static final int ALLOWED_DROP_ACTIONS = DnDConstants.ACTION_COPY_OR_MOVE;
private static final int cRequestNone = 0;
private static final int cRequestNewBond = 1;
private static final int cRequestNewChain = 2;
private static final int cRequestMoveSingle = 3;
private static final int cRequestMoveSelected = 4;
private static final int cRequestLassoSelect = 5;
private static final int cRequestSelectRect = 6;
private static final int cRequestZoomAndRotate = 7;
private static final int cRequestMapAtoms = 8;
private static final int cRequestCopySelected = 9;
private static final int cRequestMoveObject = 10;
private static final int cRequestCopyObject = 11;
public static final int FAKE_ATOM_NO = 100;
private Dimension mSize;
private int mMode, mChainAtoms, mCurrentTool, mOtherAtom, mOtherMass, mOtherValence, mOtherRadical,
mCurrentHiliteAtom, mCurrentHiliteBond, mPendingRequest,
mCurrentCursor, mReactantCount, mUpdateMode, mDisplayMode, mAtom1, mAtom2;
private int[] mChainAtom, mFragmentNo, mHiliteBondSet;
private double mX1, mY1, mX2, mY2;
private double[] mX, mY, mChainAtomX, mChainAtomY;
private boolean mShiftIsDown, mAltIsDown, mControlIsDown, mMouseIsDown, mIsAddingToSelection, mAtomColorSupported;
private boolean[] mIsSelectedAtom, mIsSelectedObject;
private String[] mAtomText;
private ExtendedDepictor mDepictor;
private StereoMolecule mMol; // molecule being modified directly by the drawing editor
private Molecule mUndoMol; // molecule in undo buffer
private StereoMolecule[] mFragment; // in case of MODE_MULTIPLE_FRAGMENTS contains valid stereo fragments
// for internal and external read-only-access (reconstructed at any change)
private DrawingObjectList mDrawingObjectList, mUndoDrawingObjectList;
private AbstractDrawingObject mCurrentHiliteObject;
private Polygon mLassoRegion;
private ArrayList mListeners;
private IClipboardHandler mClipboardHandler;
private JDialog mHelpDialog;
private StringBuilder mAtomKeyStrokeBuffer;
private IReactionMapper mMapper; //= new ReactionMapper();
/**
* @param mol an empty or valid stereo molecule
* @param mode 0 or a meaningful combination of the mode flags, e.g. MODE_REACTION | MODE_DRAWING_OBJECTS
*/
public JDrawArea(StereoMolecule mol, int mode)
{
mMol = mol;
mMode = mode;
setFocusable(true);
addKeyListener(this);
addMouseListener(this);
addMouseMotionListener(this);
mListeners = new ArrayList();
mCurrentTool = JDrawToolbar.cToolStdBond;
mCurrentHiliteAtom = -1;
mCurrentHiliteBond = -1;
mCurrentHiliteObject = null;
mAtom1 = -1;
mOtherAtom = 6;
mOtherMass = 0;
mOtherValence = -1;
mOtherRadical = 0;
mPendingRequest = cRequestNone;
mCurrentCursor = CursorHelper.cPointerCursor;
mAtomKeyStrokeBuffer = new StringBuilder();
if ((mMode & (MODE_REACTION | MODE_MARKUSH_STRUCTURE)) != 0) {
mMode |= (MODE_MULTIPLE_FRAGMENTS);
}
if ((mMode & (MODE_DRAWING_OBJECTS | MODE_REACTION)) != 0) {
mDrawingObjectList = new DrawingObjectList();
}
mUpdateMode = UPDATE_SCALE_COORDS;
initializeDragAndDrop(ALLOWED_DROP_ACTIONS);
/* dumpBytesOfGif("/chainCursor.gif");
dumpBytesOfGif("/deleteCursor.gif");
dumpBytesOfGif("/handCursor.gif");
dumpBytesOfGif("/handPlusCursor.gif");
dumpBytesOfGif("/fistCursor.gif");
dumpBytesOfGif("/lassoCursor.gif");
dumpBytesOfGif("/lassoPlusCursor.gif");
dumpBytesOfGif("/rectCursor.gif");
dumpBytesOfGif("/rectPlusCursor.gif"); */
}
/**
* Call this after initialization to get clipboard support
*
* @param h
*/
public void setClipboardHandler(IClipboardHandler h)
{
mClipboardHandler = h;
}
private void update(int mode)
{
mUpdateMode = Math.max(mUpdateMode, mode);
repaint();
}
public void setMapper(IReactionMapper mapper)
{
this.mMapper = mapper;
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
((Graphics2D) g).setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
Dimension theSize = getSize();
if (mSize == null || mSize.width != theSize.width || mSize.height != theSize.height) {
mSize = theSize;
if (mUpdateMode < UPDATE_CHECK_COORDS) {
mUpdateMode = UPDATE_CHECK_COORDS;
}
}
Color background = UIManager.getColor("TextArea.background");
Color foreground = UIManager.getColor("TextArea.foreground");
g.setColor(background);
g.fillRect(0, 0, theSize.width, theSize.height);
if ((mMode & MODE_REACTION) != 0 && mDrawingObjectList.size() == 0) {
float mx = 0.5f * (float) theSize.width;
float my = 0.5f * (float) theSize.height;
float dx = 0.5f * DEFAULT_ARROW_LENGTH * (float) theSize.width;
ReactionArrow arrow = new ReactionArrow();
arrow.setCoordinates(mx - dx, my, mx + dx, my);
arrow.setDeletable(false);
mDrawingObjectList.add(arrow);
}
boolean isScaledView = false;
if (mUpdateMode != UPDATE_NONE) {
if ((mMode & MODE_MULTIPLE_FRAGMENTS) != 0
&& mUpdateMode != UPDATE_SCALE_COORDS_USE_FRAGMENTS) {
analyzeFragmentMembership();
}
mDepictor = ((mMode & MODE_REACTION) != 0) ?
new ExtendedDepictor(new Reaction(mFragment, mReactantCount), mDrawingObjectList, false, USE_GRAPHICS_2D)
: ((mMode & MODE_MARKUSH_STRUCTURE) != 0) ?
new ExtendedDepictor(mFragment, mReactantCount, mDrawingObjectList, USE_GRAPHICS_2D)
: ((mMode & MODE_MULTIPLE_FRAGMENTS) != 0) ?
new ExtendedDepictor(mFragment, mDrawingObjectList, USE_GRAPHICS_2D)
: new ExtendedDepictor(mMol, mDrawingObjectList, USE_GRAPHICS_2D);
mDepictor.setForegroundColor(foreground, background);
mDepictor.setFragmentNoColor(((mMode & MODE_MULTIPLE_FRAGMENTS) == 0) ? null
: LookAndFeelHelper.isDarkLookAndFeel() ? ColorHelper.brighter(background, 0.85f)
: ColorHelper.darker(background, 0.85f));
mDepictor.setDisplayMode(mDisplayMode
| AbstractDepictor.cDModeHiliteAllQueryFeatures
| ((mCurrentTool == JDrawToolbar.cToolMapper) ?
AbstractDepictor.cDModeShowMapping
| AbstractDepictor.cDModeSuppressCIPParity : 0));
if ((mMode & (MODE_REACTION | MODE_MARKUSH_STRUCTURE | MODE_MULTIPLE_FRAGMENTS)) == 0) {
mDepictor.getMoleculeDepictor(0).setAtomText(mAtomText);
}
switch (mUpdateMode) {
case UPDATE_INVENT_COORDS:
case UPDATE_SCALE_COORDS:
case UPDATE_SCALE_COORDS_USE_FRAGMENTS:
cleanupCoordinates(g, mDepictor);
break;
case UPDATE_CHECK_COORDS:
DepictorTransformation t1 = mDepictor.updateCoords(g, new Rectangle2D.Double(0, 0, theSize.width, theSize.height), 0);
if (t1 != null && (mMode & MODE_MULTIPLE_FRAGMENTS) != 0) {
// in fragment mode depictor transforms mFragment[] rather than mMol
t1.applyTo(mMol);
}
break;
case UPDATE_CHECK_VIEW:
DepictorTransformation t2 = mDepictor.validateView(g, new Rectangle2D.Double(0, 0, theSize.width, theSize.height), 0);
isScaledView = (t2 != null && !t2.isVoidTransformation());
break;
}
mUpdateMode = UPDATE_NONE;
}
if (mDepictor != null) {
mDepictor.paintFragmentNumbers(g);
}
// don't hilite anything when the view is scaled and object coords don't reflect screen coords
if (!isScaledView) {
drawHiliting(g);
}
if (mDepictor != null) {
mDepictor.paintStructures(g);
mDepictor.paintDrawingObjects(g);
}
if (mCurrentHiliteAtom != -1 && mAtomKeyStrokeBuffer.length() != 0) {
int x = (int) mMol.getAtomX(mCurrentHiliteAtom);
int y = (int) mMol.getAtomY(mCurrentHiliteAtom);
String s = mAtomKeyStrokeBuffer.toString();
int validity = getAtomKeyStrokeValidity(s);
g.setColor((validity == KEY_IS_ATOM_LABEL) ? foreground
: (validity == KEY_IS_SUBSTITUENT) ? Color.BLUE
: (validity == KEY_IS_VALID_START) ? Color.GRAY : Color.RED);
if (validity == KEY_IS_INVALID)
s = s + "";
g.setFont(g.getFont().deriveFont(0, 24));
g.drawString(s, x, y);
}
g.setColor(foreground);
switch (mPendingRequest) {
case cRequestNewBond:
int x1, y1, x2, y2, xdiff, ydiff;
x1 = (int) mX1;
y1 = (int) mY1;
if (mCurrentHiliteAtom == -1 || mCurrentHiliteAtom == mAtom1) {
x2 = (int) mX2;
y2 = (int) mY2;
} else {
x2 = (int) mMol.getAtomX(mCurrentHiliteAtom);
y2 = (int) mMol.getAtomY(mCurrentHiliteAtom);
}
switch (mCurrentTool) {
case JDrawToolbar.cToolStdBond:
g.drawLine(x1, y1, x2, y2);
break;
case JDrawToolbar.cToolUpBond:
int[] x = new int[3];
int[] y = new int[3];
xdiff = (y1 - y2) / 9;
ydiff = (x2 - x1) / 9;
x[0] = x1;
y[0] = y1;
x[1] = x2 + xdiff;
y[1] = y2 + ydiff;
x[2] = x2 - xdiff;
y[2] = y2 - ydiff;
g.fillPolygon(x, y, 3);
break;
case JDrawToolbar.cToolDownBond:
int xx1, xx2, yy1, yy2;
xdiff = x2 - x1;
ydiff = y2 - y1;
for (int i = 2; i < 17; i += 2) {
xx1 = x1 + i * xdiff / 17 - i * ydiff / 128;
yy1 = y1 + i * ydiff / 17 + i * xdiff / 128;
xx2 = x1 + i * xdiff / 17 + i * ydiff / 128;
yy2 = y1 + i * ydiff / 17 - i * xdiff / 128;
g.drawLine(xx1, yy1, xx2, yy2);
}
break;
}
break;
case cRequestNewChain:
if (mChainAtoms > 0) {
g.drawLine((int) mX1, (int) mY1, (int) mChainAtomX[0], (int) mChainAtomY[0]);
}
if (mChainAtoms > 1) {
for (int i = 1; i < mChainAtoms; i++) {
g.drawLine((int) mChainAtomX[i - 1], (int) mChainAtomY[i - 1],
(int) mChainAtomX[i], (int) mChainAtomY[i]);
}
}
break;
case cRequestLassoSelect:
g.setColor(lassoColor());
g.drawPolygon(mLassoRegion);
g.setColor(foreground);
break;
case cRequestSelectRect:
int x = (mX1 < mX2) ? (int) mX1 : (int) mX2;
int y = (mY1 < mY2) ? (int) mY1 : (int) mY2;
int w = (int) Math.abs(mX2 - mX1);
int h = (int) Math.abs(mY2 - mY1);
g.setColor(lassoColor());
g.drawRect(x, y, w, h);
g.setColor(foreground);
break;
case cRequestMapAtoms:
x1 = (int) mX1;
y1 = (int) mY1;
if (mCurrentHiliteAtom == -1 || mCurrentHiliteAtom == mAtom1) {
x2 = (int) mX2;
y2 = (int) mY2;
} else {
x2 = (int) mMol.getAtomX(mCurrentHiliteAtom);
y2 = (int) mMol.getAtomY(mCurrentHiliteAtom);
}
g.setColor(mapToolColor());
g.drawLine(x1, y1, x2, y2);
g.setColor(foreground);
break;
}
}
private Color lassoColor() {
Color selectionColor = UIManager.getColor("TextArea.selectionBackground");
return ColorHelper.createColor(selectionColor, LookAndFeelHelper.isDarkLookAndFeel() ? 0.65f : 0.35f);
}
private Color selectionColor() {
return UIManager.getColor("TextArea.selectionBackground");
}
private Color mapToolColor() {
Color background = UIManager.getColor("TextArea.background");
return ColorHelper.getContrastColor(new Color(128, 0, 0), background);
}
private Color chainHiliteColor() {
Color background = UIManager.getColor("TextArea.background");
Color selectionColor = UIManager.getColor("TextArea.selectionBackground");
return ColorHelper.intermediateColor(selectionColor, background, 0.5f);
}
private void drawHiliting(Graphics g)
{
if (mHiliteBondSet != null) {
g.setColor(chainHiliteColor());
for (int i = 0; i < mHiliteBondSet.length; i++) {
hiliteBond(g, mHiliteBondSet[i]);
}
}
if (mCurrentHiliteAtom != -1) {
g.setColor(selectionColor());
hiliteAtom(g, mCurrentHiliteAtom);
if (mCurrentTool == JDrawToolbar.cToolMapper) {
int mapNo = mMol.getAtomMapNo(mCurrentHiliteAtom);
if (mapNo != 0) {
for (int atom = 0; atom < mMol.getAtoms(); atom++) {
if (atom != mCurrentHiliteAtom
&& mMol.getAtomMapNo(atom) == mapNo) {
hiliteAtom(g, atom);
}
}
}
}
}
if (mCurrentHiliteBond != -1) {
g.setColor(selectionColor());
hiliteBond(g, mCurrentHiliteBond);
}
if (mCurrentHiliteObject != null) {
mCurrentHiliteObject.hilite(g);
}
}
private void hiliteAtom(Graphics g, int atom)
{
int radius = (int) (0.32f * mMol.getAverageBondLength());
int x = (int) mMol.getAtomX(atom);
int y = (int) mMol.getAtomY(atom);
g.fillOval(x - radius, y - radius, 2 * radius, 2 * radius);
}
private void hiliteBond(Graphics g, int bond)
{
int width = (int) (0.32f * mMol.getAverageBondLength());
int x1 = (int) mMol.getAtomX(mMol.getBondAtom(0, bond));
int y1 = (int) mMol.getAtomY(mMol.getBondAtom(0, bond));
int x2 = (int) mMol.getAtomX(mMol.getBondAtom(1, bond));
int y2 = (int) mMol.getAtomY(mMol.getBondAtom(1, bond));
Stroke oldStroke = ((Graphics2D) g).getStroke();
((Graphics2D) g).setStroke(new BasicStroke((float) width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
((Graphics2D) g).drawLine(x1, y1, x2, y2);
((Graphics2D) g).setStroke(oldStroke);
}
public void addDrawAreaListener(DrawAreaListener l)
{
mListeners.add(l);
}
protected void buttonPressed(int button)
{
switch (button) {
case JDrawToolbar.cButtonClear:
if (mDrawingObjectList != null) {
mDrawingObjectList.clear();
update(UPDATE_REDRAW);
}
storeState();
boolean isFragment = mMol.isFragment();
mMol.deleteMolecule();
mMol.setFragment(isFragment);
if (mUndoMol.getAllAtoms() != 0) {
fireMoleculeChanged();
}
update(UPDATE_REDRAW);
return;
case JDrawToolbar.cButtonCleanStructure:
storeState();
fireMoleculeChanged();
update(UPDATE_INVENT_COORDS);
return;
case JDrawToolbar.cButtonUndo:
restoreState();
fireMoleculeChanged();
update(UPDATE_CHECK_VIEW);
return;
}
}
public void toolChanged(int newTool)
{
if (mCurrentTool != newTool) {
setOtherAtom(-1, 0, -1, 0);
if (mCurrentTool == JDrawToolbar.cToolMapper
|| newTool == JDrawToolbar.cToolMapper) {
update(UPDATE_REDRAW);
}
mCurrentTool = newTool;
}
}
private void setOtherAtom(int atomicNo, int mass, int valence, int radical)
{
mOtherAtom = atomicNo;
mOtherMass = mass;
mOtherValence = valence;
mOtherRadical = radical;
}
public void actionPerformed(ActionEvent e)
{
String command = e.getActionCommand();
if (command.equals("Copy")) {
copy();
} else if (command.equals("Paste")) {
paste();
} else if (command.startsWith("atomColor")) {
int index = command.indexOf(':');
int atom = Integer.parseInt(command.substring(9, index));
int color = Integer.parseInt(command.substring(index + 1));
if (mMol.isSelectedAtom(atom)) {
for (int i = 0; i < mMol.getAtoms(); i++) {
if (mMol.isSelectedAtom(i)) {
mMol.setAtomColor(i, color);
}
}
} else {
mMol.setAtomColor(atom, color);
}
}
}
private void copy()
{
boolean isReaction = ((mMode & MODE_REACTION) != 0);
boolean selectionFound = false;
boolean isBothSideSelection = false;
boolean isOnProductSide = false;
ReactionArrow arrow = null;
for (int atom = 0; atom < mMol.getAllAtoms(); atom++) {
if (mMol.isSelectedAtom(atom)) {
if (!selectionFound) {
selectionFound = true;
if (!isReaction) {
break;
}
arrow = (ReactionArrow) mDrawingObjectList.get(0);
isOnProductSide = arrow.isOnProductSide(mMol.getAtomX(atom), mMol.getAtomY(atom));
} else {
if (isOnProductSide != arrow.isOnProductSide(mMol.getAtomX(atom), mMol.getAtomY(atom))) {
isBothSideSelection = true;
break;
}
}
}
}
if (isReaction) {
if (isBothSideSelection) {
copyReaction(true);
} else if (selectionFound) {
copyMolecule(true);
} else {
copyReaction(false);
}
} else {
copyMolecule(selectionFound);
}
}
private boolean copyReaction(boolean selectionOnly)
{
Reaction rx = selectionOnly ? getSelectedReaction() : getReaction();
if (rx != null && mClipboardHandler != null) {
return mClipboardHandler.copyReaction(rx);
}
return false;
}
private Reaction getSelectedReaction()
{
Reaction rxn = new Reaction();
for (int i = 0; i < mFragment.length; i++) {
StereoMolecule selectedMol = getSelectedCopy(mFragment[i]);
if (selectedMol != null) {
if (i < mReactantCount) {
rxn.addReactant(selectedMol);
} else {
rxn.addProduct(selectedMol);
}
}
}
return rxn;
}
private StereoMolecule getSelectedCopy(StereoMolecule sourceMol)
{
int atomCount = 0;
for (int atom = 0; atom < sourceMol.getAllAtoms(); atom++) {
if (sourceMol.isSelectedAtom(atom)) {
atomCount++;
}
}
if (atomCount == 0) {
return null;
}
int bondCount = 0;
for (int bond = 0; bond < sourceMol.getAllBonds(); bond++) {
if (sourceMol.isSelectedBond(bond)) {
bondCount++;
}
}
boolean[] includeAtom = new boolean[sourceMol.getAllAtoms()];
for (int atom = 0; atom < sourceMol.getAllAtoms(); atom++) {
includeAtom[atom] = sourceMol.isSelectedAtom(atom);
}
StereoMolecule destMol = new StereoMolecule(atomCount, bondCount);
sourceMol.copyMoleculeByAtoms(destMol, includeAtom, false, null);
return destMol;
}
private boolean copyMolecule(boolean selectionOnly)
{
if (mMol.getAllAtoms() != 0 && mClipboardHandler != null) {
return mClipboardHandler.copyMolecule(selectionOnly ? getSelectedCopy(mMol) : mMol);
}
return false;
}
private void paste()
{
if ((mMode & MODE_REACTION) != 0) {
if (pasteReaction()) {
return;
}
}
pasteMolecule();
}
private boolean pasteReaction()
{
boolean ret = false;
if (mClipboardHandler != null) {
Reaction rxn = mClipboardHandler.pasteReaction();
if (rxn != null) {
for (int i = 0; i < rxn.getMolecules(); i++) {
rxn.getMolecule(i).setFragment(mMol.isFragment());
}
setReaction(rxn);
ret = true;
}
}
return ret;
}
private boolean pasteMolecule()
{
boolean ret = false;
if (mClipboardHandler != null) {
StereoMolecule mol = mClipboardHandler.pasteMolecule();
if (mol != null && mol.getAllAtoms() != 0) {
if (mMol.getAllAtoms() == 0) {
boolean isFragment = mMol.isFragment();
mol.copyMolecule(mMol);
mMol.setFragment(isFragment);
moleculeChanged(true);
} else {
int avbl = (int) mMol.getAverageBondLength();
Depictor d = new Depictor(mol);
d.updateCoords(this.getGraphics(), new Rectangle2D.Double(0, 0,
this.getWidth(),
this.getHeight()),
AbstractDepictor.cModeInflateToMaxAVBL + avbl
);
int originalAtoms = mMol.getAllAtoms();
mMol.addMolecule(mol);
for (int atom = 0; atom < mMol.getAllAtoms(); atom++) {
mMol.setAtomSelection(atom, atom >= originalAtoms);
}
moleculeChanged(true);
}
}
ret = true;
}
return ret;
}
// end changes CxR
public void mousePressed(MouseEvent e)
{
if (mCurrentHiliteAtom != -1 && mAtomKeyStrokeBuffer.length() != 0)
expandAtomKeyStrokes(mAtomKeyStrokeBuffer.toString());
mAtomKeyStrokeBuffer.setLength(0);
if (handlePopupTrigger(e)) {
return;
}
if ((e.getModifiers() & InputEvent.BUTTON1_MASK) != 0) {
if (e.getClickCount() == 2) {
return;
}
mMouseIsDown = true;
updateCursor();
mousePressedButton1(e);
}
}
public void mouseReleased(MouseEvent e)
{
if (handlePopupTrigger(e)) {
return;
}
if ((e.getModifiers() & InputEvent.BUTTON1_MASK) != 0) {
if (e.getClickCount() == 2) {
handleDoubleClick(e);
return;
}
mMouseIsDown = false;
updateCursor();
mouseReleasedButton1(e);
}
}
public void mouseEntered(MouseEvent e)
{
requestFocus();
updateCursor();
}
public void mouseExited(MouseEvent e)
{
}
public void mouseClicked(MouseEvent e)
{
}
public void mouseDragged(MouseEvent e)
{
mMouseIsDown = true;
mX2 = e.getX();
mY2 = e.getY();
boolean repaintNeeded = trackHiliting(mX2, mY2, true);
switch (mPendingRequest) {
case cRequestNewChain:
double lastX, lastY;
if (mChainAtoms > 0) {
lastX = mChainAtomX[mChainAtoms - 1];
lastY = mChainAtomY[mChainAtoms - 1];
} else {
lastX = 0;
lastY = 0;
}
double avbl = mMol.getAverageBondLength();
double s0 = (int) avbl;
double s1 = (int) (0.866 * avbl);
double s2 = (int) (0.5 * avbl);
double dx = mX2 - mX1;
double dy = mY2 - mY1;
if (Math.abs(dy) > Math.abs(dx)) {
mChainAtoms = (int) (2 * Math.abs(dy) / (s0 + s2));
if (Math.abs(dy) % (s0 + s2) > s0) {
mChainAtoms++;
}
mChainAtomX = new double[mChainAtoms];
mChainAtomY = new double[mChainAtoms];
if (mX2 < mX1) {
s1 = -s1;
}
if (mY2 < mY1) {
s0 = -s0;
s2 = -s2;
}
for (int i = 0; i < mChainAtoms; i++) {
mChainAtomX[i] = mX1 + ((i + 1) / 2) * s1;
mChainAtomY[i] = mY1 + ((i + 1) / 2) * (s0 + s2);
if ((i & 1) == 0) {
mChainAtomY[i] += s0;
}
}
} else {
mChainAtoms = (int) (Math.abs(dx) / s1);
mChainAtomX = new double[mChainAtoms];
mChainAtomY = new double[mChainAtoms];
if (mX2 < mX1) {
s1 = -s1;
}
if (mY2 < mY1) {
s2 = -s2;
}
for (int i = 0; i < mChainAtoms; i++) {
mChainAtomX[i] = mX1 + (i + 1) * s1;
mChainAtomY[i] = mY1;
if ((i & 1) == 0) {
mChainAtomY[i] += s2;
}
}
}
if (mChainAtoms > 0) {
mChainAtom = new int[mChainAtoms];
for (int i = 0; i < mChainAtoms; i++) {
mChainAtom[i] = mMol.findAtom(mChainAtomX[i], mChainAtomY[i]);
if (mChainAtom[i] != -1) {
mChainAtomX[i] = mMol.getAtomX(mChainAtom[i]);
mChainAtomY[i] = mMol.getAtomY(mChainAtom[i]);
}
}
if (mChainAtomX[mChainAtoms - 1] != lastX
|| mChainAtomY[mChainAtoms - 1] != lastY) {
repaintNeeded = true;
}
} else if (lastX != 0 || lastY != 0) {
repaintNeeded = true;
}
break;
case cRequestNewBond:
if ((mX2 - mX1) * (mX2 - mX1) + (mY2 - mY1) * (mY2 - mY1) < MIN_BOND_LENGTH_SQUARE) {
suggestNewX2AndY2(mAtom1);
}
repaintNeeded = true;
break;
case cRequestMoveSingle:
mMol.setAtomX(mAtom1, mX[mAtom1] + mX2 - mX1);
mMol.setAtomY(mAtom1, mY[mAtom1] + mY2 - mY1);
if (mAtom2 != -1) {
mMol.setAtomX(mAtom2, mX[mAtom2] + mX2 - mX1);
mMol.setAtomY(mAtom2, mY[mAtom2] + mY2 - mY1);
}
update(UPDATE_CHECK_VIEW);
break;
case cRequestCopySelected:
duplicateSelected();
mPendingRequest = cRequestMoveSelected;
case cRequestMoveSelected:
if (mDrawingObjectList != null) {
for (AbstractDrawingObject drawingObject : mDrawingObjectList) {
if (drawingObject.isSelected()) {
drawingObject.translate(mX2, mY2);
}
}
}
for (int i = 0; i < mMol.getAllAtoms(); i++) {
if (mMol.isSelectedAtom(i)) {
mMol.setAtomX(i, mX[i] + mX2 - mX1);
mMol.setAtomY(i, mY[i] + mY2 - mY1);
}
}
update(UPDATE_CHECK_VIEW);
break;
case cRequestCopyObject:
mDrawingObjectList.add(mCurrentHiliteObject.clone());
mPendingRequest = cRequestMoveObject;
case cRequestMoveObject:
mCurrentHiliteObject.translate(mX2, mY2);
update(UPDATE_CHECK_VIEW);
break;
case cRequestZoomAndRotate:
boolean selectedAtomsFound = false;
for (int atom = 0; atom < mMol.getAllAtoms() && !selectedAtomsFound; atom++) {
selectedAtomsFound = mMol.isSelectedAtom(atom);
}
boolean selectedObjectsFound = false;
if (mDrawingObjectList != null) {
for (int i = 0; i < mDrawingObjectList.size() && !selectedObjectsFound; i++) {
selectedObjectsFound = mDrawingObjectList.get(i).isSelected();
}
}
double magnification = (Math.abs(mY2 - mY1) < 20) ? 1.0 : Math.exp((mY2 - mY1) / 100);
double angleChange = (Math.abs(mX2 - mX1) < 20) ? 0.0f : (mX2 - mX1) / 50;
boolean selectedOnly = (selectedAtomsFound || selectedObjectsFound);
if (mDrawingObjectList != null && (!selectedOnly || selectedObjectsFound)) {
for (int i = 0; i < mDrawingObjectList.size(); i++) {
if (!selectedOnly || mDrawingObjectList.get(i).isSelected()) {
mDrawingObjectList.get(i).zoomAndRotate(magnification, angleChange);
}
}
update(UPDATE_CHECK_VIEW);
}
if (!selectedOnly || selectedAtomsFound) {
mMol.zoomAndRotate(magnification, angleChange, selectedOnly);
update(UPDATE_CHECK_VIEW);
}
break;
case cRequestLassoSelect:
if ((Math.abs(mX2 - mLassoRegion.xpoints[mLassoRegion.npoints - 1]) < 3)
&& (Math.abs(mY2 - mLassoRegion.ypoints[mLassoRegion.npoints - 1]) < 3)) {
break;
}
mLassoRegion.npoints--;
mLassoRegion.addPoint((int) mX2, (int) mY2);
mLassoRegion.addPoint((int) mX1, (int) mY1);
case cRequestSelectRect:
Shape selectedRegion = null;
if (mPendingRequest == cRequestLassoSelect) {
selectedRegion = mLassoRegion;
} else {
selectedRegion = new Rectangle((int) Math.min(mX1, mX2), (int) Math.min(mY1, mY2),
(int) Math.abs(mX2 - mX1), (int) Math.abs(mY2 - mY1));
}
if (mDrawingObjectList != null) {
for (int i = 0; i < mDrawingObjectList.size(); i++) {
AbstractDrawingObject object = mDrawingObjectList.get(i);
boolean isSelected = object.isSurroundedBy(selectedRegion);
if ((!mIsAddingToSelection || !mIsSelectedObject[i])
&& (isSelected != object.isSelected())) {
object.setSelected(isSelected);
mUpdateMode = Math.max(mUpdateMode, UPDATE_REDRAW);
}
}
}
for (int i = 0; i < mMol.getAllAtoms(); i++) {
boolean isSelected = selectedRegion.contains((int) mMol.getAtomX(i), (int) mMol.getAtomY(i));
if ((!mIsAddingToSelection || !mIsSelectedAtom[i])
&& (isSelected != mMol.isSelectedAtom(i))) {
mMol.setAtomSelection(i, isSelected);
mUpdateMode = Math.max(mUpdateMode, UPDATE_REDRAW);
}
}
repaintNeeded = true;
break;
case cRequestMapAtoms:
repaintNeeded = true;
break;
}
if (repaintNeeded) {
repaint();
}
}
public void mouseMoved(MouseEvent e)
{
mMouseIsDown = false;
int x = e.getX();
int y = e.getY();
if (trackHiliting(x, y, false)) {
repaint();
}
updateCursor();
}
public void keyPressed(KeyEvent e)
{
if (e.getKeyCode() == KeyEvent.VK_SHIFT) {
mShiftIsDown = true;
updateCursor();
}
if (e.getKeyCode() == KeyEvent.VK_ALT) {
mAltIsDown = true;
updateCursor();
}
if (e.getKeyCode() == KeyEvent.VK_CONTROL) {
mControlIsDown = true;
updateCursor();
}
if (mControlIsDown && e.getKeyCode() == KeyEvent.VK_Z) {
restoreState();
fireMoleculeChanged();
update(UPDATE_CHECK_VIEW);
} else if (e.getKeyCode() == KeyEvent.VK_DELETE) {
storeState();
if (mCurrentTool == JDrawToolbar.cToolMapper) {
boolean found = false;
for (int atom = 0; atom < mMol.getAllAtoms(); atom++) {
if (mMol.getAtomMapNo(atom) != 0) {
mMol.setAtomMapNo(atom, 0, false);
found = true;
}
}
if (found) {
fireMoleculeChanged();
update(UPDATE_REDRAW);
}
} else if (!deleteHilited()) {
if (mMol.deleteSelectedAtoms()) {
fireMoleculeChanged();
update(UPDATE_REDRAW);
}
}
} else if (e.getKeyCode() == KeyEvent.VK_HELP || (mCurrentHiliteAtom == -1 && e.getKeyChar() == '?')) {
showHelpDialog();
return;
} else if (mCurrentHiliteBond != -1) {
char ch = e.getKeyChar();
if (ch == 'q' && mMol.isFragment()) {
showBondQFDialog(mCurrentHiliteBond);
} else if (ch >= '4' && ch <= '7') {
if (mMol.addRingToBond(mCurrentHiliteBond, ch - '0', false)) {
fireMoleculeChanged();
update(UPDATE_CHECK_COORDS);
}
} else if (ch == 'b') {
if (mMol.addRingToBond(mCurrentHiliteBond, 6, true)) {
fireMoleculeChanged();
update(UPDATE_CHECK_COORDS);
}
} else {
boolean bondChanged = (ch == '1') ? changeHighlightedBond(Molecule.cBondTypeSingle)
: (ch == '2') ? changeHighlightedBond(Molecule.cBondTypeDouble)
: (ch == '3') ? changeHighlightedBond(Molecule.cBondTypeTriple)
: (ch == 'u') ? changeHighlightedBond(Molecule.cBondTypeUp)
: (ch == 'd') ? changeHighlightedBond(Molecule.cBondTypeDown)
: (ch == 'c') ? changeHighlightedBond(Molecule.cBondTypeCross)
: (ch == 'm') ? changeHighlightedBond(Molecule.cBondTypeMetalLigand)
: false;
if (bondChanged) {
fireMoleculeChanged();
update(UPDATE_REDRAW);
}
}
} else if (mCurrentHiliteAtom != -1) {
char ch = e.getKeyChar();
boolean isFirst = (mAtomKeyStrokeBuffer.length() == 0);
if (isFirst && (ch == '+' || ch == '-')) {
storeState();
if (mMol.changeAtomCharge(mCurrentHiliteAtom, ch == '+')) {
fireMoleculeChanged();
update(UPDATE_CHECK_COORDS);
}
} else if (isFirst && ch == '.') {
storeState();
int newRadical = (mMol.getAtomRadical(mCurrentHiliteAtom) == Molecule.cAtomRadicalStateD) ?
0 : Molecule.cAtomRadicalStateD;
mMol.setAtomRadical(mCurrentHiliteAtom, newRadical);
fireMoleculeChanged();
update(UPDATE_CHECK_COORDS);
} else if (isFirst && ch == ':') {
storeState();
int newRadical = (mMol.getAtomRadical(mCurrentHiliteAtom) == Molecule.cAtomRadicalStateT) ? Molecule.cAtomRadicalStateS
: (mMol.getAtomRadical(mCurrentHiliteAtom) == Molecule.cAtomRadicalStateS) ? 0 : Molecule.cAtomRadicalStateT;
mMol.setAtomRadical(mCurrentHiliteAtom, newRadical);
fireMoleculeChanged();
update(UPDATE_CHECK_COORDS);
} else if (isFirst && ch == 'q' && mMol.isFragment()) {
showAtomQFDialog(mCurrentHiliteAtom);
} else if (isFirst && ch == '?') {
storeState();
if (mMol.changeAtom(mCurrentHiliteAtom, 0, 0, -1, 0)) {
fireMoleculeChanged();
update(UPDATE_CHECK_COORDS);
}
} else if (isFirst && ch > 48 && ch <= 57) {
if (mMol.getFreeValence(mCurrentHiliteAtom) > 0) {
storeState();
int chainAtoms = ch - 47;
int atom1 = mCurrentHiliteAtom;
int hydrogenCount = mMol.getAllAtoms() - mMol.getAtoms();
for (int i = 1; i < chainAtoms; i++) {
suggestNewX2AndY2(atom1);
int atom2 = mMol.addAtom(mX2, mY2);
if (atom2 == -1) {
break;
}
mMol.addBond(atom1, atom2, Molecule.cBondTypeSingle);
atom1 = atom2 - hydrogenCount; // new atom was added behind all hydrogens and travels now to the front
mMol.ensureHelperArrays(Molecule.cHelperNeighbours);
}
fireMoleculeChanged();
update(UPDATE_CHECK_COORDS);
}
} else if (!isFirst && e.getKeyCode() == KeyEvent.VK_ESCAPE) {
mAtomKeyStrokeBuffer.setLength(0);
update(UPDATE_REDRAW);
} else if (!isFirst && e.getKeyCode() == KeyEvent.VK_BACK_SPACE) {
mAtomKeyStrokeBuffer.setLength(mAtomKeyStrokeBuffer.length() - 1);
update(UPDATE_REDRAW);
} else if ((ch >= 65 && ch <= 90)
|| (ch >= 97 && ch <= 122)
|| (ch >= 48 && ch <= 57)
|| (ch == '-')) {
mAtomKeyStrokeBuffer.append(ch);
update(UPDATE_REDRAW);
} else if (ch == '\n' || ch == '\r') {
expandAtomKeyStrokes(mAtomKeyStrokeBuffer.toString());
}
} else if (mCurrentHiliteAtom == -1 && mCurrentHiliteBond == -1) {
if ((mMode & (MODE_REACTION | MODE_MARKUSH_STRUCTURE | MODE_MULTIPLE_FRAGMENTS)) == 0) {
char ch = e.getKeyChar();
if (ch == 'h') {
flip(true);
update(UPDATE_REDRAW);
} if (ch == 'v') {
flip(false);
update(UPDATE_REDRAW);
}
}
}
}
private boolean changeHighlightedBond(int type)
{
storeState();
return mMol.changeBond(mCurrentHiliteBond, type);
}
public void showHelpDialog()
{
if (mHelpDialog == null || !mHelpDialog.isVisible()) {
JEditorPane helpPane = new JEditorPane();
helpPane.setEditorKit(HiDPIHelper.getUIScaleFactor() == 1f ? new HTMLEditorKit() : new ScaledEditorKit());
helpPane.setEditable(false);
try {
helpPane.setPage(getClass().getResource("/html/editor/editor.html"));
} catch (Exception ex) {
helpPane.setText(ex.toString());
}
Component c = this;
while (!(c instanceof Frame || c instanceof Dialog)) {
c = c.getParent();
}
if (c instanceof Frame) {
mHelpDialog = new JDialog((Frame) c, "Actelion Structure Editor Help", false);
} else {
mHelpDialog = new JDialog((Dialog) c, "Actelion Structure Editor Help", false);
}
mHelpDialog.setSize(HiDPIHelper.scale(520), HiDPIHelper.scale(440));
mHelpDialog.getContentPane().add(new JScrollPane(helpPane,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER));
int x = (c.getX() >= 8 + mHelpDialog.getWidth()) ? c.getX() - 8 - mHelpDialog.getWidth() : c.getX() + 8 + c.getWidth();
mHelpDialog.setLocation(x, c.getY());
mHelpDialog.setVisible(true);
} else {
Component c = this;
while (!(c instanceof Frame || c instanceof Dialog)) {
c = c.getParent();
}
int x = (mHelpDialog.getX() + mHelpDialog.getWidth() / 2 >= c.getX() + c.getWidth() / 2) ?
c.getX() - 8 - mHelpDialog.getWidth() : c.getX() + 8 + c.getWidth();
mHelpDialog.setLocation(x, c.getY());
}
}
public void keyReleased(KeyEvent e)
{
if (e.getKeyCode() == KeyEvent.VK_SHIFT) {
mShiftIsDown = false;
updateCursor();
}
if (e.getKeyCode() == KeyEvent.VK_ALT) {
mAltIsDown = false;
updateCursor();
}
if (e.getKeyCode() == KeyEvent.VK_CONTROL) {
mControlIsDown = false;
updateCursor();
}
if ((e.getModifiers() & Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()) != 0) {
if (e.getKeyCode() == KeyEvent.VK_C) {
copy();
} else if (e.getKeyCode() == KeyEvent.VK_V) {
paste();
}
}
}
public void keyTyped(KeyEvent e)
{
}
private boolean handlePopupTrigger(MouseEvent e)
{
if (e.isPopupTrigger()) {
JPopupMenu popup = null;
if (mClipboardHandler != null) {
JMenuItem menuItem1 = new JMenuItem("Copy");
menuItem1.addActionListener(this);
if (mMol.getAllAtoms() == 0) {
menuItem1.setEnabled(false);
}
JMenuItem menuItem2 = new JMenuItem("Paste");
menuItem2.addActionListener(this);
if (popup == null) {
popup = new JPopupMenu();
}
popup.add(menuItem1);
popup.add(menuItem2);
}
if (mAtomColorSupported && mCurrentHiliteAtom != -1) {
int atomColor = mMol.getAtomColor(mCurrentHiliteAtom);
if (popup == null) {
popup = new JPopupMenu();
} else {
popup.addSeparator();
}
JMenu colorMenu = new JMenu("Set Atom Color");
addColorToMenu(colorMenu, Color.BLACK, Molecule.cAtomColorNone, atomColor == Molecule.cAtomColorNone);
addColorToMenu(colorMenu, AbstractDepictor.COLOR_BLUE, Molecule.cAtomColorBlue, atomColor == Molecule.cAtomColorBlue);
addColorToMenu(colorMenu, AbstractDepictor.COLOR_DARK_RED, Molecule.cAtomColorDarkRed, atomColor == Molecule.cAtomColorDarkRed);
addColorToMenu(colorMenu, AbstractDepictor.COLOR_RED, Molecule.cAtomColorRed, atomColor == Molecule.cAtomColorRed);
addColorToMenu(colorMenu, AbstractDepictor.COLOR_DARK_GREEN, Molecule.cAtomColorDarkGreen, atomColor == Molecule.cAtomColorDarkGreen);
addColorToMenu(colorMenu, AbstractDepictor.COLOR_GREEN, Molecule.cAtomColorGreen, atomColor == Molecule.cAtomColorGreen);
addColorToMenu(colorMenu, AbstractDepictor.COLOR_MAGENTA, Molecule.cAtomColorMagenta, atomColor == Molecule.cAtomColorMagenta);
addColorToMenu(colorMenu, AbstractDepictor.COLOR_ORANGE, Molecule.cAtomColorOrange, atomColor == Molecule.cAtomColorOrange);
popup.add(colorMenu);
}
if (popup != null) {
popup.show(this, e.getX(), e.getY());
}
return true;
}
return false;
}
private void addColorToMenu(JMenu menu, Color color, int atomColor, boolean isSelected)
{
JRadioButtonMenuItem item = new JRadioButtonMenuItem(" ", isSelected);
item.setActionCommand("atomColor" + mCurrentHiliteAtom + ":" + atomColor);
item.addActionListener(this);
item.setBackground(color);
menu.add(item);
}
private void handleDoubleClick(MouseEvent e)
{
int x = e.getX();
int y = e.getY();
int atom = mMol.findAtom(x, y);
int bond = mMol.findBond(x, y);
if (mCurrentTool == JDrawToolbar.cToolLassoPointer) {
if (mMol.isFragment()) {
if (atom != -1) {
showAtomQFDialog(atom);
} else if (bond != -1) {
showBondQFDialog(bond);
} else if (mCurrentHiliteObject != null) {
if (!mShiftIsDown) {
for (int i = 0; i < mMol.getAllAtoms(); i++) {
mMol.setAtomSelection(i, false);
}
for (int i = 0; i < mDrawingObjectList.size(); i++) {
((AbstractDrawingObject) mDrawingObjectList.get(i)).setSelected(false);
}
}
mCurrentHiliteObject.setSelected(true);
update(UPDATE_REDRAW);
}
} else {
int rootAtom = -1;
if (atom != -1) {
rootAtom = atom;
} else if (bond != -1) {
rootAtom = mMol.getBondAtom(0, bond);
}
if (rootAtom != -1 || mCurrentHiliteObject != null) {
if (!mShiftIsDown) {
for (int i = 0; i < mMol.getAllAtoms(); i++) {
mMol.setAtomSelection(i, false);
}
if (mDrawingObjectList != null) {
for (AbstractDrawingObject drawingObject : mDrawingObjectList) {
drawingObject.setSelected(false);
}
}
}
if (rootAtom != -1) {
if ((mMode & MODE_MULTIPLE_FRAGMENTS) != 0) {
int fragment = mFragmentNo[rootAtom];
for (int i = 0; i < mMol.getAllAtoms(); i++) {
if (mFragmentNo[i] == fragment) {
mMol.setAtomSelection(i, true);
}
}
} else {
int[] fragmentMember = mMol.getFragmentAtoms(rootAtom);
for (int i = 0; i < fragmentMember.length; i++) {
mMol.setAtomSelection(fragmentMember[i], true);
}
}
} else {
mCurrentHiliteObject.setSelected(true);
}
update(UPDATE_REDRAW);
}
}
} else if (mCurrentTool == JDrawToolbar.cToolAtomOther) {
if (atom != -1) {
Component c = this;
while (c.getParent() != null) {
c = c.getParent();
}
storeState();
new JAtomLabelDialog((Frame) c, mMol, atom);
mOtherAtom = mMol.getAtomicNo(atom);
mOtherMass = mMol.getAtomMass(atom);
mOtherValence = mMol.getAtomAbnormalValence(atom);
mOtherRadical = mMol.getAtomRadical(atom);
fireMoleculeChanged();
update(UPDATE_REDRAW);
}
} else if (mCurrentTool == JDrawToolbar.cToolZoom) {
int fragment = -2;
if ((mMode & MODE_MULTIPLE_FRAGMENTS) != 0) {
fragment = findFragment(x, y);
}
if (fragment != -1) {
double minX = Integer.MAX_VALUE;
double maxX = Integer.MIN_VALUE;
for (int i = 0; i < mMol.getAllAtoms(); i++) {
if (fragment == -2 || mFragmentNo[i] == fragment) {
if (minX > mMol.getAtomX(i)) {
minX = mMol.getAtomX(i);
}
if (maxX < mMol.getAtomX(i)) {
maxX = mMol.getAtomX(i);
}
}
}
if (maxX > minX) {
double centerX = (maxX + minX) / 2;
for (int i = 0; i < mMol.getAllAtoms(); i++) {
if (fragment == -2 || mFragmentNo[i] == fragment) {
mMol.setAtomX(i, 2 * centerX - mMol.getAtomX(i));
}
}
for (int i = 0; i < mMol.getAllBonds(); i++) {
if (fragment == -2 || mFragmentNo[mMol.getBondAtom(0, i)] == fragment) {
switch (mMol.getBondType(i)) {
case Molecule.cBondTypeUp:
mMol.setBondType(i, Molecule.cBondTypeDown);
break;
case Molecule.cBondTypeDown:
mMol.setBondType(i, Molecule.cBondTypeUp);
break;
}
}
}
}
fireMoleculeChanged();
update(UPDATE_REDRAW);
}
}
}
private void showAtomQFDialog(int atom)
{
Component c = this;
while (c.getParent() != null) {
c = c.getParent();
}
storeState();
new JAtomQueryFeatureDialog((Frame) c, mMol, atom);
fireMoleculeChanged();
update(UPDATE_REDRAW);
}
private void showBondQFDialog(int bond)
{
Component c = this;
while (c.getParent() != null) {
c = c.getParent();
}
storeState();
new JBondQueryFeatureDialog((Frame) c, mMol, bond);
fireMoleculeChanged();
update(UPDATE_REDRAW);
}
private void mousePressedButton1(MouseEvent e)
{
mX1 = e.getX();
mY1 = e.getY();
switch (mCurrentTool) {
case JDrawToolbar.cToolZoom:
// mX1,mY1 define anker for rotation and zooming
double x = mX1;
double y = mY1;
mAtom1 = mMol.findAtom(mX1, mY1);
if (mAtom1 != -1) {
mX1 = mMol.getAtomX(mAtom1);
mY1 = mMol.getAtomY(mAtom1);
}
mMol.zoomAndRotateInit(x, y);
if (mDrawingObjectList != null) {
for (AbstractDrawingObject drawingObject : mDrawingObjectList) {
drawingObject.zoomAndRotateInit(x, y);
}
}
storeState();
mPendingRequest = cRequestZoomAndRotate;
break;
case JDrawToolbar.cToolLassoPointer:
mPendingRequest = cRequestNone;
// if atom was hit -> move atom (and if atom is selected then all selected stuff)
mAtom1 = mMol.findAtom(mX1, mY1);
if (mAtom1 != -1) {
mAtom2 = -1;
mX1 = mMol.getAtomX(mAtom1);
mY1 = mMol.getAtomY(mAtom1);
if (mMol.isSelectedAtom(mAtom1)) {
mPendingRequest = mShiftIsDown ? cRequestCopySelected : cRequestMoveSelected;
} else {
mPendingRequest = cRequestMoveSingle;
}
}
// if bond was hit -> move bond (and if atom is selected then all selected stuff)
if (mPendingRequest == cRequestNone) {
int bondClicked = mMol.findBond(mX1, mY1);
if (bondClicked != -1) {
mAtom1 = mMol.getBondAtom(0, bondClicked);
mAtom2 = mMol.getBondAtom(1, bondClicked);
if (mMol.isSelectedBond(bondClicked)) {
mPendingRequest = mShiftIsDown ? cRequestCopySelected : cRequestMoveSelected;
} else {
mPendingRequest = cRequestMoveSingle;
}
}
}
// if object was hit -> move object (and if atom is selected then all selected stuff)
if (mPendingRequest == cRequestNone) {
if (mCurrentHiliteObject != null) {
if (mCurrentHiliteObject.isSelected()) {
mPendingRequest = mShiftIsDown ? cRequestCopySelected : cRequestMoveSelected;
} else {
mPendingRequest = (mShiftIsDown && !(mCurrentHiliteObject instanceof ReactionArrow)) ?
cRequestCopyObject : cRequestMoveObject;
}
}
}
if (mPendingRequest != cRequestNone) {
mX = new double[mMol.getAllAtoms()];
mY = new double[mMol.getAllAtoms()];
for (int i = 0; i < mMol.getAllAtoms(); i++) {
mX[i] = mMol.getAtomX(i);
mY[i] = mMol.getAtomY(i);
}
if (mDrawingObjectList != null) {
for (AbstractDrawingObject drawingObject : mDrawingObjectList) {
drawingObject.translateInit(mX1, mY1);
}
}
storeState();
break;
}
// is lasso- or rectangle-selecting
mIsSelectedAtom = new boolean[mMol.getAllAtoms()];
if (mDrawingObjectList != null) {
mIsSelectedObject = new boolean[mDrawingObjectList.size()];
}
mIsAddingToSelection = e.isShiftDown();
for (int i = 0; i < mMol.getAllAtoms(); i++) {
mIsSelectedAtom[i] = mMol.isSelectedAtom(i);
}
if (mDrawingObjectList != null) {
for (int i = 0; i < mDrawingObjectList.size(); i++) {
mIsSelectedObject[i] = mDrawingObjectList.get(i).isSelected();
}
}
if (e.isAltDown()) {
mPendingRequest = cRequestSelectRect;
} else {
mLassoRegion = new Polygon();
mLassoRegion.addPoint((int) mX1, (int) mY1);
mLassoRegion.addPoint((int) mX1, (int) mY1);
mPendingRequest = cRequestLassoSelect;
}
break;
case JDrawToolbar.cToolDelete:
storeState();
deleteAt(mX1, mY1);
break;
case JDrawToolbar.cToolUnknownParity:
int theAtom = mMol.findAtom(mX1, mY1);
if (theAtom != -1) {
storeState();
mMol.setAtomConfigurationUnknown(theAtom, !mMol.isAtomConfigurationUnknown(theAtom));
fireMoleculeChanged();
update(UPDATE_REDRAW);
}
break;
case JDrawToolbar.cToolESRAbs:
case JDrawToolbar.cToolESRAnd:
case JDrawToolbar.cToolESROr:
if (mCurrentHiliteBond != -1 && qualifiesForESR(mCurrentHiliteBond)) {
storeState();
setESRInfo(mCurrentHiliteBond, (mCurrentTool == JDrawToolbar.cToolESRAbs) ?
Molecule.cESRTypeAbs :
(mCurrentTool == JDrawToolbar.cToolESRAnd) ?
Molecule.cESRTypeAnd : Molecule.cESRTypeOr);
fireMoleculeChanged();
update(UPDATE_REDRAW);
}
break;
case JDrawToolbar.cToolStdBond:
case JDrawToolbar.cToolUpBond:
case JDrawToolbar.cToolDownBond:
mAtom1 = mMol.findAtom(mX1, mY1);
if (mAtom1 == -1) {
int bond = mMol.findBond(mX1, mY1);
if (bond != -1) {
storeState();
int bondType = Molecule.cBondTypeIncreaseOrder;
if (mCurrentTool == JDrawToolbar.cToolUpBond) {
bondType = Molecule.cBondTypeUp;
} else if (mCurrentTool == JDrawToolbar.cToolDownBond) {
bondType = Molecule.cBondTypeDown;
}
if (mMol.changeBond(bond, bondType)) {
fireMoleculeChanged();
update(UPDATE_REDRAW);
}
break;
}
} else {
if (mMol.getAllConnAtoms(mAtom1) == MAX_CONNATOMS) {
return;
}
mX1 = mMol.getAtomX(mAtom1);
mY1 = mMol.getAtomY(mAtom1);
}
mPendingRequest = cRequestNewBond;
suggestNewX2AndY2(mAtom1);
repaint();
break;
case JDrawToolbar.cToolChain:
mAtom1 = mMol.findAtom(mX1, mY1);
if (mAtom1 != -1) {
if (mMol.getAllConnAtoms(mAtom1) == MAX_CONNATOMS) {
return;
}
mX1 = mMol.getAtomX(mAtom1);
mY1 = mMol.getAtomY(mAtom1);
}
mPendingRequest = cRequestNewChain;
mChainAtoms = 0;
break;
case JDrawToolbar.cTool3Ring:
storeState();
if (mMol.addRing(mX1, mY1, 3, false)) {
fireMoleculeChanged();
update(UPDATE_CHECK_COORDS);
}
break;
case JDrawToolbar.cTool4Ring:
storeState();
if (mMol.addRing(mX1, mY1, 4, false)) {
fireMoleculeChanged();
update(UPDATE_CHECK_COORDS);
}
break;
case JDrawToolbar.cTool5Ring:
storeState();
if (mMol.addRing(mX1, mY1, 5, false)) {
fireMoleculeChanged();
update(UPDATE_CHECK_COORDS);
}
break;
case JDrawToolbar.cTool6Ring:
storeState();
if (mMol.addRing(mX1, mY1, 6, false)) {
fireMoleculeChanged();
update(UPDATE_CHECK_COORDS);
}
break;
case JDrawToolbar.cTool7Ring:
storeState();
if (mMol.addRing(mX1, mY1, 7, false)) {
fireMoleculeChanged();
update(UPDATE_CHECK_COORDS);
}
break;
case JDrawToolbar.cToolAromRing:
storeState();
if (mMol.addRing(mX1, mY1, 6, true)) {
fireMoleculeChanged();
update(UPDATE_CHECK_COORDS);
}
break;
case JDrawToolbar.cToolPosCharge:
storeState();
if (mMol.changeAtomCharge(mX1, mY1, true)) {
fireMoleculeChanged();
update(UPDATE_CHECK_COORDS);
}
break;
case JDrawToolbar.cToolNegCharge:
storeState();
if (mMol.changeAtomCharge(mX1, mY1, false)) {
fireMoleculeChanged();
update(UPDATE_CHECK_COORDS);
}
break;
case JDrawToolbar.cToolAtomH:
storeState();
if (mMol.addOrChangeAtom(mX1, mY1, 1, 0, -1, 0)) {
fireMoleculeChanged();
update(UPDATE_CHECK_COORDS);
}
break;
case JDrawToolbar.cToolAtomC:
storeState();
if (mMol.addOrChangeAtom(mX1, mY1, 6, 0, -1, 0)) {
fireMoleculeChanged();
update(UPDATE_CHECK_COORDS);
}
break;
case JDrawToolbar.cToolAtomN:
storeState();
if (mMol.addOrChangeAtom(mX1, mY1, 7, 0, -1, 0)) {
fireMoleculeChanged();
update(UPDATE_CHECK_COORDS);
}
break;
case JDrawToolbar.cToolAtomO:
storeState();
if (mMol.addOrChangeAtom(mX1, mY1, 8, 0, -1, 0)) {
fireMoleculeChanged();
update(UPDATE_CHECK_COORDS);
}
break;
case JDrawToolbar.cToolAtomSi:
storeState();
if (mMol.addOrChangeAtom(mX1, mY1, 14, 0, -1, 0)) {
fireMoleculeChanged();
update(UPDATE_CHECK_COORDS);
}
break;
case JDrawToolbar.cToolAtomP:
storeState();
if (mMol.addOrChangeAtom(mX1, mY1, 15, 0, -1, 0)) {
fireMoleculeChanged();
update(UPDATE_CHECK_COORDS);
}
break;
case JDrawToolbar.cToolAtomS:
storeState();
if (mMol.addOrChangeAtom(mX1, mY1, 16, 0, -1, 0)) {
fireMoleculeChanged();
update(UPDATE_CHECK_COORDS);
}
break;
case JDrawToolbar.cToolAtomF:
storeState();
if (mMol.addOrChangeAtom(mX1, mY1, 9, 0, -1, 0)) {
fireMoleculeChanged();
update(UPDATE_CHECK_COORDS);
}
break;
case JDrawToolbar.cToolAtomCl:
storeState();
if (mMol.addOrChangeAtom(mX1, mY1, 17, 0, -1, 0)) {
fireMoleculeChanged();
update(UPDATE_CHECK_COORDS);
}
break;
case JDrawToolbar.cToolAtomBr:
storeState();
if (mMol.addOrChangeAtom(mX1, mY1, 35, 0, -1, 0)) {
fireMoleculeChanged();
update(UPDATE_CHECK_COORDS);
}
break;
case JDrawToolbar.cToolAtomI:
storeState();
if (mMol.addOrChangeAtom(mX1, mY1, 53, 0, -1, 0)) {
fireMoleculeChanged();
update(UPDATE_CHECK_COORDS);
}
break;
case JDrawToolbar.cToolAtomOther:
if (mOtherAtom != -1) {
storeState();
if (mMol.addOrChangeAtom(mX1, mY1, mOtherAtom, mOtherMass, mOtherValence, mOtherRadical)) {
fireMoleculeChanged();
update(UPDATE_CHECK_COORDS);
}
}
break;
case JDrawToolbar.cToolMapper:
mAtom1 = mMol.findAtom(mX1, mY1);
if (mAtom1 != -1 && mAtom1 < mMol.getAtoms()) {
mX1 = mMol.getAtomX(mAtom1);
mY1 = mMol.getAtomY(mAtom1);
mPendingRequest = cRequestMapAtoms;
}
break;
case JDrawToolbar.cToolText:
TextDrawingObject object = null;
if (mCurrentHiliteObject == null) {
object = new TextDrawingObject();
object.setCoordinates(mX1, mY1);
mDrawingObjectList.add(object);
} else if (mCurrentHiliteObject instanceof TextDrawingObject) {
object = (TextDrawingObject) mCurrentHiliteObject;
}
editTextObject(object);
storeState();
update(UPDATE_CHECK_COORDS);
break;
}
}
private void mouseReleasedButton1(MouseEvent e)
{
int pendingRequest = mPendingRequest;
mPendingRequest = cRequestNone;
switch (pendingRequest) {
case cRequestNewBond:
int stopAtom;
stopAtom = mMol.findAtom(mX2, mY2);
if (stopAtom != -1
&& mMol.getAllConnAtoms(stopAtom) == MAX_CONNATOMS) {
return;
}
storeState();
if (mAtom1 == -1) {
mAtom1 = mMol.addAtom(mX1, mY1);
}
if (stopAtom == -1) {
stopAtom = mMol.addAtom(mX2, mY2);
}
if ((mAtom1 != -1 && stopAtom != -1)) {
int bondType = Molecule.cBondTypeSingle;
if (mCurrentTool == JDrawToolbar.cToolUpBond) {
bondType = Molecule.cBondTypeUp;
}
if (mCurrentTool == JDrawToolbar.cToolDownBond) {
bondType = Molecule.cBondTypeDown;
}
mMol.addOrChangeBond(mAtom1, stopAtom, bondType);
}
fireMoleculeChanged();
update(UPDATE_CHECK_COORDS);
break;
case cRequestNewChain:
storeState();
if (mChainAtoms > 0) {
if (mAtom1 == -1) {
mAtom1 = mMol.addAtom(mX1, mY1);
}
if (mChainAtom[0] == -1) {
mChainAtom[0] = mMol.addAtom(mChainAtomX[0],
mChainAtomY[0]);
}
if (mChainAtom[0] != -1) {
mMol.addBond(mAtom1, mChainAtom[0], Molecule.cBondTypeSingle);
}
}
if (mChainAtoms > 1) {
for (int i = 1; i < mChainAtoms; i++) {
if (mChainAtom[i] == -1) {
mChainAtom[i] = mMol.addAtom(mChainAtomX[i],
mChainAtomY[i]);
}
if (mChainAtom[i] != -1) {
mMol.addBond(mChainAtom[i - 1], mChainAtom[i], Molecule.cBondTypeSingle);
}
}
}
fireMoleculeChanged();
update(UPDATE_CHECK_COORDS);
break;
case cRequestMoveSingle:
case cRequestMoveSelected:
case cRequestZoomAndRotate:
fireMoleculeChanged();
case cRequestMoveObject:
update(UPDATE_CHECK_COORDS);
break;
case cRequestLassoSelect:
case cRequestSelectRect:
boolean selectionChanged = false;
for (int i = 0; i < mMol.getAllAtoms(); i++) {
if (mIsSelectedAtom[i] != mMol.isSelectedAtom(i)) {
selectionChanged = true;
break;
}
}
if (selectionChanged) {
fireEvent(new DrawAreaEvent(this, DrawAreaEvent.TYPE_SELECTION_CHANGED, true));
}
repaint();
break;
case cRequestMapAtoms:
boolean mapNoChanged = false;
int atom2 = mCurrentHiliteAtom;
// System.out.printf("Map Request Atom %d => %d (%d)\n", mAtom1, mAtom2, atom2);
int mapNoAtom1 = mMol.getAtomMapNo(mAtom1);
if (atom2 == -1) {
storeState();
if (mapNoAtom1 != 0) {
mapNoChanged = true;
for (int atom = 0; atom < mMol.getAtoms(); atom++) {
if (mMol.getAtomMapNo(atom) == mapNoAtom1) {
mMol.setAtomMapNo(atom, 0, false);
}
}
if(mMapper != null)
tryAutoMapReaction();
// new MoleculeAutoMapper(mMol).autoMap();
}
} else {
storeState();
mapNoChanged = true;
// If we only clicked on a mapped atom, then reset the map number
if (mAtom1 == atom2) {
int mapNo = mMol.getAtomMapNo(mAtom1);
for (int atom = 0; atom < mMol.getAtoms(); atom++) {
if (mMol.getAtomMapNo(atom) == mapNo) {
mMol.setAtomMapNo(atom, 0, false);
}
}
} else {
// remove old mapping numbers of atom1 and atom2
int mapNoAtom2 = mMol.getAtomMapNo(atom2);
for (int atom = 0; atom < mMol.getAtoms(); atom++) {
if (mMol.getAtomMapNo(atom) == mapNoAtom1
|| mMol.getAtomMapNo(atom) == mapNoAtom2) {
mMol.setAtomMapNo(atom, 0, false);
}
}
int freeMapNo = 1;
for (int atom = 0; atom < mMol.getAtoms(); atom++) {
if (mMol.getAtomMapNo(atom) == freeMapNo) {
freeMapNo++;
atom = -1;
}
}
mMol.setAtomMapNo(mAtom1, freeMapNo, false);
mMol.setAtomMapNo(atom2, freeMapNo, false);
if(mMapper != null)
tryAutoMapReaction();
}
// new MoleculeAutoMapper(mMol).autoMap();
}
if (mapNoChanged) {
fireMoleculeChanged();
mUpdateMode = Math.max(mUpdateMode, UPDATE_REDRAW);
}
repaint();
break;
}
}
static class MySSSearcher extends SSSearcher
{
@Override
protected boolean areAtomsSimilar(int moleculeAtom, int fragmentAtom)
{
if (mMolecule.getAtomicNo(moleculeAtom) == mFragment.getAtomicNo(fragmentAtom))
if (mMolecule.isAromaticAtom(moleculeAtom) || mFragment.isAromaticAtom(fragmentAtom))
return true;
return super.areAtomsSimilar(moleculeAtom, fragmentAtom);
}
@Override
protected boolean areBondsSimilar(int moleculeBond, int fragmentBond)
{
if (mMolecule.isAromaticBond(moleculeBond) || mMolecule.isDelocalizedBond(moleculeBond) ||
mFragment.isAromaticBond(fragmentBond) || mFragment.isDelocalizedBond(fragmentBond)
)
return true;
return super.areBondsSimilar(moleculeBond, fragmentBond);
//return true;
}
}
private void tryAutoMapReaction()
{
// new MoleculeAutoMapper(mMol).autoMap();
SSSearcher sss = new MySSSearcher();
syncFragments();
Reaction rxn = getReaction();//new Reaction(reaction);
// Mark the manually mapped atoms, so we may re-assign them later
for (int i = 0; i < rxn.getMolecules(); i++) {
StereoMolecule mol = rxn.getMolecule(i);
for (int a = 0; a < mol.getAtoms(); a++) {
if (mol.getAtomMapNo(a) > 0) {
mol.setAtomicNo(a, FAKE_ATOM_NO + mol.getAtomMapNo(a));
}
}
}
rxn = mMapper.matchReaction(rxn, sss);
if (rxn != null) {
int offset = 0;
// Sync the display molecule with the reaction fragments
{
int[] fragmentAtom = new int[mFragment.length];
for (int atom = 0; atom < mMol.getAllAtoms(); atom++) {
int fragment = mFragmentNo[atom];
if (mFragment[fragment].getAtomicNo(fragmentAtom[fragment]) > FAKE_ATOM_NO) {
mMol.setAtomMapNo(atom, mFragment[fragment].getAtomicNo(fragmentAtom[fragment]) - FAKE_ATOM_NO, false);
offset = Math.max(mMol.getAtomMapNo(atom), offset);
}
fragmentAtom[fragment]++;
}
}
{
int[] fragmentAtom = new int[mFragment.length];
for (int atom = 0; atom < mMol.getAllAtoms(); atom++) {
int fragment = mFragmentNo[atom];
if (mFragment[fragment].getAtomMapNo(fragmentAtom[fragment]) > 0 && (mFragment[fragment].getAtomicNo(fragmentAtom[fragment]) <= FAKE_ATOM_NO)) {
mMol.setAtomMapNo(atom, mFragment[fragment].getAtomMapNo(fragmentAtom[fragment]) + offset, true);
}
fragmentAtom[fragment]++;
}
}
}
syncFragments();
// mMapper.resetFragments(mappedReaction);
// AStarReactionMapper m = new AStarReactionMapper();
// Reaction rxn = getReaction();
// List l = m.map(rxn);
// if (l != null && l.size() > 0) {
// AStarReactionMapper.SlimMapping sm = l.get(0);
//// int[] mps = sm.getMapping();
//// for (int i = 0; i < mps.length; i++) {
//// System.out.printf("Maps %d -> %d\n", i, mps[i]);
//// }
// m.activateMapping(sm);
//// for (int i = 0; i < mFragment.length; i++) {
//// StereoMolecule mol = mFragment[i];
//// for (int a = 0; a < mol.getAllAtoms(); a++) {
//// System.out.printf("T Map %d = %d\n", a, mol.getAtomMapNo(a));
//// }
//// }
//
// int[] fragmentAtom = new int[mFragment.length];
// for (int atom = 0; atom < mMol.getAllAtoms(); atom++) {
// int fragment = mFragmentNo[atom];
// if (mMol.getAtomMapNo(atom) == 0)
// mMol.setAtomMapNo(atom, mFragment[fragment].getAtomMapNo(fragmentAtom[fragment]), true);
// fragmentAtom[fragment]++;
// }
// }
}
/**
* Checks whether this bond is a stereo bond and whether it refers to a
* stereo center or BINAP bond, making it eligible for ESR information.
*
* @param stereoBond the up/down stereo bond
* @return
*/
private boolean qualifiesForESR(int stereoBond)
{
return mMol.isStereoBond(stereoBond) && (getESRAtom(stereoBond) != -1 || getESRBond(stereoBond) != -1);
}
/**
* Locates the stereo center with parity 1 or 2 that is defined by the stereo bond.
*
* @param stereoBond
* @return stereo center atom or -1 if no stereo center found
*/
private int getESRAtom(int stereoBond)
{
int atom = mMol.getBondAtom(0, stereoBond);
if (mMol.getAtomParity(atom) != Molecule.cAtomParityNone) {
return (mMol.isAtomParityPseudo(atom)
|| (mMol.getAtomParity(atom) != Molecule.cAtomParity1
&& mMol.getAtomParity(atom) != Molecule.cAtomParity2)) ? -1 : atom;
}
if (mMol.getAtomPi(atom) == 1) {
for (int i = 0; i < mMol.getConnAtoms(atom); i++) {
if (mMol.getConnBondOrder(atom, i) == 2) {
int connAtom = mMol.getConnAtom(atom, i);
if (mMol.getAtomPi(connAtom) == 2
&& (mMol.getAtomParity(connAtom) == Molecule.cAtomParity1
|| mMol.getAtomParity(connAtom) == Molecule.cAtomParity2)) {
return connAtom;
}
}
}
}
return -1;
}
private int getESRBond(int stereoBond)
{
int bond = mMol.findBINAPChiralityBond(mMol.getBondAtom(0, stereoBond));
if (bond != -1
&& mMol.getBondParity(bond) != Molecule.cBondParityEor1
&& mMol.getBondParity(bond) != Molecule.cBondParityZor2) {
bond = -1;
}
return bond;
}
/**
* Sets the ESR information of an atom or bond.
*
*/
private void setESRInfo(int stereoBond, int type)
{
int group = -1;
int atom = getESRAtom(stereoBond);
int bond = (atom == -1) ? getESRBond(stereoBond) : -1;
// if type requires group information (type==And or type==Or)
if (type != Molecule.cESRTypeAbs) {
int maxGroup = -1;
for (int i = 0; i < mMol.getAtoms(); i++) {
if (i != atom
&& mMol.getAtomESRType(i) == type
&& (!mMol.isSelectedBond(stereoBond) || !mMol.isSelectedAtom(i))) {
int grp = mMol.getAtomESRGroup(i);
if (maxGroup < grp) {
maxGroup = grp;
}
}
}
for (int i = 0; i < mMol.getBonds(); i++) {
if (i != bond
&& mMol.getBondESRType(i) == type
&& (!mMol.isSelectedBond(stereoBond) || !mMol.isSelectedBond(i))) {
int grp = mMol.getBondESRGroup(i);
if (maxGroup < grp) {
maxGroup = grp;
}
}
}
if ((atom == -1 ? mMol.getBondESRType(bond) : mMol.getAtomESRType(atom)) != type) {
group = Math.min(maxGroup + 1, Molecule.cESRMaxGroups - 1);
} else {
group = (atom == -1) ? mMol.getBondESRGroup(bond) : mMol.getAtomESRGroup(atom);
if (mMol.isSelectedBond(stereoBond)) {
boolean selectedShareOneGroup = true;
for (int i = 0; i < mMol.getAtoms(); i++) {
if (i != atom && mMol.isSelectedAtom(i)
&& mMol.getAtomESRType(i) == type
&& mMol.getAtomESRGroup(i) != group) {
selectedShareOneGroup = false;
break;
}
}
for (int i = 0; i < mMol.getBonds(); i++) {
if (i != bond && mMol.isSelectedBond(i)
&& mMol.getBondESRType(i) == type
&& mMol.getBondESRGroup(i) != group) {
selectedShareOneGroup = false;
break;
}
}
if (selectedShareOneGroup) {
if (group <= maxGroup) {
group++;
if (group == Molecule.cESRMaxGroups) {
group = 0;
}
} else {
group = 0;
}
}
} else {
if (group <= maxGroup) {
group++;
if (group == Molecule.cESRMaxGroups) {
group = 0;
}
} else {
group = 0;
}
}
}
}
if (mMol.isSelectedBond(stereoBond)) {
for (int i = 0; i < mMol.getBonds(); i++) {
if (mMol.isSelectedBond(i) && mMol.isStereoBond(i)) {
int a = getESRAtom(i);
int b = getESRBond(i);
if (a != -1) {
mMol.setAtomESR(a, type, group);
} else if (b != -1) {
mMol.setBondESR(b, type, group);
}
}
}
} else {
if (atom != -1) {
mMol.setAtomESR(atom, type, group);
} else if (bond != -1) {
mMol.setBondESR(bond, type, group);
}
}
}
private int findFragment(double x, double y)
{
int fragment = -1;
double minDistance = Double.MAX_VALUE;
for (int atom = 0; atom < mMol.getAllAtoms(); atom++) {
double dx = mX1 - mMol.getAtomX(atom);
double dy = mY1 - mMol.getAtomY(atom);
double distance = Math.sqrt(dx * dx + dy * dy);
if (distance < FRAGMENT_MAX_CLICK_DISTANCE
&& minDistance > distance) {
minDistance = distance;
fragment = mFragmentNo[atom];
}
}
return fragment;
}
/**
* requires cHelperNeighbours
*
* @param atom
*/
private void suggestNewX2AndY2(int atom)
{
double newAngle = Math.PI * 2 / 3;
if (atom != -1) {
double angle[] = new double[MAX_CONNATOMS + 1];
for (int i = 0; i < mMol.getAllConnAtoms(atom); i++) {
angle[i] = mMol.getBondAngle(atom, mMol.getConnAtom(atom, i));
}
if (mMol.getAllConnAtoms(atom) == 1) {
if (angle[0] < -Math.PI * 5 / 6) {
newAngle = Math.PI / 3;
} else if (angle[0] < -Math.PI / 2) {
newAngle = Math.PI * 2 / 3;
} else if (angle[0] < -Math.PI / 6) {
newAngle = Math.PI / 3;
} else if (angle[0] < 0.0) {
newAngle = Math.PI * 2 / 3;
} else if (angle[0] < Math.PI / 6) {
newAngle = -Math.PI * 2 / 3;
} else if (angle[0] < Math.PI / 2) {
newAngle = -Math.PI / 3;
} else if (angle[0] < Math.PI * 5 / 6) {
newAngle = -Math.PI * 2 / 3;
} else {
newAngle = -Math.PI / 3;
}
} else {
for (int i = mMol.getAllConnAtoms(atom) - 1; i > 0; i--) { // bubble sort
for (int j = 0; j < i; j++) {
if (angle[j] > angle[j + 1]) {
double temp = angle[j];
angle[j] = angle[j + 1];
angle[j + 1] = temp;
}
}
}
angle[mMol.getAllConnAtoms(atom)] = angle[0] + Math.PI * 2;
int largestNo = 0;
double largestDiff = 0.0;
for (int i = 0; i < mMol.getAllConnAtoms(atom); i++) {
double angleDiff = angle[i + 1] - angle[i];
if (largestDiff < angleDiff) {
largestDiff = angleDiff;
largestNo = i;
}
}
newAngle = (angle[largestNo] + angle[largestNo + 1]) / 2;
}
}
double avbl = mMol.getAverageBondLength();
mX2 = ((atom == -1) ? mX1 : mMol.getAtomX(atom)) + avbl * (float) Math.sin(newAngle);
mY2 = ((atom == -1) ? mY1 : mMol.getAtomY(atom)) + avbl * (float) Math.cos(newAngle);
}
private boolean areAtomsMappingCompatible(int atom1, int atom2)
{
if (mMol.isFragment()) {
int[] atomList1 = mMol.getAtomList(atom1);
int[] atomList2 = mMol.getAtomList(atom2);
if (atomList1 == null ^ atomList2 == null) {
return false;
}
if (atomList1 != null) {
if (atomList1.length != atomList2.length) {
return false;
}
for (int i = 0; i < atomList1.length; i++) {
if (atomList1[i] != atomList2[i]) {
return false;
}
}
}
boolean isAny1 = ((mMol.getAtomQueryFeatures(atom1) & Molecule.cAtomQFAny) != 0);
boolean isAny2 = ((mMol.getAtomQueryFeatures(atom2) & Molecule.cAtomQFAny) != 0);
if (isAny1 != isAny2) {
return false;
}
}
return mMol.getAtomicNo(atom1) == mMol.getAtomicNo(atom2);
}
private boolean trackHiliting(double x, double y, boolean isDragging)
{
int theAtom = mMol.findAtom(x, y);
int theBond = -1;
if (isDragging
&& mPendingRequest == cRequestMapAtoms
&& theAtom != -1
&& (!areAtomsMappingCompatible(mAtom1, theAtom)
|| (mMol.getAtomMapNo(mAtom1) != 0 && mMol.getAtomMapNo(mAtom1) == mMol.getAtomMapNo(theAtom) && !mMol.isAutoMappedAtom(mAtom1))
|| shareSameReactionSide(mAtom1, theAtom))) {
theAtom = -1;
}
if (theAtom != -1) {
if (mCurrentTool == JDrawToolbar.cToolESRAbs
|| mCurrentTool == JDrawToolbar.cToolESRAnd
|| mCurrentTool == JDrawToolbar.cToolESROr) {
theBond = mMol.getStereoBond(theAtom);
theAtom = -1;
} else if (mCurrentTool == JDrawToolbar.cToolMapper
&& theAtom >= mMol.getAtoms()) {
theAtom = -1;
}
}
if (theBond == -1
&& theAtom == -1
&& mCurrentTool != JDrawToolbar.cToolChain
&& mCurrentTool != JDrawToolbar.cToolMapper
&& mCurrentTool != JDrawToolbar.cToolUnknownParity
&& mCurrentTool != JDrawToolbar.cToolPosCharge
&& mCurrentTool != JDrawToolbar.cToolNegCharge
&& mCurrentTool != JDrawToolbar.cToolAtomH
&& mCurrentTool != JDrawToolbar.cToolAtomC
&& mCurrentTool != JDrawToolbar.cToolAtomN
&& mCurrentTool != JDrawToolbar.cToolAtomO
&& mCurrentTool != JDrawToolbar.cToolAtomSi
&& mCurrentTool != JDrawToolbar.cToolAtomP
&& mCurrentTool != JDrawToolbar.cToolAtomS
&& mCurrentTool != JDrawToolbar.cToolAtomF
&& mCurrentTool != JDrawToolbar.cToolAtomCl
&& mCurrentTool != JDrawToolbar.cToolAtomBr
&& mCurrentTool != JDrawToolbar.cToolAtomI
&& mCurrentTool != JDrawToolbar.cToolAtomOther) {
theBond = mMol.findBond(x, y);
}
if (theBond != -1
&& (mCurrentTool == JDrawToolbar.cToolESRAbs
|| mCurrentTool == JDrawToolbar.cToolESRAnd
|| mCurrentTool == JDrawToolbar.cToolESROr)
&& !qualifiesForESR(theBond)) {
theBond = -1;
}
// don't change object hiliting while dragging
AbstractDrawingObject hiliteObject = mCurrentHiliteObject;
if (!isDragging && mDrawingObjectList != null) {
hiliteObject = null;
if (theAtom == -1 && theBond == -1
&& (mCurrentTool == JDrawToolbar.cToolLassoPointer
|| mCurrentTool == JDrawToolbar.cToolDelete
|| mCurrentTool == JDrawToolbar.cToolText)) {
for (AbstractDrawingObject theObject : mDrawingObjectList) {
if (mCurrentTool == JDrawToolbar.cToolLassoPointer
|| (mCurrentTool == JDrawToolbar.cToolDelete && !(theObject instanceof ReactionArrow))
|| (mCurrentTool == JDrawToolbar.cToolText && theObject instanceof TextDrawingObject)) {
if (theObject.checkHiliting(x, y)) {
hiliteObject = theObject;
if (mCurrentHiliteObject != null && mCurrentHiliteObject != theObject) {
mCurrentHiliteObject.clearHiliting();
}
break;
}
}
}
}
}
boolean repaintNeeded = (mCurrentHiliteAtom != theAtom
|| mCurrentHiliteBond != theBond
|| mCurrentHiliteObject != hiliteObject
|| hiliteObject != null);
if (mCurrentHiliteAtom != theAtom) {
if (mCurrentHiliteAtom != -1 && mAtomKeyStrokeBuffer.length() != 0)
expandAtomKeyStrokes(mAtomKeyStrokeBuffer.toString());
mCurrentHiliteAtom = theAtom;
mAtomKeyStrokeBuffer.setLength(0);
fireEvent(new DrawAreaEvent(this, DrawAreaEvent.TYPE_HILITE_ATOM_CHANGED, true));
}
if (mCurrentHiliteBond != theBond) {
mCurrentHiliteBond = theBond;
fireEvent(new DrawAreaEvent(this, DrawAreaEvent.TYPE_HILITE_BOND_CHANGED, true));
}
mCurrentHiliteObject = hiliteObject;
return repaintNeeded;
}
private int getAtomKeyStrokeValidity(String s)
{
if (Molecule.getAtomicNoFromLabel(s) != 0)
return KEY_IS_ATOM_LABEL;
if (NamedSubstituents.getSubstituentIDCode(s) != null)
return KEY_IS_SUBSTITUENT;
if (isValidAtomKeyStrokeStart(s))
return KEY_IS_VALID_START;
return KEY_IS_INVALID;
}
/**
* @param s
* @return true if s is either a valid atom symbol or a valid substituent name
*/
private boolean isValidAtomKeyStroke(String s)
{
return Molecule.getAtomicNoFromLabel(s) != 0
|| NamedSubstituents.getSubstituentIDCode(s) != null;
}
/**
* @param s
* @return true if adding one or more chars may still create a valid key stroke sequence
*/
private boolean isValidAtomKeyStrokeStart(String s)
{
if (s.length() < 3)
for (int i = 1; i < Molecule.cAtomLabel.length; i++)
if (Molecule.cAtomLabel[i].startsWith(s))
return true;
return NamedSubstituents.isValidSubstituentNameStart(s);
}
private void expandAtomKeyStrokes(String keyStrokes)
{
mAtomKeyStrokeBuffer.setLength(0);
int atomicNo = Molecule.getAtomicNoFromLabel(keyStrokes);
if (atomicNo != 0) {
storeState();
if (mMol.changeAtom(mCurrentHiliteAtom, atomicNo, 0, -1, 0)) {
fireMoleculeChanged();
update(UPDATE_CHECK_COORDS);
return;
}
}
StereoMolecule substituent = NamedSubstituents.getSubstituent(keyStrokes);
if (substituent != null) {
storeState();
// Copy the the fragment containing the attachment point into a new molecule.
// Then attach the substituent, create new atom coordinates for the substituent,
// while retaining coordinates of the fragment.
StereoMolecule fragment = new StereoMolecule();
fragment.addFragment(mMol, mCurrentHiliteAtom, null);
double sourceAVBL = fragment.getAverageBondLength();
int firstAtomInFragment = fragment.getAllAtoms();
for (int atom = 0; atom < fragment.getAllAtoms(); atom++)
fragment.setAtomMarker(atom, true);
fragment.addSubstituent(substituent, 0);
new CoordinateInventor(CoordinateInventor.MODE_KEEP_MARKED_ATOM_COORDS).invent(fragment);
double dx = mMol.getAtomX(mCurrentHiliteAtom) - sourceAVBL * fragment.getAtomX(0);
double dy = mMol.getAtomY(mCurrentHiliteAtom) - sourceAVBL * fragment.getAtomY(0);
// Attach the substituent to the complete molecule and take coodinates from the
// previously created fragment-substituent species.
int firstAtomInMol = mMol.getAllAtoms();
mMol.addSubstituent(substituent, mCurrentHiliteAtom);
int substituentAtoms = mMol.getAllAtoms() - firstAtomInMol;
for (int i = 0; i < substituentAtoms; i++) {
mMol.setAtomX(firstAtomInMol + i, sourceAVBL * fragment.getAtomX(firstAtomInFragment + i) + dx);
mMol.setAtomY(firstAtomInMol + i, sourceAVBL * fragment.getAtomY(firstAtomInFragment + i) + dy);
}
mMol.setStereoBondsFromParity();
fireMoleculeChanged();
update(UPDATE_CHECK_COORDS);
}
}
private AbstractDrawingObject findDrawingObject(double x, double y, String type, boolean forDeletion)
{
if (mDrawingObjectList != null) {
for (AbstractDrawingObject drawingObject : mDrawingObjectList) {
if ((type == null || type.equals(drawingObject.getTypeString())
&& !forDeletion || drawingObject.isDeletable())
&& drawingObject.contains(x, y)) {
return drawingObject;
}
}
}
return null;
}
private void editTextObject(TextDrawingObject object)
{
Component c = this;
while (c.getParent() != null) {
c = c.getParent();
}
new JTextDrawingObjectDialog((Frame) c, object);
boolean nonWhiteSpaceFound = false;
for (int i = 0; i < object.getText().length(); i++) {
if (!Character.isWhitespace(object.getText().charAt(i))) {
nonWhiteSpaceFound = true;
break;
}
}
if (!nonWhiteSpaceFound) {
mDrawingObjectList.remove(object);
}
repaint();
}
private boolean shareSameReactionSide(int atom1, int atom2)
{
ReactionArrow arrow = (ReactionArrow) mDrawingObjectList.get(0);
return !(arrow.isOnProductSide(mMol.getAtomX(atom1), mMol.getAtomY(atom1))
^ arrow.isOnProductSide(mMol.getAtomX(atom2), mMol.getAtomY(atom2)));
}
protected void restoreState()
{
if (mUndoMol == null) {
return;
}
mUndoMol.copyMolecule(mMol);
mDrawingObjectList = (mUndoDrawingObjectList == null) ?
null : new DrawingObjectList(mUndoDrawingObjectList);
}
protected void storeState()
{
if (mUndoMol == null) {
mUndoMol = new Molecule();
}
mMol.copyMolecule(mUndoMol);
mUndoDrawingObjectList = (mDrawingObjectList == null) ?
null : new DrawingObjectList(mDrawingObjectList);
}
private boolean deleteHilited()
{
if (mCurrentHiliteAtom != -1) {
mMol.deleteAtom(mCurrentHiliteAtom);
mCurrentHiliteAtom = -1;
fireMoleculeChanged();
update(UPDATE_REDRAW);
return true;
}
if (mCurrentHiliteBond != -1) {
mMol.deleteBondAndSurrounding(mCurrentHiliteBond);
mCurrentHiliteBond = -1;
fireMoleculeChanged();
update(UPDATE_REDRAW);
return true;
}
if (mCurrentHiliteObject != null && mCurrentHiliteObject.isDeletable()) {
mDrawingObjectList.remove(mCurrentHiliteObject);
mCurrentHiliteObject = null;
update(UPDATE_REDRAW);
return true;
}
return false;
}
private boolean deleteAt(double x, double y)
{
if (mMol.deleteAtomOrBond(x, y)) {
fireMoleculeChanged();
update(UPDATE_REDRAW);
return true;
}
AbstractDrawingObject drawingObject = findDrawingObject(x, y, null, true);
if (drawingObject != null) {
mDrawingObjectList.remove(drawingObject);
mCurrentHiliteObject = null;
update(UPDATE_REDRAW);
return true;
}
return false;
}
private void duplicateSelected()
{
int atomCount = 0;
for (int atom = 0; atom < mMol.getAllAtoms(); atom++) {
if (mMol.isSelectedAtom(atom)) {
atomCount++;
}
}
// int bondCount = 0;
// for (int bond=0; bond= 0; i--) {
AbstractDrawingObject object = (AbstractDrawingObject) mDrawingObjectList.get(i);
if (object.isSelected() && !(object instanceof ReactionArrow)) {
mDrawingObjectList.add(object.clone());
}
}
}
}
private void fireMoleculeChanged()
{
fireEvent(new DrawAreaEvent(this, DrawAreaEvent.TYPE_MOLECULE_CHANGED, true));
}
private void fireEvent(DrawAreaEvent e)
{
for (DrawAreaListener l : mListeners) {
l.contentChanged(e);
}
}
/**
* Use this to inform the JDrawArea after changing its molecule from outside.
*/
public void moleculeChanged()
{
moleculeChanged(false);
}
/**
* Ideally don't use this from outside JDrawArea. Use moleculeChanged() instead.
*
* @param userChange is true if the change was done within the editor
*/
public void moleculeChanged(boolean userChange)
{
fireEvent(new DrawAreaEvent(this, DrawAreaEvent.TYPE_MOLECULE_CHANGED, userChange));
update(UPDATE_SCALE_COORDS);
}
public StereoMolecule getMolecule()
{
return mMol;
}
public void setMolecule(StereoMolecule theMolecule)
{
if (mMol == theMolecule) {
return;
}
mMol = theMolecule;
storeState();
moleculeChanged(false);
}
public StereoMolecule[] getFragments()
{
return mFragment;
}
public void setFragments(StereoMolecule[] fragment)
{
mMol.deleteMolecule();
mFragment = fragment;
for (int i = 0; i < fragment.length; i++) {
mMol.addMolecule(mFragment[i]);
}
storeState();
mFragmentNo = new int[mMol.getAllAtoms()];
for (int atom = 0, f = 0; f < mFragment.length; f++) {
for (int j = 0; j < mFragment[f].getAllAtoms(); j++) {
mFragmentNo[atom++] = f;
}
}
fireEvent(new DrawAreaEvent(this, DrawAreaEvent.TYPE_MOLECULE_CHANGED, false));
mMode = MODE_MULTIPLE_FRAGMENTS;
update(UPDATE_SCALE_COORDS_USE_FRAGMENTS);
}
/**
* @return mapped reaction with absolute coordinates, but without drawing objects
*/
public Reaction getReaction()
{
if ((mMode & MODE_REACTION) == 0 || mFragment == null) {
return null;
}
Reaction rxn = new Reaction();
for (int i = 0; i < mFragment.length; i++) {
if (i < mReactantCount) {
rxn.addReactant(mFragment[i]);
} else {
rxn.addProduct(mFragment[i]);
}
}
return rxn;
}
/**
* @return mapped reaction with absolute coordinates and drawing objects
*/
public Reaction getReactionAndDrawings()
{
Reaction rxn = getReaction();
if (rxn != null) {
rxn.setDrawingObjects(getDrawingObjects());
}
return rxn;
}
public void setReaction(Reaction rxn)
{
mMol.deleteMolecule();
mFragment = new StereoMolecule[rxn.getMolecules()];
mReactantCount = rxn.getReactants();
boolean isFragment = false;
for (int i = 0; i < rxn.getMolecules(); i++) {
isFragment |= rxn.getMolecule(i).isFragment();
mFragment[i] = rxn.getMolecule(i);
mMol.addMolecule(mFragment[i]);
}
mMol.setFragment(isFragment);
storeState();
mFragmentNo = new int[mMol.getAllAtoms()];
for (int atom = 0, f = 0; f < mFragment.length; f++) {
for (int j = 0; j < mFragment[f].getAllAtoms(); j++) {
mFragmentNo[atom++] = f;
}
}
fireEvent(new DrawAreaEvent(this, DrawAreaEvent.TYPE_MOLECULE_CHANGED, false));
mMode = MODE_MULTIPLE_FRAGMENTS | MODE_REACTION;
update(UPDATE_SCALE_COORDS_USE_FRAGMENTS);
}
public MarkushStructure getMarkushStructure()
{
if ((mMode & MODE_MARKUSH_STRUCTURE) == 0) {
return null;
}
MarkushStructure markush = new MarkushStructure();
for (int i = 0; i < mFragment.length; i++) {
if (i < mReactantCount) {
markush.addCore(mFragment[i]);
} else {
markush.addRGroup(mFragment[i]);
}
}
return markush;
}
public void setMarkushStructure(MarkushStructure markush)
{
mMol.deleteMolecule();
mFragment = new StereoMolecule[markush.getCoreCount() + markush.getRGroupCount()];
mReactantCount = markush.getCoreCount();
boolean isFragment = false;
for (int i = 0; i < markush.getCoreCount() + markush.getRGroupCount(); i++) {
mFragment[i] = (i < markush.getCoreCount()) ? markush.getCoreStructure(i)
: markush.getRGroup(i - markush.getCoreCount());
isFragment |= mFragment[i].isFragment();
mMol.addMolecule(mFragment[i]);
}
mMol.setFragment(isFragment);
storeState();
mFragmentNo = new int[mMol.getAllAtoms()];
for (int atom = 0, f = 0; f < mFragment.length; f++) {
for (int j = 0; j < mFragment[f].getAllAtoms(); j++) {
mFragmentNo[atom++] = f;
}
}
fireEvent(new DrawAreaEvent(this, DrawAreaEvent.TYPE_MOLECULE_CHANGED, false));
mMode = MODE_MULTIPLE_FRAGMENTS | MODE_MARKUSH_STRUCTURE;
update(UPDATE_SCALE_COORDS_USE_FRAGMENTS);
}
public void setDisplayMode(int dMode)
{
mDisplayMode = dMode;
update(UPDATE_REDRAW);
}
/**
* Defines additional atom text to be displayed in top right
* position of some/all atom label. If the atom is charged, then
* the atom text follows the charge information.
* If using atom text make sure to update it accordingly, if atom
* indexes change due to molecule changes.
* Atom text is not supported for MODE_REACTION, MODE_MULTIPLE_FRAGMENTS or MODE_MARKUSH_STRUCTURE.
*
* @param atomText String[] matching atom indexes (may contain null entries)
*/
public void setAtomText(String[] atomText)
{
mAtomText = atomText;
}
public DrawingObjectList getDrawingObjects()
{
return mDrawingObjectList;
}
public void setDrawingObjects(DrawingObjectList drawingObjectList)
{
mDrawingObjectList = drawingObjectList;
storeState();
update(UPDATE_SCALE_COORDS);
}
// Added by CXR
public int getMode()
{
return mMode;
}
public int getHiliteAtom()
{
return mCurrentHiliteAtom;
}
public int getHiliteBond()
{
return mCurrentHiliteBond;
}
public void setHiliteBondSet(int[] bondSet)
{
mHiliteBondSet = bondSet;
update(UPDATE_REDRAW);
}
public void setReactionMode(boolean rxn)
{
if (rxn) {
Molecule m[] = this.getFragments();
if (m == null) {
setReaction(new Reaction(new StereoMolecule[]{new StereoMolecule(this.getMolecule())}, 1));
} else {
mMode = MODE_MULTIPLE_FRAGMENTS | MODE_REACTION;
Reaction r = getReaction();
setReaction(r);
}
} else {
mMode &= ~MODE_REACTION;
}
}
// CXR Need this method for derived components
protected void setUpdateMode(int mode)
{
mUpdateMode = mode;
}
public boolean isAtomColorSupported()
{
return mAtomColorSupported;
}
public void setAtomColorSupported(boolean acs)
{
mAtomColorSupported = acs;
}
private void cleanupCoordinates(Graphics g, ExtendedDepictor depictor)
{
int selectedAtomCount = 0;
for (int atom = 0; atom < mMol.getAllAtoms(); atom++) {
if (mMol.isSelectedAtom(atom)) {
selectedAtomCount++;
}
}
boolean selectedOnly = (selectedAtomCount != 0 && selectedAtomCount != mMol.getAllAtoms());
if ((mMode & MODE_MULTIPLE_FRAGMENTS) == 0) {
cleanupMoleculeCoordinates(g, depictor, selectedOnly);
} else {
cleanupMultiFragmentCoordinates(g, depictor, selectedOnly);
}
if (selectedOnly)
mMol.removeAtomMarkers();
}
private void cleanupMoleculeCoordinates(Graphics g, ExtendedDepictor depictor, boolean selectedOnly)
{
if (mUpdateMode == UPDATE_INVENT_COORDS) {
if (selectedOnly) {
for (int atom = 0; atom < mMol.getAllAtoms(); atom++) {
mMol.setAtomMarker(atom, !mMol.isSelectedAtom(atom));
}
}
new CoordinateInventor(selectedOnly ? CoordinateInventor.MODE_KEEP_MARKED_ATOM_COORDS : 0).invent(mMol);
mMol.setStereoBondsFromParity();
}
depictor.updateCoords(g, new Rectangle2D.Double(0, 0, getWidth(), getHeight()), maxUpdateMode());
}
private void cleanupMultiFragmentCoordinates(Graphics g, ExtendedDepictor depictor, boolean selectedOnly)
{
if (selectedOnly && mUpdateMode == UPDATE_INVENT_COORDS) {
int[] fragmentAtom = new int[mFragment.length];
for (int atom = 0; atom < mMol.getAllAtoms(); atom++) {
int fragment = mFragmentNo[atom];
mFragment[fragment].setAtomMarker(fragmentAtom[fragment], !mMol.isSelectedAtom(atom));
fragmentAtom[fragment]++;
}
}
Rectangle2D.Double[] boundingRect = new Rectangle2D.Double[mFragment.length];
// float fragmentWidth = 0.0f;
for (int fragment = 0; fragment < mFragment.length; fragment++) {
if (mUpdateMode == UPDATE_INVENT_COORDS) {
new CoordinateInventor(selectedOnly ? CoordinateInventor.MODE_KEEP_MARKED_ATOM_COORDS : 0).invent(mFragment[fragment]);
mFragment[fragment].setStereoBondsFromParity();
}
Depictor d = new Depictor(mFragment[fragment]);
d.updateCoords(g, null, AbstractDepictor.cModeInflateToMaxAVBL);
boundingRect[fragment] = d.getBoundingRect();
// fragmentWidth += boundingRect[fragment].width;
}
double spacing = FRAGMENT_CLEANUP_DISTANCE * AbstractDepictor.cOptAvBondLen;
double avbl = mMol.getAverageBondLength();
double arrowWidth = ((mMode & MODE_REACTION) == 0) ?
0f
: (mUpdateMode == UPDATE_SCALE_COORDS_USE_FRAGMENTS) ?
DEFAULT_ARROW_LENGTH * getWidth()
: ((ReactionArrow) mDrawingObjectList.get(0)).getLength() * AbstractDepictor.cOptAvBondLen / avbl;
double rawX = 0.5 * spacing;
for (int fragment = 0; fragment <= mFragment.length; fragment++) {
if ((mMode & MODE_REACTION) != 0 && fragment == mReactantCount) {
((ReactionArrow) mDrawingObjectList.get(0)).setCoordinates(
rawX - spacing / 2, getHeight() / 2, rawX - spacing / 2 + arrowWidth, getHeight() / 2);
rawX += arrowWidth;
}
if (fragment == mFragment.length) {
break;
}
double dx = rawX - boundingRect[fragment].x;
double dy = 0.5 * (getHeight() - boundingRect[fragment].height)
- boundingRect[fragment].y;
mFragment[fragment].translateCoords(dx, dy);
rawX += spacing + boundingRect[fragment].width;
}
depictor.updateCoords(g, new Rectangle2D.Double(0, 0, getWidth(), getHeight()), maxUpdateMode());
int[] fragmentAtom = new int[mFragment.length];
for (int atom = 0; atom < mMol.getAllAtoms(); atom++) {
int fragment = mFragmentNo[atom];
mMol.setAtomX(atom, mFragment[fragment].getAtomX(fragmentAtom[fragment]));
mMol.setAtomY(atom, mFragment[fragment].getAtomY(fragmentAtom[fragment]));
fragmentAtom[fragment]++;
}
mMol.setStereoBondsFromParity();
}
private void analyzeFragmentMembership()
{
mMol.ensureHelperArrays(Molecule.cHelperParities);
int[] fragmentNo = new int[mMol.getAllAtoms()];
int fragments = mMol.getFragmentNumbers(fragmentNo, false);
fragments = joinCloseFragments(fragmentNo, fragments);
sortFragmentsByPosition(fragmentNo, fragments);
mFragmentNo = fragmentNo;
mFragment = mMol.getFragments(fragmentNo, fragments);
}
private void syncFragments()
{
mMol.ensureHelperArrays(Molecule.cHelperParities);
int[] fragmentNo = new int[mMol.getAllAtoms()];
int fragments = mMol.getFragmentNumbers(fragmentNo, false);
// fragments = joinCloseFragments(fragmentNo, fragments);
sortFragmentsByPosition(fragmentNo, fragments);
mFragmentNo = fragmentNo;
mFragment = mMol.getFragments(fragmentNo, fragments);
for (StereoMolecule m : mFragment) {
m.ensureHelperArrays(Molecule.cHelperParities);
}
}
private int joinCloseFragments(int[] fragmentNo, int fragments)
{
if (fragments < 2) {
return fragments;
}
boolean[][] mergeFragments = new boolean[fragments][];
for (int i = 1; i < fragments; i++) {
mergeFragments[i] = new boolean[i];
}
double avbl = mMol.getAverageBondLength();
for (int atom1 = 1; atom1 < mMol.getAllAtoms(); atom1++) {
for (int atom2 = 0; atom2 < atom1; atom2++) {
double dx = mMol.getAtomX(atom2) - mMol.getAtomX(atom1);
double dy = mMol.getAtomY(atom2) - mMol.getAtomY(atom1);
double distance = Math.sqrt(dx * dx + dy * dy);
if (distance < FRAGMENT_GROUPING_DISTANCE * avbl) {
int fragment1 = fragmentNo[atom1];
int fragment2 = fragmentNo[atom2];
if (fragment1 != fragment2) {
if (fragment1 > fragment2) {
mergeFragments[fragment1][fragment2] = true;
} else {
mergeFragments[fragment2][fragment1] = true;
}
}
}
}
}
int[] newFragmentIndex = new int[fragments];
for (int fragment = 0; fragment < fragments; fragment++) {
newFragmentIndex[fragment] = fragment;
}
int mergeCount = 0;
for (int i = 1; i < fragments; i++) {
for (int j = 0; j < i; j++) {
if (mergeFragments[i][j]) {
int index1 = newFragmentIndex[i];
int index2 = newFragmentIndex[j];
if (index1 != index2) {
mergeCount++;
int minIndex = Math.min(index1, index2);
int maxIndex = Math.max(index1, index2);
for (int k = 0; k < fragments; k++) {
if (newFragmentIndex[k] == maxIndex) {
newFragmentIndex[k] = minIndex;
} else if (newFragmentIndex[k] > maxIndex) {
newFragmentIndex[k]--;
}
}
}
}
}
}
for (int atom = 0; atom < mMol.getAllAtoms(); atom++) {
fragmentNo[atom] = newFragmentIndex[fragmentNo[atom]];
}
return fragments - mergeCount;
}
private void sortFragmentsByPosition(int[] fragmentNo, int fragments)
{
int[][] fragmentDescriptor = new int[fragments][((mMode & (MODE_REACTION | MODE_MARKUSH_STRUCTURE)) != 0) ? 2 : 1];
for (int fragment = 0; fragment < fragments; fragment++) {
fragmentDescriptor[fragment][0] = fragment;
}
Point[] fragmentCOG = calculateFragmentCenterOfGravity(fragmentNo, fragments);
if ((mMode & MODE_REACTION) != 0) {
mReactantCount = 0;
ReactionArrow arrow = ((mMode & MODE_REACTION) != 0) ? (ReactionArrow) mDrawingObjectList.get(0) : null;
for (int fragment = 0; fragment < fragments; fragment++) {
fragmentDescriptor[fragment][1] = (arrow.isOnProductSide(fragmentCOG[fragment].x,
fragmentCOG[fragment].y)) ? 1 : 0;
if (fragmentDescriptor[fragment][1] == 0) {
mReactantCount++;
}
}
} else if ((mMode & MODE_MARKUSH_STRUCTURE) != 0) {
mReactantCount = fragments;
for (int atom = 0; atom < mMol.getAllAtoms(); atom++) {
if (mMol.getAtomicNo(atom) == 0 && fragmentDescriptor[fragmentNo[atom]][1] == 0) {
fragmentDescriptor[fragmentNo[atom]][1] = 1;
mReactantCount--;
}
}
}
final Point[] cog = fragmentCOG;
Arrays.sort(fragmentDescriptor, new Comparator()
{
public int compare(int[] fragmentDescriptor1, int[] fragmentDescriptor2)
{
if ((mMode & (MODE_REACTION | MODE_MARKUSH_STRUCTURE)) != 0) {
if (fragmentDescriptor1[1] != fragmentDescriptor2[1]) {
return (fragmentDescriptor1[1] == 0) ? -1 : 1;
}
}
return (cog[fragmentDescriptor1[0]].x
+ cog[fragmentDescriptor1[0]].y
< cog[fragmentDescriptor2[0]].x
+ cog[fragmentDescriptor2[0]].y) ? -1 : 1;
}
});
int[] newFragmentIndex = new int[fragments];
Point[] centerOfGravity = new Point[fragments];
for (int fragment = 0; fragment < fragments; fragment++) {
int oldIndex = ((int[]) fragmentDescriptor[fragment])[0];
newFragmentIndex[oldIndex] = fragment;
centerOfGravity[fragment] = fragmentCOG[oldIndex];
}
fragmentCOG = centerOfGravity;
for (int atom1 = 0; atom1 < mMol.getAllAtoms(); atom1++) {
fragmentNo[atom1] = newFragmentIndex[fragmentNo[atom1]];
}
}
private Point[] calculateFragmentCenterOfGravity(int[] fragmentNo, int fragments)
{
Point[] fragmentCOG = new Point[fragments];
int[] fragmentAtoms = new int[fragments];
for (int fragment = 0; fragment < fragments; fragment++) {
fragmentCOG[fragment] = new Point(0, 0);
}
for (int atom = 0; atom < mMol.getAllAtoms(); atom++) {
fragmentCOG[fragmentNo[atom]].x += mMol.getAtomX(atom);
fragmentCOG[fragmentNo[atom]].y += mMol.getAtomY(atom);
fragmentAtoms[fragmentNo[atom]]++;
}
for (int fragment = 0; fragment < fragments; fragment++) {
fragmentCOG[fragment].x /= fragmentAtoms[fragment];
fragmentCOG[fragment].y /= fragmentAtoms[fragment];
}
return fragmentCOG;
}
private void updateCursor()
{
int cursor = -1;
switch (mCurrentTool) {
case JDrawToolbar.cToolZoom:
cursor = CursorHelper.cZoomCursor;
break;
case JDrawToolbar.cToolLassoPointer:
if ((mCurrentHiliteAtom != -1 && mMol.isSelectedAtom(mCurrentHiliteAtom))
|| (mCurrentHiliteBond != -1 && mMol.isSelectedBond(mCurrentHiliteBond))) {
cursor = mMouseIsDown ? CursorHelper.cFistCursor
: mShiftIsDown ? CursorHelper.cHandPlusCursor
: CursorHelper.cHandCursor;
} else if (mCurrentHiliteAtom != -1
|| mCurrentHiliteBond != -1) {
cursor = CursorHelper.cPointerCursor;
} else if (mCurrentHiliteObject != null) {
cursor = mMouseIsDown ? CursorHelper.cFistCursor
: (mShiftIsDown
&& !(mCurrentHiliteObject instanceof ReactionArrow)) ?
CursorHelper.cHandPlusCursor : CursorHelper.cHandCursor;
} else {
cursor = mShiftIsDown ?
(mAltIsDown ? CursorHelper.cSelectRectPlusCursor : CursorHelper.cLassoPlusCursor)
: (mAltIsDown ? CursorHelper.cSelectRectCursor : CursorHelper.cLassoCursor);
}
break;
case JDrawToolbar.cToolDelete:
cursor = CursorHelper.cDeleteCursor;
break;
case JDrawToolbar.cToolChain:
cursor = CursorHelper.cChainCursor;
break;
case JDrawToolbar.cToolText:
cursor = CursorHelper.cTextCursor;
break;
default:
cursor = CursorHelper.cPointerCursor;
break;
}
if (mCurrentCursor != cursor) {
mCurrentCursor = cursor;
setCursor(CursorHelper.getCursor(cursor));
}
}
private void initializeDragAndDrop(int dropAction)
{
if (dropAction != DnDConstants.ACTION_NONE) {
MoleculeDropAdapter d = new MoleculeDropAdapter()
{
public void onDropMolecule(StereoMolecule m, Point pt)
{
if (m != null && m.getAllAtoms() != 0) {
for (int atom = 0; atom < mMol.getAllAtoms(); atom++) {
mMol.setAtomSelection(atom, false); // deselect all current atoms
}
double avbl = mMol.getAverageBondLength();
double dropAVBL = m.getAverageBondLength();
m.scaleCoords(avbl / dropAVBL);
Point cog = new Point();
for (int atom = 0; atom < m.getAllAtoms(); atom++) {
m.setAtomSelection(atom, true); // select all new atoms
cog.x += m.getAtomX(atom);
cog.y += m.getAtomY(atom);
}
cog.x /= m.getAllAtoms();
cog.y /= m.getAllAtoms();
m.translateCoords(pt.x - cog.x, pt.y - cog.y);
m.removeAtomColors();
m.removeBondHiliting();
mMol.addMolecule(m);
fireMoleculeChanged();
update(UPDATE_CHECK_COORDS);
}
}
};
new DropTarget(this, dropAction, d, true, new OurFlavorMap());
}
}
// This class is needed for inter-jvm drag&drop. Although not neccessary for standard environments, it prevents
// nasty "no native data was transfered" errors. It still might create ClassNotFoundException in the first place by
// the SystemFlavorMap, but as I found it does not hurt, since the context classloader will be installed after
// the first call. I know, that this depends heavely on a specific behaviour of the systemflavormap, but for now
// there's nothing I can do about it.
static class OurFlavorMap implements java.awt.datatransfer.FlavorMap
{
@Override
public java.util.Map getNativesForFlavors(DataFlavor[] dfs)
{
java.awt.datatransfer.FlavorMap m = java.awt.datatransfer.SystemFlavorMap.getDefaultFlavorMap();
return m.getNativesForFlavors(dfs);
}
@Override
public java.util.Map getFlavorsForNatives(String[] natives)
{
java.awt.datatransfer.FlavorMap m = java.awt.datatransfer.SystemFlavorMap.getDefaultFlavorMap();
return m.getFlavorsForNatives(natives);
}
}
/* private void dumpBytesOfGif(String imageFileName) {
byte[] data = new byte[10000];
try {
BufferedInputStream in=new BufferedInputStream(getClass().getResourceAsStream(imageFileName));
int count = in.read(data);
Image image = Toolkit.getDefaultToolkit().createImage(data);
PixelGrabber pg = new PixelGrabber(image, 0, 0, 16, 16, false);
try {
pg.grabPixels();
byte[] pixels = (byte[])pg.getPixels();
int pixelRow = 0;
for (int i=0; i 0 ? new Point2D.Double(sumx / atoms, sumy / atoms) : null;
}
private void flip(boolean horiz)
{
Point2D pt = calculateCenterOfGravity();
if (pt != null) {
// center
moveCoords((float)-pt.getX(), (float)-pt.getY());
if (horiz) {
scaleCoords(-1,1);
} else {
scaleCoords(1,-1);
}
moveCoords((float)pt.getX(), (float)pt.getY());
// invert stereo bonds
for (int bond=0; bond
© 2015 - 2025 Weber Informatics LLC | Privacy Policy