[Git][java-team/openchemlib][master] 3 commits: New upstream version 2020.12.0+dfsg

Andrius Merkys gitlab at salsa.debian.org
Thu Dec 10 16:05:32 GMT 2020



Andrius Merkys pushed to branch master at Debian Java Maintainers / openchemlib


Commits:
6e68284f by Andrius Merkys at 2020-12-10T10:31:42-05:00
New upstream version 2020.12.0+dfsg
- - - - -
ad0a97fd by Andrius Merkys at 2020-12-10T10:31:56-05:00
Update upstream source from tag 'upstream/2020.12.0+dfsg'

Update to upstream version '2020.12.0+dfsg'
with Debian dir b6abfc9f15c8ba1fda4652d201760160ebc63ac7
- - - - -
c1316dfc by Andrius Merkys at 2020-12-10T10:33:04-05:00
Update changelog for 2020.12.0+dfsg-1 release

- - - - -


8 changed files:

- debian/changelog
- pom.xml
- src/main/java/com/actelion/research/gui/CompoundCollectionPane.java
- src/main/java/com/actelion/research/gui/JChemistryView.java
- src/main/java/com/actelion/research/gui/JEditableChemistryView.java
- src/main/java/com/actelion/research/gui/JEditableStructureView.java
- src/main/java/com/actelion/research/gui/JStructureView.java
- src/main/java/com/actelion/research/gui/dnd/MoleculeDropAdapter.java


Changes:

=====================================
debian/changelog
=====================================
@@ -1,3 +1,9 @@
+openchemlib (2020.12.0+dfsg-1) unstable; urgency=medium
+
+  * New upstream version 2020.12.0+dfsg
+
+ -- Andrius Merkys <merkys at debian.org>  Thu, 10 Dec 2020 10:33:00 -0500
+
 openchemlib (2020.11.4+dfsg-1) unstable; urgency=medium
 
   * New upstream version 2020.11.4+dfsg


=====================================
pom.xml
=====================================
@@ -8,7 +8,7 @@
     Please follow the naming scheme YEAR.MONTH.RELEASE_NO_OF_MONTH
     (eg. 2016.4.1 for second release in Apr 2016)
     -->
-    <version>2020.11.4</version>
+    <version>2020.12.0</version>
 
     <name>OpenChemLib</name>
     <description>Open Source Chemistry Library</description>
@@ -73,6 +73,12 @@
                                 <goals>
                                     <goal>sign</goal>
                                 </goals>
+<!--                                <configuration>-->
+<!--                                    <gpgArguments>-->
+<!--                                        <arg>--pinentry-mode</arg>-->
+<!--                                        <arg>loopback</arg>-->
+<!--                                    </gpgArguments>-->
+<!--                                </configuration>-->
                             </execution>
                         </executions>
                     </plugin>
@@ -189,7 +195,7 @@
         <connection>scm:git:git at github.com:Actelion/openchemlib.git</connection>
         <developerConnection>scm:git:git at github.com:Actelion/openchemlib.git</developerConnection>
         <url>https://github.com/Actelion/openchemlib</url>
-      <tag>openchemlib-2020.11.4</tag>
+      <tag>openchemlib-2020.12.0</tag>
   </scm>
 
     <distributionManagement>


=====================================
src/main/java/com/actelion/research/gui/CompoundCollectionPane.java
=====================================
@@ -25,6 +25,7 @@ import com.actelion.research.gui.dnd.MoleculeDropAdapter;
 import com.actelion.research.gui.dnd.MoleculeTransferable;
 import com.actelion.research.gui.hidpi.HiDPIHelper;
 import com.actelion.research.util.ColorHelper;
+import com.actelion.research.util.CursorHelper;
 
 import javax.swing.*;
 import java.awt.*;
@@ -43,41 +44,41 @@ import java.io.OutputStreamWriter;
 import java.util.ArrayList;
 
 public class CompoundCollectionPane<T> extends JScrollPane
-            implements ActionListener,CompoundCollectionListener,MouseListener,MouseMotionListener,StructureListener {
-    private static final long serialVersionUID = 0x20060904;
+			implements ActionListener,CompoundCollectionListener,MouseListener,MouseMotionListener,StructureListener {
+	private static final long serialVersionUID = 0x20060904;
 
 	private static final String[] MESSAGE = { "<to add compounds use popup menu,", "drag&drop, or paste structure or name(s)>" };
 
 	private static final String ADD = "Add...";
-    private static final String EDIT = "Edit...";
-    private static final String REMOVE = "Remove";
-    private static final String REMOVE_ALL = "Remove All";
-    private static final String COPY = "Copy";
-    private static final String PASTE = "Paste";
-    private static final String OPEN = "Add From File...";
-    private static final String SAVE_DWAR = "Save DataWarrior-File...";
-    private static final String SAVE_SDF2 = "Save SD-File V2...";
-    private static final String SAVE_SDF3 = "Save SD-File V3...";
-
-    public static final int FILE_SUPPORT_NONE = 0;
-    public static final int FILE_SUPPORT_OPEN_FILES = 1;
-    public static final int FILE_SUPPORT_SAVE_FILES = 2;
-    public static final int FILE_SUPPORT_OPEN_AND_SAVE_FILES = 3;
-
-    private static final int ALLOWED_DRAG_ACTIONS = DnDConstants.ACTION_COPY_OR_MOVE;
-    private static final int ALLOWED_DROP_ACTIONS = DnDConstants.ACTION_COPY_OR_MOVE;
-
-    private final static int cWhiteSpace = 4;
+	private static final String EDIT = "Edit...";
+	private static final String REMOVE = "Remove";
+	private static final String REMOVE_ALL = "Remove All";
+	private static final String COPY = "Copy";
+	private static final String PASTE = "Paste";
+	private static final String OPEN = "Add From File...";
+	private static final String SAVE_DWAR = "Save DataWarrior-File...";
+	private static final String SAVE_SDF2 = "Save SD-File V2...";
+	private static final String SAVE_SDF3 = "Save SD-File V3...";
+
+	public static final int FILE_SUPPORT_NONE = 0;
+	public static final int FILE_SUPPORT_OPEN_FILES = 1;
+	public static final int FILE_SUPPORT_SAVE_FILES = 2;
+	public static final int FILE_SUPPORT_OPEN_AND_SAVE_FILES = 3;
+
+	private static final int ALLOWED_DRAG_ACTIONS = DnDConstants.ACTION_COPY_OR_MOVE;
+	private static final int ALLOWED_DROP_ACTIONS = DnDConstants.ACTION_COPY_OR_MOVE;
+
+	private final static int cWhiteSpace = 4;
 
 	private CompoundCollectionModel<T> mModel;
-    private IClipboardHandler   mClipboardHandler;
-    private MoleculeFilter		mCompoundFilter;
-	private int			        mDisplayMode,mSelectedIndex,mHighlightedIndex,
-	                            mEditedIndex,mFileSupport,mStructureSize;
-	private Dimension           mContentSize,mCellSize;
-	private JPanel              mContentPanel;
-	private boolean             mIsVertical,mIsEditable,mIsSelectable,mCreateFragments,
-	                            mShowDropBorder,mIsEnabled,mShowValidationError;
+	private IClipboardHandler   mClipboardHandler;
+	private MoleculeFilter		mCompoundFilter;
+	private int					mDragActions,mDisplayMode,mSelectedIndex,mHighlightedIndex,
+								mEditedIndex,mFileSupport,mStructureSize,mDragIndex,mDropIndex;
+	private Dimension		    mContentSize,mCellSize;
+	private JPanel			    mContentPanel;
+	private boolean			    mIsVertical,mIsEditable,mIsSelectable,mCreateFragments,
+								mIsEnabled,mShowValidationError,mInternalDragAndDropIsMove;
 
 	/**
 	 * This is a visual component to display and edit a compound collection maintained
@@ -93,33 +94,44 @@ public class CompoundCollectionPane<T> extends JScrollPane
 		}
 
 	public CompoundCollectionPane(CompoundCollectionModel<T> model, boolean isVertical, int displayMode,
-	                              int dragAction, int dropAction) {
-	    mModel = model;
-	    mModel.addCompoundCollectionListener(this);
-	    mIsEnabled = true;
-        mIsVertical = isVertical;
-        mDisplayMode = displayMode;
-        mFileSupport = FILE_SUPPORT_OPEN_AND_SAVE_FILES;
-        mStructureSize = 0;
-        mSelectedIndex = -1;
-        mHighlightedIndex = -1;
+								  int dragAction, int dropAction) {
+		mModel = model;
+		mModel.addCompoundCollectionListener(this);
+		mIsEnabled = true;
+		mIsVertical = isVertical;
+		mDisplayMode = displayMode;
+		mFileSupport = FILE_SUPPORT_OPEN_AND_SAVE_FILES;
+		mStructureSize = 0;
+		mSelectedIndex = -1;
+		mHighlightedIndex = -1;
+		mDragIndex = -1;
+		mDropIndex = -1;
 		init();
-        initializeDragAndDrop(dragAction, dropAction);
+		initializeDragAndDrop(dragAction, dropAction);
 		}
 
 	public CompoundCollectionModel<T> getModel() {
-	    return mModel;
-	    }
+		return mModel;
+		}
 
 	public void setEnabled(boolean b) {
-	    super.setEnabled(b);
-	    if (mIsVertical)
-	        getVerticalScrollBar().setEnabled(b);
-	    else
-            getHorizontalScrollBar().setEnabled(b);
-	    mIsEnabled = b;
-	    repaint();
-	    }
+		super.setEnabled(b);
+		if (mIsVertical)
+			getVerticalScrollBar().setEnabled(b);
+		else
+			getHorizontalScrollBar().setEnabled(b);
+		mIsEnabled = b;
+		repaint();
+		}
+
+	/**
+	 * Defines the behaviour for internal drag&drop. The default is false,
+	 * which means that a dragged structure is copied to the new internal position.
+	 * @param b
+	 */
+	public void setInternalDragAndDropIsMove(boolean b) {
+		mInternalDragAndDropIsMove = b;
+		}
 
 	/**
 	 * Defines the width or height of individual structure cells,
@@ -131,7 +143,7 @@ public class CompoundCollectionPane<T> extends JScrollPane
 	public void setStructureSize(int size) {
 		mStructureSize = size;
 		validateSize();
-	    repaint();
+		repaint();
 		}
 
 	/**
@@ -139,9 +151,9 @@ public class CompoundCollectionPane<T> extends JScrollPane
 	 * @param editable
 	 */
 	public void setEditable(boolean editable) {
-	    mIsEditable = editable;
-        updateMouseListening();
-	    }
+		mIsEditable = editable;
+		updateMouseListening();
+		}
 
 	/**
 	 * Defines, whether the popup menu contains 'Open' and/or 'Save' items.
@@ -163,17 +175,17 @@ public class CompoundCollectionPane<T> extends JScrollPane
 		}
 
 	public void setSelectable(boolean selectable) {
-	    mIsSelectable = selectable;
-	    updateMouseListening();
-	    }
+		mIsSelectable = selectable;
+		updateMouseListening();
+		}
 
 	/**
 	 * Defines whether new created structures are fragments of molecules.
 	 * @param createFragments
 	 */
 	public void setCreateFragments(boolean createFragments) {
-	    mCreateFragments = createFragments;
-	    }
+		mCreateFragments = createFragments;
+		}
 
 	/**
 	 * Defines whether a large red question mark is shown
@@ -181,61 +193,61 @@ public class CompoundCollectionPane<T> extends JScrollPane
 	 * @param showError
 	 */
 	public void setShowValidationError(boolean showError) {
-	    mShowValidationError = showError;
-	    }
+		mShowValidationError = showError;
+		}
 
 	/**
-     *  call this in order to get clipboard support
-     */
-    public void setClipboardHandler(IClipboardHandler h) {
-        mClipboardHandler = h;
-        }
+	 *  call this in order to get clipboard support
+	 */
+	public void setClipboardHandler(IClipboardHandler h) {
+		mClipboardHandler = h;
+		}
 
