$darkmode
DENOPTIM
GUIVertexInspector.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
21import java.awt.BorderLayout;
22import java.awt.Color;
23import java.awt.Component;
24import java.awt.Cursor;
25import java.awt.Dimension;
26import java.awt.event.ActionEvent;
27import java.awt.event.ActionListener;
28import java.beans.PropertyChangeEvent;
29import java.beans.PropertyChangeListener;
30import java.io.BufferedReader;
31import java.io.File;
32import java.io.InputStreamReader;
33import java.util.ArrayList;
34import java.util.List;
35import java.util.Map;
36import java.util.concurrent.atomic.AtomicInteger;
37
38import javax.swing.BoxLayout;
39import javax.swing.DefaultListModel;
40import javax.swing.GroupLayout;
41import javax.swing.JButton;
42import javax.swing.JComponent;
43import javax.swing.JLabel;
44import javax.swing.JList;
45import javax.swing.JOptionPane;
46import javax.swing.JPanel;
47import javax.swing.JScrollPane;
48import javax.swing.JSeparator;
49import javax.swing.JSpinner;
50import javax.swing.JSpinner.DefaultEditor;
51import javax.swing.ListSelectionModel;
52import javax.swing.SpinnerNumberModel;
53import javax.swing.SwingConstants;
54import javax.swing.UIManager;
55import javax.swing.event.ChangeEvent;
56import javax.swing.event.ChangeListener;
57import javax.swing.table.DefaultTableModel;
58import javax.vecmath.Point3d;
59
60import org.openscience.cdk.interfaces.IAtom;
61import org.openscience.cdk.interfaces.IAtomContainer;
62import org.openscience.cdk.interfaces.IBond;
63
64import denoptim.constants.DENOPTIMConstants;
65import denoptim.exception.DENOPTIMException;
66import denoptim.files.FileAndFormat;
67import denoptim.files.FileUtils;
68import denoptim.fragmenter.FragmenterTools;
69import denoptim.graph.APClass;
70import denoptim.graph.AttachmentPoint;
71import denoptim.graph.DGraph;
72import denoptim.graph.Edge.BondType;
73import denoptim.graph.EmptyVertex;
74import denoptim.graph.Fragment;
75import denoptim.graph.Vertex;
76import denoptim.graph.Vertex.BBType;
77import denoptim.io.DenoptimIO;
78import denoptim.programs.fragmenter.CuttingRule;
79import denoptim.programs.fragmenter.FragmenterParameters;
80import denoptim.utils.DummyAtomHandler;
81import denoptim.utils.MoleculeUtils;
82
83
92public class GUIVertexInspector extends GUICardPanel
93{
97 private static final long serialVersionUID = 912850110991449553L;
98
102 public static AtomicInteger prepVrtxTabUID = new AtomicInteger(1);
103
107 private ArrayList<Vertex> verticesLibrary = new ArrayList<Vertex>();
108
112 private Vertex vertex;
113
117 private int currVrtxIdx = 0;
118
122 private boolean unsavedChanges = false;
123
125 private JPanel ctrlPane;
126 private JPanel navigPanel;
127 private JPanel navigPanel2;
128 private JPanel navigPanel3;
129
130 private JButton btnAddVrtx;
131 private JButton btnDelVrtx;
132
133 private JButton btnOpenVrtxs;
134
135 private JSpinner navigSpinner;
136 private JLabel totalVrtxsLabel;
139
140 private JPanel pnlImportStruct;
141 private JButton btnOpenMol;
142 private JButton btnOpenSMILES;
143
144 private JPanel pnlEmptFrag;
145 private JButton btnEmptFrag;
146
147 private JPanel pnlAtmToAP;
148 private JButton btnAtmToAP;
149
150 private JPanel pnlTmplBasedChop;
151 private JButton btnTmplBldChop;
152
153 private JPanel pnlChop;
154 private JButton btnChop;
155
156 private JPanel pnlDelSel;
157 private JButton btnDelSel;
158
159 private JPanel pnlSaveEdits;
160 private JButton btnSaveEdits;
161
162
163//-----------------------------------------------------------------------------
164
169 {
170 super(mainPanel, "Vertex Inspector #" + prepVrtxTabUID.getAndIncrement());
171 super.setLayout(new BorderLayout());
172 initialize();
173 }
174
175//-----------------------------------------------------------------------------
176
180 private void initialize() {
181
182 // BorderLayout is needed to allow dynamic resizing!
183 this.setLayout(new BorderLayout());
184
185 // This card structure includes center, east and south panels:
186 // - (Center) molecular/graph viewer and APs
187 // - (East) vertex controls
188 // - (South) general controls (load, save, close)
189
190 // The viewer with Jmol and APtable
191 vertexViewer = new VertexViewPanel(true);
192 vertexViewer.addPropertyChangeListener(
194 new PropertyChangeListener() {
195 @Override
196 public void propertyChange(PropertyChangeEvent evt) {
198 }
199 });
200 this.add(vertexViewer,BorderLayout.CENTER);
201
202 // General panel on the right: it containing all controls
203 ctrlPane = new JPanel();
204 ctrlPane.setVisible(true);
205 ctrlPane.setLayout(new BoxLayout(ctrlPane, SwingConstants.VERTICAL));
206 ctrlPane.add(new JSeparator());
207
208 // NB: avoid GroupLayout because it interferes with Jmol viewer and causes exception
209
210 // Controls to navigate the list of vertices
211 navigPanel = new JPanel();
212 navigPanel2 = new JPanel();
213 navigPanel3 = new JPanel();
214 JLabel navigationLabel1 = new JLabel("Vertex # ");
215 JLabel navigationLabel2 = new JLabel("Current library size: ");
216 totalVrtxsLabel = new JLabel("0");
217
218 navigSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 0, 1));
219 navigSpinner.setToolTipText("Move to vertex number # in the currently loaded library.");
220 navigSpinner.setPreferredSize(new Dimension(75,20));
221 navigSpinner.addChangeListener(vrtxSpinnerListener);
222 navigPanel.add(navigationLabel1);
224 ctrlPane.add(navigPanel);
225
226 navigPanel2.add(navigationLabel2);
229
230 btnAddVrtx = new JButton("Add");
231 btnAddVrtx.setToolTipText("Append vertices taken from a file.");
232 btnAddVrtx.addActionListener(new ActionListener() {
233 public void actionPerformed(ActionEvent e) {
234 File inFile = GUIFileOpener.pickFile(btnAddVrtx);
235 if (inFile == null || inFile.getAbsolutePath().equals(""))
236 {
237 return;
238 }
239
240 ArrayList<Vertex> vrtxLib = new ArrayList<>();
241 try {
242 vrtxLib = DenoptimIO.readVertexes(inFile, BBType.FRAGMENT);
243 } catch (Exception e1) {
244 e1.printStackTrace();
245 JOptionPane.showMessageDialog(btnAddVrtx,
246 "<html>Could not read building blocks from file"
247 + "<br>'" + inFile + "'"
248 + "<br>Hint on cause: " + e1.getMessage()
249 +"</html>",
250 "Error",
251 JOptionPane.ERROR_MESSAGE,
252 UIManager.getIcon("OptionPane.errorIcon"));
253 return;
254 }
255
256 if (vrtxLib.size() == 0)
257 {
258 JOptionPane.showMessageDialog(btnAddVrtx,
259 "<html>No building blocks in file"
260 + "<br>'" + inFile + "'</html>",
261 "Error",
262 JOptionPane.ERROR_MESSAGE,
263 UIManager.getIcon("OptionPane.errorIcon"));
264 return;
265 }
266
267 if (vrtxLib.size() == 1)
268 {
269 importVertices(vrtxLib);
270 return;
271 }
272
273 String[] options = new String[]{"All",
274 "Selected",
275 "Cancel"};
276 String txt = "<html><body width='%1s'>Do you want to "
277 + "append all building blocks or only selected ones?"
278 + "</html>";
279 int res = JOptionPane.showOptionDialog(btnAddVrtx,
280 String.format(txt,200),
281 "Append Building Blocks",
282 JOptionPane.DEFAULT_OPTION,
283 JOptionPane.QUESTION_MESSAGE,
284 UIManager.getIcon("OptionPane.warningIcon"),
285 options,
286 options[0]);
287
288 if (res == 2)
289 {
290 return;
291 }
292
293 switch (res)
294 {
295 case 0:
296 importVertices(vrtxLib);
297 break;
298
299 case 1:
300 ArrayList<Vertex> selectedVrtxs =
301 new ArrayList<Vertex>();
302 GUIVertexSelector vrtxSelector = new GUIVertexSelector(
303 btnAddVrtx,true);
304 vrtxSelector.setRequireApSelection(false);
305 vrtxSelector.load(vrtxLib, 0);
306 Object selected = vrtxSelector.showDialog();
307
308 if (selected != null)
309 {
310 @SuppressWarnings("unchecked")
311 ArrayList<ArrayList<Integer>> selList =
312 (ArrayList<ArrayList<Integer>>) selected;
313 for (ArrayList<Integer> pair : selList)
314 {
315 selectedVrtxs.add(vrtxLib.get(pair.get(0)));
316 }
317 }
318 importVertices(selectedVrtxs);
319 break;
320
321 default:
322 return;
323 }
324 }
325 });
326 btnDelVrtx = new JButton("Remove");
327 btnDelVrtx.setToolTipText("Remove the present building block from the "
328 + "library.");
329 btnDelVrtx.addActionListener(new ActionListener() {
330 public void actionPerformed(ActionEvent e) {
331 try {
333 } catch (DENOPTIMException e1) {
334 System.out.println("Exception while removing the current "
335 + "building block:");
336 e1.printStackTrace();
337 }
338 }
339 });
343
344 ctrlPane.add(new JSeparator());
345
346 pnlImportStruct = new JPanel();
347 GroupLayout lyoImportStructure = new GroupLayout(pnlImportStruct);
348 JLabel lblImportStruct = new JLabel("Import a structure from");
349 btnOpenMol = new JButton("File");
350 btnOpenMol.setToolTipText("Imports a chemical system"
351 + " from file.");
352 btnOpenMol.addActionListener(new ActionListener() {
353 public void actionPerformed(ActionEvent e) {
354 File inFile = GUIFileOpener.pickFile(btnOpenMol);
355 if (inFile == null || inFile.getAbsolutePath().equals(""))
356 {
357 return;
358 }
360 }
361 });
362
363 // the '+' is to prevent search/replace of the string
364 btnOpenSMILES = new JButton("SMI"+"LES");
365 btnOpenSMILES.setToolTipText("<html>Imports chemical system"
366 + " from SMILES string.<br>The conversion of SMILES "
367 + "to 3D structure requires"
368 + "<br> an internet connection.</html>");
369 btnOpenSMILES.addActionListener(new ActionListener() {
370 public void actionPerformed(ActionEvent e) {
371 String smiles = JOptionPane.showInputDialog(btnOpenSMILES,
372 "Please input SMILES: ");
373 if (smiles != null && !smiles.trim().equals(""))
374 {
376 }
377 }
378 });
379
380 pnlImportStruct.setLayout(lyoImportStructure);
381 lyoImportStructure.setAutoCreateGaps(true);
382 lyoImportStructure.setAutoCreateContainerGaps(true);
383 lyoImportStructure.setHorizontalGroup(lyoImportStructure.createParallelGroup(
384 GroupLayout.Alignment.CENTER)
385 .addComponent(lblImportStruct)
386 .addGroup(lyoImportStructure.createSequentialGroup()
387 .addComponent(btnOpenMol)
388 .addComponent(btnOpenSMILES)));
389 lyoImportStructure.setVerticalGroup(lyoImportStructure.createSequentialGroup()
390 .addComponent(lblImportStruct)
391 .addGroup(lyoImportStructure.createParallelGroup()
392 .addComponent(btnOpenMol)
393 .addComponent(btnOpenSMILES)));
395
396 ctrlPane.add(new JSeparator());
397
398 pnlEmptFrag = new JPanel();
399 btnEmptFrag = new JButton("Create Empty Vertex");
400 btnEmptFrag.setToolTipText("<html>Creates an empty vertex:<br>a vertex "
401 + "that contains no molecular structure.<html>");
402 btnEmptFrag.addActionListener(new ActionListener() {
403 public void actionPerformed(ActionEvent e) {
404 GUIEmptyVertexMaker makeEmptyVertexDialog =
406 makeEmptyVertexDialog.pack();
407 Object ev = makeEmptyVertexDialog.showDialog();
408 if (ev == null)
409 {
410 return;
411 }
412 ArrayList<Vertex> lst = new ArrayList<Vertex>(1);
413 lst.add((EmptyVertex) ev);
414 GUIVertexSelector fragSelector = new GUIVertexSelector(
415 btnEmptFrag,false);
416 fragSelector.load(lst, 0);
417 fragSelector.btnDone.setText("Confirm");
418 fragSelector.ctrlPane.setVisible(false);
419 fragSelector.setRequireApSelection(false);
420 Object selected = fragSelector.showDialog();
421 if (selected == null)
422 {
423 return;
424 }
425 importVertices(lst);
426 }
427 });
430
431 ctrlPane.add(new JSeparator());
432
433 pnlAtmToAP = new JPanel();
434 btnAtmToAP = new JButton("Atom to AP");
435 btnAtmToAP.setToolTipText("<html>Replaces the selected atoms with "
436 + "attachment points.<br>Click on atoms to select"
437 + " them. Click again to unselect.<br>"
438 + "<br><b>WARNING:</b> this action cannot be undone!<html>");
439 btnAtmToAP.addActionListener(new ActionListener() {
440 public void actionPerformed(ActionEvent e) {
442
443 ArrayList<IAtom> selectedAtms =
445
446 if (selectedAtms.size() == 0)
447 {
448 JOptionPane.showMessageDialog(btnAtmToAP,
449 "<html>No atom selected! Click on atoms to select"
450 + " them.<br>Click again to unselect.</html>",
451 "Error",
452 JOptionPane.ERROR_MESSAGE,
453 UIManager.getIcon("OptionPane.errorIcon"));
454 return;
455 }
456 else
457 {
458 //TODO: ask about hapticity:
459 // if multihapto, then use all the selected atoms for 1 AP
460 // and ask to select another set for other end of bond to
461 // break.
462
463 List<APClass> selectedAPCs = choseOrCreateNewAPClass(
464 btnAtmToAP, true);
465
466 //The size of the list is either 0 or 1
467 if (selectedAPCs.size() == 0)
468 {
469 // We have pressed cancel or closed the dialog, so abandon
470 return;
471 }
472 String apClass = selectedAPCs.get(0).toString();
473
474 ArrayList<IAtom> failed = new ArrayList<IAtom>();
475 for (IAtom atm : selectedAtms)
476 {
477 if (!convertAtomToAP(atm, apClass))
478 {
479 failed.add(atm);
480 }
481 }
482 for (IAtom atm : failed)
483 {
484 selectedAtms.remove(atm);
485 }
486 if (selectedAtms.size() == 0)
487 {
488 return;
489 }
490
491 removeAtoms(selectedAtms);
492
494
495 unsavedChanges = true;
497 }
498 }
499 });
501 ctrlPane.add(pnlAtmToAP);
502
503 pnlChop = new JPanel();
504 btnChop = new JButton("Chop Structure");
505 btnChop.setToolTipText(String.format("<html><body width='%1s'>"
506 + "Applies cutting rules on "
507 + "the current structure to generate fragments.</html>", 400));
508 btnChop.addActionListener(new ActionListener() {
509 public void actionPerformed(ActionEvent event) {
511 if (vertex==null
512 || vertex.getIAtomContainer().getBondCount() == 0)
513 {
514 JOptionPane.showMessageDialog(btnChop,
515 "<html>System contains 0 bonds. "
516 + "Nothing to chop.</html>",
517 "Error",
518 JOptionPane.ERROR_MESSAGE,
519 UIManager.getIcon("OptionPane.errorIcon"));
520 return;
521 }
522
524 boolean result = dialogToDefineCuttingRules(
525 settings,
526 this.getClass().getClassLoader(),
527 btnChop,
528 false);
529 if (!result)
530 return;
531
532 String pathnameLastUsedCutRules =
534 if (pathnameLastUsedCutRules != null
535 && !pathnameLastUsedCutRules.isBlank())
536 {
537 GUIPreferences.lastCutRulesFile =
538 new File(pathnameLastUsedCutRules);
539 }
540
541 // Now chop the structure to produce fragments
542 List<Vertex> fragments;
543 try
544 {
545 fragments = FragmenterTools.fragmentation(
547 settings.getCuttingRules(),
548 settings.getLogger());
549 } catch (DENOPTIMException e)
550 {
551 JOptionPane.showMessageDialog(btnChop,String.format(
552 "<html><body width='%1s'"
553 + "Could not complete fragmentation. Hint: "
554 + e.getMessage() + "</html>", 400),
555 "Error",
556 JOptionPane.ERROR_MESSAGE,
557 UIManager.getIcon("OptionPane.errorIcon"));
558 return;
559 }
560
561 // Add linearity-breaking dummy atoms
562 for (Vertex frag : fragments)
563 {
565 settings.getLinearAngleLimit());
566 }
567
568 // Signal no result obtained
569 if (fragments.size() < 1 || (fragments.size() == 1 &&
570 ((Fragment)fragments.get(0)).isIsomorphicTo(vertex)))
571 {
572 JOptionPane.showMessageDialog(btnAddVrtx,
573 "<html>Fragmentation produced no fragments!</html>",
574 "Error",
575 JOptionPane.WARNING_MESSAGE,
576 UIManager.getIcon("OptionPane.warningIcon"));
577 return;
578 }
579
580 // The resulting fragments are loaded into the viewer, without
581 // removing the original structure.
582
583 String[] options = new String[]{"All",
584 "Select",
585 "Cancel"};
586 String txt = "<html><body width='%1s'>Fragmentation produced "
587 + fragments.size() + " fragments. Do you want to "
588 + "append all or select some?"
589 + "</html>";
590 int answer = JOptionPane.showOptionDialog(btnAddVrtx,
591 String.format(txt,200),
592 "Append Building Blocks",
593 JOptionPane.DEFAULT_OPTION,
594 JOptionPane.QUESTION_MESSAGE,
595 UIManager.getIcon("OptionPane.warningIcon"),
596 options,
597 options[0]);
598
599 if (answer == 2)
600 {
601 return;
602 }
603
604 switch (answer)
605 {
606 case 0:
607 importVertices(fragments);
608 break;
609
610 case 1:
611 List<Vertex> selectedVrtxs =
612 new ArrayList<Vertex>();
613 GUIVertexSelector vrtxSelector = new GUIVertexSelector(
614 btnAddVrtx,true);
615 vrtxSelector.setRequireApSelection(false);
616 vrtxSelector.load(fragments, 0);
617 Object selected = vrtxSelector.showDialog();
618
619 if (selected != null)
620 {
621 @SuppressWarnings("unchecked")
622 List<ArrayList<Integer>> selList =
623 (ArrayList<ArrayList<Integer>>) selected;
624 for (ArrayList<Integer> pair : selList)
625 {
626 selectedVrtxs.add(fragments.get(pair.get(0)));
627 }
628 }
629 importVertices(selectedVrtxs);
630 break;
631
632 default:
633 return;
634 }
635 }
636 });
637 pnlChop.add(btnChop);
638 ctrlPane.add(pnlChop);
639
640 pnlTmplBasedChop = new JPanel();
641 btnTmplBldChop = new JButton("Chop by Template");
642 btnTmplBldChop.setToolTipText(String.format("<html><body width='%1s'>"
643 + "Chops the current structure according to a given graph template expected to be a substructure of the current structure.</html>", 400));
644 btnTmplBldChop.addActionListener(new ActionListener() {
645 public void actionPerformed(ActionEvent event) {
646 // NB: we call it vertex because we are in the vertex viewer, but it is a structure that may not be a vertex.
648 if (vertex==null)
649 {
650 return;
651 }
652
654 if (inFile == null || inFile.getAbsolutePath().equals(""))
655 {
656 return;
657 }
658
659 List<DGraph> fragmentationTmpls = new ArrayList<>();
660 try
661 {
662 fragmentationTmpls = DenoptimIO.readDENOPTIMGraphsFromFile(inFile);
663 } catch (Throwable e)
664 {
665 JOptionPane.showMessageDialog(btnTmplBldChop,String.format(
666 "<html><body width='%1s'"
667 + "Could not read template file. Hint: "
668 + e.getMessage() + "</html>", 400),
669 "Error",
670 JOptionPane.ERROR_MESSAGE,
671 UIManager.getIcon("OptionPane.errorIcon"));
672 return;
673 }
674
675 if (fragmentationTmpls.size() == 0)
676 {
677 JOptionPane.showMessageDialog(btnTmplBldChop,
678 "<html>No graphs found in file '"
679 + inFile + "'.</html>",
680 "Error",
681 JOptionPane.ERROR_MESSAGE,
682 UIManager.getIcon("OptionPane.errorIcon"));
683 return;
684 }
685
687 settings.setFragmentationTmpls(fragmentationTmpls);
688
689 // Now chop the structure to produce fragments
690 List<Vertex> fragments;
691 try
692 {
694 settings.getFragmentationTmpls(),
695 settings.getMaxBufferShellSize(),
696 settings.getRandomizer(),
697 settings.getLogger());
698 } catch (DENOPTIMException e)
699 {
700 JOptionPane.showMessageDialog(btnChop,String.format(
701 "<html><body width='%1s'"
702 + "Could not complete fragmentation. Hint: "
703 + e.getMessage() + "</html>", 400),
704 "Error",
705 JOptionPane.ERROR_MESSAGE,
706 UIManager.getIcon("OptionPane.errorIcon"));
707 return;
708 }
709
710 // Add linearity-breaking dummy atoms
711 for (Vertex frag : fragments)
712 {
714 settings.getLinearAngleLimit());
715 }
716
717 // Signal no result obtained
718 if (fragments.size() < 1 || (fragments.size() == 1 &&
719 ((Fragment)fragments.get(0)).isIsomorphicTo(vertex)))
720 {
721 JOptionPane.showMessageDialog(btnAddVrtx,
722 "<html>Fragmentation produced no fragments!</html>",
723 "Error",
724 JOptionPane.WARNING_MESSAGE,
725 UIManager.getIcon("OptionPane.warningIcon"));
726 return;
727 }
728
729 // The resulting fragments are loaded into the viewer, without
730 // removing the original structure.
731
732 String[] options = new String[]{"All",
733 "Select",
734 "Cancel"};
735 String txt = "<html><body width='%1s'>Fragmentation produced "
736 + fragments.size() + " fragments. Do you want to "
737 + "append all or select some?"
738 + "</html>";
739 int answer = JOptionPane.showOptionDialog(btnAddVrtx,
740 String.format(txt,200),
741 "Append Building Blocks",
742 JOptionPane.DEFAULT_OPTION,
743 JOptionPane.QUESTION_MESSAGE,
744 UIManager.getIcon("OptionPane.warningIcon"),
745 options,
746 options[0]);
747
748 if (answer == 2)
749 {
750 return;
751 }
752
753 switch (answer)
754 {
755 case 0:
756 importVertices(fragments);
757 break;
758
759 case 1:
760 List<Vertex> selectedVrtxs =
761 new ArrayList<Vertex>();
762 GUIVertexSelector vrtxSelector = new GUIVertexSelector(
763 btnAddVrtx,true);
764 vrtxSelector.setRequireApSelection(false);
765 vrtxSelector.load(fragments, 0);
766 Object selected = vrtxSelector.showDialog();
767
768 if (selected != null)
769 {
770 @SuppressWarnings("unchecked")
771 List<ArrayList<Integer>> selList =
772 (ArrayList<ArrayList<Integer>>) selected;
773 for (ArrayList<Integer> pair : selList)
774 {
775 selectedVrtxs.add(fragments.get(pair.get(0)));
776 }
777 }
778 importVertices(selectedVrtxs);
779 break;
780
781 default:
782 return;
783 }
784 }
785 });
788
789 pnlDelSel = new JPanel();
790 btnDelSel = new JButton("Remove Atoms");
791 btnDelSel.setToolTipText("<html>Removes all selected atoms from the "
792 + "system.<br><br><b>WARNING:</b> this action cannot be "
793 + "undone!");
794 btnDelSel.addActionListener(new ActionListener() {
795 public void actionPerformed(ActionEvent e) {
796 ArrayList<IAtom> selectedAtms =
798
799 if (selectedAtms.size() == 0)
800 {
801 JOptionPane.showMessageDialog(btnDelSel,
802 "<html>No atom selected! Click on atoms to select"
803 + " them.<br>Click again to unselect.</html>",
804 "Error",
805 JOptionPane.ERROR_MESSAGE,
806 UIManager.getIcon("OptionPane.errorIcon"));
807 return;
808 }
809 else
810 {
811 removeAtoms(selectedAtms);
812
814
815 unsavedChanges = true;
817 }
818 }
819 });
820 pnlDelSel.add(btnDelSel);
821 ctrlPane.add(pnlDelSel);
822
823 ctrlPane.add(new JSeparator());
824
825 pnlSaveEdits = new JPanel();
826 btnSaveEdits = new JButton("Save Changes");
827 //btnSaveEdits.setForeground(Color.RED);
828 btnSaveEdits.setEnabled(true);
829 btnSaveEdits.setToolTipText("<html>Save the current system replacing"
830 + " <br>the original one in the loaded library.</html>");
831 btnSaveEdits.addActionListener(new ActionListener() {
832 public void actionPerformed(ActionEvent e) {
834 }
835 });
838 this.add(ctrlPane, BorderLayout.EAST);
839
840
841 // Panel with buttons to the bottom of the frame
842 ButtonsBar commandsPane = new ButtonsBar();
843 super.add(commandsPane, BorderLayout.SOUTH);
844
845 btnOpenVrtxs = new JButton("Load Library of Building Blocks");
846 btnOpenVrtxs.setToolTipText("Reads building blocks or structures from "
847 + "file.");
848 btnOpenVrtxs.addActionListener(new ActionListener() {
849 public void actionPerformed(ActionEvent e) {
850 File inFile = GUIFileOpener.pickFile(btnOpenVrtxs);
851 if (inFile == null || inFile.getAbsolutePath().equals(""))
852 {
853 return;
854 }
855 ArrayList<Vertex> vrtxLib = new ArrayList<>();
856 try {
857 vrtxLib = DenoptimIO.readVertexes(inFile, BBType.FRAGMENT);
858 } catch (Exception e1) {
859 e1.printStackTrace();
860 JOptionPane.showMessageDialog(btnAddVrtx,
861 "<html>Could not read building blocks from file"
862 + "<br>'" + inFile + "'"
863 + "<br>Hint on cause: " + e1.getMessage()
864 +"</html>",
865 "Error",
866 JOptionPane.ERROR_MESSAGE,
867 UIManager.getIcon("OptionPane.errorIcon"));
868 return;
869 }
870
871 if (vrtxLib.size() == 0)
872 {
873 JOptionPane.showMessageDialog(btnAddVrtx,
874 "<html>No building blocks in file"
875 + "<br>'" + inFile + "'</html>",
876 "Error",
877 JOptionPane.ERROR_MESSAGE,
878 UIManager.getIcon("OptionPane.errorIcon"));
879 return;
880 }
881 importVertices(vrtxLib);
882 }
883 });
884 commandsPane.add(btnOpenVrtxs);
885
886 JButton btnSaveVrtxs = new JButton("Save Library of Building Blocks");
887 btnSaveVrtxs.setToolTipText("Write all building blocks to a file.");
888 btnSaveVrtxs.addActionListener(new ActionListener() {
889 public void actionPerformed(ActionEvent e) {
890 FileAndFormat fileAndFormat =
892 if (fileAndFormat == null)
893 {
894 return;
895 }
896 File outFile = fileAndFormat.file;
897 try
898 {
899 // The writing method may change the extension. So we need
900 // to get the return value.
901 outFile = DenoptimIO.writeVertexesToFile(outFile,
902 fileAndFormat.format,
904 }
905 catch (Exception ex)
906 {
907 ex.printStackTrace();
908 JOptionPane.showMessageDialog(btnSaveVrtxs,
909 "Could not write to '" + outFile + "'! "
910 + "Hint: "+ex.getMessage(),
911 "Error",
912 JOptionPane.PLAIN_MESSAGE,
913 UIManager.getIcon("OptionPane.errorIcon"));
914 return;
915 }
916 navigSpinner.setModel(new SpinnerNumberModel(currVrtxIdx+1, 1,
917 verticesLibrary.size(), 1));
919 unsavedChanges = false;
920 FileUtils.addToRecentFiles(outFile, fileAndFormat.format);
921 }
922 });
923 commandsPane.add(btnSaveVrtxs);
924
925 JButton btnCanc = new JButton("Close Tab");
926 btnCanc.setToolTipText("Closes this tab.");
927 btnCanc.addActionListener(new removeCardActionListener(this));
928 commandsPane.add(btnCanc);
929
930 JButton btnHelp = new JButton("?");
931 btnHelp.setToolTipText("<html>Hover over the buttons and fields "
932 + "to get a tip.</html>");
933 btnHelp.addActionListener(new ActionListener() {
934 public void actionPerformed(ActionEvent e) {
935 String txt = "<html><body width='%1s'>"
936 + "<p>This tab allows to create, inspect, and edit "
937 + "building blocks and "
938 + "three-dimensional molecular fragments.</p>"
939 + "<p>New fragments can be created starting from any "
940 + "chemical structure that can be loaded from file or "
941 + "generated from SMILES (SMILES-to-3D conversion "
942 + "requires an Internet connection).</p>"
943 + "<p>Any terminal atom (i.e., atoms that have only "
944 + "one connected neighbor) can be transformed into "
945 + "on attachment point (AP). Click on the atom to "
946 + "select it, and press <code><b>Atom to AP</b></code>."
947 + "</p>"
948 + "<p>Attachment points are depicted in the molecular "
949 + "viewer as yellow arrows in the 3D space, and their "
950 + "attachment point class (APClass) is specified in "
951 + "the table below the viewer. Double-click on a "
952 + "specific APClass field to change its value.</p>"
953 + "<br>"
954 + "<p>Hover over buttons get a tip.</p>"
955 + "<br>"
956 + "<p>Right-click on the Jmol viewer will open the "
957 + "Jmol menu. However, since Jmol cannot handle the "
958 + "attachment points data. Therefore, Jmol "
959 + "functionality should only be used on systems "
960 + "that have no attachment points, or for alterations "
961 + "of the molecular structure that do not change the "
962 + "list of atoms au to the last atom decorated with an "
963 + "attachment point.</p></html>";
964 JOptionPane.showMessageDialog(btnHelp,
965 String.format(txt, 400),
966 "Tips",
967 JOptionPane.PLAIN_MESSAGE);
968 }
969 });
970 commandsPane.add(btnHelp);
971 }
972
973//-----------------------------------------------------------------------------
974
985 public static boolean dialogToDefineCuttingRules(
986 FragmenterParameters settings, ClassLoader classLoader,
987 Component parent,
988 boolean setMolToGraphSettings)
989 {
990 settings.startConsoleLogger("GUI-controlledFragmenterLogger");
991
992 List<CuttingRule> defaultCuttingRules = new ArrayList<CuttingRule>();
993 BufferedReader reader = null;
994 try
995 {
996 try {
997 reader = new BufferedReader(
998 new InputStreamReader(classLoader.getResourceAsStream(
999 "data/cutting_rules")));
1000 DenoptimIO.readCuttingRules(reader, defaultCuttingRules,
1001 "bundled jar");
1002 } finally {
1003 if (reader!=null)
1004 reader.close();
1005 }
1006 } catch (Exception e )
1007 {
1008 e.printStackTrace();
1009 JOptionPane.showMessageDialog(parent,String.format(
1010 "<html><body width='%1s'>"
1011 + "Could not read default cutting rules from "
1012 + "bundled jar. "
1013 + "Hint: "
1014 + e.getMessage() + "</html>", 400),
1015 "Error",
1016 JOptionPane.ERROR_MESSAGE,
1017 UIManager.getIcon("OptionPane.errorIcon"));
1018 return false;
1019 }
1020
1021 // Read last used cutting rules
1022 List<CuttingRule> customCuttingRules = new ArrayList<CuttingRule>();
1023 boolean useDefaultCuttingRules = true;
1024 try
1025 {
1027 {
1030 customCuttingRules);
1031 useDefaultCuttingRules = false;
1032 }
1033 } catch (DENOPTIMException e)
1034 {
1035 JOptionPane.showMessageDialog(parent,String.format(
1036 "<html><body width='%1s'"
1037 + "Could not read last-used cutting rules from '"
1039 + "Hint: "
1040 + e.getMessage() + "</html>", 400),
1041 "Error",
1042 JOptionPane.ERROR_MESSAGE,
1043 UIManager.getIcon("OptionPane.errorIcon"));
1044 return false;
1045 }
1046
1047 // Build a dialog that offers the possibility to see and edit
1048 // default cutting rules, and to define custom ones from scratch
1049 CuttingRulesSelectionDialog crs = null;
1050 if (setMolToGraphSettings)
1051 {
1053 defaultCuttingRules, customCuttingRules,
1054 useDefaultCuttingRules, parent, settings);
1055 } else {
1057 defaultCuttingRules, customCuttingRules,
1058 useDefaultCuttingRules, parent, settings);
1059 }
1060 crs.pack();
1061 crs.setVisible(true);
1062
1063 if (crs.result==null)
1064 return false;
1065
1066 String pathnameLastUsedCutRules = settings.getCuttingRulesFilePathname();
1067 if (pathnameLastUsedCutRules != null
1068 && !pathnameLastUsedCutRules.isBlank())
1069 {
1070 GUIPreferences.lastCutRulesFile = new File(pathnameLastUsedCutRules);
1071 }
1072
1073 return true;
1074 }
1075
1076//-----------------------------------------------------------------------------
1077
1078 public void importStructureFromFile(File file)
1079 {
1080 this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
1081
1082 // Cleanup
1084
1085 try {
1086 for (IAtomContainer mol : DenoptimIO.readAllAtomContainers(file))
1087 {
1088 // We mean to import only the structure: get rid of AP
1089 mol.setProperty(DENOPTIMConstants.APSTAG,null);
1090
1091 // NB: here we let the vertexViewer create a fragment object that we
1092 // then put into the local library. This to make sure that the
1093 // references to atoms selected in the viewer are referring to
1094 // members of the "vertex" object
1097
1098 // the system is not a fragment but, this is done for consistency:
1099 // when we have a molecule loaded the list is not empty
1100 // The currently viewed fragment (if any) is always part of the lib
1102 currVrtxIdx = verticesLibrary.size()-1;
1103 }
1105 unsavedChanges = true;
1106 btnDelSel.setEnabled(true);
1107 btnAtmToAP.setEnabled(true);
1108 } catch (Exception e) {
1109 this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1110 e.printStackTrace();
1111 JOptionPane.showMessageDialog(this,
1112 "<html>Could not read file '" + file.getAbsolutePath()
1113 + "'!<br>Hint about reason: " + e.getCause() + "</html>",
1114 "Error",
1115 JOptionPane.PLAIN_MESSAGE,
1116 UIManager.getIcon("OptionPane.errorIcon"));
1117 }
1118
1119 this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1120 }
1121
1122//-----------------------------------------------------------------------------
1123
1130 public void importStructureFromSMILES(String smiles)
1131 {
1132 this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
1133
1134 // Cleanup
1136
1137 // Load the structure using CACTUS service or CDK builder
1138 try {
1139 vertexViewer.loadSMILES(smiles);
1140 } catch (Exception e) {
1141 this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1142 return;
1143 }
1144
1146
1147 // The system is not a fragment but, this is done for consistency:
1148 // when we have a molecule loaded the list is not empty:
1149 // The currently viewed fragment (if any) is always part of the library
1151 currVrtxIdx = verticesLibrary.size()-1;
1152
1153 // finalize GUI status
1155 unsavedChanges = true;
1156 btnDelSel.setEnabled(true);
1157 btnAtmToAP.setEnabled(true);
1158 btnSaveEdits.setEnabled(true);
1159
1160 this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1161 }
1162
1163//-----------------------------------------------------------------------------
1164
1169 public void importVerticesFromFile(File file)
1170 {
1171 this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
1172
1173 ArrayList<Vertex> vrtxLib = new ArrayList<>();
1174 try {
1175 vrtxLib = DenoptimIO.readVertexes(file, BBType.FRAGMENT);
1176 } catch (Exception e1) {
1177 e1.printStackTrace();
1178 JOptionPane.showMessageDialog(btnAddVrtx,
1179 "<html>Could not read building blocks from file"
1180 + "<br>'" + file + "'"
1181 + "<br>Hint on cause: " + e1.getMessage()
1182 +"</html>",
1183 "Error",
1184 JOptionPane.ERROR_MESSAGE,
1185 UIManager.getIcon("OptionPane.errorIcon"));
1186 return;
1187 }
1188
1189 if (vrtxLib.size() == 0)
1190 {
1191 JOptionPane.showMessageDialog(btnAddVrtx,
1192 "<html>No building blocks in file"
1193 + "<br>'" + file + "'</html>",
1194 "Error",
1195 JOptionPane.ERROR_MESSAGE,
1196 UIManager.getIcon("OptionPane.errorIcon"));
1197 return;
1198 }
1199 importVertices(vrtxLib);
1200 this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1201 }
1202
1203//-----------------------------------------------------------------------------
1204
1209 public void importVertices(List<Vertex> fragments)
1210 {
1211 this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
1212
1213 int firstOfNew = 0;
1214 boolean libFromScrtch = false;
1215 if (verticesLibrary == null)
1216 {
1217 libFromScrtch = true;
1218 verticesLibrary = new ArrayList<Vertex>();
1219 }
1220 else
1221 {
1222 firstOfNew = verticesLibrary.size();
1223 }
1224
1225 boolean addedOne = false;
1226 if (fragments.size() > 0)
1227 {
1228 verticesLibrary.addAll(fragments);
1229 addedOne = true;
1230
1231 // Display the first
1232 if (libFromScrtch)
1233 {
1234 currVrtxIdx = 0;
1235 }
1236 else if (addedOne)
1237 {
1238 currVrtxIdx = firstOfNew;
1239 }
1241
1242 // Update the fragment spinner
1244 } else {
1245 this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1246 JOptionPane.showMessageDialog(this,
1247 "<html>No vertices to import from the given list.</html>",
1248 "Error",
1249 JOptionPane.PLAIN_MESSAGE,
1250 UIManager.getIcon("OptionPane.errorIcon"));
1251 }
1252 this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1253 }
1254
1255
1256//-----------------------------------------------------------------------------
1257
1266 {
1267 if (verticesLibrary == null)
1268 {
1269 JOptionPane.showMessageDialog(this,
1270 "No list of building blocks loaded.",
1271 "Error",
1272 JOptionPane.PLAIN_MESSAGE,
1273 UIManager.getIcon("OptionPane.errorIcon"));
1274 return;
1275 }
1276
1278
1281 if (vertex == null || vertex instanceof Fragment == false)
1282 {
1283 btnDelSel.setEnabled(false);
1284 btnAtmToAP.setEnabled(false);
1285 } else {
1286 btnDelSel.setEnabled(true);
1287 btnAtmToAP.setEnabled(true);
1288 }
1289 }
1290
1291//-----------------------------------------------------------------------------
1292
1293 private void clearCurrentSystem()
1294 {
1295 // Get rid of currently loaded mol
1296 vertex = null;
1298 }
1299
1300//-----------------------------------------------------------------------------
1301
1303 {
1304 navigSpinner.setModel(new SpinnerNumberModel(currVrtxIdx+1, 1,
1305 verticesLibrary.size(), 1));
1306 totalVrtxsLabel.setText(Integer.toString(verticesLibrary.size()));
1307 }
1308
1309//-----------------------------------------------------------------------------
1310
1320 private boolean convertAtomToAP(IAtom trgAtm, String ruleAndSubClass)
1321 {
1322 if (!(vertex instanceof Fragment))
1323 {
1324 return false;
1325 }
1326 Fragment frag = (Fragment) vertex;
1327 // Accept ONLY if the atom has one and only one connected neighbour
1328 if (frag.getConnectedAtomsCount(trgAtm) != 1)
1329 {
1330 String str = "";
1331 for (IAtom atm : frag.getConnectedAtomsList(trgAtm))
1332 {
1333 str = str + " " + atm.getSymbol()
1334 + (frag.indexOf(atm));
1335 }
1336 System.out.println("Connected atoms: "+str);
1337
1338 JOptionPane.showMessageDialog(this,
1339 "<html>Atom "+ trgAtm.getSymbol()
1340 + (frag.indexOf(trgAtm))
1341 + " has zero or more than one neighbour.<br>I can only "
1342 + "transform atoms"
1343 + " that have one and only one neighbour.</html>",
1344 "Error",
1345 JOptionPane.ERROR_MESSAGE,
1346 UIManager.getIcon("OptionPane.errorIcon"));
1347 return false;
1348 }
1349
1350 IAtom srcAtm = frag.getConnectedAtomsList(trgAtm).get(0);
1351 BondType bt = BondType.valueOf(
1352 srcAtm.getBond(trgAtm).getOrder().toString());
1353
1354 Point3d srcP3d = MoleculeUtils.getPoint3d(srcAtm);
1355 Point3d trgP3d = MoleculeUtils.getPoint3d(trgAtm);
1356 Point3d vector = new Point3d();
1357 vector.x = srcP3d.x + (trgP3d.x - srcP3d.x);
1358 vector.y = srcP3d.y + (trgP3d.y - srcP3d.y);
1359 vector.z = srcP3d.z + (trgP3d.z - srcP3d.z);
1360
1361 //NB: assumption of validity!
1362 String[] parts = ruleAndSubClass.split(
1364 try {
1365 // NB: here we change the bond type to make it fit with the one we
1366 // have in the molecular model.
1367 frag.addAPOnAtom(srcAtm, APClass.make(parts[0],
1368 Integer.parseInt(parts[1]), bt), vector);
1369 } catch (DENOPTIMException e) {
1370 e.printStackTrace();
1371 JOptionPane.showMessageDialog(this,
1372 "<html>Could not make AP.<br>Possible cause: "
1373 + e.getMessage() +"</html>",
1374 "Error",
1375 JOptionPane.ERROR_MESSAGE,
1376 UIManager.getIcon("OptionPane.errorIcon"));
1377 return false;
1378 }
1379 return true;
1380 }
1381
1382//----------------------------------------------------------------------------
1383
1384 private void removeAtoms(ArrayList<IAtom> atmsToDels)
1385 {
1386 if (!(vertex instanceof Fragment))
1387 {
1388 return;
1389 }
1390 Fragment frag = (Fragment) vertex;
1391 ArrayList<IBond> bnsToDel = new ArrayList<IBond>();
1392 for (IAtom atm : atmsToDels)
1393 {
1394 for (IBond bnd : frag.bonds())
1395 {
1396 if (bnd.contains(atm))
1397 {
1398 bnsToDel.add(bnd);
1399 }
1400 }
1401 }
1402 for (IBond bnd : bnsToDel)
1403 {
1404 frag.removeBond(bnd);
1405 }
1406 for (IAtom atm : atmsToDels)
1407 {
1408 if (atm.getProperty(DENOPTIMConstants.ATMPROPAPS)!=null)
1409 {
1410 ArrayList<AttachmentPoint> apsOnAtm = frag.getAPsFromAtom(atm);
1411 frag.getAttachmentPoints().removeAll(apsOnAtm);
1412 }
1413 frag.removeAtom(atm);
1414 }
1415 frag.updateAPs();
1416 }
1417
1418//-----------------------------------------------------------------------------
1419
1420 private class VrtxSpinnerChangeEvent implements ChangeListener
1421 {
1422 private boolean inEnabled = true;
1423
1425 {}
1426
1432 public void setEnabled(boolean var)
1433 {
1434 this.inEnabled = var;
1435 }
1436
1437 @Override
1438 public void stateChanged(ChangeEvent event)
1439 {
1440 if (!inEnabled)
1441 {
1442 return;
1443 }
1444
1446
1447 //NB here we convert from 1-based index in GUI to 0-based index
1448 currVrtxIdx = ((Integer) navigSpinner.getValue()).intValue() - 1;
1450
1452 }
1453 }
1454
1455//-----------------------------------------------------------------------------
1456
1458 {
1459 //btnSaveEdits.setEnabled(false);
1460 btnAddVrtx.setEnabled(true);
1461 btnOpenVrtxs.setEnabled(true);
1462 btnOpenSMILES.setEnabled(true);
1463 btnOpenMol.setEnabled(true);
1464 btnEmptFrag.setEnabled(true);
1465 if (vertex == null || vertex instanceof Fragment == false)
1466 {
1467 btnDelSel.setEnabled(false);
1468 btnAtmToAP.setEnabled(false);
1469 } else {
1470 btnDelSel.setEnabled(true);
1471 btnAtmToAP.setEnabled(true);
1472 }
1473
1474 ((DefaultEditor) navigSpinner.getEditor())
1475 .getTextField().setEditable(true);
1476 ((DefaultEditor) navigSpinner.getEditor())
1477 .getTextField().setForeground(Color.BLACK);
1480
1482 }
1483
1484//-----------------------------------------------------------------------------
1485
1486 private void protectEditedSystem()
1487 {
1488 btnSaveEdits.setEnabled(true);
1489 btnAddVrtx.setEnabled(false);
1490 btnOpenVrtxs.setEnabled(false);
1491 btnOpenSMILES.setEnabled(false);
1492 btnOpenMol.setEnabled(false);
1493 btnEmptFrag.setEnabled(false);
1494 //btnDelSel.setEnabled(false);
1495 //btnAtmToAP.setEnabled(false);
1496
1497 navigSpinner.setModel(new SpinnerNumberModel(currVrtxIdx+1,
1498 currVrtxIdx+1, currVrtxIdx+1, 1));
1499 ((DefaultEditor) navigSpinner.getEditor())
1500 .getTextField().setEditable(false);
1501 ((DefaultEditor) navigSpinner.getEditor())
1502 .getTextField().setForeground(Color.GRAY);
1503
1505
1507 }
1508
1509//-----------------------------------------------------------------------------
1510
1511 private void activateTabEditsListener(boolean var)
1512 {
1514 }
1515
1516//-----------------------------------------------------------------------------
1517
1519 {
1521 {
1522 String[] options = new String[]{"Yes","No"};
1523 int res = JOptionPane.showOptionDialog(this,
1524 "<html>Removing unsaved vertex?",
1525 "Warning",
1526 JOptionPane.DEFAULT_OPTION,
1527 JOptionPane.QUESTION_MESSAGE,
1528 UIManager.getIcon("OptionPane.warningIcon"),
1529 options,
1530 options[1]);
1531 if (res == 1)
1532 {
1533 return;
1534 }
1535 }
1536
1538
1539 // Actual removal from the library
1540 if (verticesLibrary.size()>0)
1541 {
1543 int libSize = verticesLibrary.size();
1544
1545 if (currVrtxIdx>=0 && currVrtxIdx<libSize)
1546 {
1547 //we keep currFrgIdx as it will correspond to the next item
1548 }
1549 else
1550 {
1552 }
1553
1554 if (currVrtxIdx==-1 || verticesLibrary.size()==0)
1555 {
1556 // The viewer gets hidden so we do not need to clear it
1557 // with 'zap' (which is a very slow operation)
1559 currVrtxIdx = 0;
1560 navigSpinner.setModel(new SpinnerNumberModel(0,0,0,1));
1561 totalVrtxsLabel.setText(Integer.toString(0));
1563 }
1564 else
1565 {
1568 navigSpinner.setModel(new SpinnerNumberModel(currVrtxIdx+1, 1,
1569 verticesLibrary.size(), 1));
1571 }
1572 }
1573 }
1574
1575//-----------------------------------------------------------------------------
1576
1577 private void saveUnsavedChanges()
1578 {
1579 this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
1580
1582 {
1583 DefaultTableModel tabModel = vertexViewer.getAPTableModel();
1584 // Import changes from AP table into molecular representation
1585 for (int i=0; i<tabModel.getRowCount(); i++)
1586 {
1587 int apId = ((Integer) tabModel.getValueAt(i, 0)).intValue();
1588 String currApClass = tabModel.getValueAt(i, 1).toString();
1589
1590 // Make sure the new class has a proper syntax
1591 GUIAPClassDefinitionDialog apcDefiner =
1593 apcDefiner.setTitle("Confirm APClass on AP #"+i);
1594 apcDefiner.setPreDefinedAPClass(currApClass);
1595 Object chosen = apcDefiner.showDialog();
1596 if (chosen != null)
1597 {
1598 Object[] pair = (Object[]) chosen;
1599 currApClass = pair[0].toString();
1600 } else {
1601 currApClass = "dafaultAPClass:0";
1602 }
1603
1604 Map<Integer, AttachmentPoint> mapAPs =
1606
1607 if (mapAPs.containsKey(apId))
1608 {
1609 String origApClass = mapAPs.get(apId).getAPClass().toString();
1610 if (!origApClass.equals(currApClass))
1611 {
1612 try {
1613 mapAPs.get(apId).setAPClass(currApClass);
1614 } catch (DENOPTIMException e) {
1615 // We made sure the class is valid, so this
1616 // should never happen, though one never knows
1617 e.printStackTrace();
1618 JOptionPane.showMessageDialog(btnSaveEdits,
1619 "<html>Could not save due to errors setting a "
1620 + "new APClass.<br>Please report this to the "
1621 + "DENOPTIM team.</html>",
1622 "Error",
1623 JOptionPane.PLAIN_MESSAGE,
1624 UIManager.getIcon("OptionPane.errorIcon"));
1625 return;
1626 }
1627 }
1628 }
1629 else
1630 {
1631 JOptionPane.showMessageDialog(btnSaveEdits,
1632 "<html>Could not save due to mistmatch between AP "
1633 + "table and map.<br>Please report this to the "
1634 + "DENOPTIM team.</html>",
1635 "Error",
1636 JOptionPane.PLAIN_MESSAGE,
1637 UIManager.getIcon("OptionPane.errorIcon"));
1638 return;
1639 }
1640 }
1641 }
1642
1643 // Retrieve chemical object from the viewer, if edited, otherwise
1644 // we get what is already in 'vertex'
1646 if (verticesLibrary.size()==0
1647 && (vertex==null || vertex.getNumberOfAPs()==0))
1648 {
1649 //Nothing to same
1650 this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1651 return;
1652 }
1654
1655 // Reload fragment from library to refresh table and viewer
1657 try {
1659 } catch (Throwable t) {
1660 //This can happen if the viewer has been started but is empty
1661 // E.G:, if the cactvs server is down).
1662 // We just keep going, and make sure we get the default cursor back.
1663 }
1664 // Release constraints
1666 navigSpinner.setModel(new SpinnerNumberModel(currVrtxIdx+1, 1,
1667 verticesLibrary.size(), 1));
1669
1670 this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1671 }
1672
1673//----------------------------------------------------------------------------
1674
1683 public static List<APClass> choseOrCreateNewAPClass(JComponent parent,
1684 boolean singleSelection)
1685 {
1686 // To facilitate selection of existing APCs we offer a list...
1687 DefaultListModel<String> apClassLstModel =
1688 new DefaultListModel<String>();
1689 JList<String> apClassList = new JList<String>(apClassLstModel);
1690 for (String apc : APClass.getAllAPClassesAsString())
1691 {
1692 apClassLstModel.addElement(apc);
1693 }
1694 //...and to the list we add the option to create a new APClass.
1695 apClassLstModel.addElement(
1696 "<html><b><i>Define a new APClass...<i></b></html>");
1697 if (singleSelection)
1698 {
1699 apClassList.setSelectionMode(
1700 ListSelectionModel.SINGLE_SELECTION);
1701 } else {
1702 apClassList.setSelectionMode(
1703 ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
1704 }
1705 if (apClassList.getModel().getSize() == 1)
1706 {
1707 apClassList.setSelectedIndex(0);
1708 } else {
1709 apClassList.setSelectedIndex(apClassLstModel.getSize()-1);
1710 }
1711
1712 //Make and launch dialog for the user to make the selection
1713 JPanel chooseApPanel = new JPanel();
1714 JLabel header = new JLabel("Choose APClass:");
1715 JScrollPane apClassScroll = new JScrollPane(apClassList);
1716 chooseApPanel.add(header);
1717 chooseApPanel.add(apClassScroll);
1718
1719 int res = JOptionPane.showConfirmDialog(parent,
1720 chooseApPanel,
1721 "Choose APClasses to Add",
1722 JOptionPane.OK_CANCEL_OPTION,
1723 JOptionPane.PLAIN_MESSAGE,
1724 null);
1725 if (res != JOptionPane.OK_OPTION)
1726 {
1727 return new ArrayList<APClass>();
1728 }
1729
1730 // Interpret the selection made by the user
1731 ArrayList<APClass> selectedSPCs = new ArrayList<APClass>();
1732 int[] selectedIds = apClassList.getSelectedIndices();
1733 if (selectedIds.length > 0)
1734 {
1735 for (int ii=0; ii<selectedIds.length; ii++)
1736 {
1737 APClass apc = null;
1738 Integer idAPC = selectedIds[ii];
1739
1740 try {
1741 if (idAPC.intValue() == (apClassLstModel.size()-1))
1742 {
1743 // We chose to create a new class
1744 GUIAPClassDefinitionDialog apcDefiner =
1745 new GUIAPClassDefinitionDialog(parent, false);
1746 Object chosen = apcDefiner.showDialog();
1747 if (chosen != null)
1748 {
1749 Object[] pair = (Object[]) chosen;
1750 apc = APClass.make(pair[0].toString(),
1751 (BondType) pair[1]);
1752 }
1753 } else {
1754 apc = APClass.make(apClassLstModel.getElementAt(idAPC));
1755 }
1756 } catch (Exception e1) {
1757 // We have pressed cancel or closed the dialog: abandon
1758 continue;
1759 }
1760 selectedSPCs.add(apc);
1761 }
1762 }
1763 return selectedSPCs;
1764 }
1765
1766//-----------------------------------------------------------------------------
1767
1776 public static String ensureGoodAPRuleString(String currApRule,
1777 String title, boolean mustReply, JComponent parent)
1778 throws DENOPTIMException
1779 {
1780 String preStr = "";
1781 while (!APClass.isValidAPRuleString(currApRule))
1782 {
1783 if (currApRule != "")
1784 {
1785 preStr = "APRule '" + currApRule + "' is not valid!<br>"
1786 + "The valid syntax for APClass is:<br><br><code>APRule"
1787 + DENOPTIMConstants.SEPARATORAPPROPSCL
1788 + "subClass</code><br><br> where "
1789 + "<ul><li><code>APRule</code>"
1790 + " is the string you should provide now, and is "
1791 + "typically any string with no spaces,</li>"
1792 + "<li><code>subClass</code> is an integer.</ul>";
1793 }
1794
1795 currApRule = JOptionPane.showInputDialog(parent,String.format(
1796 "<html><body width='%1s'>" + preStr
1797 + " Please, provide a valid APClass rule string: </html>",
1798 300),
1799 title,
1800 JOptionPane.PLAIN_MESSAGE);
1801
1802 if (currApRule == null)
1803 {
1804 currApRule = "";
1805 if (!mustReply)
1806 {
1807 throw new DENOPTIMException();
1808 }
1809 }
1810 }
1811
1812 return currApRule;
1813 }
1814
1815//-----------------------------------------------------------------------------
1816
1822 public boolean hasUnsavedChanges()
1823 {
1824 return unsavedChanges;
1825 }
1826
1827//-----------------------------------------------------------------------------
1828
1829 /*
1830 * This is needed to stop Jmol threads upon closure of this gui card.
1831 */
1832 public void dispose()
1833 {
1835 }
1836
1837//-----------------------------------------------------------------------------
1838
1839}
General set of constants used in DENOPTIM.
static final String ATMPROPAPS
String tag of Atom property used to store attachment points.
static final String APSTAG
SDF tag defining attachment points.
static final String SEPARATORAPPROPSCL
Separator between APClass and APSubClass and coordinates.
A file with a conventional representation of its format.
static void addToRecentFiles(String fileName, FileFormat ff)
Appends an entry to the list of recent files.
Definition: FileUtils.java:67
static List< Vertex > fragmentation(IAtomContainer mol, FragmenterParameters settings)
Performs fragmentation according to the given settings.
static List< String > getAllAPClassesAsString()
Returns the list of the names of all APClasses.
Definition: APClass.java:331
static boolean isValidAPRuleString(String s)
Evaluates the given string as a candidate attachment point rule, i.e., as name of a fragmentation rul...
Definition: APClass.java:424
static APClass make(String ruleAndSubclass)
Creates an APClass if it does not exist already, or returns the reference to the existing instance.
Definition: APClass.java:164
An empty vertex has the behaviors of a vertex, but has no molecular structure.
Class representing a continuously connected portion of chemical object holding attachment points.
Definition: Fragment.java:61
IBond removeBond(int position)
Definition: Fragment.java:878
List< IAtom > getConnectedAtomsList(IAtom atom)
Definition: Fragment.java:943
AttachmentPoint addAPOnAtom(IAtom srcAtm, APClass apc, Point3d vector)
Add an attachment point to the specifies atom.
Definition: Fragment.java:424
List< AttachmentPoint > getAttachmentPoints()
Definition: Fragment.java:1141
ArrayList< AttachmentPoint > getAPsFromAtom(IAtom srcAtm)
Definition: Fragment.java:455
void removeAtom(IAtom atom)
Definition: Fragment.java:899
void updateAPs()
Changes the properties of each APs as to reflect the current atom list.
Definition: Fragment.java:511
Iterable< IBond > bonds()
Definition: Fragment.java:829
int getConnectedAtomsCount(IAtom atom)
Definition: Fragment.java:950
int indexOf(IAtom atom)
Definition: Fragment.java:850
A vertex is a data structure that has an identity and holds a list of AttachmentPoints.
Definition: Vertex.java:61
abstract IAtomContainer getIAtomContainer()
Standardised horizontal bar with padded components, which are meant to be JButtons.
Definition: ButtonsBar.java:36
Component add(Component comp)
Definition: ButtonsBar.java:53
A modal dialog to define parameters for fragmentation and fragment filtering.
void setPreDefinedAPClass(String text)
Sets the content of the text field with the given predefined text.
Remove the card from the deck of cards and takes care of removing also the entry in the list of activ...
Class of GUI panels meant to occupy one card in the deck-of-cards layout of the main panel.
GUIMainPanel mainPanel
The main panel (cards deck)
File opener for DENOPTIM GUI.
static File pickFile(Component parent)
GUI component to provide pathname where to save stuff.
static FileAndFormat pickFileForSavingVertexes(Component parent)
The main panel is a deck of cards that occupies all the GUI frame.
JButton btnDone
The button that is used to launch the processing of the data given to the open dialog,...
Object result
The result to be returned once the dialog is closed.
Object showDialog()
Shows the dialog and restrains the modality to it, until the dialog gets closed.
The collection of tunable preferences.
static File lastCutRulesFile
File with last used cutting rules.
void setEnabled(boolean var)
Enables/disable the listener.
A panel with a viewer capable of visualising DENOPTIM fragments and allows to create and edit fragmen...
static String ensureGoodAPRuleString(String currApRule, String title, boolean mustReply, JComponent parent)
Forces the user to specify a properly formatted APRule, i.e., the first component of an APClass.
ArrayList< Vertex > verticesLibrary
The currently loaded list of fragments.
final VrtxSpinnerChangeEvent vrtxSpinnerListener
Vertex vertex
The currently loaded vertex.
void importStructureFromSMILES(String smiles)
Imports the given SMILES into the viewer.
static AtomicInteger prepVrtxTabUID
Unique identified for instances of this inspector.
void importVerticesFromFile(File file)
Imports fragments from a file.
void importVertices(List< Vertex > fragments)
Imports vertices.
boolean convertAtomToAP(IAtom trgAtm, String ruleAndSubClass)
Removes an atom and replaces it with an attachment point.
boolean unsavedChanges
Flag signaling that loaded data has changes since last save.
int currVrtxIdx
The index of the currently loaded fragment [0–(n-1)}.
boolean hasUnsavedChanges()
Check whether there are unsaved changes.
static List< APClass > choseOrCreateNewAPClass(JComponent parent, boolean singleSelection)
Runs a dialog aimed at selecting an existing APClass or defining a new one.
GUIVertexInspector(GUIMainPanel mainPanel)
Constructor.
void initialize()
Initialize the panel and add buttons.
void loadCurrentVrtxIdxToViewer()
Loads the fragments corresponding to the field index.
void removeAtoms(ArrayList< IAtom > atmsToDels)
static final long serialVersionUID
Version UID.
static boolean dialogToDefineCuttingRules(FragmenterParameters settings, ClassLoader classLoader, Component parent, boolean setMolToGraphSettings)
Starts a dialog to define the on-the-fly fragmentation settings.
A modal dialog with a viewer that understands the different types of DENOPTIM vertex and allows to se...
void load(List< Vertex > fragments, int initialId)
Load the list of vertexes to choose from.
void setRequireApSelection(boolean enforced)
Allows to control whether confirming the selection of a vertex without having selected an attachment ...
A panel for visualizing vertices.
void clearCurrentSystem()
Removes the currently visualized molecule and AP table.
boolean hasUnsavedAPEdits()
Check for unsaved edits to the AP data.
DefaultTableModel getAPTableModel()
void activateTabEditsListener(boolean var)
Allows to activate and deactivate the listener.
Map< Integer, AttachmentPoint > getActiveMapAPs()
Returns the map of attachment points in the currently active viewer.
void deprotectEdits()
Overrides the flag signaling unsaved edits to saying that there are no altered data.
void loadVertexToViewer(Vertex v)
Loads the given vertex to this viewer.
Vertex getLoadedStructure()
Returns the currently loaded vertex.
void loadPlainStructure(IAtomContainer mol)
Loads a structure in the Jmol viewer.
boolean loadSMILES(String smiles)
Loads a molecule build from a smiles string.
void setSwitchable(boolean switchable)
Enable/disable switch-able view.
ArrayList< IAtom > getAtomsSelectedFromJMol()
Identifies the atoms that are selected in the Jmol viewer.
void clearMolecularViewer(boolean dataIsComing)
Clears the molecular viewer.
Utility methods for input/output.
static File writeVertexesToFile(File file, FileFormat format, List< Vertex > vertexes)
Writes vertexes to file.
static void readCuttingRules(BufferedReader reader, List< CuttingRule > cutRules, String source)
Read cutting rules from a stream.
static ArrayList< DGraph > readDENOPTIMGraphsFromFile(File inFile)
Reads a list of DGraphs from file.
static ArrayList< Vertex > readVertexes(File file, Vertex.BBType bbt)
Reads Vertexes from any file that can contain such items.
static List< IAtomContainer > readAllAtomContainers(File file)
Returns a single collection with all atom containers found in a file of any format.
Logger getLogger()
Get the name of the program specific logger.
Logger startConsoleLogger(String loggerIdentifier)
Starts a program-specific logger that prints to System.err stream.
Randomizer getRandomizer()
Returns the current program-specific randomizer.
Parameters controlling execution of the fragmenter.
void setFragmentationTmpls(List< DGraph > fragmentationTmpls)
Sets the list of graph templates for fragmentation.
Toll to add/remove dummy atoms from linearities or multi-hapto sites.
static void addDummiesOnLinearities(Fragment frag, double angLim)
Append dummy atoms on otherwise linear arrangements of atoms.
Utilities for molecule conversion.
static Point3d getPoint3d(IAtom atm)
Return the 3D coordinates, if present.
Possible chemical bond types an edge can represent.
Definition: Edge.java:305
The type of building block.
Definition: Vertex.java:86
Interface for all vertex viewers that intend to allow selection of attachment points.