Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.actelion.research.gui.CompoundCollectionPane Maven / Gradle / Ivy
/*
* Copyright 2017 Idorsia Pharmaceuticals Ltd., Hegenheimermattweg 91, CH-4123 Allschwil, Switzerland
*
* This file is part of DataWarrior.
*
* DataWarrior is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* DataWarrior is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
* You should have received a copy of the GNU General Public License along with DataWarrior.
* If not, see http://www.gnu.org/licenses/.
*
* @author Thomas Sander
*/
package com.actelion.research.gui;
import com.actelion.research.chem.*;
import com.actelion.research.gui.clipboard.IClipboardHandler;
import com.actelion.research.gui.dnd.MoleculeDragAdapter;
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 javax.swing.*;
import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.event.*;
import java.awt.geom.Rectangle2D;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
public class CompoundCollectionPane extends JScrollPane
implements ActionListener,CompoundCollectionListener,MouseListener,MouseMotionListener,StructureListener {
private static final long serialVersionUID = 0x20060904;
private static final String MESSAGE = "";
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 CompoundCollectionModel 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;
/**
* This is a visual component to display and edit a compound collection maintained
* by a CompoundCollectionModel. Three variations of DefaultCompoundCollectionModel
* (.Native, .Molecule, and .IDCode) are available, which internally keep molecule
* instances as Object, StereoMolecule or String, respectively. If one of these
* default model is used, than the CompoundCollectionPane's T must match this class.
* @param model
* @param isVertical
*/
public CompoundCollectionPane(CompoundCollectionModel model, boolean isVertical) {
this(model, isVertical, 0, ALLOWED_DRAG_ACTIONS, ALLOWED_DROP_ACTIONS);
}
public CompoundCollectionPane(CompoundCollectionModel 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;
init();
initializeDragAndDrop(dragAction, dropAction);
}
public CompoundCollectionModel getModel() {
return mModel;
}
public void setEnabled(boolean b) {
super.setEnabled(b);
if (mIsVertical)
getVerticalScrollBar().setEnabled(b);
else
getHorizontalScrollBar().setEnabled(b);
mIsEnabled = b;
repaint();
}
/**
* Defines the width or height of individual structure cells,
* depending on whether the the CompoundCollectionPane is horizontal
* or vertical, respectively. Setting size to 0 (default) causes
* an automatic behavior with a square cell areas and width and height
* being implicitly defined by the CompoundCollectionPane component size.
*/
public void setStructureSize(int size) {
mStructureSize = size;
validateSize();
repaint();
}
/**
* Defines, whether the list and individual structures can be edited.
* @param editable
*/
public void setEditable(boolean editable) {
mIsEditable = editable;
updateMouseListening();
}
/**
* Defines, whether the popup menu contains 'Open' and/or 'Save' items.
* As default both, OPEN and SAVE options are active.
* @param fileSupport one of the FILE_SUPPORT_... options
*/
public void setFileSupport(int fileSupport) {
mFileSupport = fileSupport;
}
public void setCompoundFilter(MoleculeFilter filter) {
if (mCompoundFilter != filter) {
mCompoundFilter = filter;
if (mCompoundFilter != null)
for (int i=mModel.getSize()-1; i>=0; i--)
if (!mCompoundFilter.moleculeQualifies(mModel.getMolecule(i)))
mModel.remove(i);
}
}
public void setSelectable(boolean selectable) {
mIsSelectable = selectable;
updateMouseListening();
}
/**
* Defines whether new created structures are fragments of molecules.
* @param createFragments
*/
public void setCreateFragments(boolean createFragments) {
mCreateFragments = createFragments;
}
/**
* Defines whether a large red question mark is shown
* in case of a structure validation error.
* @param showError
*/
public void setShowValidationError(boolean showError) {
mShowValidationError = showError;
}
/**
* call this in order to get clipboard support
*/
public void setClipboardHandler(IClipboardHandler h) {
mClipboardHandler = h;
}
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;
StereoMolecule mol = mClipboardHandler.pasteMolecule();
if (mol != null) {
mol.setFragment(mCreateFragments);
if (mCompoundFilter == null || mCompoundFilter.moleculeQualifies(mol))
mModel.addMolecule(index, mol);
else
JOptionPane.showMessageDialog(getParentFrame(),"The compound could not be added, because it 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 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);
count++;
}
}
if (count != 0) {
JOptionPane.showMessageDialog(getParentFrame(),Integer.toString(count).concat(" compounds were removed, because they don't qualify."));
}
}
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("");
writer.newLine();
writer.write("");
writer.newLine();
writer.write("");
writer.newLine();
writer.write(" ");
writer.newLine();
writer.write("");
writer.newLine();
writer.write("");
writer.newLine();
writer.write("");
writer.newLine();
writer.write("");
writer.newLine();
writer.write("");
writer.newLine();
writer.write("");
writer.newLine();
writer.write(" ");
writer.newLine();
writer.write(title+"\tcoords");
writer.newLine();
for (int i=0; i= fromIndex && mSelectedIndex <= toIndex)
mSelectedIndex = -1;
if (mHighlightedIndex >= fromIndex && mHighlightedIndex <= toIndex)
mHighlightedIndex = -1;
repaint();
}
private Rectangle getMoleculeBounds(int molIndex) {
int x = cWhiteSpace/2;
int y = cWhiteSpace/2;
if (mIsVertical)
y += molIndex * mCellSize.height;
else
x += molIndex * mCellSize.width;
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;
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);
}
}
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))
mModel.addMolecule(mModel.getSize(), mol);
else
JOptionPane.showMessageDialog(getParentFrame(),"The compound could not be added, because it doesn't qualify.");
}
}
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 changes don't qualify.");
}
}
}
/**
* May be overridden to act on selection changes
* @param molIndex
*/
public void setSelection(int molIndex) {}
private void validateSize() {
Rectangle viewportBounds = getViewportBorderBounds();
int width = mIsVertical ? viewportBounds.width : mStructureSize == 0 ? viewportBounds.height : mStructureSize;
int height = !mIsVertical ? viewportBounds.height : mStructureSize == 0 ? viewportBounds.width : mStructureSize;
mCellSize = new Dimension(width, height);
if (mIsVertical) {
height *= mModel.getSize();
if (height < viewportBounds.height)
height = viewportBounds.height;
}
else {
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.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 getNativesForFlavors(DataFlavor[] dfs) {
java.awt.datatransfer.FlavorMap m = java.awt.datatransfer.SystemFlavorMap.getDefaultFlavorMap();
return m.getNativesForFlavors(dfs);
}
public java.util.Map getFlavorsForNatives(String[] natives) {
java.awt.datatransfer.FlavorMap m = java.awt.datatransfer.SystemFlavorMap.getDefaultFlavorMap();
return m.getFlavorsForNatives(natives);
}
}
}