-    public IClipboardHandler getClipboardHandler() {
-        return mClipboardHandler;
-        }
+	public IClipboardHandler getClipboardHandler() {
+		return mClipboardHandler;
+		}
 
 	public void actionPerformed(ActionEvent e) {
-	    if (e.getActionCommand().equals(COPY) && mHighlightedIndex != -1) {
-	        mClipboardHandler.copyMolecule(mModel.getMolecule(mHighlightedIndex));
-	        }
-	    else if (e.getActionCommand().equals(PASTE)) {
-            int index = (mHighlightedIndex == -1) ? mModel.getSize() : mHighlightedIndex;
-            ArrayList<StereoMolecule> molList = mClipboardHandler.pasteMolecules();
-	        if (molList != null) {
-	        	int errorCount = 0;
-	        	for (StereoMolecule mol:molList) {
-		            mol.setFragment(mCreateFragments);
-		            if (mCompoundFilter == null || mCompoundFilter.moleculeQualifies(mol))
-			            mModel.addMolecule(index, mol);
-		            else
-		            	errorCount++;
-		            }
-	        	if (errorCount != 0)
-			        JOptionPane.showMessageDialog(getParentFrame(), errorCount+" compound(s) could not be added, because they doesn't qualify.");
-		        }
-            }
-        else if (e.getActionCommand().equals(ADD)) {
-            editStructure(-1);
-            }
-	    else if (e.getActionCommand().equals(EDIT) && mHighlightedIndex != -1) {
-	        editStructure(mHighlightedIndex);
-	        }
-        else if (e.getActionCommand().equals(REMOVE) && mHighlightedIndex != -1) {
-            mModel.remove(mHighlightedIndex);
-            mHighlightedIndex = -1;
-            }
-        else if (e.getActionCommand().equals(REMOVE_ALL)) {
-            mModel.clear();
-            mHighlightedIndex = -1;
-            }
-        else if (e.getActionCommand().equals(OPEN)) {
-        	ArrayList<StereoMolecule> compounds = new FileHelper(getParentFrame()).readStructuresFromFile(true);
-        	if (compounds != null) {
-        		for (StereoMolecule compound:compounds)
-        			compound.setFragment(mCreateFragments);
-        		if (mCompoundFilter != null) {
-        			int count = 0;
+		if (e.getActionCommand().equals(COPY) && mHighlightedIndex != -1) {
+			mClipboardHandler.copyMolecule(mModel.getMolecule(mHighlightedIndex));
+			}
+		else if (e.getActionCommand().equals(PASTE)) {
+			int index = (mHighlightedIndex == -1) ? mModel.getSize() : mHighlightedIndex;
+			ArrayList<StereoMolecule> molList = mClipboardHandler.pasteMolecules();
+			if (molList != null) {
+				int errorCount = 0;
+				for (StereoMolecule mol:molList) {
+					mol.setFragment(mCreateFragments);
+					if (mCompoundFilter == null || mCompoundFilter.moleculeQualifies(mol))
+						mModel.addMolecule(index, mol);
+					else
+						errorCount++;
+					}
+				if (errorCount != 0)
+					JOptionPane.showMessageDialog(getParentFrame(), errorCount+" compound(s) could not be added, because they doesn't qualify.");
+				}
+			}
+		else if (e.getActionCommand().equals(ADD)) {
+			editStructure(-1);
+			}
+		else if (e.getActionCommand().equals(EDIT) && mHighlightedIndex != -1) {
+			editStructure(mHighlightedIndex);
+			}
+		else if (e.getActionCommand().equals(REMOVE) && mHighlightedIndex != -1) {
+			mModel.remove(mHighlightedIndex);
+			mHighlightedIndex = -1;
+			}
+		else if (e.getActionCommand().equals(REMOVE_ALL)) {
+			mModel.clear();
+			mHighlightedIndex = -1;
+			}
+		else if (e.getActionCommand().equals(OPEN)) {
+			ArrayList<StereoMolecule> compounds = new FileHelper(getParentFrame()).readStructuresFromFile(true);
+			if (compounds != null) {
+				for (StereoMolecule compound:compounds)
+					compound.setFragment(mCreateFragments);
+				if (mCompoundFilter != null) {
+					int count = 0;
 					for (int i = compounds.size() - 1; i >= 0; i--) {
 						if (!mCompoundFilter.moleculeQualifies(compounds.get(i))) {
 							compounds.remove(i);
@@ -247,205 +259,209 @@ public class CompoundCollectionPane<T> extends JScrollPane
 						}
 					}
 				mModel.addMoleculeList(compounds);
-        		}
-        	}
-        else if (e.getActionCommand().equals(SAVE_DWAR)) {
-        	String filename = new FileHelper(getParentFrame()).selectFileToSave(
-        			"Save DataWarrior File", FileHelper.cFileTypeDataWarrior, "Untitled");
-        	if (filename != null) {
-	        	try {
-	        		String title = mCreateFragments ? "Fragment" : "Structure";
-	        		BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filename),"UTF-8"));
-	        		writer.write("<datawarrior-fileinfo>");
-	        		writer.newLine();
-	        		writer.write("<version=\"3.1\">");
-	        		writer.newLine();
-	        		writer.write("<rowcount=\""+mModel.getSize()+"\">");
-	        		writer.newLine();
-	        		writer.write("</datawarrior-fileinfo>");
-	        		writer.newLine();
-	        		writer.write("<column properties>");
-	        		writer.newLine();
-	        		writer.write("<columnName=\""+title+"\">");
-	        		writer.newLine();
-	        		writer.write("<columnProperty=\"specialType\tidcode\">");
-	        		writer.newLine();
-	        		writer.write("<columnName=\"coords\">");
-	        		writer.newLine();
-	        		writer.write("<columnProperty=\"specialType\tidcoordinates2D\">");
-	        		writer.newLine();
-	        		writer.write("<columnProperty=\"parent\t"+title+"\">");
-	        		writer.newLine();
-	        		writer.write("</column properties>");
-	        		writer.newLine();
-	        		writer.write(title+"\tcoords");
-	        		writer.newLine();
-	        		for (int i=0; i<mModel.getSize(); i++) {
-	        			if (mModel instanceof DefaultCompoundCollectionModel.IDCode) {
-	        				String idcode = (String)mModel.getCompound(i);
-	        				int index = idcode.indexOf(' ');
-	        				if (index == -1) {
-	        					writer.write(idcode.substring(0, index));
-	        					writer.write('\t');
-	        					writer.write(idcode.substring(index+1));
-	        					}
-	        				else {
-	        					writer.write(idcode);
-	        					writer.write('\t');
-	        					}
-	        				}
-	        			else {
-	        				Canonizer canonizer = new Canonizer(mModel.getMolecule(i));
-	        				writer.write(canonizer.getIDCode());
-        					writer.write('\t');
-	        				writer.write(canonizer.getEncodedCoordinates());
-	        				}
-	            		writer.newLine();
-	        			}
-	    			writer.close();
-	        		}
-	        	catch (IOException ioe) {
-	                JOptionPane.showMessageDialog(getParentFrame(), ioe.toString());
-	        		}
-        		}
-        	}
-        else if (e.getActionCommand().equals(SAVE_SDF2)
-        	  || e.getActionCommand().equals(SAVE_SDF3)) {
-        	String version = "Version " + (e.getActionCommand().equals(SAVE_SDF2) ? "2" : "3");
-        	String filename = new FileHelper(getParentFrame()).selectFileToSave(
-        			"Save SD-File "+version, FileHelper.cFileTypeSD, "Untitled");
-        	if (filename != null) {
-        		try {
-	    			BufferedWriter theWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filename),"UTF-8"));
+				}
+			}
+		else if (e.getActionCommand().equals(SAVE_DWAR)) {
+			String filename = new FileHelper(getParentFrame()).selectFileToSave(
+					"Save DataWarrior File", FileHelper.cFileTypeDataWarrior, "Untitled");
+			if (filename != null) {
+				try {
+					String title = mCreateFragments ? "Fragment" : "Structure";
+					BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filename),"UTF-8"));
+					writer.write("<datawarrior-fileinfo>");
+					writer.newLine();
+					writer.write("<version=\"3.1\">");
+					writer.newLine();
+					writer.write("<rowcount=\""+mModel.getSize()+"\">");
+					writer.newLine();
+					writer.write("</datawarrior-fileinfo>");
+					writer.newLine();
+					writer.write("<column properties>");
+					writer.newLine();
+					writer.write("<columnName=\""+title+"\">");
+					writer.newLine();
+					writer.write("<columnProperty=\"specialType\tidcode\">");
+					writer.newLine();
+					writer.write("<columnName=\"coords\">");
+					writer.newLine();
+					writer.write("<columnProperty=\"specialType\tidcoordinates2D\">");
+					writer.newLine();
+					writer.write("<columnProperty=\"parent\t"+title+"\">");
+					writer.newLine();
+					writer.write("</column properties>");
+					writer.newLine();
+					writer.write(title+"\tcoords");
+					writer.newLine();
+					for (int i=0; i<mModel.getSize(); i++) {
+						if (mModel instanceof DefaultCompoundCollectionModel.IDCode) {
+							String idcode = (String)mModel.getCompound(i);
+							int index = idcode.indexOf(' ');
+							if (index == -1) {
+								writer.write(idcode.substring(0, index));
+								writer.write('\t');
+								writer.write(idcode.substring(index+1));
+								}
+							else {
+								writer.write(idcode);
+								writer.write('\t');
+								}
+							}
+						else {
+							Canonizer canonizer = new Canonizer(mModel.getMolecule(i));
+							writer.write(canonizer.getIDCode());
+							writer.write('\t');
+							writer.write(canonizer.getEncodedCoordinates());
+							}
+						writer.newLine();
+						}
+					writer.close();
+					}
+				catch (IOException ioe) {
+					JOptionPane.showMessageDialog(getParentFrame(), ioe.toString());
+					}
+				}
+			}
+		else if (e.getActionCommand().equals(SAVE_SDF2)
+			  || e.getActionCommand().equals(SAVE_SDF3)) {
+			String version = "Version " + (e.getActionCommand().equals(SAVE_SDF2) ? "2" : "3");
+			String filename = new FileHelper(getParentFrame()).selectFileToSave(
+					"Save SD-File "+version, FileHelper.cFileTypeSD, "Untitled");
+			if (filename != null) {
+				try {
+					BufferedWriter theWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filename),"UTF-8"));
 	
-	    			for (int i=0; i<mModel.getSize(); i++) {
-	    				StereoMolecule mol = mModel.getMolecule(i);
+					for (int i=0; i<mModel.getSize(); i++) {
+						StereoMolecule mol = mModel.getMolecule(i);
 	
-	                    if (e.getActionCommand().equals(SAVE_SDF3))
-	                        new MolfileV3Creator(mol).writeMolfile(theWriter);
-	                    else
-	                        new MolfileCreator(mol).writeMolfile(theWriter);
+						if (e.getActionCommand().equals(SAVE_SDF3))
+							new MolfileV3Creator(mol).writeMolfile(theWriter);
+						else
+							new MolfileCreator(mol).writeMolfile(theWriter);
 	
-	    				theWriter.write("$$$$");
-	    				theWriter.newLine();
-	    				}
-	    			theWriter.close();
-        			}
-	        	catch (IOException ioe) {
-	                JOptionPane.showMessageDialog(getParentFrame(), ioe.toString());
-	        		}
-        		}
-        	}
-	    }
+						theWriter.write("$$$$");
+						theWriter.newLine();
+						}
+					theWriter.close();
+					}
+				catch (IOException ioe) {
+					JOptionPane.showMessageDialog(getParentFrame(), ioe.toString());
+					}
+				}
+			}
+		}
 
 	private void editStructure(int index) {
-        mEditedIndex = index;
-        StereoMolecule mol = null;
-        if (index == -1) {
-        	mol = new StereoMolecule();
-            mol.setFragment(mCreateFragments);
-        	}
-        else {
-        	mol = mModel.getMolecule(mEditedIndex);
-        	}
-        Component c = getParentFrame();
-        JDrawDialog theDialog = (c instanceof Frame) ? new JDrawDialog((Frame)c, mol) : new JDrawDialog((Dialog)c, mol);
-        theDialog.addStructureListener(this);
-        theDialog.setVisible(true);
-	    }
+		mEditedIndex = index;
+		StereoMolecule mol = null;
+		if (index == -1) {
+			mol = new StereoMolecule();
+			mol.setFragment(mCreateFragments);
+			}
+		else {
+			mol = mModel.getMolecule(mEditedIndex);
+			}
+		Component c = getParentFrame();
+		JDrawDialog theDialog = (c instanceof Frame) ? new JDrawDialog((Frame)c, mol) : new JDrawDialog((Dialog)c, mol);
+		theDialog.addStructureListener(this);
+		theDialog.setVisible(true);
+		}
 
 	private void updateMouseListening() {
-	    if (mIsSelectable || mIsEditable) {
-	        addMouseListener(this);
-	        addMouseMotionListener(this);
-	        }
-	    else {
-	        removeMouseListener(this);
-	        removeMouseMotionListener(this);
-	        }
-	    }
+		if (mIsSelectable || mIsEditable) {
+			addMouseListener(this);
+			addMouseMotionListener(this);
+			}
+		else {
+			removeMouseListener(this);
+			removeMouseMotionListener(this);
+			}
+		}
 
 	private void init() {
 		mContentSize = new Dimension();
 		mContentPanel = new JPanel() {
-            private static final long serialVersionUID = 0x20060904;
+			private static final long serialVersionUID = 0x20060904;
 
-            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);
+			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);
 
 				validateSize();
 
 				Rectangle clipRect = g.getClipBounds();
 
