$darkmode
DENOPTIM
FragmentViewPanel.java
Go to the documentation of this file.
1/*
2 * DENOPTIM
3 * Copyright (C) 2020 Marco Foscato <marco.foscato@uib.no>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as published
7 * by the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19package denoptim.gui;
20
21
22import java.awt.Color;
23import java.awt.Cursor;
24import java.awt.Dimension;
25import java.io.File;
26import java.io.IOException;
27import java.util.ArrayList;
28import java.util.Date;
29import java.util.HashMap;
30import java.util.List;
31import java.util.Map;
32import java.util.logging.Logger;
33
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;
45
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;
61
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;
70
71
78public class FragmentViewPanel extends JSplitPane implements IVertexAPSelection
79{
83 private static final long serialVersionUID = 912850110991449553L;
84
89
93 protected Map<Integer,AttachmentPoint> mapAPs = null;
94
98 public boolean alteredAPData = false;
99
101 private JScrollPane tabPanel;
102 protected DefaultTableModel apTabModel;
103 protected JTable apTable;
104
105 private boolean editableAPTable = false;
106
107 private final String NL = System.getProperty("line.separator");
108
109 private String tmpSDFFile;
110
111 private JComponent parent;
112
113
114//-----------------------------------------------------------------------------
115
121 public FragmentViewPanel(boolean editableTable)
122 {
123 this(null, editableTable);
124 }
125
126//-----------------------------------------------------------------------------
127
134 public FragmentViewPanel(JComponent parent, boolean editableTable)
135 {
136 this(parent,editableTable,340);
137 }
138
139//-----------------------------------------------------------------------------
140
148 public FragmentViewPanel(JComponent parent, boolean editableTable,
149 int dividerPosition)
150 {
151 this.parent = parent;
152 editableAPTable = editableTable;
153 initialize(dividerPosition);
154 }
155
156//-----------------------------------------------------------------------------
157
158 @SuppressWarnings("serial")
159 private void initialize(int dividerPosition)
160 {
161 this.setOrientation(JSplitPane.VERTICAL_SPLIT);
162 this.setOneTouchExpandable(true);
163 this.setDividerLocation(dividerPosition);
164 this.setResizeWeight(0.5);
165
166 // Jmol viewer panel
167 jmolPanel = new JmolPanel();
168 this.setTopComponent(jmolPanel);
169
170 // List of attachment points
171 apTabModel = new DefaultTableModel() {
172 @Override
173 public boolean isCellEditable(int row, int column) {
174 if (column == 0)
175 {
176 return false;
177 }
178 else
179 {
180 return editableAPTable;
181 }
182 }
183 };
184 apTabModel.setColumnCount(2);
185 String column_names[]= {"<html><b>AP#</b></html>", "<html><b>APClass</b></html>"};
186 apTabModel.setColumnIdentifiers(column_names);
187 apTable = new JTable(apTabModel);
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));
193 apTabModel.addTableModelListener(new PausableTableModelListener());
194 tabPanel = new JScrollPane(apTable);
195 tabPanel.setMinimumSize(new Dimension(100,30));
196 this.setBottomComponent(tabPanel);
197
198 //Find a proper tmp disk space
199 tmpSDFFile = Utils.getTempFile("Denoptim_FragViewer_loadedMol.sdf");
200 }
201
202//-----------------------------------------------------------------------------
203
208 public boolean hasUnsavedAPEdits()
209 {
210 return alteredAPData;
211 }
212
213//-----------------------------------------------------------------------------
214
219 public void deprotectEdits()
220 {
221 alteredAPData = false;
222 }
223
224//-----------------------------------------------------------------------------
225
230 private void waitForJmolViewer(int milliSecFirst, String cause)
231 {
232 if (parent!=null)
233 {
234 parent.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
235 } else {
236 this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
237 }
238
239 // We wait
240 try {
241 Thread.sleep(milliSecFirst);
242 } catch (InterruptedException e) {
243 e.printStackTrace();
244 }
245
246 // Waiting cycles
247 Date date = new Date();
248 long startTime = date.getTime();
249 long wallTime = startTime + 5000; // 5 seconds
250 while (jmolPanel.viewer.isScriptExecuting())
251 {
252 Date newDate = new Date();
253 long now = newDate.getTime();
254 System.out.println("Waiting for Jmol (timespot "+now+")");
255 if (now > wallTime)
256 {
257 if (parent!=null)
258 {
259 parent.setCursor(Cursor.getPredefinedCursor(
260 Cursor.DEFAULT_CURSOR));
261 } else {
262 this.setCursor(Cursor.getPredefinedCursor(
263 Cursor.DEFAULT_CURSOR));
264 }
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"),
272 options,
273 options[1]);
274 if (res == 1)
275 {
276 System.out.println("Give up waiting");
277 jmolPanel.viewer.haltScriptExecution();
278 //no data is coming, so we have to 'zap'
280 if (parent!=null)
281 {
282 parent.setCursor(Cursor.getPredefinedCursor(
283 Cursor.DEFAULT_CURSOR));
284 } else {
285 this.setCursor(Cursor.getPredefinedCursor(
286 Cursor.DEFAULT_CURSOR));
287 }
288 break;
289 }
290 else
291 {
292 System.out.println("Keep waiting...");
293 if (parent!=null)
294 {
295 parent.setCursor(Cursor.getPredefinedCursor(
296 Cursor.WAIT_CURSOR));
297 } else {
298 this.setCursor(Cursor.getPredefinedCursor(
299 Cursor.WAIT_CURSOR));
300 }
301 newDate = new Date();
302 wallTime = newDate.getTime() + 5000; // 5 seconds
303 }
304 }
305 try {
306 Thread.sleep(500);
307 } catch (InterruptedException e) {
308 e.printStackTrace();
309 }
310 }
311 }
312
313//-----------------------------------------------------------------------------
314
315 private void make3DusingCDKgenerator(String smiles) throws CDKException,
316 CloneNotSupportedException, IOException
317 {
318 IAtomContainer mol = null;
319
320 SmilesParser sp = new SmilesParser(
321 DefaultChemObjectBuilder.getInstance());
322 mol = sp.parseSmiles(smiles);
323
324 // This seems to be needed in the opposite order since cdk-2.3
325 /*
326 CDKHydrogenAdder adder = CDKHydrogenAdder.getInstance(
327 mol.getBuilder());
328 adder.addImplicitHydrogens(mol);
329 */
330
331 CDKAtomTypeMatcher matcher = CDKAtomTypeMatcher.getInstance(
332 mol.getBuilder());
333 for (IAtom atom : mol.atoms())
334 {
335 IAtomType type = matcher.findMatchingAtomType(mol, atom);
336 AtomTypeManipulator.configure(atom, type);
337 }
338
339 CDKHydrogenAdder adder = CDKHydrogenAdder.getInstance(
340 mol.getBuilder());
341 adder.addImplicitHydrogens(mol);
342
343 AtomContainerManipulator.convertImplicitToExplicitHydrogens(mol);
344
345 ModelBuilder3D mb3d = ModelBuilder3D.getInstance(
346 TemplateHandler3D.getInstance(), "mmff94",
347 DefaultChemObjectBuilder.getInstance());
348 mol = mb3d.generate3DCoordinates(mol, false);
349
350 // Load the system int oJmol
352
353 // Run Jmol MM energy minimization
354 jmolPanel.viewer.evalString("minimize");
355 waitForJmolViewer(1500,"Energy minimization is taking some time.");
356
357 // Re-load
358 IAtomContainer iac;
359 try {
362 } catch (DENOPTIMException e) {
363 e.printStackTrace();
364 //no data is coming, so we have to 'zap'
366 }
367 }
368
369//-----------------------------------------------------------------------------
370
371 public String getDataFromJmol()
372 {
373 String data = jmolPanel.viewer.getData("*", "txt");
374 data = data.replaceAll(" Xx ",
376 return data;
377 }
378
379//-----------------------------------------------------------------------------
380
392 public boolean loadSMILES(String smiles)
393 {
394 if (parent!=null)
395 {
396 parent.setCursor(Cursor.getPredefinedCursor(
397 Cursor.WAIT_CURSOR));
398 } else {
399 this.setCursor(Cursor.getPredefinedCursor(
400 Cursor.WAIT_CURSOR));
401 }
402
404 {
405 case CACTVS:
406 jmolPanel.viewer.evalString("load $"+smiles);
408 "Slow response from https://cactus.nci.nih.gov");
409 // NB: this is a workaround to the lack of try/catch mechanism when
410 // executing Jmol commands.
411 // We want to catch errors that prevent loading the structure
412 // For example, offline mode and invalid SMILES
413 String data = getDataFromJmol();
414
415 if (data == null || data.equals(""))
416 {
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:"
421 + "<ul>"
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>'"
425 + smiles
426 + "'</li></ul><br>"
427 + "You can try to build a guess 3D structure, or<br>"
428 + "prepare a refined structure elsewhere and import "
429 + "it as "
430 + "SDF file.</html>",
431 "Error",
432 JOptionPane.OK_CANCEL_OPTION,
433 JOptionPane.QUESTION_MESSAGE,
434 UIManager.getIcon("OptionPane.errorIcon"),
435 options,
436 options[0]);
437 if (res == 0)
438 {
439 try {
441 } catch (CDKException | CloneNotSupportedException
442 | IOException e1) {
443 e1.printStackTrace();
444 JOptionPane.showMessageDialog(this,
445 "<html>Could not make 3D structure from SMILES."
446 + " Cause: '"+e1.getMessage()+"'</html>",
447 "Error",
448 JOptionPane.ERROR_MESSAGE,
449 UIManager.getIcon("OptionPane.errorIcon"));
450 if (parent!=null)
451 {
452 parent.setCursor(Cursor.getPredefinedCursor(
453 Cursor.DEFAULT_CURSOR));
454 } else {
455 this.setCursor(Cursor.getPredefinedCursor(
456 Cursor.DEFAULT_CURSOR));
457 }
458 return false;
459 }
460 } else {
461 if (parent!=null)
462 {
463 parent.setCursor(Cursor.getPredefinedCursor(
464 Cursor.DEFAULT_CURSOR));
465 } else {
466 this.setCursor(Cursor.getPredefinedCursor(
467 Cursor.DEFAULT_CURSOR));
468 }
469 return false;
470 }
471 }
472 break;
473
474 case CDK:
475 try {
477 } catch (CDKException | CloneNotSupportedException
478 | IOException e1) {
479 e1.printStackTrace();
480 JOptionPane.showMessageDialog(this,
481 "<html>Could not make 3D structure from SMILES. "
482 + "Cause: '"+e1.getMessage()+"'</html>",
483 "Error",
484 JOptionPane.ERROR_MESSAGE,
485 UIManager.getIcon("OptionPane.errorIcon"));
486 if (parent!=null)
487 {
488 parent.setCursor(Cursor.getPredefinedCursor(
489 Cursor.DEFAULT_CURSOR));
490 } else {
491 this.setCursor(Cursor.getPredefinedCursor(
492 Cursor.DEFAULT_CURSOR));
493 }
494 return false;
495 }
496 break;
497 }
498
499 // Now we should have a structure loaded in the viewer,
500 // so we take that one and put it in the IAtomContainer representation
501 try {
502 // WARNING! Here we assume we are always making undefined building
503 // blocks. Namely, neither a scaffold, nor a Fragment, nor a
504 // capping group. This because when we make frags
505 // from the GUI we are only saving an SDF file. The latter is later
506 // imported as part of the library of scaffolds/fragments/capping
507 // groups, and in that moment the BBType is re-assigned.
510 } catch (Exception e) {
511 e.printStackTrace();
512 JOptionPane.showMessageDialog(this,
513 "<html>Could not understand Jmol system.</html>",
514 "Error",
515 JOptionPane.ERROR_MESSAGE,
516 UIManager.getIcon("OptionPane.errorIcon"));
517 return false;
518 }
519
521
522 jmolPanel.viewer.evalString("zoom OUT");
523
524 if (parent!=null)
525 {
526 parent.setCursor(Cursor.getPredefinedCursor(
527 Cursor.DEFAULT_CURSOR));
528 } else {
529 this.setCursor(Cursor.getPredefinedCursor(
530 Cursor.DEFAULT_CURSOR));
531 }
532 return true;
533 }
534
535//-----------------------------------------------------------------------------
536
537 private IAtomContainer getStructureFromJmolViewer()
538 throws DENOPTIMException
539 {
540 IChemObjectBuilder builder = SilentChemObjectBuilder.getInstance();
541 IAtomContainer mol = builder.newAtomContainer();
542
543 String strData = getDataFromJmol();
544 if (strData.trim().equals(""))
545 {
546 throw new DENOPTIMException("Attempt to get data from viewer, but data is empty");
547 }
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)
552 {
553 //no data is coming, so we have to 'zap'
555 clearAPTable();
556 throw new DENOPTIMException("Unexpected format in Jmol molecular "
557 + "data: '" + strData + "'");
558 }
559 if (!lines[3].matches(".*999 V2000.*"))
560 {
561 //no data is coming, so we have to 'zap'
563 clearAPTable();
564 throw new DENOPTIMException("Unexpected format in Jmol molecular "
565 + "data: " + strData);
566 }
567 int nAtms = Integer.parseInt(lines[3].substring(0,3).trim());
568 int nBonds = Integer.parseInt(lines[3].substring(3,6).trim());
569
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++)
575 {
576 sb.append(lines[3+i+1]).append(" 0 0 0 0 0 0").append(NL);
577 }
578 for (int i=0; i<nBonds; i++)
579 {
580 sb.append(lines[3+nAtms+i+1]).append(" 0").append(NL);
581 }
582 sb.append("M END").append(NL).append("$$$$");
583
584 DenoptimIO.writeData(tmpSDFFile, sb.toString(), false);
585 try
586 {
587 mol = DenoptimIO.readAllAtomContainers(new File(tmpSDFFile)).get(0);
588 } catch (Exception e)
589 {
590 throw new DENOPTIMException("Unable to fetch molecule from tmp SDF",
591 e);
592 }
593 return mol;
594 }
595
596//-----------------------------------------------------------------------------
597
608 {
609 Fragment fromViewer = new Fragment();
610 try {
611 fromViewer = new Fragment(getStructureFromJmolViewer(),
614 } catch (DENOPTIMException e) {
615 //e.printStackTrace();
616 return fragment;
617 }
618
619 // NB: APs cannot be added directly into Jmol, so the only thing
620 // that can change there is the molecular structure
621 if (fromViewer.getAtomCount() != fragment.getAtomCount())
622 {
623 fragment = fromViewer;
624 return fragment;
625 }
626
627 boolean sameSMILES = false;
628 try {
629 Logger logger = Logger.getLogger(GUI.GUILOGGER);
631 fromViewer.getIAtomContainer(),logger)
633 fragment.getIAtomContainer(), logger));
634 } catch (DENOPTIMException e) {
635 // we get false
636 }
637 if (!sameSMILES)
638 {
639 fragment = fromViewer;
640 return fragment;
641 }
642
643 //Maybe the geometry is different
644 double thrld = 0.0001;
645 for (int i=0; i<fragment.getAtomCount(); i++)
646 {
647 Point3d pA = MoleculeUtils.getPoint3d(
648 fromViewer.getAtom(i));
649 Point3d pB = MoleculeUtils.getPoint3d(
650 fragment.getAtom(i));
651 if (pA.distance(pB)>thrld)
652 {
653 fragment = fromViewer;
654 return fragment;
655 }
656 }
657
659
660 return fragment;
661 }
662
663//-----------------------------------------------------------------------------
664
666 throws DENOPTIMException
667 {
668 if (mapAPs == null || mapAPs.isEmpty())
669 {
670 return;
671 }
672
673 if (mol.getNumberOfAPs() == mapAPs.size())
674 {
675 return;
676 }
677
678 for (int apId : mapAPs.keySet())
679 {
680 AttachmentPoint ap = mapAPs.get(apId);
681 int srcAtmId = ap.getAtomPositionNumber();
682
683 //NB here the inequity considers two completely disjoint indexes
684 //but is the only thing that seems valid at the stage were the atoms
685 //contained in the Jmol viewer may vary freely from the APs
686 //collected in mapAPs
687 if (srcAtmId > mol.getAtomCount())
688 {
689 throw new DENOPTIMException("The atom list has changed and is "
690 + "no longer compatible with the list of attachment "
691 + "points. Cannot convert the current system to a "
692 + "valid fragment. "
693 + "srcAtmId:" + srcAtmId
694 + " #atms:" + mol.getAtomCount());
695 }
696 mol.addAP(srcAtmId, ap.getAPClass(),
697 new Point3d(ap.getDirectionVector()));
698 }
699 }
700
701//-----------------------------------------------------------------------------
702
710 public void loadPlainStructure(IAtomContainer mol)
711 {
712 try {
715 } catch (DENOPTIMException e) {
716 //Should never happen
717 e.printStackTrace();
718 }
719 }
720
721//-----------------------------------------------------------------------------
722
727 private void loadStructure()
728 {
729
730 if (fragment == null)
731 {
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>",
736 "Error",
737 JOptionPane.PLAIN_MESSAGE,
738 UIManager.getIcon("OptionPane.errorIcon"));
739 return;
740 }
741
742 // NB: we use the nasty trick of a tmp file to by-pass the
743 // fragile/discontinued CDK-to-Jmol support.
744
745 try {
747 false);
748 } catch (DENOPTIMException e) {
749 e.printStackTrace();
750 System.out.println("Error writing TMP file '" + tmpSDFFile + "'");
751 System.out.println("Please, report this to the DENOPTIM team.");
752 }
753
754 jmolPanel.viewer.openFile(tmpSDFFile);
755
757
758 jmolPanel.viewer.evalString("zoom OUT");
759 }
760
761//-----------------------------------------------------------------------------
762
772 {
773 this.fragment = frag;
774 loadStructure();
777 preSelectAPs();
778 }
779
780//-----------------------------------------------------------------------------
781
786 private void updateAPsMapAndTable()
787 {
788 clearAPTable();
789 mapAPs = new HashMap<Integer,AttachmentPoint>();
790
791 List<AttachmentPoint> lstAPs = fragment.getCurrentAPs();
792 if (lstAPs.size() == 0)
793 {
794 return;
795 }
796
798 int arrId = 0;
799 for (AttachmentPoint ap : lstAPs)
800 {
801 arrId++;
802 apTabModel.addRow(new Object[]{arrId, ap.getAPClass()});
803 mapAPs.put(arrId,ap);
804 }
806 }
807
808//-----------------------------------------------------------------------------
809
810 private void preSelectAPs()
811 {
812 String PRESELPROP = GUIVertexSelector.PRESELECTEDAPSFIELD;
813 String PRESELPROPSEP = GUIVertexSelector.PRESELECTEDAPSFIELDSEP;
814
815 if (fragment.getProperty(PRESELPROP) == null)
816 {
817 return;
818 }
819
820 String prop = fragment.getProperty(PRESELPROP).toString();
821 String[] parts =prop.split(PRESELPROPSEP);
822
824 for (int i=0; i<parts.length; i++)
825 {
826 int apId = Integer.parseInt(parts[i]); //0-based
827 apTable.getSelectionModel().addSelectionInterval(apId, apId);
828 }
830 }
831
832//-----------------------------------------------------------------------------
833
841 public void clearAll(boolean dataIsComing)
842 {
843 clearAPTable();
844 clearMolecularViewer(dataIsComing);
845 }
846
847//-----------------------------------------------------------------------------
848
852 public void clearAPTable()
853 {
855 int initRowCount = apTabModel.getRowCount();
856 for (int i=0; i<initRowCount; i++)
857 {
858 //Always remove the first to avoid dealing with changing row ids
859 apTabModel.removeRow(0);
860 }
862 }
863
864//-----------------------------------------------------------------------------
865
874 public void clearMolecularViewer(boolean dataIsComing)
875 {
876 if (!dataIsComing)
877 jmolPanel.viewer.evalString("zap");
878 }
879
880//-----------------------------------------------------------------------------
881
883 {
884 if (mapAPs == null || mapAPs.isEmpty())
885 {
886 return;
887 }
888
889 StringBuilder sb = new StringBuilder();
890 for (int arrId : mapAPs.keySet())
891 {
892 AttachmentPoint ap = mapAPs.get(arrId);
893 int srcAtmId = ap.getAtomPositionNumber();
894 Point3d srcAtmPlace = MoleculeUtils.getPoint3d(
895 fragment.getAtom(srcAtmId));
896 double[] startArrow = new double[]{
897 srcAtmPlace.x,
898 srcAtmPlace.y,
899 srcAtmPlace.z};
900 double[] endArrow = new double[]{ap.getDirectionVector().x,
902
903 if (startArrow == null || endArrow==null)
904 {
905 System.out.println("WARNING: AP without geometrical data will "
906 + "be ignored! Ignoring ap "+ap);
907 continue;
908 }
909
910 double[] offSet = MathUtils.scale(
911 MathUtils.subtract(endArrow,startArrow), 0.2);
912 double[] positionLabel = MathUtils.add(endArrow,offSet);
913 sb.append("draw arrow").append(arrId).append(" arrow ");
914 sb.append(getJmolPositionStr(startArrow));
915 sb.append(getJmolPositionStr(endArrow));
916 sb.append(" width 0.1");
917 sb.append(NL);
918 sb.append("set echo apLab").append(arrId);
919 sb.append(getJmolPositionStr(positionLabel));
920 sb.append("; echo ").append(arrId);
921 sb.append("; color echo yellow");
922 sb.append(NL);
923 }
924 jmolPanel.viewer.evalString(sb.toString());
925 }
926
927//-----------------------------------------------------------------------------
928
929 private String getJmolPositionStr(double[] position)
930 {
931 StringBuilder sb = new StringBuilder();
932 sb.append(" {");
933 sb.append(position[0]).append(" ");
934 sb.append(position[1]).append(" ");
935 sb.append(position[2]).append("} ");
936 return sb.toString();
937 }
938
939//-----------------------------------------------------------------------------
940
941 private void setJmolViewer()
942 {
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);
947 jmolPanel.viewer.evalString(sb.toString());
948 }
949
950//-----------------------------------------------------------------------------
951
956 public ArrayList<AttachmentPoint> getSelectedAPs()
957 {
958 ArrayList<AttachmentPoint> selected =
959 new ArrayList<AttachmentPoint>();
960
961 for (int rowId : apTable.getSelectedRows())
962 {
963 selected.add(mapAPs.get(apTable.getValueAt(rowId, 0)));
964 }
965 return selected;
966 }
967
968//-----------------------------------------------------------------------------
969
974 public ArrayList<Integer> getSelectedAPIDs()
975 {
976 ArrayList<Integer> selected = new ArrayList<Integer>();
977 for (int rowId : apTable.getSelectedRows())
978 {
979 selected.add(rowId);
980 }
981 return selected;
982 }
983
984//-----------------------------------------------------------------------------
985
990 public ArrayList<IAtom> getAtomsSelectedFromJMol()
991 {
992 ArrayList<IAtom> selectedAtms = new ArrayList<IAtom>();
993
994 if (fragment == null || fragment.getAtomCount()==0)
995 {
996 return selectedAtms;
997 }
998
999 for (int i =0; i< fragment.getAtomCount(); i++)
1000 {
1001 if (((Viewer) jmolPanel.viewer).slm.isSelected(i))
1002 {
1003 selectedAtms.add(fragment.getAtom(i));
1004 }
1005 }
1006
1007 return selectedAtms;
1008 }
1009
1010//-----------------------------------------------------------------------------
1011
1012 private class PausableTableModelListener implements TableModelListener
1013 {
1014 private boolean isActive = false;
1015
1017 {};
1018
1019 @Override
1020 public void tableChanged(TableModelEvent e)
1021 {
1022 if (isActive && !alteredAPData
1023 && e.getType() == TableModelEvent.UPDATE)
1024 {
1025 alteredAPData = true;
1026 firePropertyChange(IVertexAPSelection.APDATACHANGEEVENT, false,
1027 true);
1028 }
1029 }
1030
1031 public void setActive(boolean var)
1032 {
1033 isActive = var;
1034 }
1035 }
1036
1037//-----------------------------------------------------------------------------
1038
1043 public void activateTabEditsListener(boolean var)
1044 {
1045 try
1046 {
1048 apTabModel.getTableModelListeners()[0];
1049 l.setActive(var);
1050 } catch (Throwable t) {
1051 //t.printStackTrace();
1052 System.out.println("Bad attempt to contro listener: "
1053 + t.getMessage());
1054 System.out.println(t.getCause());
1055 }
1056 }
1057
1058//-----------------------------------------------------------------------------
1059
1060 /*
1061 * This is needed to stop Jmol threads
1062 */
1063 public void dispose()
1064 {
1066 }
1067
1068//-----------------------------------------------------------------------------
1069
1070 @Override
1071 public Map<Integer, AttachmentPoint> getMapOfAPsInTable()
1072 {
1073 return mapAPs;
1074 }
1075
1076//-----------------------------------------------------------------------------
1077
1078 @Override
1079 public DefaultTableModel getAPTableModel()
1080 {
1081 return apTabModel;
1082 }
1083
1084//-----------------------------------------------------------------------------
1085
1086}
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.
Definition: Fragment.java:61
List< AttachmentPoint > getCurrentAPs()
Collects APs currently defined as properties of the atoms.
Definition: Fragment.java:541
IAtom getAtom(int number)
Definition: Fragment.java:843
IAtomContainer getIAtomContainer()
Definition: Fragment.java:788
A vertex is a data structure that has an identity and holds a list of AttachmentPoints.
Definition: Vertex.java:61
Object getProperty(Object property)
Definition: Vertex.java:1136
A panel with a molecular viewer and attachment point table.
static final long serialVersionUID
Version UID.
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.
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.
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.
Definition: GUI.java:57
static final String GUILOGGER
Name of logger for the GUI.
Definition: GUI.java:91
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.
Definition: Utils.java:36
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.
Definition: MathUtils.java:39
static double[] add(double[] v0, double[] v1)
Perform vector addition.
Definition: MathUtils.java:198
static double[] subtract(double[] v0, double[] v1)
Perform vector subtraction.
Definition: MathUtils.java:180
static double[] scale(double[] d, double a)
Scales a vector.
Definition: MathUtils.java:102
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.
Definition: Vertex.java:86
Interface for all vertex viewers that intend to allow selection of attachment points.