$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.getRandomizer(),
696 settings.getLogger());
697 } catch (DENOPTIMException e)
698 {
699 JOptionPane.showMessageDialog(btnChop,String.format(
700 "<html><body width='%1s'"
701 + "Could not complete fragmentation. Hint: "
702 + e.getMessage() + "</html>", 400),
703 "Error",
704 JOptionPane.ERROR_MESSAGE,
705 UIManager.getIcon("OptionPane.errorIcon"));
706 return;
707 }
708
709 // Add linearity-breaking dummy atoms
710 for (Vertex frag : fragments)
711 {
713 settings.getLinearAngleLimit());
714 }
715
716 // Signal no result obtained
717 if (fragments.size() < 1 || (fragments.size() == 1 &&
718 ((Fragment)fragments.get(0)).isIsomorphicTo(vertex)))
719 {
720 JOptionPane.showMessageDialog(btnAddVrtx,
721 "<html>Fragmentation produced no fragments!</html>",
722 "Error",
723 JOptionPane.WARNING_MESSAGE,
724 UIManager.getIcon("OptionPane.warningIcon"));
725 return;
726 }
727
728 // The resulting fragments are loaded into the viewer, without
729 // removing the original structure.
730
731 String[] options = new String[]{"All",
732 "Select",
733 "Cancel"};
734 String txt = "<html><body width='%1s'>Fragmentation produced "
735 + fragments.size() + " fragments. Do you want to "
736 + "append all or select some?"
737 + "</html>";
738 int answer = JOptionPane.showOptionDialog(btnAddVrtx,
739 String.format(txt,200),
740 "Append Building Blocks",
741 JOptionPane.DEFAULT_OPTION,
742 JOptionPane.QUESTION_MESSAGE,
743 UIManager.getIcon("OptionPane.warningIcon"),
744 options,
745 options[0]);
746
747 if (answer == 2)
748 {
749 return;
750 }
751
752 switch (answer)
753 {
754 case 0:
755 importVertices(fragments);
756 break;
757
758 case 1:
759 List<Vertex> selectedVrtxs =
760 new ArrayList<Vertex>();
761 GUIVertexSelector vrtxSelector = new GUIVertexSelector(
762 btnAddVrtx,true);
763 vrtxSelector.setRequireApSelection(false);
764 vrtxSelector.load(fragments, 0);
765 Object selected = vrtxSelector.showDialog();
766
767 if (selected != null)
768 {
769 @SuppressWarnings("unchecked")
770 List<ArrayList<Integer>> selList =
771 (ArrayList<ArrayList<Integer>>) selected;
772 for (ArrayList<Integer> pair : selList)
773 {
774 selectedVrtxs.add(fragments.get(pair.get(0)));
775 }
776 }
777 importVertices(selectedVrtxs);
778 break;
779
780 default:
781 return;
782 }
783 }
784 });
787
788 pnlDelSel = new JPanel();
789 btnDelSel = new JButton("Remove Atoms");
790 btnDelSel.setToolTipText("<html>Removes all selected atoms from the "
791 + "system.<br><br><b>WARNING:</b> this action cannot be "
792 + "undone!");
793 btnDelSel.addActionListener(new ActionListener() {
794 public void actionPerformed(ActionEvent e) {
795 ArrayList<IAtom> selectedAtms =
797
798 if (selectedAtms.size() == 0)
799 {
800 JOptionPane.showMessageDialog(btnDelSel,
801 "<html>No atom selected! Click on atoms to select"
802 + " them.<br>Click again to unselect.</html>",
803 "Error",
804 JOptionPane.ERROR_MESSAGE,
805 UIManager.getIcon("OptionPane.errorIcon"));
806 return;
807 }
808 else
809 {
810 removeAtoms(selectedAtms);
811
813
814 unsavedChanges = true;
816 }
817 }
818 });
819 pnlDelSel.add(btnDelSel);
820 ctrlPane.add(pnlDelSel);
821
822 ctrlPane.add(new JSeparator());
823
824 pnlSaveEdits = new JPanel();
825 btnSaveEdits = new JButton("Save Changes");
826 //btnSaveEdits.setForeground(Color.RED);
827 btnSaveEdits.setEnabled(true);
828 btnSaveEdits.setToolTipText("<html>Save the current system replacing"
829 + " <br>the original one in the loaded library.</html>");
830 btnSaveEdits.addActionListener(new ActionListener() {
831 public void actionPerformed(ActionEvent e) {
833 }
834 });
837 this.add(ctrlPane, BorderLayout.EAST);
838
839
840 // Panel with buttons to the bottom of the frame
841 ButtonsBar commandsPane = new ButtonsBar();
842 super.add(commandsPane, BorderLayout.SOUTH);
843
844 btnOpenVrtxs = new JButton("Load Library of Building Blocks");
845 btnOpenVrtxs.setToolTipText("Reads building blocks or structures from "
846 + "file.");
847 btnOpenVrtxs.addActionListener(new ActionListener() {
848 public void actionPerformed(ActionEvent e) {
849 File inFile = GUIFileOpener.pickFile(btnOpenVrtxs);
850 if (inFile == null || inFile.getAbsolutePath().equals(""))
851 {
852 return;
853 }
854 ArrayList<Vertex> vrtxLib = new ArrayList<>();
855 try {
856 vrtxLib = DenoptimIO.readVertexes(inFile, BBType.FRAGMENT);
857 } catch (Exception e1) {
858 e1.printStackTrace();
859 JOptionPane.showMessageDialog(btnAddVrtx,
860 "<html>Could not read building blocks from file"
861 + "<br>'" + inFile + "'"
862 + "<br>Hint on cause: " + e1.getMessage()
863 +"</html>",
864 "Error",
865 JOptionPane.ERROR_MESSAGE,
866 UIManager.getIcon("OptionPane.errorIcon"));
867 return;
868 }
869
870 if (vrtxLib.size() == 0)
871 {
872 JOptionPane.showMessageDialog(btnAddVrtx,
873 "<html>No building blocks in file"
874 + "<br>'" + inFile + "'</html>",
875 "Error",
876 JOptionPane.ERROR_MESSAGE,
877 UIManager.getIcon("OptionPane.errorIcon"));
878 return;
879 }
880 importVertices(vrtxLib);
881 }
882 });
883 commandsPane.add(btnOpenVrtxs);
884
885 JButton btnSaveVrtxs = new JButton("Save Library of Building Blocks");
886 btnSaveVrtxs.setToolTipText("Write all building blocks to a file.");
887 btnSaveVrtxs.addActionListener(new ActionListener() {
888 public void actionPerformed(ActionEvent e) {
889 FileAndFormat fileAndFormat =
891 if (fileAndFormat == null)
892 {
893 return;
894 }
895 File outFile = fileAndFormat.file;
896 try
897 {
898 // The writing method may change the extension. So we need
899 // to get the return value.
900 outFile = DenoptimIO.writeVertexesToFile(outFile,
901 fileAndFormat.format,
903 }
904 catch (Exception ex)
905 {
906 ex.printStackTrace();
907 JOptionPane.showMessageDialog(btnSaveVrtxs,
908 "Could not write to '" + outFile + "'! "
909 + "Hint: "+ex.getMessage(),
910 "Error",
911 JOptionPane.PLAIN_MESSAGE,
912 UIManager.getIcon("OptionPane.errorIcon"));
913 return;
914 }
915 navigSpinner.setModel(new SpinnerNumberModel(currVrtxIdx+1, 1,
916 verticesLibrary.size(), 1));
918 unsavedChanges = false;
919 FileUtils.addToRecentFiles(outFile, fileAndFormat.format);
920 }
921 });
922 commandsPane.add(btnSaveVrtxs);
923
924 JButton btnCanc = new JButton("Close Tab");
925 btnCanc.setToolTipText("Closes this tab.");
926 btnCanc.addActionListener(new removeCardActionListener(this));
927 commandsPane.add(btnCanc);
928
929 JButton btnHelp = new JButton("?");
930 btnHelp.setToolTipText("<html>Hover over the buttons and fields "
931 + "to get a tip.</html>");
932 btnHelp.addActionListener(new ActionListener() {
933 public void actionPerformed(ActionEvent e) {
934 String txt = "<html><body width='%1s'>"
935 + "<p>This tab allows to create, inspect, and edit "
936 + "building blocks and "
937 + "three-dimensional molecular fragments.</p>"
938 + "<p>New fragments can be created starting from any "
939 + "chemical structure that can be loaded from file or "
940 + "generated from SMILES (SMILES-to-3D conversion "
941 + "requires an Internet connection).</p>"
942 + "<p>Any terminal atom (i.e., atoms that have only "
943 + "one connected neighbor) can be transformed into "
944 + "on attachment point (AP). Click on the atom to "
945 + "select it, and press <code><b>Atom to AP</b></code>."
946 + "</p>"
947 + "<p>Attachment points are depicted in the molecular "
948 + "viewer as yellow arrows in the 3D space, and their "
949 + "attachment point class (APClass) is specified in "
950 + "the table below the viewer. Double-click on a "
951 + "specific APClass field to change its value.</p>"
952 + "<br>"
953 + "<p>Hover over buttons get a tip.</p>"
954 + "<br>"
955 + "<p>Right-click on the Jmol viewer will open the "
956 + "Jmol menu. However, since Jmol cannot handle the "
957 + "attachment points data. Therefore, Jmol "
958 + "functionality should only be used on systems "
959 + "that have no attachment points, or for alterations "
960 + "of the molecular structure that do not change the "
961 + "list of atoms au to the last atom decorated with an "
962 + "attachment point.</p></html>";
963 JOptionPane.showMessageDialog(btnHelp,
964 String.format(txt, 400),
965 "Tips",
966 JOptionPane.PLAIN_MESSAGE);
967 }
968 });
969 commandsPane.add(btnHelp);
970 }
971
972//-----------------------------------------------------------------------------
973
984 public static boolean dialogToDefineCuttingRules(
985 FragmenterParameters settings, ClassLoader classLoader,
986 Component parent,
987 boolean setMolToGraphSettings)
988 {
989 settings.startConsoleLogger("GUI-controlledFragmenterLogger");
990
991 List<CuttingRule> defaultCuttingRules = new ArrayList<CuttingRule>();
992 BufferedReader reader = null;
993 try
994 {
995 try {
996 reader = new BufferedReader(
997 new InputStreamReader(classLoader.getResourceAsStream(
998 "data/cutting_rules")));
999 DenoptimIO.readCuttingRules(reader, defaultCuttingRules,
1000 "bundled jar");
1001 } finally {
1002 if (reader!=null)
1003 reader.close();
1004 }
1005 } catch (Exception e )
1006 {
1007 e.printStackTrace();
1008 JOptionPane.showMessageDialog(parent,String.format(
1009 "<html><body width='%1s'>"
1010 + "Could not read default cutting rules from "
1011 + "bundled jar. "
1012 + "Hint: "
1013 + e.getMessage() + "</html>", 400),
1014 "Error",
1015 JOptionPane.ERROR_MESSAGE,
1016 UIManager.getIcon("OptionPane.errorIcon"));
1017 return false;
1018 }
1019
1020 // Read last used cutting rules
1021 List<CuttingRule> customCuttingRules = new ArrayList<CuttingRule>();
1022 boolean useDefaultCuttingRules = true;
1023 try
1024 {
1026 {
1029 customCuttingRules);
1030 useDefaultCuttingRules = false;
1031 }
1032 } catch (DENOPTIMException e)
1033 {
1034 JOptionPane.showMessageDialog(parent,String.format(
1035 "<html><body width='%1s'"
1036 + "Could not read last-used cutting rules from '"
1038 + "Hint: "
1039 + e.getMessage() + "</html>", 400),
1040 "Error",
1041 JOptionPane.ERROR_MESSAGE,
1042 UIManager.getIcon("OptionPane.errorIcon"));
1043 return false;
1044 }
1045
1046 // Build a dialog that offers the possibility to see and edit
1047 // default cutting rules, and to define custom ones from scratch
1048 CuttingRulesSelectionDialog crs = null;
1049 if (setMolToGraphSettings)
1050 {
1052 defaultCuttingRules, customCuttingRules,
1053 useDefaultCuttingRules, parent, settings);
1054 } else {
1056 defaultCuttingRules, customCuttingRules,
1057 useDefaultCuttingRules, parent, settings);
1058 }
1059 crs.pack();
1060 crs.setVisible(true);
1061
1062 if (crs.result==null)
1063 return false;
1064
1065 String pathnameLastUsedCutRules = settings.getCuttingRulesFilePathname();
1066 if (pathnameLastUsedCutRules != null
1067 && !pathnameLastUsedCutRules.isBlank())
1068 {
1069 GUIPreferences.lastCutRulesFile = new File(pathnameLastUsedCutRules);
1070 }
1071
1072 return true;
1073 }
1074
1075//-----------------------------------------------------------------------------
1076
1077 public void importStructureFromFile(File file)
1078 {
1079 this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
1080
1081 // Cleanup
1083
1084 try {
1085 for (IAtomContainer mol : DenoptimIO.readAllAtomContainers(file))
1086 {
1087 // We mean to import only the structure: get rid of AP
1088 mol.setProperty(DENOPTIMConstants.APSTAG,null);
1089
1090 // NB: here we let the vertexViewer create a fragment object that we
1091 // then put into the local library. This to make sure that the
1092 // references to atoms selected in the viewer are referring to
1093 // members of the "vertex" object
1096
1097 // the system is not a fragment but, this is done for consistency:
1098 // when we have a molecule loaded the list is not empty
1099 // The currently viewed fragment (if any) is always part of the lib
1101 currVrtxIdx = verticesLibrary.size()-1;
1102 }
1104 unsavedChanges = true;
1105 btnDelSel.setEnabled(true);
1106 btnAtmToAP.setEnabled(true);
1107 } catch (Exception e) {
1108 this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1109 e.printStackTrace();
1110 JOptionPane.showMessageDialog(this,
1111 "<html>Could not read file '" + file.getAbsolutePath()
1112 + "'!<br>Hint about reason: " + e.getCause() + "</html>",
1113 "Error",
1114 JOptionPane.PLAIN_MESSAGE,
1115 UIManager.getIcon("OptionPane.errorIcon"));
1116 }
1117
1118 this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1119 }
1120
1121//-----------------------------------------------------------------------------
1122
1129 public void importStructureFromSMILES(String smiles)
1130 {
1131 this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
1132
1133 // Cleanup
1135
1136 // Load the structure using CACTUS service or CDK builder
1137 try {
1138 vertexViewer.loadSMILES(smiles);
1139 } catch (Exception e) {
1140 this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1141 return;
1142 }
1143
1145
1146 // The system is not a fragment but, this is done for consistency:
1147 // when we have a molecule loaded the list is not empty:
1148 // The currently viewed fragment (if any) is always part of the library
1150 currVrtxIdx = verticesLibrary.size()-1;
1151
1152 // finalize GUI status
1154 unsavedChanges = true;
1155 btnDelSel.setEnabled(true);
1156 btnAtmToAP.setEnabled(true);
1157 btnSaveEdits.setEnabled(true);
1158
1159 this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1160 }
1161
1162//-----------------------------------------------------------------------------
1163
1168 public void importVerticesFromFile(File file)
1169 {
1170 this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
1171
1172 ArrayList<Vertex> vrtxLib = new ArrayList<>();
1173 try {
1174 vrtxLib = DenoptimIO.readVertexes(file, BBType.FRAGMENT);
1175 } catch (Exception e1) {
1176 e1.printStackTrace();
1177 JOptionPane.showMessageDialog(btnAddVrtx,
1178 "<html>Could not read building blocks from file"
1179 + "<br>'" + file + "'"
1180 + "<br>Hint on cause: " + e1.getMessage()
1181 +"</html>",
1182 "Error",
1183 JOptionPane.ERROR_MESSAGE,
1184 UIManager.getIcon("OptionPane.errorIcon"));
1185 return;
1186 }
1187
1188 if (vrtxLib.size() == 0)
1189 {
1190 JOptionPane.showMessageDialog(btnAddVrtx,
1191 "<html>No building blocks in file"
1192 + "<br>'" + file + "'</html>",
1193 "Error",
1194 JOptionPane.ERROR_MESSAGE,
1195 UIManager.getIcon("OptionPane.errorIcon"));
1196 return;
1197 }
1198 importVertices(vrtxLib);
1199 this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1200 }
1201
1202//-----------------------------------------------------------------------------
1203
1208 public void importVertices(List<Vertex> fragments)
1209 {
1210 this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
1211
1212 int firstOfNew = 0;
1213 boolean libFromScrtch = false;
1214 if (verticesLibrary == null)
1215 {
1216 libFromScrtch = true;
1217 verticesLibrary = new ArrayList<Vertex>();
1218 }
1219 else
1220 {
1221 firstOfNew = verticesLibrary.size();
1222 }
1223
1224 boolean addedOne = false;
1225 if (fragments.size() > 0)
1226 {
1227 verticesLibrary.addAll(fragments);
1228 addedOne = true;
1229
1230 // Display the first
1231 if (libFromScrtch)
1232 {
1233 currVrtxIdx = 0;
1234 }
1235 else if (addedOne)
1236 {
1237 currVrtxIdx = firstOfNew;
1238 }
1240
1241 // Update the fragment spinner
1243 } else {
1244 this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1245 JOptionPane.showMessageDialog(this,
1246 "<html>No vertices to import from the given list.</html>",
1247 "Error",
1248 JOptionPane.PLAIN_MESSAGE,
1249 UIManager.getIcon("OptionPane.errorIcon"));
1250 }
1251 this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1252 }
1253
1254
1255//-----------------------------------------------------------------------------
1256
1265 {
1266 if (verticesLibrary == null)
1267 {
1268 JOptionPane.showMessageDialog(this,
1269 "No list of building blocks loaded.",
1270 "Error",
1271 JOptionPane.PLAIN_MESSAGE,
1272 UIManager.getIcon("OptionPane.errorIcon"));
1273 return;
1274 }
1275
1277
1280 if (vertex == null || vertex instanceof Fragment == false)
1281 {
1282 btnDelSel.setEnabled(false);
1283 btnAtmToAP.setEnabled(false);
1284 } else {
1285 btnDelSel.setEnabled(true);
1286 btnAtmToAP.setEnabled(true);
1287 }
1288 }
1289
1290//-----------------------------------------------------------------------------
1291
1292 private void clearCurrentSystem()
1293 {
1294 // Get rid of currently loaded mol
1295 vertex = null;
1297 }
1298
1299//-----------------------------------------------------------------------------
1300
1302 {
1303 navigSpinner.setModel(new SpinnerNumberModel(currVrtxIdx+1, 1,
1304 verticesLibrary.size(), 1));
1305 totalVrtxsLabel.setText(Integer.toString(verticesLibrary.size()));
1306 }
1307
1308//-----------------------------------------------------------------------------
1309
1319 private boolean convertAtomToAP(IAtom trgAtm, String ruleAndSubClass)
1320 {
1321 if (!(vertex instanceof Fragment))
1322 {
1323 return false;
1324 }
1325 Fragment frag = (Fragment) vertex;
1326 // Accept ONLY if the atom has one and only one connected neighbour
1327 if (frag.getConnectedAtomsCount(trgAtm) != 1)
1328 {
1329 String str = "";
1330 for (IAtom atm : frag.getConnectedAtomsList(trgAtm))
1331 {
1332 str = str + " " + atm.getSymbol()
1333 + (frag.indexOf(atm));
1334 }
1335 System.out.println("Connected atoms: "+str);
1336
1337 JOptionPane.showMessageDialog(this,
1338 "<html>Atom "+ trgAtm.getSymbol()
1339 + (frag.indexOf(trgAtm))
1340 + " has zero or more than one neighbour.<br>I can only "
1341 + "transform atoms"
1342 + " that have one and only one neighbour.</html>",
1343 "Error",
1344 JOptionPane.ERROR_MESSAGE,
1345 UIManager.getIcon("OptionPane.errorIcon"));
1346 return false;
1347 }
1348
1349 IAtom srcAtm = frag.getConnectedAtomsList(trgAtm).get(0);
1350 BondType bt = BondType.valueOf(
1351 srcAtm.getBond(trgAtm).getOrder().toString());
1352
1353 Point3d srcP3d = MoleculeUtils.getPoint3d(srcAtm);
1354 Point3d trgP3d = MoleculeUtils.getPoint3d(trgAtm);
1355 Point3d vector = new Point3d();
1356 vector.x = srcP3d.x + (trgP3d.x - srcP3d.x);
1357 vector.y = srcP3d.y + (trgP3d.y - srcP3d.y);
1358 vector.z = srcP3d.z + (trgP3d.z - srcP3d.z);
1359
1360 //NB: assumption of validity!
1361 String[] parts = ruleAndSubClass.split(
1363 try {
1364 // NB: here we change the bond type to make it fit with the one we
1365 // have in the molecular model.
1366 frag.addAPOnAtom(srcAtm, APClass.make(parts[0],
1367 Integer.parseInt(parts[1]), bt), vector);
1368 } catch (DENOPTIMException e) {
1369 e.printStackTrace();
1370 JOptionPane.showMessageDialog(this,
1371 "<html>Could not make AP.<br>Possible cause: "
1372 + e.getMessage() +"</html>",
1373 "Error",
1374 JOptionPane.ERROR_MESSAGE,
1375 UIManager.getIcon("OptionPane.errorIcon"));
1376 return false;
1377 }
1378 return true;
1379 }
1380
1381//----------------------------------------------------------------------------
1382
1383 private void removeAtoms(ArrayList<IAtom> atmsToDels)
1384 {
1385 if (!(vertex instanceof Fragment))
1386 {
1387 return;
1388 }
1389 Fragment frag = (Fragment) vertex;
1390 ArrayList<IBond> bnsToDel = new ArrayList<IBond>();
1391 for (IAtom atm : atmsToDels)
1392 {
1393 for (IBond bnd : frag.bonds())
1394 {
1395 if (bnd.contains(atm))
1396 {
1397 bnsToDel.add(bnd);
1398 }
1399 }
1400 }
1401 for (IBond bnd : bnsToDel)
1402 {
1403 frag.removeBond(bnd);
1404 }
1405 for (IAtom atm : atmsToDels)
1406 {
1407 if (atm.getProperty(DENOPTIMConstants.ATMPROPAPS)!=null)
1408 {
1409 ArrayList<AttachmentPoint> apsOnAtm = frag.getAPsFromAtom(atm);
1410 frag.getAttachmentPoints().removeAll(apsOnAtm);
1411 }
1412 frag.removeAtom(atm);
1413 }
1414 frag.updateAPs();
1415 }
1416
1417//-----------------------------------------------------------------------------
1418
1419 private class VrtxSpinnerChangeEvent implements ChangeListener
1420 {
1421 private boolean inEnabled = true;
1422
1424 {}
1425
1431 public void setEnabled(boolean var)
1432 {
1433 this.inEnabled = var;
1434 }
1435
1436 @Override
1437 public void stateChanged(ChangeEvent event)
1438 {
1439 if (!inEnabled)
1440 {
1441 return;
1442 }
1443
1445
1446 //NB here we convert from 1-based index in GUI to 0-based index
1447 currVrtxIdx = ((Integer) navigSpinner.getValue()).intValue() - 1;
1449
1451 }
1452 }
1453
1454//-----------------------------------------------------------------------------
1455
1457 {
1458 //btnSaveEdits.setEnabled(false);
1459 btnAddVrtx.setEnabled(true);
1460 btnOpenVrtxs.setEnabled(true);
1461 btnOpenSMILES.setEnabled(true);
1462 btnOpenMol.setEnabled(true);
1463 btnEmptFrag.setEnabled(true);
1464 if (vertex == null || vertex instanceof Fragment == false)
1465 {
1466 btnDelSel.setEnabled(false);
1467 btnAtmToAP.setEnabled(false);
1468 } else {
1469 btnDelSel.setEnabled(true);
1470 btnAtmToAP.setEnabled(true);
1471 }
1472
1473 ((DefaultEditor) navigSpinner.getEditor())
1474 .getTextField().setEditable(true);
1475 ((DefaultEditor) navigSpinner.getEditor())
1476 .getTextField().setForeground(Color.BLACK);
1479
1481 }
1482
1483//-----------------------------------------------------------------------------
1484
1485 private void protectEditedSystem()
1486 {
1487 btnSaveEdits.setEnabled(true);
1488 btnAddVrtx.setEnabled(false);
1489 btnOpenVrtxs.setEnabled(false);
1490 btnOpenSMILES.setEnabled(false);
1491 btnOpenMol.setEnabled(false);
1492 btnEmptFrag.setEnabled(false);
1493 //btnDelSel.setEnabled(false);
1494 //btnAtmToAP.setEnabled(false);
1495
1496 navigSpinner.setModel(new SpinnerNumberModel(currVrtxIdx+1,
1497 currVrtxIdx+1, currVrtxIdx+1, 1));
1498 ((DefaultEditor) navigSpinner.getEditor())
1499 .getTextField().setEditable(false);
1500 ((DefaultEditor) navigSpinner.getEditor())
1501 .getTextField().setForeground(Color.GRAY);
1502
1504
1506 }
1507
1508//-----------------------------------------------------------------------------
1509
1510 private void activateTabEditsListener(boolean var)
1511 {
1513 }
1514
1515//-----------------------------------------------------------------------------
1516
1518 {
1520 {
1521 String[] options = new String[]{"Yes","No"};
1522 int res = JOptionPane.showOptionDialog(this,
1523 "<html>Removing unsaved vertex?",
1524 "Warning",
1525 JOptionPane.DEFAULT_OPTION,
1526 JOptionPane.QUESTION_MESSAGE,
1527 UIManager.getIcon("OptionPane.warningIcon"),
1528 options,
1529 options[1]);
1530 if (res == 1)
1531 {
1532 return;
1533 }
1534 }
1535
1537
1538 // Actual removal from the library
1539 if (verticesLibrary.size()>0)
1540 {
1542 int libSize = verticesLibrary.size();
1543
1544 if (currVrtxIdx>=0 && currVrtxIdx<libSize)
1545 {
1546 //we keep currFrgIdx as it will correspond to the next item
1547 }
1548 else
1549 {
1551 }
1552
1553 if (currVrtxIdx==-1 || verticesLibrary.size()==0)
1554 {
1555 // The viewer gets hidden so we do not need to clear it
1556 // with 'zap' (which is a very slow operation)
1558 currVrtxIdx = 0;
1559 navigSpinner.setModel(new SpinnerNumberModel(0,0,0,1));
1560 totalVrtxsLabel.setText(Integer.toString(0));
1562 }
1563 else
1564 {
1567 navigSpinner.setModel(new SpinnerNumberModel(currVrtxIdx+1, 1,
1568 verticesLibrary.size(), 1));
1570 }
1571 }
1572 }
1573
1574//-----------------------------------------------------------------------------
1575
1576 private void saveUnsavedChanges()
1577 {
1578 this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
1579
1581 {
1582 DefaultTableModel tabModel = vertexViewer.getAPTableModel();
1583 // Import changes from AP table into molecular representation
1584 for (int i=0; i<tabModel.getRowCount(); i++)
1585 {
1586 int apId = ((Integer) tabModel.getValueAt(i, 0)).intValue();
1587 String currApClass = tabModel.getValueAt(i, 1).toString();
1588
1589 // Make sure the new class has a proper syntax
1590 GUIAPClassDefinitionDialog apcDefiner =
1592 apcDefiner.setTitle("Confirm APClass on AP #"+i);
1593 apcDefiner.setPreDefinedAPClass(currApClass);
1594 Object chosen = apcDefiner.showDialog();
1595 if (chosen != null)
1596 {
1597 Object[] pair = (Object[]) chosen;
1598 currApClass = pair[0].toString();
1599 } else {
1600 currApClass = "dafaultAPClass:0";
1601 }
1602
1603 Map<Integer, AttachmentPoint> mapAPs =
1605
1606 if (mapAPs.containsKey(apId))
1607 {
1608 String origApClass = mapAPs.get(apId).getAPClass().toString();
1609 if (!origApClass.equals(currApClass))
1610 {
1611 try {
1612 mapAPs.get(apId).setAPClass(currApClass);
1613 } catch (DENOPTIMException e) {
1614 // We made sure the class is valid, so this
1615 // should never happen, though one never knows
1616 e.printStackTrace();
1617 JOptionPane.showMessageDialog(btnSaveEdits,
1618 "<html>Could not save due to errors setting a "
1619 + "new APClass.<br>Please report this to the "
1620 + "DENOPTIM team.</html>",
1621 "Error",
1622 JOptionPane.PLAIN_MESSAGE,
1623 UIManager.getIcon("OptionPane.errorIcon"));
1624 return;
1625 }
1626 }
1627 }
1628 else
1629 {
1630 JOptionPane.showMessageDialog(btnSaveEdits,
1631 "<html>Could not save due to mistmatch between AP "
1632 + "table and map.<br>Please report this to the "
1633 + "DENOPTIM team.</html>",
1634 "Error",
1635 JOptionPane.PLAIN_MESSAGE,
1636 UIManager.getIcon("OptionPane.errorIcon"));
1637 return;
1638 }
1639 }
1640 }
1641
1642 // Retrieve chemical object from the viewer, if edited, otherwise
1643 // we get what is already in 'vertex'
1645 if (verticesLibrary.size()==0
1646 && (vertex==null || vertex.getNumberOfAPs()==0))
1647 {
1648 //Nothing to same
1649 this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1650 return;
1651 }
1653
1654 // Reload fragment from library to refresh table and viewer
1656 try {
1658 } catch (Throwable t) {
1659 //This can happen if the viewer has been started but is empty
1660 // E.G:, if the cactvs server is down).
1661 // We just keep going, and make sure we get the default cursor back.
1662 }
1663 // Release constraints
1665 navigSpinner.setModel(new SpinnerNumberModel(currVrtxIdx+1, 1,
1666 verticesLibrary.size(), 1));
1668
1669 this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1670 }
1671
1672//----------------------------------------------------------------------------
1673
1682 public static List<APClass> choseOrCreateNewAPClass(JComponent parent,
1683 boolean singleSelection)
1684 {
1685 // To facilitate selection of existing APCs we offer a list...
1686 DefaultListModel<String> apClassLstModel =
1687 new DefaultListModel<String>();
1688 JList<String> apClassList = new JList<String>(apClassLstModel);
1689 for (String apc : APClass.getAllAPClassesAsString())
1690 {
1691 apClassLstModel.addElement(apc);
1692 }
1693 //...and to the list we add the option to create a new APClass.
1694 apClassLstModel.addElement(
1695 "<html><b><i>Define a new APClass...<i></b></html>");
1696 if (singleSelection)
1697 {
1698 apClassList.setSelectionMode(
1699 ListSelectionModel.SINGLE_SELECTION);
1700 } else {
1701 apClassList.setSelectionMode(
1702 ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
1703 }
1704 if (apClassList.getModel().getSize() == 1)
1705 {
1706 apClassList.setSelectedIndex(0);
1707 } else {
1708 apClassList.setSelectedIndex(apClassLstModel.getSize()-1);
1709 }
1710
1711 //Make and launch dialog for the user to make the selection
1712 JPanel chooseApPanel = new JPanel();
1713 JLabel header = new JLabel("Choose APClass:");
1714 JScrollPane apClassScroll = new JScrollPane(apClassList);
1715 chooseApPanel.add(header);
1716 chooseApPanel.add(apClassScroll);
1717
1718 int res = JOptionPane.showConfirmDialog(parent,
1719 chooseApPanel,
1720 "Choose APClasses to Add",
1721 JOptionPane.OK_CANCEL_OPTION,
1722 JOptionPane.PLAIN_MESSAGE,
1723 null);
1724 if (res != JOptionPane.OK_OPTION)
1725 {
1726 return new ArrayList<APClass>();
1727 }
1728
1729 // Interpret the selection made by the user
1730 ArrayList<APClass> selectedSPCs = new ArrayList<APClass>();
1731 int[] selectedIds = apClassList.getSelectedIndices();
1732 if (selectedIds.length > 0)
1733 {
1734 for (int ii=0; ii<selectedIds.length; ii++)
1735 {
1736 APClass apc = null;
1737 Integer idAPC = selectedIds[ii];
1738
1739 try {
1740 if (idAPC.intValue() == (apClassLstModel.size()-1))
1741 {
1742 // We chose to create a new class
1743 GUIAPClassDefinitionDialog apcDefiner =
1744 new GUIAPClassDefinitionDialog(parent, false);
1745 Object chosen = apcDefiner.showDialog();
1746 if (chosen != null)
1747 {
1748 Object[] pair = (Object[]) chosen;
1749 apc = APClass.make(pair[0].toString(),
1750 (BondType) pair[1]);
1751 }
1752 } else {
1753 apc = APClass.make(apClassLstModel.getElementAt(idAPC));
1754 }
1755 } catch (Exception e1) {
1756 // We have pressed cancel or closed the dialog: abandon
1757 continue;
1758 }
1759 selectedSPCs.add(apc);
1760 }
1761 }
1762 return selectedSPCs;
1763 }
1764
1765//-----------------------------------------------------------------------------
1766
1775 public static String ensureGoodAPRuleString(String currApRule,
1776 String title, boolean mustReply, JComponent parent)
1777 throws DENOPTIMException
1778 {
1779 String preStr = "";
1780 while (!APClass.isValidAPRuleString(currApRule))
1781 {
1782 if (currApRule != "")
1783 {
1784 preStr = "APRule '" + currApRule + "' is not valid!<br>"
1785 + "The valid syntax for APClass is:<br><br><code>APRule"
1786 + DENOPTIMConstants.SEPARATORAPPROPSCL
1787 + "subClass</code><br><br> where "
1788 + "<ul><li><code>APRule</code>"
1789 + " is the string you should provide now, and is "
1790 + "typically any string with no spaces,</li>"
1791 + "<li><code>subClass</code> is an integer.</ul>";
1792 }
1793
1794 currApRule = JOptionPane.showInputDialog(parent,String.format(
1795 "<html><body width='%1s'>" + preStr
1796 + " Please, provide a valid APClass rule string: </html>",
1797 300),
1798 title,
1799 JOptionPane.PLAIN_MESSAGE);
1800
1801 if (currApRule == null)
1802 {
1803 currApRule = "";
1804 if (!mustReply)
1805 {
1806 throw new DENOPTIMException();
1807 }
1808 }
1809 }
1810
1811 return currApRule;
1812 }
1813
1814//-----------------------------------------------------------------------------
1815
1821 public boolean hasUnsavedChanges()
1822 {
1823 return unsavedChanges;
1824 }
1825
1826//-----------------------------------------------------------------------------
1827
1828 /*
1829 * This is needed to stop Jmol threads upon closure of this gui card.
1830 */
1831 public void dispose()
1832 {
1834 }
1835
1836//-----------------------------------------------------------------------------
1837
1838}
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 boolean fragmentation(File input, FragmenterParameters settings, File output, Logger logger)
Performs fragmentation according to the given cutting rules.
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:303
The type of building block.
Definition: Vertex.java:86
Interface for all vertex viewers that intend to allow selection of attachment points.