-	            Color background = UIManager.getColor(mIsEnabled ? "TextArea.background" : "TextArea.inactiveBackground");
-	            Color foreground = UIManager.getColor(mIsEnabled ? "TextArea.foreground" : "TextArea.inactiveForeground");
+				Color background = UIManager.getColor(mIsEnabled ? "TextArea.background" : "TextArea.inactiveBackground");
+				Color foreground = UIManager.getColor(mIsEnabled ? "TextArea.foreground" : "TextArea.inactiveForeground");
 				g.setColor(background);
 				g.fillRect(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
 
-		        if (mModel.getSize() != 0) {
-    		        int i1 = Math.max(0, mIsVertical ? clipRect.y / mCellSize.height
-    		                                         : clipRect.x / mCellSize.width);
-    		        int i2 = Math.min(mModel.getSize(), mIsVertical ? 1+(clipRect.y+clipRect.height) / mCellSize.height
-		                                                            : 1+(clipRect.x+clipRect.width) / mCellSize.width);
-			        Color warningColor = (ColorHelper.perceivedBrightness(background) < 0.5f) ?
-					        ColorHelper.brighter(background, 0.8f) : ColorHelper.darker(background, 0.8f);
-
-    		        for (int i=i1; i<i2; i++) {
-                        Rectangle bounds = getMoleculeBounds(i);
-
-                        StereoMolecule compound = mModel.getMoleculeForDisplay(i);
-    					if (mShowValidationError) {
-    					    try {
-    					        compound.validate();
-    					        }
-    					    catch (Exception e) {
-    					        int size = Math.min(bounds.width, bounds.height);
-    					        g.setColor(warningColor);
-    					        g.setFont(g.getFont().deriveFont(Font.BOLD, size));
-    					        FontMetrics m = g.getFontMetrics();
-    					        Rectangle2D b = m.getStringBounds("?", g);
-    					        g.drawString("?", bounds.x+(bounds.width-(int)b.getWidth())/2, bounds.y+(bounds.height-(int)b.getHeight())/2+m.getAscent());
-    					        }
-    					    }
-    
-    					Depictor2D d = new Depictor2D(compound, mDisplayMode);
-    					d.validateView(g,
-    								   new Rectangle2D.Double(bounds.x, bounds.y, bounds.width, bounds.height),
-    								   AbstractDepictor.cModeInflateToMaxAVBL);
-
-		                d.setForegroundColor(foreground, background);
-    					d.paint(g);
-
-    					if (mSelectedIndex == i || mHighlightedIndex == i) {
-    					    g.setColor(!mIsEnabled ? ColorHelper.getContrastColor(Color.GRAY, background)
-    					             : (mSelectedIndex != i) ? Color.BLUE
-    					             : (mHighlightedIndex != i) ? Color.RED : Color.MAGENTA);
-                            g.drawRect(bounds.x-2, bounds.y-2, bounds.width+3, bounds.height+3);
-    					    g.drawRect(bounds.x-1, bounds.y-1, bounds.width+1, bounds.height+1);
-    					    }
-
-    					if (mShowDropBorder) {
-    			            g.setColor(ColorHelper.getContrastColor(Color.GRAY, background));
-    			            Rectangle vb = getViewport().getViewRect();
-    			            g.drawRect(vb.x, vb.y, vb.width-1,vb.height-1);
-    			            g.drawRect(vb.x+1, vb.y+1, vb.width-3, vb.height-3);
-    					    }      
-    					}
+				int i1 = 0;
+				int i2 = 0;
+
+				if (mModel.getSize() != 0) {
+					i1 = Math.max(0, mIsVertical ? clipRect.y / mCellSize.height
+													 : clipRect.x / mCellSize.width);
+					i2 = Math.min(mModel.getSize(), mIsVertical ? 1+(clipRect.y+clipRect.height) / mCellSize.height
+																	: 1+(clipRect.x+clipRect.width) / mCellSize.width);
+					Color warningColor = (ColorHelper.perceivedBrightness(background) < 0.5f) ?
+							ColorHelper.brighter(background, 0.8f) : ColorHelper.darker(background, 0.8f);
+
+					for (int i=i1; i<i2; i++) {
+						Rectangle bounds = getMoleculeBounds(i);
+
+						StereoMolecule compound = mModel.getMoleculeForDisplay(i);
+						if (mShowValidationError) {
+							try {
+								compound.validate();
+								}
+							catch (Exception e) {
+								int size = Math.min(bounds.width, bounds.height);
+								g.setColor(warningColor);
+								g.setFont(g.getFont().deriveFont(Font.BOLD, size));
+								FontMetrics m = g.getFontMetrics();
+								Rectangle2D b = m.getStringBounds("?", g);
+								g.drawString("?", bounds.x+(bounds.width-(int)b.getWidth())/2, bounds.y+(bounds.height-(int)b.getHeight())/2+m.getAscent());
+								}
+							}
+	
+						Depictor2D d = new Depictor2D(compound, mDisplayMode);
+						d.validateView(g,
+									   new Rectangle2D.Double(bounds.x, bounds.y, bounds.width, bounds.height),
+									   AbstractDepictor.cModeInflateToMaxAVBL);
+
+						d.setForegroundColor(foreground, background);
+						d.paint(g);
+
+						if (mSelectedIndex == i || mHighlightedIndex == i) {
+							g.setColor(!mIsEnabled ? ColorHelper.getContrastColor(Color.GRAY, background)
+									 : (mSelectedIndex != i) ? Color.BLUE
+									 : (mHighlightedIndex != i) ? Color.RED : Color.MAGENTA);
+							g.drawRect(bounds.x-2, bounds.y-2, bounds.width+3, bounds.height+3);
+							g.drawRect(bounds.x-1, bounds.y-1, bounds.width+1, bounds.height+1);
+							}
+						}
 					}
-		        else {
+				else {
 					Rectangle bounds = getViewportBorderBounds();
 					g.setColor(foreground);
 					g.setFont(g.getFont().deriveFont(Font.PLAIN, HiDPIHelper.scale(12)));
 					FontMetrics m = g.getFontMetrics();
-					int fontHeight = m.getHeight();
 					for (int i=0; i<MESSAGE.length; i++) {
 						Rectangle2D b = m.getStringBounds(MESSAGE[i], g);
 						g.drawString(MESSAGE[i], bounds.x + (bounds.width - (int)b.getWidth()) / 2,
 								bounds.y + i*m.getHeight() + (bounds.height - MESSAGE.length * m.getHeight()) / 2 + m.getAscent());
 						}
 					}
+
+				if (mIsEnabled && mIsEditable && mDropIndex>=i1 && mDropIndex<=i2) {
+					Rectangle bounds = getMoleculeBounds(mDropIndex);
+					g.setColor(ColorHelper.getContrastColor(Color.GRAY, background));
+					if (mIsVertical)
+						g.fillRect(bounds.x-2, bounds.y-4, bounds.width+4, 5);
+					else
+						g.fillRect(bounds.x-4, bounds.y-2, 5, bounds.height+4);
+					}
 				}
 			};
 		setHorizontalScrollBarPolicy(mIsVertical ? JScrollPane.HORIZONTAL_SCROLLBAR_NEVER : JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
@@ -453,160 +469,162 @@ public class CompoundCollectionPane<T> extends JScrollPane
 		setViewportView(mContentPanel);
 		}
 
-    public void collectionUpdated(int fromIndex, int toIndex) {
-        if (mSelectedIndex >= fromIndex && mSelectedIndex <= toIndex)
-            mSelectedIndex = -1;
-        if (mHighlightedIndex >= fromIndex && mHighlightedIndex <= toIndex)
-            mHighlightedIndex = -1;
+	public void collectionUpdated(int fromIndex, int toIndex) {
+		if (mSelectedIndex >= fromIndex && mSelectedIndex <= toIndex)
+			mSelectedIndex = -1;
+		if (mHighlightedIndex >= fromIndex && mHighlightedIndex <= toIndex)
+			mHighlightedIndex = -1;
 
-        repaint();
-        }
+		repaint();
+		}
 
 	private Rectangle getMoleculeBounds(int molIndex) {
-        int x = cWhiteSpace/2;
-        int y = cWhiteSpace/2;
+		int x = cWhiteSpace/2;
+		int y = cWhiteSpace/2;
 
-        if (mIsVertical)
-            y += molIndex * mCellSize.height;
-        else
-            x += molIndex * mCellSize.width;
+		if (mIsVertical)
+			y += molIndex * mCellSize.height;
+		else
+			x += molIndex * mCellSize.width;
 
-	    return new Rectangle(x, y, mCellSize.width-cWhiteSpace, mCellSize.height-cWhiteSpace);
-	    }
+		return new Rectangle(x, y, mCellSize.width-cWhiteSpace, mCellSize.height-cWhiteSpace);
+		}
 
-    private int getMoleculeIndex(int x, int y) {
-        if (mModel.getSize() == 0 || mCellSize.width == 0 || mCellSize.height == 0)
-            return -1;
+	private int getMoleculeIndex(int x, int y) {
+		if (mModel.getSize() == 0 || mCellSize.width == 0 || mCellSize.height == 0)
+			return -1;
 
-        Point p = getViewport().getViewPosition();
-        int index = (mIsVertical) ? (y+p.y) / mCellSize.height
-                                  : (x+p.x) / mCellSize.width;
-        return (index < mModel.getSize()) ? index : -1;
-        }
+		Point p = getViewport().getViewPosition();
+		int index = (mIsVertical) ? (y+p.y) / mCellSize.height
+								  : (x+p.x) / mCellSize.width;
+		return (index < mModel.getSize()) ? index : -1;
+		}
 
 	public void mouseClicked(MouseEvent e) {
-        if (mIsEnabled && mIsEditable && e.getClickCount() == 2 && mHighlightedIndex != -1) {
-            editStructure(mHighlightedIndex);
-            }
-	    }
+		if (mIsEnabled && mIsEditable && e.getClickCount() == 2 && mHighlightedIndex != -1) {
+			editStructure(mHighlightedIndex);
+			}
+		}
 
 	public void mouseEntered(MouseEvent e) {}
