23import java.awt.Cursor;
24import java.awt.Dimension;
26import java.io.IOException;
27import java.util.ArrayList;
29import java.util.HashMap;
32import java.util.logging.Logger;
34import javax.swing.JComponent;
35import javax.swing.JOptionPane;
36import javax.swing.JScrollPane;
37import javax.swing.JSplitPane;
38import javax.swing.JTable;
39import javax.swing.UIManager;
40import javax.swing.event.TableModelEvent;
41import javax.swing.event.TableModelListener;
42import javax.swing.table.DefaultTableModel;
43import javax.swing.table.JTableHeader;
44import javax.vecmath.Point3d;
46import org.jmol.viewer.Viewer;
47import org.openscience.cdk.DefaultChemObjectBuilder;
48import org.openscience.cdk.atomtype.CDKAtomTypeMatcher;
49import org.openscience.cdk.exception.CDKException;
50import org.openscience.cdk.interfaces.IAtom;
51import org.openscience.cdk.interfaces.IAtomContainer;
52import org.openscience.cdk.interfaces.IAtomType;
53import org.openscience.cdk.interfaces.IChemObjectBuilder;
54import org.openscience.cdk.modeling.builder3d.ModelBuilder3D;
55import org.openscience.cdk.modeling.builder3d.TemplateHandler3D;
56import org.openscience.cdk.silent.SilentChemObjectBuilder;
57import org.openscience.cdk.smiles.SmilesParser;
58import org.openscience.cdk.tools.CDKHydrogenAdder;
59import org.openscience.cdk.tools.manipulator.AtomContainerManipulator;
60import org.openscience.cdk.tools.manipulator.AtomTypeManipulator;
62import denoptim.constants.DENOPTIMConstants;
63import denoptim.exception.DENOPTIMException;
64import denoptim.graph.AttachmentPoint;
65import denoptim.graph.Fragment;
66import denoptim.graph.Vertex;
67import denoptim.io.DenoptimIO;
68import denoptim.utils.MathUtils;
69import denoptim.utils.MoleculeUtils;
93 protected Map<Integer,AttachmentPoint>
mapAPs =
null;
107 private final String
NL = System.getProperty(
"line.separator");
123 this(
null, editableTable);
136 this(
parent,editableTable,340);
158 @SuppressWarnings(
"serial")
161 this.setOrientation(JSplitPane.VERTICAL_SPLIT);
162 this.setOneTouchExpandable(
true);
163 this.setDividerLocation(dividerPosition);
164 this.setResizeWeight(0.5);
173 public boolean isCellEditable(
int row,
int column) {
185 String column_names[]= {
"<html><b>AP#</b></html>",
"<html><b>APClass</b></html>"};
186 apTabModel.setColumnIdentifiers(column_names);
188 apTable.putClientProperty(
"terminateEditOnFocusLost",
true);
189 apTable.getColumnModel().getColumn(0).setMaxWidth(75);
190 apTable.setGridColor(Color.LIGHT_GRAY);
191 JTableHeader apTabHeader =
apTable.getTableHeader();
192 apTabHeader.setPreferredSize(
new Dimension(100, 20));
195 tabPanel.setMinimumSize(
new Dimension(100,30));
234 parent.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
236 this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
241 Thread.sleep(milliSecFirst);
242 }
catch (InterruptedException e) {
247 Date date =
new Date();
248 long startTime = date.getTime();
249 long wallTime = startTime + 5000;
252 Date newDate =
new Date();
253 long now = newDate.getTime();
254 System.out.println(
"Waiting for Jmol (timespot "+now+
")");
259 parent.setCursor(Cursor.getPredefinedCursor(
260 Cursor.DEFAULT_CURSOR));
262 this.setCursor(Cursor.getPredefinedCursor(
263 Cursor.DEFAULT_CURSOR));
265 String[] options =
new String[]{
"Yes",
"No"};
266 int res = JOptionPane.showOptionDialog(
this,
267 "<html>" + cause +
".<br>Keep waiting? (5 sec)</html>",
268 "Should we wait for another 5 seconds?",
269 JOptionPane.DEFAULT_OPTION,
270 JOptionPane.QUESTION_MESSAGE,
271 UIManager.getIcon(
"OptionPane.warningIcon"),
276 System.out.println(
"Give up waiting");
282 parent.setCursor(Cursor.getPredefinedCursor(
283 Cursor.DEFAULT_CURSOR));
285 this.setCursor(Cursor.getPredefinedCursor(
286 Cursor.DEFAULT_CURSOR));
292 System.out.println(
"Keep waiting...");
295 parent.setCursor(Cursor.getPredefinedCursor(
296 Cursor.WAIT_CURSOR));
298 this.setCursor(Cursor.getPredefinedCursor(
299 Cursor.WAIT_CURSOR));
301 newDate =
new Date();
302 wallTime = newDate.getTime() + 5000;
307 }
catch (InterruptedException e) {
316 CloneNotSupportedException, IOException
318 IAtomContainer mol =
null;
320 SmilesParser sp =
new SmilesParser(
321 DefaultChemObjectBuilder.getInstance());
322 mol = sp.parseSmiles(smiles);
331 CDKAtomTypeMatcher matcher = CDKAtomTypeMatcher.getInstance(
333 for (IAtom atom : mol.atoms())
335 IAtomType type = matcher.findMatchingAtomType(mol, atom);
336 AtomTypeManipulator.configure(atom, type);
339 CDKHydrogenAdder adder = CDKHydrogenAdder.getInstance(
341 adder.addImplicitHydrogens(mol);
343 AtomContainerManipulator.convertImplicitToExplicitHydrogens(mol);
345 ModelBuilder3D mb3d = ModelBuilder3D.getInstance(
346 TemplateHandler3D.getInstance(),
"mmff94",
347 DefaultChemObjectBuilder.getInstance());
348 mol = mb3d.generate3DCoordinates(mol,
false);
374 data = data.replaceAll(
" Xx ",
396 parent.setCursor(Cursor.getPredefinedCursor(
397 Cursor.WAIT_CURSOR));
399 this.setCursor(Cursor.getPredefinedCursor(
400 Cursor.WAIT_CURSOR));
408 "Slow response from https://cactus.nci.nih.gov");
415 if (data ==
null || data.equals(
""))
417 String[] options =
new String[] {
"Build 3D guess",
"Abandon"};
418 int res = JOptionPane.showOptionDialog(
this,
419 "<html>Could not find a valid structure.<br>"
420 +
"Possible reasons are:"
422 +
"<li>unreachable remote service (we are offline)</li>"
423 +
"<li>too slow response from online service</li>"
424 +
"<li>no available structure for SMILES string<br>'"
427 +
"You can try to build a guess 3D structure, or<br>"
428 +
"prepare a refined structure elsewhere and import "
430 +
"SDF file.</html>",
432 JOptionPane.OK_CANCEL_OPTION,
433 JOptionPane.QUESTION_MESSAGE,
434 UIManager.getIcon(
"OptionPane.errorIcon"),
441 }
catch (CDKException | CloneNotSupportedException
443 e1.printStackTrace();
444 JOptionPane.showMessageDialog(
this,
445 "<html>Could not make 3D structure from SMILES."
446 +
" Cause: '"+e1.getMessage()+
"'</html>",
448 JOptionPane.ERROR_MESSAGE,
449 UIManager.getIcon(
"OptionPane.errorIcon"));
452 parent.setCursor(Cursor.getPredefinedCursor(
453 Cursor.DEFAULT_CURSOR));
455 this.setCursor(Cursor.getPredefinedCursor(
456 Cursor.DEFAULT_CURSOR));
463 parent.setCursor(Cursor.getPredefinedCursor(
464 Cursor.DEFAULT_CURSOR));
466 this.setCursor(Cursor.getPredefinedCursor(
467 Cursor.DEFAULT_CURSOR));
477 }
catch (CDKException | CloneNotSupportedException
479 e1.printStackTrace();
480 JOptionPane.showMessageDialog(
this,
481 "<html>Could not make 3D structure from SMILES. "
482 +
"Cause: '"+e1.getMessage()+
"'</html>",
484 JOptionPane.ERROR_MESSAGE,
485 UIManager.getIcon(
"OptionPane.errorIcon"));
488 parent.setCursor(Cursor.getPredefinedCursor(
489 Cursor.DEFAULT_CURSOR));
491 this.setCursor(Cursor.getPredefinedCursor(
492 Cursor.DEFAULT_CURSOR));
510 }
catch (Exception e) {
512 JOptionPane.showMessageDialog(
this,
513 "<html>Could not understand Jmol system.</html>",
515 JOptionPane.ERROR_MESSAGE,
516 UIManager.getIcon(
"OptionPane.errorIcon"));
526 parent.setCursor(Cursor.getPredefinedCursor(
527 Cursor.DEFAULT_CURSOR));
529 this.setCursor(Cursor.getPredefinedCursor(
530 Cursor.DEFAULT_CURSOR));
540 IChemObjectBuilder builder = SilentChemObjectBuilder.getInstance();
541 IAtomContainer mol = builder.newAtomContainer();
544 if (strData.trim().equals(
""))
546 throw new DENOPTIMException(
"Attempt to get data from viewer, but data is empty");
548 strData = strData.replaceAll(
"[0-9] \\s+ 999 V2000",
549 "0 0 0 0 0999 V2000");
550 String[] lines = strData.split(
"\\n");
551 if (lines.length < 5)
557 +
"data: '" + strData +
"'");
559 if (!lines[3].matches(
".*999 V2000.*"))
565 +
"data: " + strData);
567 int nAtms = Integer.parseInt(lines[3].substring(0,3).trim());
568 int nBonds = Integer.parseInt(lines[3].substring(3,6).trim());
570 StringBuilder sb =
new StringBuilder();
571 sb.append(
"Structure in JmolViewer").append(
NL);
572 sb.append(
"Jmol").append(
NL).append(
NL);
573 sb.append(lines[3]).append(
NL);
574 for (
int i=0; i<nAtms; i++)
576 sb.append(lines[3+i+1]).append(
" 0 0 0 0 0 0").append(
NL);
578 for (
int i=0; i<nBonds; i++)
580 sb.append(lines[3+nAtms+i+1]).append(
" 0").append(
NL);
582 sb.append(
"M END").append(
NL).append(
"$$$$");
588 }
catch (Exception e)
627 boolean sameSMILES =
false;
644 double thrld = 0.0001;
651 if (pA.distance(pB)>thrld)
673 if (mol.getNumberOfAPs() ==
mapAPs.size())
678 for (
int apId :
mapAPs.keySet())
687 if (srcAtmId > mol.getAtomCount())
690 +
"no longer compatible with the list of attachment "
691 +
"points. Cannot convert the current system to a "
693 +
"srcAtmId:" + srcAtmId
694 +
" #atms:" + mol.getAtomCount());
732 JOptionPane.showMessageDialog(
this,
733 "<html>No structure loaded.<br>This is most likely a bug "
734 +
"in FragmentViewPanel. "
735 +
"Please report it to the development team.</html>",
737 JOptionPane.PLAIN_MESSAGE,
738 UIManager.getIcon(
"OptionPane.errorIcon"));
750 System.out.println(
"Error writing TMP file '" +
tmpSDFFile +
"'");
751 System.out.println(
"Please, report this to the DENOPTIM team.");
773 this.fragment = frag;
789 mapAPs =
new HashMap<Integer,AttachmentPoint>();
792 if (lstAPs.size() == 0)
802 apTabModel.addRow(
new Object[]{arrId, ap.getAPClass()});
821 String[] parts =prop.split(PRESELPROPSEP);
824 for (
int i=0; i<parts.length; i++)
826 int apId = Integer.parseInt(parts[i]);
827 apTable.getSelectionModel().addSelectionInterval(apId, apId);
856 for (
int i=0; i<initRowCount; i++)
889 StringBuilder sb =
new StringBuilder();
890 for (
int arrId :
mapAPs.keySet())
896 double[] startArrow =
new double[]{
903 if (startArrow ==
null || endArrow==
null)
905 System.out.println(
"WARNING: AP without geometrical data will "
906 +
"be ignored! Ignoring ap "+ap);
912 double[] positionLabel =
MathUtils.
add(endArrow,offSet);
913 sb.append(
"draw arrow").append(arrId).append(
" arrow ");
916 sb.append(
" width 0.1");
918 sb.append(
"set echo apLab").append(arrId);
920 sb.append(
"; echo ").append(arrId);
921 sb.append(
"; color echo yellow");
931 StringBuilder sb =
new StringBuilder();
933 sb.append(position[0]).append(
" ");
934 sb.append(position[1]).append(
" ");
935 sb.append(position[2]).append(
"} ");
936 return sb.toString();
943 StringBuilder sb =
new StringBuilder();
944 sb.append(
"select none").append(
NL);
945 sb.append(
"SelectionHalos ON").append(
NL);
946 sb.append(
"set picking ATOMS").append(
NL);
958 ArrayList<AttachmentPoint> selected =
959 new ArrayList<AttachmentPoint>();
961 for (
int rowId :
apTable.getSelectedRows())
976 ArrayList<Integer> selected =
new ArrayList<Integer>();
977 for (
int rowId :
apTable.getSelectedRows())
992 ArrayList<IAtom> selectedAtms =
new ArrayList<IAtom>();
1007 return selectedAtms;
1023 && e.getType() == TableModelEvent.UPDATE)
1050 }
catch (Throwable t) {
1052 System.out.println(
"Bad attempt to contro listener: "
1054 System.out.println(t.getCause());
General set of constants used in DENOPTIM.
static final String DUMMYATMSYMBOL
Symbol of dummy atom.
An attachment point (AP) is a possibility to attach a Vertex onto the vertex holding the AP (i....
APClass getAPClass()
Returns the Attachment Point class.
int getAtomPositionNumber()
The index of the source atom in the atom list of the fragment.
Point3d getDirectionVector()
Returns the end of the direction vector.
Class representing a continuously connected portion of chemical object holding attachment points.
List< AttachmentPoint > getCurrentAPs()
Collects APs currently defined as properties of the atoms.
IAtom getAtom(int number)
IAtomContainer getIAtomContainer()
A vertex is a data structure that has an identity and holds a list of AttachmentPoints.
Object getProperty(Object property)
PausableTableModelListener()
void tableChanged(TableModelEvent e)
void setActive(boolean var)
A panel with a molecular viewer and attachment point table.
void updateAPsInJmolViewer()
static final long serialVersionUID
Version UID.
IAtomContainer getStructureFromJmolViewer()
FragmentViewPanel(boolean editableTable)
Constructor that allows to specify whether the AP table is editable or not.
Map< Integer, AttachmentPoint > mapAPs
Temporary list of attachment points of the current fragment.
void make3DusingCDKgenerator(String smiles)
FragmentViewPanel(JComponent parent, boolean editableTable, int dividerPosition)
Constructor that allows to specify whether the AP table is editable or not.
DefaultTableModel getAPTableModel()
void waitForJmolViewer(int milliSecFirst, String cause)
Waits until Jmol is finished.
void updateAPsMapAndTable()
Uses the AP of the Fragment to create a new map and table of APs.
void initialize(int dividerPosition)
boolean hasUnsavedAPEdits()
Check for unsaved edits to the AP data.
boolean loadSMILES(String smiles)
Loads a molecule build from a smiles string.
void clearAll(boolean dataIsComing)
Removes the currently visualized molecule and AP table.
ArrayList< IAtom > getAtomsSelectedFromJMol()
Identifies the atoms that are selected in the Jmol viewer.
void clearAPTable()
Clears the table of attachment points.
void loadPlainStructure(IAtomContainer mol)
Loads a structure in the Jmol viewer.
void putAPsFromTableIntoIAtomContainer(Fragment mol)
ArrayList< Integer > getSelectedAPIDs()
Identifies which attachment points are selected in the visualized table.
String getJmolPositionStr(double[] position)
Fragment fragment
The currently loaded fragment.
FragmentViewPanel(JComponent parent, boolean editableTable)
Constructor that allows to specify whether the AP table is editable or not.
void deprotectEdits()
Overrides the flag signaling unsaved edits to saying that there are no altered data.
DefaultTableModel apTabModel
void loadStructure()
Loads the structure of the currently loaded 'fragment' (i.e., our member) into the Jmol viewer.
ArrayList< AttachmentPoint > getSelectedAPs()
Identifies which attachment points are selected in the visualized table.
void loadFragmentToViewer(Fragment frag)
Loads the given fragments to this viewer.
Map< Integer, AttachmentPoint > getMapOfAPsInTable()
void activateTabEditsListener(boolean var)
Allows to activate and deactivate the listener.
void clearMolecularViewer(boolean dataIsComing)
Clears the molecular viewer.
Fragment getLoadedStructure()
Returns the chemical representation of the currently loaded chemical object.
boolean alteredAPData
Flag signalling that data about APs has been changed in the GUI.
Graphical User Interface of the DENOPTIM package.
static final String GUILOGGER
Name of logger for the GUI.
The collection of tunable preferences.
static SMITo3DEngine smiTo3dResolver
Selects the engine used to do SMILES-to-3D conversion.
A modal dialog with a viewer that understands the different types of DENOPTIM vertex and allows to se...
static final String PRESELECTEDAPSFIELD
Property used to pre-select APs.
static final String PRESELECTEDAPSFIELDSEP
Separator in property used to pre-select APs.
static String getTempFile(String tmpFileName)
Returns the pathname to a tmp file.
Utility methods for input/output.
static void writeSDFFile(String fileName, IAtomContainer mol)
Writes IAtomContainer to SDF file.
static void writeData(String fileName, String data, boolean append)
Write text-like data file.
static List< IAtomContainer > readAllAtomContainers(File file)
Returns a single collection with all atom containers found in a file of any format.
Some useful math operations.
static double[] add(double[] v0, double[] v1)
Perform vector addition.
static double[] subtract(double[] v0, double[] v1)
Perform vector subtraction.
static double[] scale(double[] d, double a)
Scales a vector.
Utilities for molecule conversion.
static String getSMILESForMolecule(IAtomContainer mol, Logger logger)
Returns the SMILES representation of the molecule.
static Point3d getPoint3d(IAtom atm)
Return the 3D coordinates, if present.
The type of building block.
Interface for all vertex viewers that intend to allow selection of attachment points.
final String APDATACHANGEEVENT