-    public void mouseExited(MouseEvent e) {}
-
-    public void mousePressed(MouseEvent e) {
-        if (mIsEnabled) {
-            if (e.isPopupTrigger()) {
-                handlePopupTrigger(e);
-                }
-            else if (mIsSelectable) {
-                int index = getMoleculeIndex(e.getX(), e.getY());
-                if (mSelectedIndex != index) {
-                    mSelectedIndex = index;
-                    setSelection(index);
-                    repaint();
-                    }
-                }
-            }
-        }
-
-    public void mouseReleased(MouseEvent e) {
-        if (mIsEnabled && e.isPopupTrigger())
-            handlePopupTrigger(e);
-        }
-
-    public void mouseDragged(MouseEvent e) {}
-
-    public void mouseMoved(MouseEvent e) {
-        if (mIsEnabled) {
-            int index = getMoleculeIndex(e.getX(), e.getY());
-            if (mHighlightedIndex != index) {
-                mHighlightedIndex = index;
-                repaint();
-                }
-            }
-        }
-
-    private void handlePopupTrigger(MouseEvent e) {
-        JPopupMenu popup = new JPopupMenu();
-        JMenuItem item = new JMenuItem(ADD);
-        item.addActionListener(this);
-        popup.add(item);
-        if (mHighlightedIndex != -1) {
-            item = new JMenuItem(EDIT);
-            item.addActionListener(this);
-            popup.add(item);
-            item = new JMenuItem(REMOVE);
-            item.addActionListener(this);
-            popup.add(item);
-            }
-
-        if (mModel.getSize() != 0) {
-            item = new JMenuItem(REMOVE_ALL);
-            item.addActionListener(this);
-            popup.add(item);
-        	}
-
-        if (mClipboardHandler != null) {
-            popup.addSeparator();
-            if (mHighlightedIndex != -1) {
-                item = new JMenuItem(COPY);
-                item.addActionListener(this);
-                popup.add(item);
-                }
-            item = new JMenuItem(PASTE);
-            item.addActionListener(this);
-            popup.add(item);
-            }
-
-        if (mFileSupport != 0) {
-            popup.addSeparator();
-            if ((mFileSupport & FILE_SUPPORT_OPEN_FILES) != 0) {
-                item = new JMenuItem(OPEN);
-                item.addActionListener(this);
-                popup.add(item);
-                }
-            if ((mFileSupport & FILE_SUPPORT_SAVE_FILES) != 0 && mModel.getSize() != 0) {
-	            item = new JMenuItem(SAVE_DWAR);
-	            item.addActionListener(this);
-	            popup.add(item);
-	            item = new JMenuItem(SAVE_SDF2);
-	            item.addActionListener(this);
-	            popup.add(item);
-	            item = new JMenuItem(SAVE_SDF3);
-	            item.addActionListener(this);
-	            popup.add(item);
-            	}
-            }
-
-        popup.show(this, e.getX(), e.getY());
-        }
-
-    public void structureChanged(StereoMolecule mol) {
-    	if (mEditedIndex == -1) {	// new structure
-    		if (mol.getAllAtoms() != 0) {
-    			if (mCompoundFilter == null || mCompoundFilter.moleculeQualifies(mol))
+	public void mouseExited(MouseEvent e) {}
+
+	public void mousePressed(MouseEvent e) {
+		if (mIsEnabled) {
+			if (e.isPopupTrigger()) {
+				handlePopupTrigger(e);
+				}
+			else if (mIsSelectable) {
+				int index = getMoleculeIndex(e.getX(), e.getY());
+				if (mSelectedIndex != index) {
+					mSelectedIndex = index;
+					setSelection(index);
+					repaint();
+					}
+				}
+			}
+		}
+
+	public void mouseReleased(MouseEvent e) {
+		if (mIsEnabled && e.isPopupTrigger())
+			handlePopupTrigger(e);
+		}
+
+	public void mouseDragged(MouseEvent e) {}
+
+	public void mouseMoved(MouseEvent e) {
+		if (mIsEnabled) {
+			int index = getMoleculeIndex(e.getX(), e.getY());
+			if (mHighlightedIndex != index) {
+				mHighlightedIndex = index;
+				setCursor(CursorHelper.getCursor(index == -1 ? CursorHelper.cPointerCursor : CursorHelper.cHandCursor));
+				repaint();
+				}
+			}
+		mDragIndex = -1;
+		}
+
+	private void handlePopupTrigger(MouseEvent e) {
+		JPopupMenu popup = new JPopupMenu();
+		JMenuItem item = new JMenuItem(ADD);
+		item.addActionListener(this);
+		popup.add(item);
+		if (mHighlightedIndex != -1) {
+			item = new JMenuItem(EDIT);
+			item.addActionListener(this);
+			popup.add(item);
+			item = new JMenuItem(REMOVE);
+			item.addActionListener(this);
+			popup.add(item);
+			}
+
+		if (mModel.getSize() != 0) {
+			item = new JMenuItem(REMOVE_ALL);
+			item.addActionListener(this);
+			popup.add(item);
+			}
+
+		if (mClipboardHandler != null) {
+			popup.addSeparator();
+			if (mHighlightedIndex != -1) {
+				item = new JMenuItem(COPY);
+				item.addActionListener(this);
+				popup.add(item);
+				}
+			item = new JMenuItem(PASTE);
+			item.addActionListener(this);
+			popup.add(item);
+			}
+
+		if (mFileSupport != 0) {
+			popup.addSeparator();
+			if ((mFileSupport & FILE_SUPPORT_OPEN_FILES) != 0) {
+				item = new JMenuItem(OPEN);
+				item.addActionListener(this);
+				popup.add(item);
+				}
+			if ((mFileSupport & FILE_SUPPORT_SAVE_FILES) != 0 && mModel.getSize() != 0) {
+				item = new JMenuItem(SAVE_DWAR);
+				item.addActionListener(this);
+				popup.add(item);
+				item = new JMenuItem(SAVE_SDF2);
+				item.addActionListener(this);
+				popup.add(item);
+				item = new JMenuItem(SAVE_SDF3);
+				item.addActionListener(this);
+				popup.add(item);
+				}
+			}
+
+		popup.show(this, e.getX(), e.getY());
+		}
+
+	public void structureChanged(StereoMolecule mol) {
+		if (mEditedIndex == -1) {	// new structure
+			if (mol.getAllAtoms() != 0) {
+				if (mCompoundFilter == null || mCompoundFilter.moleculeQualifies(mol))
 					mModel.addMolecule(mModel.getSize(), mol);
-    			else
+				else
 					JOptionPane.showMessageDialog(getParentFrame(),"The compound could not be added, because it doesn't fullfil all criteria.");
 				}
-    		}
-    	else {
-	        if (mol.getAllAtoms() == 0)
-	            mModel.remove(mEditedIndex);
-	        else {
+			}
+		else {
+			if (mol.getAllAtoms() == 0)
+				mModel.remove(mEditedIndex);
+			else {
 				if (mCompoundFilter == null || mCompoundFilter.moleculeQualifies(mol))
 					mModel.setMolecule(mEditedIndex, mol);
 				else
 					JOptionPane.showMessageDialog(getParentFrame(),"The compound could not be changed, because the changed structure doesn't fullfil all criteria.");
 				}
-    		}
-        }
+			}
+		}
 
-    /**
-     * May be overridden to act on selection changes
-     * @param molIndex
-     */
-    public void setSelection(int molIndex) {}
+	/**
+	 * May be overridden to act on selection changes
+	 * @param molIndex
+	 */
+	public void setSelection(int molIndex) {}
 
 	private void validateSize() {
 		Rectangle viewportBounds = getViewportBorderBounds();
@@ -617,76 +635,111 @@ public class CompoundCollectionPane<T> extends JScrollPane
 		mCellSize = new Dimension(width, height);
 
 		if (mIsVertical) {
-            height *= mModel.getSize();
-            if (height < viewportBounds.height)
-                height = viewportBounds.height;
-		    }
+			height *= mModel.getSize();
+			if (height < viewportBounds.height)
+				height = viewportBounds.height;
+			}
 		else {
-		    width *= mModel.getSize();
-	        if (width < viewportBounds.width)
-	            width = viewportBounds.width;
-		    }
+			width *= mModel.getSize();
+			if (width < viewportBounds.width)
+				width = viewportBounds.width;
+			}
 
 		if (mContentSize.width != width
 		 || mContentSize.height != height) {
 			mContentSize.width = width;
 			mContentSize.height = height;
-	        mContentPanel.setPreferredSize(mContentSize);
+			mContentPanel.setPreferredSize(mContentSize);
 			mContentPanel.revalidate();
-		    }
-		}
-
-    private void initializeDragAndDrop(int dragAction, int dropAction) {
-        if (dragAction != DnDConstants.ACTION_NONE) {
-            new MoleculeDragAdapter(this) {
-                public Transferable getTransferable(Point p) {
-                    if (mHighlightedIndex == -1)
-                        return null;
-                    return new MoleculeTransferable(mModel.getMolecule(mHighlightedIndex));
-                    }
-                };
-            }
-
-        if (dropAction != DnDConstants.ACTION_NONE) {
-            MoleculeDropAdapter d = new MoleculeDropAdapter() {
-                public void onDropMolecule(StereoMolecule mol, Point pt) {
-                    if (mIsEnabled && mIsEditable && mol != null && mol.getAllAtoms() != 0) {
-                        for (int atom=0; atom<mol.getAllAtoms(); atom++) {
-                            mol.setAtomColor(atom, Molecule.cAtomColorNone); // don't copy atom coloring
-                            }
-
-                        int index = (mHighlightedIndex == -1) ? mModel.getSize() : mHighlightedIndex;
-                        mol.setFragment(mCreateFragments);
-						if (mCompoundFilter == null || mCompoundFilter.moleculeQualifies(mol))
-							mModel.addMolecule(index, mol);
-						else
+			}
+		}
+
+	private void initializeDragAndDrop(int dragAction, int dropAction) {
+		mDragActions = dragAction;
+		if (dragAction != DnDConstants.ACTION_NONE) {
+			new MoleculeDragAdapter(this) {
+				public Transferable getTransferable(Point p) {
+					if (mHighlightedIndex == -1)
+						return null;
+					setCursor(CursorHelper.getCursor(CursorHelper.cFistCursor));
+					mDragIndex = mHighlightedIndex;
+					return new MoleculeTransferable(mModel.getMolecule(mHighlightedIndex));
+					}
+				};
+			}
+
+		if (dropAction != DnDConstants.ACTION_NONE) {
+			MoleculeDropAdapter d = new MoleculeDropAdapter() {
+				@Override
+				public void onDropMolecule(StereoMolecule mol, Point pt) {
+					if (mIsEnabled && mIsEditable && mol != null && mol.getAllAtoms() != 0 && mDropIndex != -1) {
+						for (int atom=0; atom<mol.getAllAtoms(); atom++)
+							mol.setAtomColor(atom, Molecule.cAtomColorNone); // don't copy atom coloring
+
+						mol.setFragment(mCreateFragments);
+						if (mCompoundFilter == null || mCompoundFilter.moleculeQualifies(mol)) {
+							if (mDragIndex != -1 && mInternalDragAndDropIsMove) {
+								mModel.remove(mDragIndex);
+								if (mDropIndex > mDragIndex)
+									mDropIndex--;
+								}
+
+							mModel.addMolecule(mDropIndex, mol);
+							}
+						else {
 							JOptionPane.showMessageDialog(getParentFrame(),"The compound could not be added, because it doesn't qualify.");
-                        }
-                    updateBorder(false);
-                    }
-
-                public void dragEnter(DropTargetDragEvent e) {
-                    boolean drop = mIsEnabled && mIsEditable && isDropOK(e) ;
-                    if (!drop)
-                        e.rejectDrag();
-                    updateBorder(drop);
-                    }
-
-                public void dragExit(DropTargetEvent e) {
-                    updateBorder(false);
-                    }
-                };
-
-            new DropTarget(this, dropAction, d, true, new OurFlavorMap());
-            }
-        }
-
-    private void updateBorder(boolean showBorder) {
-        if (mIsEnabled && mIsEditable && (mShowDropBorder != showBorder)) {
-            mShowDropBorder = showBorder;
-            repaint();
-            }
-        }
+							}
+						}
+					updateDropPosition(-1);
+					}
+
+				@Override
+				public void dragEnter(DropTargetDragEvent e) {
+					boolean drop = mIsEnabled && mIsEditable && isDropOK(e) ;
+					if (!drop) {
+						e.rejectDrag();
+						}
+					else {
+						updateDropPosition(getDropIndex(e));
+						}
+					}
+
+				@Override
+				public void dragOver(DropTargetDragEvent e) {
+					updateDropPosition(getDropIndex(e));
+					}
+
+				@Override
+				public void dragExit(DropTargetEvent e) {
+					updateDropPosition(-1);
+					}
+
+				private int getDropIndex(DropTargetDragEvent e) {
+					int x = e.getLocation().x + (mIsVertical ? 0 : mCellSize.width / 2);
+					int y = e.getLocation().y + (mIsVertical ? mCellSize.height / 2 : 0);
+					int dropIndex = getMoleculeIndex(x, y);
+					if (dropIndex == -1)
+						dropIndex = mModel.getSize();
+
+					// if we move internally onto the same position, don't indicate it
+					if (mInternalDragAndDropIsMove
+					 && (dropIndex == mDragIndex || dropIndex == mDragIndex+1))
+						dropIndex = -1;
+
+					return dropIndex;
+					}
+				};
+
+			new DropTarget(this, dropAction, d, true, new OurFlavorMap());
+			}
+		}
+
+	private void updateDropPosition(int dropIndex) {
+		if (mIsEnabled && mIsEditable && mDropIndex != dropIndex) {
+			mDropIndex = dropIndex;
+			repaint();
+			}
+		}
 
 	private Component getParentFrame() {
 		Component c = this;
@@ -695,20 +748,20 @@ public class CompoundCollectionPane<T> extends JScrollPane
 		return c;
 		}
 
-        // 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 {
-        public java.util.Map<DataFlavor,String> getNativesForFlavors(DataFlavor[] dfs) {
-            java.awt.datatransfer.FlavorMap m = java.awt.datatransfer.SystemFlavorMap.getDefaultFlavorMap();
-            return m.getNativesForFlavors(dfs);
-            }
-
-        public java.util.Map<String,DataFlavor> getFlavorsForNatives(String[] natives) {
-            java.awt.datatransfer.FlavorMap m = java.awt.datatransfer.SystemFlavorMap.getDefaultFlavorMap();
-            return m.getFlavorsForNatives(natives);
-            }
-        }
-    }
+		// 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 {
+		public java.util.Map<DataFlavor,String> getNativesForFlavors(DataFlavor[] dfs) {
+			java.awt.datatransfer.FlavorMap m = java.awt.datatransfer.SystemFlavorMap.getDefaultFlavorMap();
+			return m.getNativesForFlavors(dfs);
+			}
+
+		public java.util.Map<String,DataFlavor> getFlavorsForNatives(String[] natives) {
+			java.awt.datatransfer.FlavorMap m = java.awt.datatransfer.SystemFlavorMap.getDefaultFlavorMap();
+			return m.getFlavorsForNatives(natives);
+			}
+		}
+	}


=====================================
src/main/java/com/actelion/research/gui/JChemistryView.java
=====================================
@@ -41,6 +41,7 @@ import com.actelion.research.gui.dnd.MoleculeDropAdapter;
 import com.actelion.research.gui.dnd.MoleculeTransferable;
 import com.actelion.research.gui.dnd.ReactionDropAdapter;
 import com.actelion.research.gui.dnd.ReactionTransferable;
+import com.actelion.research.gui.hidpi.HiDPIHelper;
 import com.actelion.research.util.ColorHelper;
 import com.actelion.research.util.CursorHelper;
 
@@ -79,11 +80,14 @@ public class JChemistryView extends JComponent
 	private static final int ALLOWED_DRAG_ACTIONS = DnDConstants.ACTION_COPY_OR_MOVE;
 	private static final int ALLOWED_DROP_ACTIONS = DnDConstants.ACTION_COPY_OR_MOVE;
 
-	private ExtendedDepictor mDepictor;
+	private static final int DRAG_TYPE_NONE = -1;
+	private static final int DRAG_TYPE_REACTION = -2;   // or molecule index >= 0
+
+	private ExtendedDepictor    mDepictor;
 	private ArrayList<StructureListener> mListener;
-	private Dimension mSize;
-	private int					mChemistryType,mUpdateMode,mDisplayMode,mDragMolecule,mCopyOrDragActions,mPasteOrDropActions,mPasteAndDropOptions;
-	private boolean				mShowBorder,mIsDraggingThis,mAllowDropOrPasteWhenDisabled;
+	private Dimension           mSize;
+	private int					mChemistryType,mUpdateMode,mDisplayMode,mDragType,mCopyOrDragActions,mPasteOrDropActions,mPasteAndDropOptions;
+	private boolean				mIsDragging,mAllowDropOrPasteWhenDisabled,mIsEditable,mShowBorder,mOpaqueBackground;
 	private Color				mFragmentNoColor;
 	private MoleculeDropAdapter mMoleculeDropAdapter = null;
 	private ReactionDropAdapter mReactionDropAdapter = null;
@@ -93,7 +97,9 @@ public class JChemistryView extends JComponent
 	 * Creates a new JChemistryView for showing a reaction or molecules.
 	 * A JChemistryView uses an ExtendedDepictor to handle multiple molecules or a reaction.
 	 * For showing one molecule use a JStructureView.
-	 * This default implementation will support copy/paste and drag&drop.
+	 * The default will support copy/paste and drag&drop from this view only,
+	 * but dropping anything onto this view doesn't have an effect.
+	 * Call setEditable(true) to allow changes through drag&drop and pasting.
 	 * @param chemistryType one of ExtendedDepictor.TYPE_MOLECULES and ExtendedDepictor.TYPE_REACTION
 	 */
 	public JChemistryView(int chemistryType) {
@@ -104,6 +110,9 @@ public class JChemistryView extends JComponent
 	 * Creates a new JChemistryView for showing a reaction or molecules.
 	 * A JChemistryView uses an ExtendedDepictor to handle multiple molecules or a reaction.
 	 * For showing one molecule use a JStructureView.
+	 * The default will support copy/paste and drag&drop from this view only,
+	 * but dropping anything onto this view doesn't have an effect.
+	 * Call setEditable(true) to allow changes through drag&drop and pasting.
 	 * @param chemistryType one of the ExtendedDepictor.TYPE_... options
 	 * @param allowedCopyOrDragActions DnDConstants.ACTION_xxx
 	 * @param allowedPasteOrDropActions DnDConstants.ACTION_xxx
@@ -116,7 +125,8 @@ public class JChemistryView extends JComponent
 		initializeDragAndDrop();
 	    addMouseListener(this);
 	    addMouseMotionListener(this);
-	    mDragMolecule = -1;
+	    mDragType = DRAG_TYPE_NONE;
+		mIsEditable = false;
 	    }
 
     public int getChemistryType() {
@@ -140,7 +150,7 @@ public class JChemistryView extends JComponent
 		mDepictor.setDisplayMode(mDisplayMode);
 		mDepictor.setFragmentNoColor(mFragmentNoColor);
 		mUpdateMode = UPDATE_SCALE_COORDS;
-	    mDragMolecule = -1;
+		mDragType = DRAG_TYPE_NONE;
 		repaint();
 		}
 
@@ -149,7 +159,7 @@ public class JChemistryView extends JComponent
 		mDepictor.setDisplayMode(mDisplayMode);
 		mDepictor.setFragmentNoColor(mFragmentNoColor);
 		mUpdateMode = UPDATE_SCALE_COORDS;
-	    mDragMolecule = -1;
+		mDragType = DRAG_TYPE_NONE;
 		repaint();
 		}
 
@@ -158,10 +168,14 @@ public class JChemistryView extends JComponent
 		mDepictor.setDisplayMode(mDisplayMode);
 		mDepictor.setFragmentNoColor(mFragmentNoColor);
 		mUpdateMode = UPDATE_SCALE_COORDS;
-	    mDragMolecule = -1;
+		mDragType = DRAG_TYPE_NONE;
 		repaint();
 		}
 
+	public void setOpaqueBackground(boolean b) {
+		mOpaqueBackground = b;
+		}
+
 	/**
 	 * fragment status change on drop is allowed then dropping a fragment (molecule)
 	 * on a molecule (fragment) inverts the status of the view's chemical object.
@@ -193,6 +207,15 @@ public class JChemistryView extends JComponent
 			}
 		}
 
+	public boolean isEditable() {
+		return mIsEditable;
+		}
+
+	public void setEditable(boolean b) {
+		if (mIsEditable != b)
+			mIsEditable = b;
+		}
+
 	public void setFragmentNoColor(Color c) {
 	        // use setFragmentNoColor(null) if you don't want fragment numbers to be shown
 		mFragmentNoColor = c;
@@ -233,27 +256,72 @@ public class JChemistryView extends JComponent
 
 		mSize = theSize;
 
-//		g.setColor(getBackground());
-//		g.fillRect(0, 0, theSize.width, theSize.height);
-
 		Graphics2D g2 = (Graphics2D)g;
 		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
 		g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
 		g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
 
-		mDepictor.setForegroundColor(getForeground(), getBackground());
+		Color fg = g2.getColor();
+		g2.setColor(UIManager.getColor(isEditable() && isEnabled() ? "TextField.background" : "TextField.inactiveBackground"));
+		g2.fill(new Rectangle(insets.left, insets.top, theSize.width, theSize.height));
+		g2.setColor(fg);
 
-		mDepictor.paint(g);
+		mDepictor.setForegroundColor(getForeground(), getBackground());
 
-		if (mShowBorder) {
-			g.setColor(Color.gray);
-			g.drawRect(insets.left,insets.top,theSize.width - 1,theSize.height - 1);
-			g.drawRect(insets.left + 1,insets.top + 1,theSize.width - 3,theSize.height - 3);
+		if (mShowBorder && mDragType != DRAG_TYPE_NONE) {
+			Color bg = getBackground();
+			Color color = ColorHelper.perceivedBrightness(bg) < 0.5f ? bg.brighter() : bg.darker();
+			g.setColor(color);
+			Rectangle2D.Double rect = (mDragType == DRAG_TYPE_REACTION) ? getChemistryBounds() : getMoleculeBounds(mDragType);
+			Stroke oldStroke = ((Graphics2D)g).getStroke();
+			((Graphics2D)g).setStroke(new BasicStroke(HiDPIHelper.scale(2), BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+			int arc = (int)Math.min(rect.height/4, Math.min(rect.width/4, HiDPIHelper.scale(10)));
+			g.drawRoundRect((int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height, arc, arc);
+			((Graphics2D)g).setStroke(oldStroke);
+			if (mDragType == DRAG_TYPE_REACTION) {
+				final String msg = "<press 'ALT' to drag individual molecules>";
+				int fontSize = HiDPIHelper.scale(7);
+				g.setFont(getFont().deriveFont((float)fontSize));
+				int msgWidth = g.getFontMetrics().stringWidth(msg);
+				int x = (int)(rect.x+(rect.width-msgWidth)/2);
+				int y = (int)(rect.y+rect.height-fontSize/2);
+				g.drawString(msg, x, y);
+				}
 			}
 
+		mDepictor.paint(g);
+
 		mUpdateMode = UPDATE_REDRAW_ONLY;
 		}
 
+	/**
+	 * Returns the bounding rectangle of the indicated molecule,
+	 * if multiple molecules are shown, e.g. of a reaction.
+	 * @param i
+	 * @return bounds or null, if i is out of range
+	 */
+	public Rectangle2D.Double getMoleculeBounds(int i) {
+		return mDepictor == null || i >= mDepictor.getMoleculeCount() ?
+				null : mDepictor.getMoleculeDepictor(i).getBoundingRect();
+		}
+
+	public Rectangle2D.Double getChemistryBounds() {
+		if (mDepictor == null || mDepictor.getMoleculeCount() == 0)
+			return null;
+
+		Rectangle2D.Double rect = null;
+		for (int i=0; i<mDepictor.getMoleculeCount(); i++) {
+			Rectangle2D.Double mrect = mDepictor.getMoleculeDepictor(i).getBoundingRect();
+			if (mrect != null) {
+				if (rect == null)
+					rect = mrect;
+				else
+					rect = (Rectangle2D.Double)rect.createUnion(mrect);
+				}
+			}
+		return rect;
+		}
+
 	private void updateBorder(boolean showBorder) {
 		if(mShowBorder != showBorder){
 			mShowBorder = showBorder;
@@ -290,21 +358,21 @@ public class JChemistryView extends JComponent
 			ch.copyReaction(rxn);
 			}
 
-		if (e.getActionCommand().equals(ITEM_PASTE_MOLS)) {
+		if (e.getActionCommand().equals(ITEM_PASTE_MOLS) && mIsEditable) {
 			ClipboardHandler ch = new ClipboardHandler();
 			StereoMolecule mol = ch.pasteMolecule();
 			if (mol != null)
 				pasteOrDropMolecule(mol);
 			}
 
-		if (e.getActionCommand().equals(ITEM_PASTE_RXN)) {
+		if (e.getActionCommand().equals(ITEM_PASTE_RXN) && mIsEditable) {
 			ClipboardHandler ch = new ClipboardHandler();
 			Reaction rxn = ch.pasteReaction();
 			if (rxn != null)
 				pasteOrDropReaction(rxn);
 			}
 
-		if (e.getActionCommand().equals(ITEM_OPEN_RXN)) {
+		if (e.getActionCommand().equals(ITEM_OPEN_RXN) && mIsEditable) {
 			File rxnFile = FileHelper.getFile(this, "Please select a reaction file", FileHelper.cFileTypeRXN);
 			if (rxnFile != null) {
 				try {
@@ -325,7 +393,8 @@ public class JChemistryView extends JComponent
 	public void mousePressed(MouseEvent e) {
 		handlePopupTrigger(e);
 
-		setCursor(CursorHelper.getCursor((mDragMolecule == -1) ? CursorHelper.cPointerCursor : CursorHelper.cFistCursor));
+		setCursor(CursorHelper.getCursor((mDragType == DRAG_TYPE_NONE) ?
+				CursorHelper.cPointerCursor : CursorHelper.cFistCursor));
 		}
 
 	@Override
@@ -342,7 +411,7 @@ public class JChemistryView extends JComponent
 					item.addActionListener(this);
 					popup.add(item);
 					}
-				if (isEnabled() || mAllowDropOrPasteWhenDisabled) {
+				if ((isEnabled() || mAllowDropOrPasteWhenDisabled) && mIsEditable) {
 					if (mCopyOrDragActions != DnDConstants.ACTION_NONE) {
 						JMenuItem item = new JMenuItem(ITEM_PASTE_MOLS);
 						item.addActionListener(this);
@@ -357,7 +426,7 @@ public class JChemistryView extends JComponent
 					item.addActionListener(this);
 					popup.add(item);
 					}
-				if (isEnabled() || mAllowDropOrPasteWhenDisabled) {
+				if ((isEnabled() || mAllowDropOrPasteWhenDisabled) && mIsEditable) {
 					if (mCopyOrDragActions != DnDConstants.ACTION_NONE) {
 						JMenuItem item = new JMenuItem(ITEM_PASTE_RXN);
 						item.addActionListener(this);
@@ -391,35 +460,52 @@ public class JChemistryView extends JComponent
 
 	@Override public void mouseClicked(MouseEvent e) {}
 	@Override public void mouseEntered(MouseEvent e) {}
-	@Override public void mouseExited(MouseEvent e) {}
+
+	@Override
+	public void mouseExited(MouseEvent e) {
+		if (!mIsDragging) {
+			mDragType = DRAG_TYPE_NONE;
+			repaint();
+		}
+	}
 
 	@Override
 	public void mouseDragged(MouseEvent e) {
-		if (mDragMolecule != -1)
-			setCursor(CursorHelper.getCursor(CursorHelper.cFistCursor));
+//		if (mDragType != DRAG_TYPE_NONE)
+//			setCursor(CursorHelper.getCursor(CursorHelper.cFistCursor));
 		}
 
 	@Override
 	public void mouseMoved(MouseEvent e) {
 		int x = e.getX();
 		int y = e.getY();
-		if (mDepictor != null) {
-			int dragMolecule = -1;
-			for (int i=0; i<mDepictor.getMoleculeCount(); i++) {
-				Rectangle2D.Double bounds = mDepictor.getMoleculeDepictor(i).getBoundingRect();
-				if (bounds != null && bounds.contains(x, y)) {
-					dragMolecule = i;
-					break;
+		int dragType = DRAG_TYPE_NONE;
+		if (mDepictor != null && (mCopyOrDragActions & DnDConstants.ACTION_COPY) != 0) {
+			boolean dragIndividualMolecule = mChemistryType != ExtendedDepictor.TYPE_REACTION || e.isAltDown();
+			if (dragIndividualMolecule) {
+				for (int i=0; i<mDepictor.getMoleculeCount(); i++) {
+					Rectangle2D.Double bounds = mDepictor.getMoleculeDepictor(i).getBoundingRect();
+					if (bounds != null && bounds.contains(x, y)) {
+						dragType = i;
+						break;
+						}
 					}
 				}
-			if (mDragMolecule != dragMolecule) {
-				mDragMolecule = dragMolecule;
+			else {
+				Rectangle2D.Double bounds = getChemistryBounds();
+				if (bounds != null && bounds.contains(x, y))
+					dragType = DRAG_TYPE_REACTION;
+				}
+
+			if (mDragType != dragType) {
+				mDragType = dragType;
 				repaint();
 				}
 			}
 
-		setCursor(CursorHelper.getCursor((mDragMolecule == -1) ? CursorHelper.cPointerCursor
-															   : CursorHelper.cHandCursor));
+		updateBorder(dragType != DRAG_TYPE_NONE);
+		setCursor(CursorHelper.getCursor((mDragType == DRAG_TYPE_NONE) ?
+				CursorHelper.cPointerCursor : CursorHelper.cHandCursor));
 		}
 
 	public void addStructureListener(StructureListener l) {
@@ -441,7 +527,7 @@ public class JChemistryView extends JComponent
 		}
 
 	public boolean canDrop() {
-		return (isEnabled() || mAllowDropOrPasteWhenDisabled) && !mIsDraggingThis;
+		return isEditable() && (isEnabled() || mAllowDropOrPasteWhenDisabled) && !mIsDragging;
 		}
 
 	private void initializeDragAndDrop() {
@@ -463,16 +549,16 @@ public class JChemistryView extends JComponent
 						boolean drop = canDrop() && isDropOK(e) ;
 						if (!drop)
 							e.rejectDrag();
-						updateBorder(drop);
+//						updateBorder(drop);
 						}
 
 					public void dragExit(DropTargetEvent e) {
-						updateBorder(false);
+//						updateBorder(false);
 						}
 					};
 
 				new DropTarget(this, mPasteOrDropActions, mMoleculeDropAdapter, true);
-//			new DropTarget(this,mAllowedDropAction,mDropAdapter,true, getSystemFlavorMap());
+//			    new DropTarget(this,mAllowedDropAction,mDropAdapter,true, getSystemFlavorMap());
 				}
 
 			if (mChemistryType == ExtendedDepictor.TYPE_REACTION) {
@@ -489,11 +575,11 @@ public class JChemistryView extends JComponent
 						boolean drop = canDrop() && isDropOK(e) ;
 						if (!drop)
 							e.rejectDrag();
-						updateBorder(drop);
+//						updateBorder(drop);
 						}
 
 					public void dragExit(DropTargetEvent e) {
-						updateBorder(false);
+//						updateBorder(false);
 						}
 					};
 
@@ -504,21 +590,19 @@ public class JChemistryView extends JComponent
 		}
 
 	private void pasteOrDropMolecule(StereoMolecule m) {
-		boolean isFragment = mDepictor.isFragment();
 		StereoMolecule mol = new StereoMolecule(m);
 		if ((mPasteAndDropOptions & PASTE_AND_DROP_OPTION_KEEP_ATOM_COLORS) == 0)
 			mol.removeAtomColors();
 		if ((mPasteAndDropOptions & PASTE_AND_DROP_OPTION_KEEP_BOND_HIGHLIGHTING) == 0)
 			mol.removeBondHiliting();
-		if ((mPasteAndDropOptions & PASTE_AND_DROP_OPTION_ALLOW_FRAGMENT_STATE_CHANGE) == 0)
-			mol.setFragment(isFragment);
+		if ((mPasteAndDropOptions & PASTE_AND_DROP_OPTION_ALLOW_FRAGMENT_STATE_CHANGE) == 0 && mDepictor != null)
+			mol.setFragment(mDepictor.isFragment());
 		setContent(mol);
 		repaint();
 		informListeners();
 		}
 
 	private void pasteOrDropReaction(Reaction r) {
-		boolean isFragment = mDepictor.isFragment();
 		Reaction rxn = new Reaction(r);
 		if ((mPasteAndDropOptions & PASTE_AND_DROP_OPTION_KEEP_ATOM_COLORS) == 0)
 			for (int m=0; m<rxn.getMolecules(); m++)
@@ -526,8 +610,8 @@ public class JChemistryView extends JComponent
 		if ((mPasteAndDropOptions & PASTE_AND_DROP_OPTION_KEEP_BOND_HIGHLIGHTING) == 0)
 			for (int m=0; m<rxn.getMolecules(); m++)
 				rxn.getMolecule(m).removeBondHiliting();
-		if ((mPasteAndDropOptions & PASTE_AND_DROP_OPTION_ALLOW_FRAGMENT_STATE_CHANGE) == 0)
-			rxn.setFragment(isFragment);
+		if ((mPasteAndDropOptions & PASTE_AND_DROP_OPTION_ALLOW_FRAGMENT_STATE_CHANGE) == 0 && mDepictor != null)
+			rxn.setFragment(mDepictor.isFragment());
 		if ((mPasteAndDropOptions & PASTE_AND_DROP_OPTION_REMOVE_CATALYSTS) != 0)
 			rxn.removeCatalysts();
 		if ((mPasteAndDropOptions & PASTE_AND_DROP_OPTION_REMOVE_DRAWING_OBJECTS) != 0)
@@ -537,9 +621,8 @@ public class JChemistryView extends JComponent
 		informListeners();
 		}
 
-	public Transferable getMoleculeTransferable(Point pt) {
-		StereoMolecule mol = (mDragMolecule == -1) ? null
-							 : mDepictor.getMolecule(mDragMolecule).getCompactCopy();
+	public Transferable getMoleculeTransferable() {
+		StereoMolecule mol = (mDragType < 0) ? null : mDepictor.getMolecule(mDragType).getCompactCopy();
 		return new MoleculeTransferable(mol);
 		}
 
@@ -562,7 +645,9 @@ public class JChemistryView extends JComponent
 	public void dragExit(DragSourceEvent e) {}
 	public void dragDropEnd(DragSourceDropEvent e) {
 		System.out.println("dragDropEnd()");
-		mIsDraggingThis = false;
+		mIsDragging = false;
+		mDragType = DRAG_TYPE_NONE;
+		repaint();
 		}
 
 	public void dropActionChanged(DragSourceDragEvent e) {
@@ -577,13 +662,13 @@ public class JChemistryView extends JComponent
 		}
 
 	public void dragGestureRecognized(DragGestureEvent e) {
-		if((e.getDragAction() & ALLOWED_DRAG_ACTIONS) != 0) {
-			Transferable transferable = (mChemistryType == ExtendedDepictor.TYPE_REACTION) ?
-					getReactionTransferable() : getMoleculeTransferable(e.getDragOrigin());
+		if((e.getDragAction() & ALLOWED_DRAG_ACTIONS) != 0 && mDragType != DRAG_TYPE_NONE) {
+			Transferable transferable = (mDragType == DRAG_TYPE_REACTION) ?
+					getReactionTransferable() : getMoleculeTransferable();
 			if (transferable != null) {
 				try {
 					e.startDrag(CursorHelper.getCursor(CursorHelper.cFistCursor), transferable, this);
-					mIsDraggingThis = true;
+					mIsDragging = true;
 //					e.startDrag(DragSource.DefaultCopyNoDrop, transferable, this);
 					}
 				catch (InvalidDnDOperationException idoe) {


=====================================
src/main/java/com/actelion/research/gui/JEditableChemistryView.java
=====================================
@@ -14,7 +14,6 @@ public class JEditableChemistryView extends JChemistryView {
 	private static final String EDIT_MESSAGE = "<double click or drag & drop>";
 	private StereoMolecule[] mMolecules;
 	private Reaction mReaction;
-	private boolean mIsEditable;
 
 	/**
 	 * Creates a new JEditableChemistryView for showing & editing a reaction or molecule(s).
@@ -23,7 +22,14 @@ public class JEditableChemistryView extends JChemistryView {
 	 */
 	public JEditableChemistryView(int chemistryType) {
 		super(chemistryType);
-		mIsEditable = true;
+		if (chemistryType == ExtendedDepictor.TYPE_REACTION) {
+			mReaction = new Reaction();
+			}
+		else {
+			mMolecules = new StereoMolecule[1];
+			mMolecules[0] = new StereoMolecule();
+			}
+		setEditable(true);
 		}
 
 	/**
@@ -60,19 +66,9 @@ public class JEditableChemistryView extends JChemistryView {
 		super.setContent(rxn, drawingObjectList);
 		}
 
-	public boolean isEditable() {
-		return mIsEditable;
-	}
-
-	public void setEditable(boolean b) {
-		if (mIsEditable != b) {
-			mIsEditable = b;
-			}
-		}
-
 	@Override
 	public void mouseClicked(MouseEvent e) {
-		if (e.getClickCount() == 2 && isEnabled() && mIsEditable) {
+		if (e.getClickCount() == 2 && isEnabled() && isEditable()) {
 			if (getChemistryType() == ExtendedDepictor.TYPE_REACTION)
 				editReaction();
 			else
@@ -83,6 +79,18 @@ public class JEditableChemistryView extends JChemistryView {
 		super.mouseClicked(e);
 		}
 
+	private boolean isEmpty() {
+		if (mReaction != null && !mReaction.isEmpty())
+			return false;
+
+		if (mMolecules != null)
+			for (StereoMolecule mol:mMolecules)
+				if (mol.getAllAtoms() != 0)
+					return false;
+
+		return true;
+		}
+
 	@Override
 	public void paintComponent(Graphics g) {
 		Dimension theSize = getSize();
@@ -92,7 +100,7 @@ public class JEditableChemistryView extends JChemistryView {
 
 		super.paintComponent(g);
 
-		if (isEnabled() && mIsEditable && mReaction.isEmpty()) {
+		if (isEnabled() && isEditable() && isEmpty()) {
 			g.setFont(g.getFont().deriveFont(Font.PLAIN, HiDPIHelper.scale(10)));
 			FontMetrics metrics = g.getFontMetrics();
 			Rectangle2D bounds = metrics.getStringBounds(EDIT_MESSAGE, g);
@@ -101,11 +109,6 @@ public class JEditableChemistryView extends JChemistryView {
 			}
 		}
 
-	@Override
-	public boolean canDrop() {
-		return mIsEditable && super.canDrop();
-	}
-
 	private void editReaction() {
 		Component c = this;
 		while (!(c instanceof Frame))


=====================================
src/main/java/com/actelion/research/gui/JEditableStructureView.java
=====================================
@@ -29,29 +29,29 @@ public class JEditableStructureView extends JStructureView {
     static final long serialVersionUID = 0x20090727;
 
     private static final String EDIT_MESSAGE = "<double click or drag & drop>";
-    private boolean mIsEditable,mAllowQueryFeatures;
+    private boolean mAllowQueryFeatures;
 
     public JEditableStructureView() {
         super(null);
-        mIsEditable = true;
+	    setEditable(true);
 		mAllowQueryFeatures = true;
 		}
 
 	public JEditableStructureView(StereoMolecule mol) {
         super(mol);
-        mIsEditable = true;
+		setEditable(true);
 		mAllowQueryFeatures = true;
 	    }
 
 	public JEditableStructureView(int dragAction, int dropAction) {
         super(null, dragAction, dropAction);
-        mIsEditable = true;
+		setEditable(true);
 		mAllowQueryFeatures = true;
 	    }
 
 	public JEditableStructureView(StereoMolecule mol, int dragAction, int dropAction) {
         super(mol, dragAction, dropAction);
-        mIsEditable = true;
+		setEditable(true);
 		mAllowQueryFeatures = true;
 	    }
 
@@ -64,7 +64,7 @@ public class JEditableStructureView extends JStructureView {
 
 		super.paintComponent(g);
 
-		if (isEnabled() && mIsEditable && getMolecule().getAllAtoms() == 0) {
+		if (isEnabled() && isEditable() && getMolecule().getAllAtoms() == 0) {
 	        g.setFont(g.getFont().deriveFont(Font.PLAIN, HiDPIHelper.scale(10)));
 	        FontMetrics metrics = g.getFontMetrics();
 	        Rectangle2D bounds = metrics.getStringBounds(EDIT_MESSAGE, g);
@@ -74,7 +74,7 @@ public class JEditableStructureView extends JStructureView {
 	    }
 
     public void mouseClicked(MouseEvent e) {
-        if (e.getClickCount() == 2 && isEnabled() && mIsEditable) {
+        if (e.getClickCount() == 2 && isEnabled() && isEditable()) {
             Component c = this;
             while (!(c instanceof Frame || c instanceof Dialog))
                 c = c.getParent();
@@ -90,20 +90,6 @@ public class JEditableStructureView extends JStructureView {
 			mAllowQueryFeatures = allow;
 			if (!allow && getMolecule().removeQueryFeatures())
 				structureChanged();
-		}
-	}
-
-	public boolean isEditable() {
-		return mIsEditable;
-	}
-
-	public void setEditable(boolean b) {
-		if (mIsEditable != b) {
-		    mIsEditable = b;
 			}
 		}
-
-	public boolean canDrop() {
-	    return mIsEditable && super.canDrop();
-        }
     }


=====================================
src/main/java/com/actelion/research/gui/JStructureView.java
=====================================
@@ -18,7 +18,10 @@
 
 package com.actelion.research.gui;
 
-import com.actelion.research.chem.*;
+import com.actelion.research.chem.AbstractDepictor;
+import com.actelion.research.chem.Depictor2D;
+import com.actelion.research.chem.IDCodeParser;
+import com.actelion.research.chem.StereoMolecule;
 import com.actelion.research.chem.name.StructureNameResolver;
 import com.actelion.research.gui.clipboard.IClipboardHandler;
 import com.actelion.research.gui.dnd.MoleculeDragAdapter;
@@ -26,20 +29,17 @@ import com.actelion.research.gui.dnd.MoleculeDropAdapter;
 import com.actelion.research.gui.dnd.MoleculeTransferable;
 import com.actelion.research.gui.hidpi.HiDPIHelper;
 import com.actelion.research.util.ColorHelper;
+import com.actelion.research.util.CursorHelper;
 
 import javax.swing.*;
-
 import java.awt.*;
 import java.awt.datatransfer.*;
 import java.awt.dnd.*;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.MouseEvent;
-import java.awt.event.MouseListener;
+import java.awt.event.*;
 import java.awt.geom.Rectangle2D;
 import java.util.ArrayList;
 
-public class JStructureView extends JPanel implements ActionListener,MouseListener,StructureListener {
+public class JStructureView extends JComponent implements ActionListener,MouseListener,MouseMotionListener,StructureListener {
     static final long serialVersionUID = 0x20061113;
 
     private static final String ITEM_COPY = "Copy Structure";
@@ -51,14 +51,14 @@ public class JStructureView extends JPanel implements ActionListener,MouseListen
 	private String mIDCode;
 	private StereoMolecule mMol,mDisplayMol;
     private Depictor2D mDepictor;
-	private boolean mShowBorder, mAllowFragmentStatusChangeOnPasteOrDrop,mIsDraggingThis,mOpaqueBackground;
+	private boolean mShowBorder,mAllowFragmentStatusChangeOnPasteOrDrop,mIsDraggingThis,mOpaqueBackground,
+					mIsEditable,mDisableBorder;
 	private int mChiralTextPosition,mDisplayMode;
 	private String[] mAtomText;
 	private IClipboardHandler mClipboardHandler;
 	protected MoleculeDropAdapter mDropAdapter = null;
 	protected int mAllowedDragAction;
 	protected int mAllowedDropAction;
-	protected boolean borderFlag = true; // Allow subclasses to disable border painting
 
 	public JStructureView() {
         this(null);
@@ -67,6 +67,9 @@ public class JStructureView extends JPanel implements ActionListener,MouseListen
 	/**
 	 * This creates a standard structure view where the displayed molecule is
 	 * used for D&D and clipboard transfer after removing atom colors and bond highlights.
+	 * The default will support copy/paste and drag&drop from this view only,
+	 * but dropping anything onto this view doesn't have an effect.
+	 * Call setEditable(true) to allow changes through drag&drop and pasting.
 	 * @param mol used for display, clipboard copy and d&d
 	 */
 	public JStructureView(StereoMolecule mol) {
@@ -79,6 +82,9 @@ public class JStructureView extends JPanel implements ActionListener,MouseListen
 	 * molecule is structurally different, e.g. uses custom atom labels or additional
 	 * illustrative atoms or bonds, which shall not be copied.
 	 * Custom atom colors or highlighted bonds don't require a displayMol.
+	 * The default will support copy/paste and drag&drop from this view only,
+	 * but dropping anything onto this view doesn't have an effect.
+	 * Call setEditable(true) to allow changes through drag&drop and pasting.
 	 * @param mol used for clipboard copy and d&d; used for display if displayMol is null
 	 * @param displayMol null if mol shall be displayed
 	 */
@@ -93,6 +99,9 @@ public class JStructureView extends JPanel implements ActionListener,MouseListen
 	/**
 	 * This creates a standard structure view where the displayed molecule is
 	 * used for D&D and clipboard transfer after removing atom colors and bond highlights.
+	 * The default will support copy/paste and drag&drop from this view only,
+	 * but dropping anything onto this view doesn't have an effect.
+	 * Call setEditable(true) to allow changes through drag&drop and pasting.
 	 * @param mol used for display, clipboard copy and d&d
 	 * @param dragAction
 	 * @param dropAction
@@ -107,6 +116,9 @@ public class JStructureView extends JPanel implements ActionListener,MouseListen
 	 * molecule is structurally different, e.g. uses custom atom labels or additional
 	 * illustrative atoms or bonds, which shall not be copied.
 	 * Custom atom colors or highlighted bonds don't require a displayMol.
+	 * The default will support copy/paste and drag&drop from this view only,
+	 * but dropping anything onto this view doesn't have an effect.
+	 * Call setEditable(true) to allow changes through drag&drop and pasting.
 	 * @param mol used for clipboard copy and d&d; used for display if displayMol is null
 	 * @param displayMol null if mol shall be displayed
 	 * @param dragAction
@@ -116,7 +128,9 @@ public class JStructureView extends JPanel implements ActionListener,MouseListen
 		mMol = (mol == null) ? new StereoMolecule() : new StereoMolecule(mol);
 		mDisplayMol = (displayMol == null) ? mMol : displayMol;
 		mDisplayMode = AbstractDepictor.cDModeHiliteAllQueryFeatures;
+		mIsEditable = false;
 		addMouseListener(this);
+		addMouseMotionListener(this);
 		initializeDragAndDrop(dragAction, dropAction);
 	    }
 
@@ -141,8 +155,8 @@ public class JStructureView extends JPanel implements ActionListener,MouseListen
 	    mDisplayMode = mode;
 	    }
 
-	public void setOpaqueBackground(boolean b) {
-		mOpaqueBackground = b;
+	public void setDisableBorder(boolean b) {
+		mDisableBorder = b;
 		}
 
 	/**
@@ -167,6 +181,16 @@ public class JStructureView extends JPanel implements ActionListener,MouseListen
 		super.setEnabled(enable);
 		}
 
+
+	public boolean isEditable() {
+		return mIsEditable;
+	}
+
+	public void setEditable(boolean b) {
+		if (mIsEditable != b)
+			mIsEditable = b;
+		}
+
 	/**
 	 * When fragment status change on drop is allowed then dropping a fragment (molecule)
 	 * on a molecule (fragment) inverts the status of the view's chemical object.
@@ -178,7 +202,7 @@ public class JStructureView extends JPanel implements ActionListener,MouseListen
 		}
 
 	public boolean canDrop() {
-		return isEnabled() && !mIsDraggingThis;
+		return mIsEditable && isEnabled() && !mIsDraggingThis;
 	    }
 
 	@Override
@@ -190,7 +214,7 @@ public class JStructureView extends JPanel implements ActionListener,MouseListen
 		theSize.width -= insets.left + insets.right;
 		theSize.height -= insets.top + insets.bottom;
 
-        if(theSize.width <= 0 || theSize.height <= 0)
+        if (theSize.width <= 0 || theSize.height <= 0)
             return;
 
         Graphics2D g2 = (Graphics2D)g;
@@ -198,12 +222,10 @@ public class JStructureView extends JPanel implements ActionListener,MouseListen
         g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
         g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
 
-        if (mOpaqueBackground) {
-        	Color fg = g2.getColor();
-	        g2.setColor(UIManager.getColor(isEnabled() ? "TextField.background" : "TextField.inactiveBackground"));
-	        g2.fill(new Rectangle(insets.left, insets.top, theSize.width, theSize.height));
-	        g2.setColor(fg);
-            }
+		Color fg = g2.getColor();
+		g2.setColor(UIManager.getColor(isEditable() && isEnabled() ? "TextField.background" : "TextField.inactiveBackground"));
+		g2.fill(new Rectangle(insets.left, insets.top, theSize.width, theSize.height));
+		g2.setColor(fg);
 
 		if (mDisplayMol != null && mDisplayMol.getAllAtoms() != 0) {
 			mDepictor = new Depictor2D(mDisplayMol);
@@ -221,10 +243,17 @@ public class JStructureView extends JPanel implements ActionListener,MouseListen
             mDepictor.paint(g);
 			}
 
-		if (borderFlag && mShowBorder) {
-			g.setColor(Color.gray);
-			g.drawRect(insets.left,insets.top,theSize.width - 1,theSize.height - 1);
-			g.drawRect(insets.left + 1,insets.top + 1,theSize.width - 3,theSize.height - 3);
+		if (mShowBorder && !mDisableBorder) {
+			Rectangle2D.Double rect = mDepictor.getBoundingRect();
+			if (rect != null) {
+				Color bg = getBackground();
+				g.setColor(ColorHelper.perceivedBrightness(bg) < 0.5f ? bg.brighter() : bg.darker());
+				Stroke oldStroke = ((Graphics2D)g).getStroke();
+				((Graphics2D)g).setStroke(new BasicStroke(HiDPIHelper.scale(2), BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+				int arc = (int)Math.min(rect.height/4, Math.min(rect.width/4, HiDPIHelper.scale(10)));
+				g.drawRoundRect((int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height, arc, arc);
+				((Graphics2D)g).setStroke(oldStroke);
+				}
 			}
 		}
 
@@ -257,7 +286,7 @@ public class JStructureView extends JPanel implements ActionListener,MouseListen
 	 */
 	public synchronized void structureChanged(StereoMolecule mol) {
 		if (mol == null) {
-			mMol.deleteMolecule();
+			mMol.clear();
 			}
 		else {
 			mol.copyMolecule(mMol);
@@ -275,7 +304,7 @@ public class JStructureView extends JPanel implements ActionListener,MouseListen
 	 */
 	public synchronized void structureChanged(StereoMolecule mol, StereoMolecule displayMol) {
 		if (mol == null) {
-			mMol.deleteMolecule();
+			mMol.clear();
 			}
 		else {
 			mol.copyMolecule(mMol);
@@ -311,7 +340,7 @@ public class JStructureView extends JPanel implements ActionListener,MouseListen
 
     public void addStructureListener(StructureListener l) {
 		if(mListener == null)
-			mListener = new ArrayList<StructureListener>();
+			mListener = new ArrayList<>();
 
 		mListener.add(l);
 		}
@@ -325,23 +354,42 @@ public class JStructureView extends JPanel implements ActionListener,MouseListen
 		mChiralTextPosition = p;
 		}
 
-	public void mouseClicked(MouseEvent e) {}
-	public void mouseEntered(MouseEvent e) {}
-	public void mouseExited(MouseEvent e) {}
+	@Override public void mouseClicked(MouseEvent e) {}
+	@Override public void mouseEntered(MouseEvent e) {}
+	@Override public void mouseExited(MouseEvent e) {}
 
+	@Override
 	public void mousePressed(MouseEvent e) {
 		handlePopupTrigger(e);
 		}
 
+	@Override
 	public void mouseReleased(MouseEvent e) {
 		handlePopupTrigger(e);
 		}
 
+	@Override
+	public void mouseMoved(MouseEvent e) {
+		int x = e.getX();
+		int y = e.getY();
+		boolean isInRect = false;
+		if (mDepictor != null && (mAllowedDragAction & DnDConstants.ACTION_COPY) != 0) {
+			Rectangle2D.Double bounds = mDepictor.getBoundingRect();
+			if (bounds != null && bounds.contains(x, y))
+				isInRect = true;
+			}
+
+		updateBorder(isInRect);
+		setCursor(CursorHelper.getCursor(isInRect ? CursorHelper.cHandCursor : CursorHelper.cPointerCursor));
+		}
+
+	@Override public void mouseDragged(MouseEvent e) {}
+
 	public void actionPerformed(ActionEvent e) {
 		if (e.getActionCommand().equals(ITEM_COPY)) {
 			mClipboardHandler.copyMolecule(mMol);
 			}
-		if (e.getActionCommand().startsWith(ITEM_PASTE)) {
+		if (e.getActionCommand().startsWith(ITEM_PASTE) && mIsEditable) {
 			StereoMolecule mol = mClipboardHandler.pasteMolecule();
 			if (mol != null) {
 				if (!mAllowFragmentStatusChangeOnPasteOrDrop)
@@ -351,7 +399,7 @@ public class JStructureView extends JPanel implements ActionListener,MouseListen
 				structureChanged();
 				}
 			}
-		if (e.getActionCommand().equals(ITEM_CLEAR)) {
+		if (e.getActionCommand().equals(ITEM_CLEAR) && mIsEditable) {
 			mMol.deleteMolecule();
 			mDisplayMol = mMol;
 			structureChanged();
@@ -367,17 +415,19 @@ public class JStructureView extends JPanel implements ActionListener,MouseListen
 			item1.setEnabled(mMol.getAllAtoms() != 0);
 			popup.add(item1);
 
-			String itemText = StructureNameResolver.getInstance() == null ? ITEM_PASTE : ITEM_PASTE_WITH_NAME;
-			JMenuItem item2 = new JMenuItem(itemText);
-			item2.addActionListener(this);
-			popup.add(item2);
+			if (mIsEditable) {
+				String itemText = StructureNameResolver.getInstance() == null ? ITEM_PASTE : ITEM_PASTE_WITH_NAME;
+				JMenuItem item2 = new JMenuItem(itemText);
+				item2.addActionListener(this);
+				popup.add(item2);
 
-			popup.addSeparator();
+				popup.addSeparator();
 
-			JMenuItem item3 = new JMenuItem(ITEM_CLEAR);
-			item3.addActionListener(this);
-			item3.setEnabled(mMol.getAllAtoms() != 0);
-			popup.add(item3);
+				JMenuItem item3 = new JMenuItem(ITEM_CLEAR);
+				item3.addActionListener(this);
+				item3.setEnabled(mMol.getAllAtoms() != 0);
+				popup.add(item3);
+				}
 
 			popup.show(this, e.getX(), e.getY());
 			}
@@ -445,11 +495,11 @@ public class JStructureView extends JPanel implements ActionListener,MouseListen
 					boolean drop = canDrop() && isDropOK(e) ;
 					if (!drop)
 						e.rejectDrag();
-					updateBorder(drop);
+//					updateBorder(drop);
 				}
 
 				public void dragExit(DropTargetEvent e) {
-					updateBorder(false);
+//					updateBorder(false);
 				}
 			};
 
@@ -470,13 +520,13 @@ public class JStructureView extends JPanel implements ActionListener,MouseListen
 	protected void onDrop() {}
 
 	private void updateBorder(boolean showBorder) {
-		if(mShowBorder != showBorder){
+		if (mShowBorder != showBorder) {
 			mShowBorder = showBorder;
 			repaint();
+			}
 		}
-	}
 
-	public java.awt.datatransfer.FlavorMap getSystemFlavorMap() {
+/*	public java.awt.datatransfer.FlavorMap getSystemFlavorMap() {
 	    return new OurFlavorMap();
 	    }
 
@@ -487,18 +537,18 @@ public class JStructureView extends JPanel implements ActionListener,MouseListen
     // there's nothing I can do about it.
     static class OurFlavorMap implements FlavorMap, FlavorTable {
     	public java.util.Map<DataFlavor,String> getNativesForFlavors(DataFlavor[] dfs) {
-    /*	    System.out.println("getNativesForFlavors " + dfs.length);
-    	    for (int i = 0; i < dfs.length; i++)
-    		    System.out.println(" -> " + dfs[i]);
-    */
+    //	    System.out.println("getNativesForFlavors " + dfs.length);
+    //	    for (int i = 0; i < dfs.length; i++)
+    //		    System.out.println(" -> " + dfs[i]);
+    //
     	    return SystemFlavorMap.getDefaultFlavorMap().getNativesForFlavors(dfs);
     	    }
     
     	public java.util.Map<String,DataFlavor> getFlavorsForNatives(String[] natives) {
-    /*	    System.out.println("getFlavorsForNatives " + natives.length);
-    	    for (int i = 0; i < natives.length; i++)
-    	        System.out.println(" -> " + natives[i]);
-    */
+    //	    System.out.println("getFlavorsForNatives " + natives.length);
+    //	    for (int i = 0; i < natives.length; i++)
+    //	        System.out.println(" -> " + natives[i]);
+    //
     	    return SystemFlavorMap.getDefaultFlavorMap().getFlavorsForNatives(natives);
     	    }
     
@@ -512,5 +562,5 @@ public class JStructureView extends JPanel implements ActionListener,MouseListen
     //	    System.out.println("getNativesForFlavor " + flav);
     	    return ((SystemFlavorMap)SystemFlavorMap.getDefaultFlavorMap()).getNativesForFlavor(flav);
     	    }
-        }
+        }*/
     }


=====================================
src/main/java/com/actelion/research/gui/dnd/MoleculeDropAdapter.java
=====================================
@@ -56,12 +56,13 @@ public class MoleculeDropAdapter implements DropTargetListener
         DEBUG("MoleculeDropAdapter.onDropMolecule(). Override this! " + m);
     }
 
-
+    @Override
     public void dragEnter(DropTargetDragEvent e)
     {
         DEBUG("DragEnter");
     }
 
+    @Override
     public void dragOver(DropTargetDragEvent e)
     {
        DEBUG("DragOver");
@@ -76,17 +77,20 @@ public class MoleculeDropAdapter implements DropTargetListener
     {
         return active_;
     }
+
+    @Override
     public void dropActionChanged(DropTargetDragEvent e)
     {
         DEBUG("dropActionChanged");
     }
 
+    @Override
     public void dragExit(DropTargetEvent e)
     {
         DEBUG("dragExit");
     }
 
-
+    @Override
     public void drop(DropTargetDropEvent e)
     {
         if (active_) {
@@ -130,7 +134,6 @@ public class MoleculeDropAdapter implements DropTargetListener
         return ChemistryFlavors.MOLECULE_FLAVORS;
     }
 
-
     protected StereoMolecule createFromDataFlavor(DataFlavor chosen, Object o) throws Exception
     {
         StereoMolecule mol = null;
@@ -162,7 +165,6 @@ public class MoleculeDropAdapter implements DropTargetListener
         return mol;
     }
     
-            
     protected boolean isDragFlavorSupported(DropTargetDragEvent e)
     {
         for (int i=0; i<ChemistryFlavors.MOLECULE_FLAVORS.length; i++) {



View it on GitLab: https://salsa.debian.org/java-team/openchemlib/-/compare/471be68b69b2ce89cf5a0dbc01048909209c2dc3...c1316dfc3459d1af730a8dbceb79f5a32377fce3

-- 
View it on GitLab: https://salsa.debian.org/java-team/openchemlib/-/compare/471be68b69b2ce89cf5a0dbc01048909209c2dc3...c1316dfc3459d1af730a8dbceb79f5a32377fce3
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-java-commits/attachments/20201210/ed23bfc1/attachment.html>


More information about the pkg-java-commits mailing list