$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 pnlChop;
151 private JButton btnChop;
152
153 private JPanel pnlDelSel;
154 private JButton btnDelSel;
155
156 private JPanel pnlSaveEdits;
157 private JButton btnSaveEdits;
158
159
160//-----------------------------------------------------------------------------
161
166 {
167 super(mainPanel, "Vertex Inspector #" + prepVrtxTabUID.getAndIncrement());
168 super.setLayout(new BorderLayout());
169 initialize();
170 }
171
172//-----------------------------------------------------------------------------
173
177 private void initialize() {
178
179 // BorderLayout is needed to allow dynamic resizing!
180 this.setLayout(new BorderLayout());
181
182 // This card structure includes center, east and south panels:
183 // - (Center) molecular/graph viewer and APs
184 // - (East) vertex controls
185 // - (South) general controls (load, save, close)
186
187 // The viewer with Jmol and APtable
188 vertexViewer = new VertexViewPanel(true);
189 vertexViewer.addPropertyChangeListener(
191 new PropertyChangeListener() {
192 @Override
193 public void propertyChange(PropertyChangeEvent evt) {
195 }
196 });
197 this.add(vertexViewer,BorderLayout.CENTER);
198
199 // General panel on the right: it containing all controls
200 ctrlPane = new JPanel();
201 ctrlPane.setVisible(true);
202 ctrlPane.setLayout(new BoxLayout(ctrlPane, SwingConstants.VERTICAL));
203 ctrlPane.add(new JSeparator());
204
205 // NB: avoid GroupLayout because it interferes with Jmol viewer and causes exception
206
207 // Controls to navigate the list of vertices
208 navigPanel = new JPanel();
209 navigPanel2 = new JPanel();
210 navigPanel3 = new JPanel();
211 JLabel navigationLabel1 = new JLabel("Vertex # ");
212 JLabel navigationLabel2 = new JLabel("Current library size: ");
213 totalVrtxsLabel = new JLabel("0");
214
215 navigSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 0, 1));
216 navigSpinner.setToolTipText("Move to vertex number # in the currently loaded library.");
217 navigSpinner.setPreferredSize(new Dimension(75,20));
218 navigSpinner.addChangeListener(vrtxSpinnerListener);
219 navigPanel.add(navigationLabel1);
221 ctrlPane.add(navigPanel);
222
223 navigPanel2.add(navigationLabel2);
226
227 btnAddVrtx = new JButton("Add");
228 btnAddVrtx.setToolTipText("Append vertices taken from a file.");
229 btnAddVrtx.addActionListener(new ActionListener() {
230 public void actionPerformed(ActionEvent e) {
231 File inFile = GUIFileOpener.pickFile(btnAddVrtx);
232 if (inFile == null || inFile.getAbsolutePath().equals(""))
233 {
234 return;
235 }
236
237 ArrayList<Vertex> vrtxLib = new ArrayList<>();
238 try {
239 vrtxLib = DenoptimIO.readVertexes(inFile, BBType.FRAGMENT);
240 } catch (Exception e1) {
241 e1.printStackTrace();
242 JOptionPane.showMessageDialog(btnAddVrtx,
243 "<html>Could not read building blocks from file"
244 + "<br>'" + inFile + "'"
245 + "<br>Hint on cause: " + e1.getMessage()
246 +"</html>",
247 "Error",
248 JOptionPane.ERROR_MESSAGE,
249 UIManager.getIcon("OptionPane.errorIcon"));
250 return;
251 }
252
253 if (vrtxLib.size() == 0)
254 {
255 JOptionPane.showMessageDialog(btnAddVrtx,
256 "<html>No building blocks in file"
257 + "<br>'" + inFile + "'</html>",
258 "Error",
259 JOptionPane.ERROR_MESSAGE,
260 UIManager.getIcon("OptionPane.errorIcon"));
261 return;
262 }
263
264 if (vrtxLib.size() == 1)
265 {
266 importVertices(vrtxLib);
267 return;
268 }
269
270 String[] options = new String[]{"All",
271 "Selected",
272 "Cancel"};
273 String txt = "<html><body width='%1s'>Do you want to "
274 + "append all building blocks or only selected ones?"
275 + "</html>";
276 int res = JOptionPane.showOptionDialog(btnAddVrtx,
277 String.format(txt,200),
278 "Append Building Blocks",
279 JOptionPane.DEFAULT_OPTION,
280 JOptionPane.QUESTION_MESSAGE,
281 UIManager.getIcon("OptionPane.warningIcon"),
282 options,
283 options[0]);
284
285 if (res == 2)
286 {
287 return;
288 }
289
290 switch (res)
291 {
292 case 0:
293 importVertices(vrtxLib);
294 break;
295
296 case 1:
297 ArrayList<Vertex> selectedVrtxs =
298 new ArrayList<Vertex>();
299 GUIVertexSelector vrtxSelector = new GUIVertexSelector(
300 btnAddVrtx,true);
301 vrtxSelector.setRequireApSelection(false);
302 vrtxSelector.load(vrtxLib, 0);
303 Object selected = vrtxSelector.showDialog();
304
305 if (selected != null)
306 {
307 @SuppressWarnings("unchecked")
308 ArrayList<ArrayList<Integer>> selList =
309 (ArrayList<ArrayList<Integer>>) selected;
310 for (ArrayList<Integer> pair : selList)
311 {
312 selectedVrtxs.add(vrtxLib.get(pair.get(0)));
313 }
314 }
315 importVertices(selectedVrtxs);
316 break;
317
318 default:
319 return;
320 }
321 }
322 });
323 btnDelVrtx = new JButton("Remove");
324 btnDelVrtx.setToolTipText("Remove the present building block from the "
325 + "library.");
326 btnDelVrtx.addActionListener(new ActionListener() {
327 public void actionPerformed(ActionEvent e) {
328 try {
330 } catch (DENOPTIMException e1) {
331 System.out.println("Exception while removing the current "
332 + "building block:");
333 e1.printStackTrace();
334 }
335 }
336 });
340
341 ctrlPane.add(new JSeparator());
342
343 pnlImportStruct = new JPanel();
344 GroupLayout lyoImportStructure = new GroupLayout(pnlImportStruct);
345 JLabel lblImportStruct = new JLabel("Import a structure from");
346 btnOpenMol = new JButton("File");
347 btnOpenMol.setToolTipText("Imports a chemical system"
348 + " from file.");
349 btnOpenMol.addActionListener(new ActionListener() {
350 public void actionPerformed(ActionEvent e) {
351 File inFile = GUIFileOpener.pickFile(btnOpenMol);
352 if (inFile == null || inFile.getAbsolutePath().equals(""))
353 {
354 return;
355 }
357 }
358 });
359
360 // the '+' is to prevent search/replace of the string
361 btnOpenSMILES = new JButton("SMI"+"LES");
362 btnOpenSMILES.setToolTipText("<html>Imports chemical system"
363 + " from SMILES string.<br>The conversion of SMILES "
364 + "to 3D structure requires"
365 + "<br> an internet connection.</html>");
366 btnOpenSMILES.addActionListener(new ActionListener() {
367 public void actionPerformed(ActionEvent e) {
368 String smiles = JOptionPane.showInputDialog(btnOpenSMILES,
369 "Please input SMILES: ");
370 if (smiles != null && !smiles.trim().equals(""))
371 {
373 }
374 }
375 });
376
377 pnlImportStruct.setLayout(lyoImportStructure);
378 lyoImportStructure.setAutoCreateGaps(true);
379 lyoImportStructure.setAutoCreateContainerGaps(true);
380 lyoImportStructure.setHorizontalGroup(lyoImportStructure.createParallelGroup(
381 GroupLayout.Alignment.CENTER)
382 .addComponent(lblImportStruct)
383 .addGroup(lyoImportStructure.createSequentialGroup()
384 .addComponent(btnOpenMol)
385 .addComponent(btnOpenSMILES)));
386 lyoImportStructure.setVerticalGroup(lyoImportStructure.createSequentialGroup()
387 .addComponent(lblImportStruct)
388 .addGroup(lyoImportStructure.createParallelGroup()
389 .addComponent(btnOpenMol)
390 .addComponent(btnOpenSMILES)));
392
393 ctrlPane.add(new JSeparator());
394
395 pnlEmptFrag = new JPanel();
396 btnEmptFrag = new JButton("Create Empty Vertex");
397 btnEmptFrag.setToolTipText("<html>Creates an empty vertex:<br>a vertex "
398 + "that contains no molecular structure.<html>");
399 btnEmptFrag.addActionListener(new ActionListener() {
400 public void actionPerformed(ActionEvent e) {
401 GUIEmptyVertexMaker makeEmptyVertexDialog =
403 makeEmptyVertexDialog.pack();
404 Object ev = makeEmptyVertexDialog.showDialog();
405 if (ev == null)
406 {
407 return;
408 }
409 ArrayList<Vertex> lst = new ArrayList<Vertex>(1);
410 lst.add((EmptyVertex) ev);
411 GUIVertexSelector fragSelector = new GUIVertexSelector(
412 btnEmptFrag,false);
413 fragSelector.load(lst, 0);
414 fragSelector.btnDone.setText("Confirm");
415 fragSelector.ctrlPane.setVisible(false);
416 fragSelector.setRequireApSelection(false);
417 Object selected = fragSelector.showDialog();
418 if (selected == null)
419 {
420 return;
421 }
422 importVertices(lst);
423 }
424 });
427
428 ctrlPane.add(new JSeparator());
429
430 pnlAtmToAP = new JPanel();
431 btnAtmToAP = new JButton("Atom to AP");
432 btnAtmToAP.setToolTipText("<html>Replaces the selected atoms with "
433 + "attachment points.<br>Click on atoms to select"
434 + " them. Click again to unselect.<br>"
435 + "<br><b>WARNING:</b> this action cannot be undone!<html>");
436 btnAtmToAP.addActionListener(new ActionListener() {
437 public void actionPerformed(ActionEvent e) {
439
440 ArrayList<IAtom> selectedAtms =
442
443 if (selectedAtms.size() == 0)
444 {
445 JOptionPane.showMessageDialog(btnAtmToAP,
446 "<html>No atom selected! Click on atoms to select"
447 + " them.<br>Click again to unselect.</html>",
448 "Error",
449 JOptionPane.ERROR_MESSAGE,
450 UIManager.getIcon("OptionPane.errorIcon"));
451 return;
452 }
453 else
454 {
455 //TODO: ask about hapticity:
456 // if multihapto, then use all the selected atoms for 1 AP
457 // and ask to select another set for other end of bond to
458 // break.
459
460 List<APClass> selectedAPCs = choseOrCreateNewAPClass(
461 btnAtmToAP, true);
462
463 //The size of the list is either 0 or 1
464 if (selectedAPCs.size() == 0)
465 {
466 // We have pressed cancel or closed the dialog, so abandon
467 return;
468 }
469 String apClass = selectedAPCs.get(0).toString();
470
471 ArrayList<IAtom> failed = new ArrayList<IAtom>();
472 for (IAtom atm : selectedAtms)
473 {
474 if (!convertAtomToAP(atm, apClass))
475 {
476 failed.add(atm);
477 }
478 }
479 for (IAtom atm : failed)
480 {
481 selectedAtms.remove(atm);
482 }
483 if (selectedAtms.size() == 0)
484 {
485 return;
486 }
487
488 removeAtoms(selectedAtms);
489
491
492 unsavedChanges = true;
494 }
495 }
496 });
498 ctrlPane.add(pnlAtmToAP);
499
500 pnlChop = new JPanel();
501 btnChop = new JButton("Chop Structure");
502 btnChop.setToolTipText(String.format("<html><body width='%1s'>"
503 + "Applies cutting rules on "
504 + "the current structure to generate fragments.</html>", 400));
505 btnChop.addActionListener(new ActionListener() {
506 public void actionPerformed(ActionEvent event) {
508 if (vertex==null
509 || vertex.getIAtomContainer().getBondCount() == 0)
510 {
511 JOptionPane.showMessageDialog(btnChop,
512 "<html>System contains 0 bonds. "
513 + "Nothing to chop.</html>",
514 "Error",
515 JOptionPane.ERROR_MESSAGE,
516 UIManager.getIcon("OptionPane.errorIcon"));
517 return;
518 }
519
521 boolean result = dialogToDefineCuttingRules(
522 settings,
523 this.getClass().getClassLoader(),
524 btnChop,
525 false);
526 if (!result)
527 return;
528
529 String pathnameLastUsedCutRules =
531 if (pathnameLastUsedCutRules != null
532 && !pathnameLastUsedCutRules.isBlank())
533 {
534 GUIPreferences.lastCutRulesFile =
535 new File(pathnameLastUsedCutRules);
536 }
537
538 // Now chop the structure to produce fragments
539 List<Vertex> fragments;
540 try
541 {
542 fragments = FragmenterTools.fragmentation(
544 settings.getCuttingRules(),
545 settings.getLogger());
546 } catch (DENOPTIMException e)
547 {
548 JOptionPane.showMessageDialog(btnChop,String.format(
549 "<html><body width='%1s'"
550 + "Could not complete fragmentation. Hint: "
551 + e.getMessage() + "</html>", 400),
552 "Error",
553 JOptionPane.ERROR_MESSAGE,
554 UIManager.getIcon("OptionPane.errorIcon"));
555 return;
556 }
557
558 // Add linearity-breaking dummy atoms
559 for (Vertex frag : fragments)
560 {
562 settings.getLinearAngleLimit());
563 }
564
565 // Signal no result obtained
566 if (fragments.size() < 1 || (fragments.size() == 1 &&
567 ((Fragment)fragments.get(0)).isIsomorphicTo(vertex)))
568 {
569 JOptionPane.showMessageDialog(btnAddVrtx,
570 "<html>Fragmentation produced no fragments!</html>",
571 "Error",
572 JOptionPane.WARNING_MESSAGE,
573 UIManager.getIcon("OptionPane.warningIcon"));
574 return;
575 }
576
577 // The resulting fragments are loaded into the viewer, without
578 // removing the original structure.
579
580 String[] options = new String[]{"All",
581 "Select",
582 "Cancel"};
583 String txt = "<html><body width='%1s'>Fragmentation produced "
584 + fragments.size() + " fragments. Do you want to "
585 + "append all or select some?"
586 + "</html>";
587 int answer = JOptionPane.showOptionDialog(btnAddVrtx,
588 String.format(txt,200),
589 "Append Building Blocks",
590 JOptionPane.DEFAULT_OPTION,
591 JOptionPane.QUESTION_MESSAGE,
592 UIManager.getIcon("OptionPane.warningIcon"),
593 options,
594 options[0]);
595
596 if (answer == 2)
597 {
598 return;
599 }
600
601 switch (answer)
602 {
603 case 0:
604 importVertices(fragments);
605 break;
606
607 case 1:
608 List<Vertex> selectedVrtxs =
609 new ArrayList<Vertex>();
610 GUIVertexSelector vrtxSelector = new GUIVertexSelector(
611 btnAddVrtx,true);
612 vrtxSelector.setRequireApSelection(false);
613 vrtxSelector.load(fragments, 0);
614 Object selected = vrtxSelector.showDialog();
615
616 if (selected != null)
617 {
618 @SuppressWarnings("unchecked")
619 List<ArrayList<Integer>> selList =
620 (ArrayList<ArrayList<Integer>>) selected;
621 for (ArrayList<Integer> pair : selList)
622 {
623 selectedVrtxs.add(fragments.get(pair.get(0)));
624 }
625 }
626 importVertices(selectedVrtxs);
627 break;
628
629 default:
630 return;
631 }
632 }
633 });
634 pnlChop.add(btnChop);
635 ctrlPane.add(pnlChop);
636
637
638 pnlDelSel = new JPanel();
639 btnDelSel = new JButton("Remove Atoms");
640 btnDelSel.setToolTipText("<html>Removes all selected atoms from the "
641 + "system.<br><br><b>WARNING:</b> this action cannot be "
642 + "undone!");
643 btnDelSel.addActionListener(new ActionListener() {
644 public void actionPerformed(ActionEvent e) {
645 ArrayList<IAtom> selectedAtms =
647
648 if (selectedAtms.size() == 0)
649 {
650 JOptionPane.showMessageDialog(btnDelSel,
651 "<html>No atom selected! Click on atoms to select"
652 + " them.<br>Click again to unselect.</html>",
653 "Error",
654 JOptionPane.ERROR_MESSAGE,
655 UIManager.getIcon("OptionPane.errorIcon"));
656 return;
657 }
658 else
659 {
660 removeAtoms(selectedAtms);
661
663
664 unsavedChanges = true;
666 }
667 }
668 });
669 pnlDelSel.add(btnDelSel);
670 ctrlPane.add(pnlDelSel);
671
672 ctrlPane.add(new JSeparator());
673
674 pnlSaveEdits = new JPanel();
675 btnSaveEdits = new JButton("Save Changes");
676 //btnSaveEdits.setForeground(Color.RED);
677 btnSaveEdits.setEnabled(true);
678 btnSaveEdits.setToolTipText("<html>Save the current system replacing"
679 + " <br>the original one in the loaded library.</html>");
680 btnSaveEdits.addActionListener(new ActionListener() {
681 public void actionPerformed(ActionEvent e) {
683 }
684 });
687 this.add(ctrlPane, BorderLayout.EAST);
688
689
690 // Panel with buttons to the bottom of the frame
691 ButtonsBar commandsPane = new ButtonsBar();
692 super.add(commandsPane, BorderLayout.SOUTH);
693
694 btnOpenVrtxs = new JButton("Load Library of Building Blocks");
695 btnOpenVrtxs.setToolTipText("Reads building blocks or structures from "
696 + "file.");
697 btnOpenVrtxs.addActionListener(new ActionListener() {
698 public void actionPerformed(ActionEvent e) {
699 File inFile = GUIFileOpener.pickFile(btnOpenVrtxs);
700 if (inFile == null || inFile.getAbsolutePath().equals(""))
701 {
702 return;
703 }
704 ArrayList<Vertex> vrtxLib = new ArrayList<>();
705 try {
706 vrtxLib = DenoptimIO.readVertexes(inFile, BBType.FRAGMENT);
707 } catch (Exception e1) {
708 e1.printStackTrace();
709 JOptionPane.showMessageDialog(btnAddVrtx,
710 "<html>Could not read building blocks from file"
711 + "<br>'" + inFile + "'"
712 + "<br>Hint on cause: " + e1.getMessage()
713 +"</html>",
714 "Error",
715 JOptionPane.ERROR_MESSAGE,
716 UIManager.getIcon("OptionPane.errorIcon"));
717 return;
718 }
719
720 if (vrtxLib.size() == 0)
721 {
722 JOptionPane.showMessageDialog(btnAddVrtx,
723 "<html>No building blocks in file"
724 + "<br>'" + inFile + "'</html>",
725 "Error",
726 JOptionPane.ERROR_MESSAGE,
727 UIManager.getIcon("OptionPane.errorIcon"));
728 return;
729 }
730 importVertices(vrtxLib);
731 }
732 });
733 commandsPane.add(btnOpenVrtxs);
734
735 JButton btnSaveVrtxs = new JButton("Save Library of Building Blocks");
736 btnSaveVrtxs.setToolTipText("Write all building blocks to a file.");
737 btnSaveVrtxs.addActionListener(new ActionListener() {
738 public void actionPerformed(ActionEvent e) {
739 FileAndFormat fileAndFormat =
741 if (fileAndFormat == null)
742 {
743 return;
744 }
745 File outFile = fileAndFormat.file;
746 try
747 {
748 // The writing method may change the extension. So we need
749 // to get the return value.
750 outFile = DenoptimIO.writeVertexesToFile(outFile,
751 fileAndFormat.format,
753 }
754 catch (Exception ex)
755 {
756 ex.printStackTrace();
757 JOptionPane.showMessageDialog(btnSaveVrtxs,
758 "Could not write to '" + outFile + "'! "
759 + "Hint: "+ex.getMessage(),
760 "Error",
761 JOptionPane.PLAIN_MESSAGE,
762 UIManager.getIcon("OptionPane.errorIcon"));
763 return;
764 }
765 navigSpinner.setModel(new SpinnerNumberModel(currVrtxIdx+1, 1,
766 verticesLibrary.size(), 1));
768 unsavedChanges = false;
769 FileUtils.addToRecentFiles(outFile, fileAndFormat.format);
770 }
771 });
772 commandsPane.add(btnSaveVrtxs);
773
774 JButton btnCanc = new JButton("Close Tab");
775 btnCanc.setToolTipText("Closes this tab.");
776 btnCanc.addActionListener(new removeCardActionListener(this));
777 commandsPane.add(btnCanc);
778
779 JButton btnHelp = new JButton("?");
780 btnHelp.setToolTipText("<html>Hover over the buttons and fields "
781 + "to get a tip.</html>");
782 btnHelp.addActionListener(new ActionListener() {
783 public void actionPerformed(ActionEvent e) {
784 String txt = "<html><body width='%1s'>"
785 + "<p>This tab allows to create, inspect, and edit "
786 + "building blocks and "
787 + "three-dimensional molecular fragments.</p>"
788 + "<p>New fragments can be created starting from any "
789 + "chemical structure that can be loaded from file or "
790 + "generated from SMILES (SMILES-to-3D conversion "
791 + "requires an Internet connection).</p>"
792 + "<p>Any terminal atom (i.e., atoms that have only "
793 + "one connected neighbor) can be transformed into "
794 + "on attachment point (AP). Click on the atom to "
795 + "select it, and press <code><b>Atom to AP</b></code>."
796 + "</p>"
797 + "<p>Attachment points are depicted in the molecular "
798 + "viewer as yellow arrows in the 3D space, and their "
799 + "attachment point class (APClass) is specified in "
800 + "the table below the viewer. Double-click on a "
801 + "specific APClass field to change its value.</p>"
802 + "<br>"
803 + "<p>Hover over buttons get a tip.</p>"
804 + "<br>"
805 + "<p>Right-click on the Jmol viewer will open the "
806 + "Jmol menu. However, since Jmol cannot handle the "
807 + "attachment points data. Therefore, Jmol "
808 + "functionality should only be used on systems "
809 + "that have no attachment points, or for alterations "
810 + "of the molecular structure that do not change the "
811 + "list of atoms au to the last atom decorated with an "
812 + "attachment point.</p></html>";
813 JOptionPane.showMessageDialog(btnHelp,
814 String.format(txt, 400),
815 "Tips",
816 JOptionPane.PLAIN_MESSAGE);
817 }
818 });
819 commandsPane.add(btnHelp);
820 }
821
822//-----------------------------------------------------------------------------
823
834 public static boolean dialogToDefineCuttingRules(
835 FragmenterParameters settings, ClassLoader classLoader,
836 Component parent,
837 boolean setMolToGraphSettings)
838 {
839 settings.startConsoleLogger("GUI-controlledFragmenterLogger");
840
841 List<CuttingRule> defaultCuttingRules = new ArrayList<CuttingRule>();
842 BufferedReader reader = null;
843 try
844 {
845 try {
846 reader = new BufferedReader(
847 new InputStreamReader(classLoader.getResourceAsStream(
848 "data/cutting_rules")));
849 DenoptimIO.readCuttingRules(reader, defaultCuttingRules,
850 "bundled jar");
851 } finally {
852 if (reader!=null)
853 reader.close();
854 }
855 } catch (Exception e )
856 {
857 e.printStackTrace();
858 JOptionPane.showMessageDialog(parent,String.format(
859 "<html><body width='%1s'>"
860 + "Could not read default cutting rules from "
861 + "bundled jar. "
862 + "Hint: "
863 + e.getMessage() + "</html>", 400),
864 "Error",
865 JOptionPane.ERROR_MESSAGE,
866 UIManager.getIcon("OptionPane.errorIcon"));
867 return false;
868 }
869
870 // Read last used cutting rules
871 List<CuttingRule> customCuttingRules = new ArrayList<CuttingRule>();
872 boolean useDefaultCuttingRules = true;
873 try
874 {
876 {
879 customCuttingRules);
880 useDefaultCuttingRules = false;
881 }
882 } catch (DENOPTIMException e)
883 {
884 JOptionPane.showMessageDialog(parent,String.format(
885 "<html><body width='%1s'"
886 + "Could not read last-used cutting rules from '"
888 + "Hint: "
889 + e.getMessage() + "</html>", 400),
890 "Error",
891 JOptionPane.ERROR_MESSAGE,
892 UIManager.getIcon("OptionPane.errorIcon"));
893 return false;
894 }
895
896 // Build a dialog that offers the possibility to see and edit
897 // default cutting rules, and to define custom ones from scratch
899 if (setMolToGraphSettings)
900 {
902 defaultCuttingRules, customCuttingRules,
903 useDefaultCuttingRules, parent, settings);
904 } else {
906 defaultCuttingRules, customCuttingRules,
907 useDefaultCuttingRules, parent, settings);
908 }
909 crs.pack();
910 crs.setVisible(true);
911
912 if (crs.result==null)
913 return false;
914
915 String pathnameLastUsedCutRules = settings.getCuttingRulesFilePathname();
916 if (pathnameLastUsedCutRules != null
917 && !pathnameLastUsedCutRules.isBlank())
918 {
919 GUIPreferences.lastCutRulesFile = new File(pathnameLastUsedCutRules);
920 }
921
922 return true;
923 }
924
925//-----------------------------------------------------------------------------
926
927 public void importStructureFromFile(File file)
928 {
929 this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
930
931 // Cleanup
933
934 try {
935 for (IAtomContainer mol : DenoptimIO.readAllAtomContainers(file))
936 {
937 // We mean to import only the structure: get rid of AP
938 mol.setProperty(DENOPTIMConstants.APSTAG,null);
939
940 // NB: here we let the vertexViewer create a fragment object that we
941 // then put into the local library. This to make sure that the
942 // references to atoms selected in the viewer are referring to
943 // members of the "vertex" object
946
947 // the system is not a fragment but, this is done for consistency:
948 // when we have a molecule loaded the list is not empty
949 // The currently viewed fragment (if any) is always part of the lib
951 currVrtxIdx = verticesLibrary.size()-1;
952 }
954 unsavedChanges = true;
955 btnDelSel.setEnabled(true);
956 btnAtmToAP.setEnabled(true);
957 } catch (Exception e) {
958 this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
959 e.printStackTrace();
960 JOptionPane.showMessageDialog(this,
961 "<html>Could not read file '" + file.getAbsolutePath()
962 + "'!<br>Hint about reason: " + e.getCause() + "</html>",
963 "Error",
964 JOptionPane.PLAIN_MESSAGE,
965 UIManager.getIcon("OptionPane.errorIcon"));
966 }
967
968 this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
969 }
970
971//-----------------------------------------------------------------------------
972
979 public void importStructureFromSMILES(String smiles)
980 {
981 this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
982
983 // Cleanup
985
986 // Load the structure using CACTUS service or CDK builder
987 try {
988 vertexViewer.loadSMILES(smiles);
989 } catch (Exception e) {
990 this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
991 return;
992 }
993
995
996 // The system is not a fragment but, this is done for consistency:
997 // when we have a molecule loaded the list is not empty:
998 // The currently viewed fragment (if any) is always part of the library
1000 currVrtxIdx = verticesLibrary.size()-1;
1001
1002 // finalize GUI status
1004 unsavedChanges = true;
1005 btnDelSel.setEnabled(true);
1006 btnAtmToAP.setEnabled(true);
1007 btnSaveEdits.setEnabled(true);
1008
1009 this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1010 }
1011
1012//-----------------------------------------------------------------------------
1013
1018 public void importVerticesFromFile(File file)
1019 {
1020 this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
1021
1022 ArrayList<Vertex> vrtxLib = new ArrayList<>();
1023 try {
1024 vrtxLib = DenoptimIO.readVertexes(file, BBType.FRAGMENT);
1025 } catch (Exception e1) {
1026 e1.printStackTrace();
1027 JOptionPane.showMessageDialog(btnAddVrtx,
1028 "<html>Could not read building blocks from file"
1029 + "<br>'" + file + "'"
1030 + "<br>Hint on cause: " + e1.getMessage()
1031 +"</html>",
1032 "Error",
1033 JOptionPane.ERROR_MESSAGE,
1034 UIManager.getIcon("OptionPane.errorIcon"));
1035 return;
1036 }
1037
1038 if (vrtxLib.size() == 0)
1039 {
1040 JOptionPane.showMessageDialog(btnAddVrtx,
1041 "<html>No building blocks in file"
1042 + "<br>'" + file + "'</html>",
1043 "Error",
1044 JOptionPane.ERROR_MESSAGE,
1045 UIManager.getIcon("OptionPane.errorIcon"));
1046 return;
1047 }
1048 importVertices(vrtxLib);
1049 this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1050 }
1051
1052//-----------------------------------------------------------------------------
1053
1058 public void importVertices(List<Vertex> fragments)
1059 {
1060 this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
1061
1062 int firstOfNew = 0;
1063 boolean libFromScrtch = false;
1064 if (verticesLibrary == null)
1065 {
1066 libFromScrtch = true;
1067 verticesLibrary = new ArrayList<Vertex>();
1068 }
1069 else
1070 {
1071 firstOfNew = verticesLibrary.size();
1072 }
1073
1074 boolean addedOne = false;
1075 if (fragments.size() > 0)
1076 {
1077 verticesLibrary.addAll(fragments);
1078 addedOne = true;
1079
1080 // Display the first
1081 if (libFromScrtch)
1082 {
1083 currVrtxIdx = 0;
1084 }
1085 else if (addedOne)
1086 {
1087 currVrtxIdx = firstOfNew;
1088 }
1090
1091 // Update the fragment spinner
1093 } else {
1094 this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1095 JOptionPane.showMessageDialog(this,
1096 "<html>No vertices to import from the given list.</html>",
1097 "Error",
1098 JOptionPane.PLAIN_MESSAGE,
1099 UIManager.getIcon("OptionPane.errorIcon"));
1100 }
1101 this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1102 }
1103
1104
1105//-----------------------------------------------------------------------------
1106
1115 {
1116 if (verticesLibrary == null)
1117 {
1118 JOptionPane.showMessageDialog(this,
1119 "No list of building blocks loaded.",
1120 "Error",
1121 JOptionPane.PLAIN_MESSAGE,
1122 UIManager.getIcon("OptionPane.errorIcon"));
1123 return;
1124 }
1125
1127
1130 if (vertex == null || vertex instanceof Fragment == false)
1131 {
1132 btnDelSel.setEnabled(false);
1133 btnAtmToAP.setEnabled(false);
1134 } else {
1135 btnDelSel.setEnabled(true);
1136 btnAtmToAP.setEnabled(true);
1137 }
1138 }
1139
1140//-----------------------------------------------------------------------------
1141
1142 private void clearCurrentSystem()
1143 {
1144 // Get rid of currently loaded mol
1145 vertex = null;
1147 }
1148
1149//-----------------------------------------------------------------------------
1150
1152 {
1153 navigSpinner.setModel(new SpinnerNumberModel(currVrtxIdx+1, 1,
1154 verticesLibrary.size(), 1));
1155 totalVrtxsLabel.setText(Integer.toString(verticesLibrary.size()));
1156 }
1157
1158//-----------------------------------------------------------------------------
1159
1169 private boolean convertAtomToAP(IAtom trgAtm, String ruleAndSubClass)
1170 {
1171 if (!(vertex instanceof Fragment))
1172 {
1173 return false;
1174 }
1175 Fragment frag = (Fragment) vertex;
1176 // Accept ONLY if the atom has one and only one connected neighbour
1177 if (frag.getConnectedAtomsCount(trgAtm) != 1)
1178 {
1179 String str = "";
1180 for (IAtom atm : frag.getConnectedAtomsList(trgAtm))
1181 {
1182 str = str + " " + atm.getSymbol()
1183 + (frag.indexOf(atm));
1184 }
1185 System.out.println("Connected atoms: "+str);
1186
1187 JOptionPane.showMessageDialog(this,
1188 "<html>Atom "+ trgAtm.getSymbol()
1189 + (frag.indexOf(trgAtm))
1190 + " has zero or more than one neighbour.<br>I can only "
1191 + "transform atoms"
1192 + " that have one and only one neighbour.</html>",
1193 "Error",
1194 JOptionPane.ERROR_MESSAGE,
1195 UIManager.getIcon("OptionPane.errorIcon"));
1196 return false;
1197 }
1198
1199 IAtom srcAtm = frag.getConnectedAtomsList(trgAtm).get(0);
1200 BondType bt = BondType.valueOf(
1201 srcAtm.getBond(trgAtm).getOrder().toString());
1202
1203 Point3d srcP3d = MoleculeUtils.getPoint3d(srcAtm);
1204 Point3d trgP3d = MoleculeUtils.getPoint3d(trgAtm);
1205 Point3d vector = new Point3d();
1206 vector.x = srcP3d.x + (trgP3d.x - srcP3d.x);
1207 vector.y = srcP3d.y + (trgP3d.y - srcP3d.y);
1208 vector.z = srcP3d.z + (trgP3d.z - srcP3d.z);
1209
1210 //NB: assumption of validity!
1211 String[] parts = ruleAndSubClass.split(
1213 try {
1214 // NB: here we change the bond type to make it fit with the one we
1215 // have in the molecular model.
1216 frag.addAPOnAtom(srcAtm, APClass.make(parts[0],
1217 Integer.parseInt(parts[1]), bt), vector);
1218 } catch (DENOPTIMException e) {
1219 e.printStackTrace();
1220 JOptionPane.showMessageDialog(this,
1221 "<html>Could not make AP.<br>Possible cause: "
1222 + e.getMessage() +"</html>",
1223 "Error",
1224 JOptionPane.ERROR_MESSAGE,
1225 UIManager.getIcon("OptionPane.errorIcon"));
1226 return false;
1227 }
1228 return true;
1229 }
1230
1231//----------------------------------------------------------------------------
1232
1233 private void removeAtoms(ArrayList<IAtom> atmsToDels)
1234 {
1235 if (!(vertex instanceof Fragment))
1236 {
1237 return;
1238 }
1239 Fragment frag = (Fragment) vertex;
1240 ArrayList<IBond> bnsToDel = new ArrayList<IBond>();
1241 for (IAtom atm : atmsToDels)
1242 {
1243 for (IBond bnd : frag.bonds())
1244 {
1245 if (bnd.contains(atm))
1246 {
1247 bnsToDel.add(bnd);
1248 }
1249 }
1250 }
1251 for (IBond bnd : bnsToDel)
1252 {
1253 frag.removeBond(bnd);
1254 }
1255 for (IAtom atm : atmsToDels)
1256 {
1257 if (atm.getProperty(DENOPTIMConstants.ATMPROPAPS)!=null)
1258 {
1259 ArrayList<AttachmentPoint> apsOnAtm = frag.getAPsFromAtom(atm);
1260 frag.getAttachmentPoints().removeAll(apsOnAtm);
1261 }
1262 frag.removeAtom(atm);
1263 }
1264 frag.updateAPs();
1265 }
1266
1267//-----------------------------------------------------------------------------
1268
1269 private class VrtxSpinnerChangeEvent implements ChangeListener
1270 {
1271 private boolean inEnabled = true;
1272
1274 {}
1275
1281 public void setEnabled(boolean var)
1282 {
1283 this.inEnabled = var;
1284 }
1285
1286 @Override
1287 public void stateChanged(ChangeEvent event)
1288 {
1289 if (!inEnabled)
1290 {
1291 return;
1292 }
1293
1295
1296 //NB here we convert from 1-based index in GUI to 0-based index
1297 currVrtxIdx = ((Integer) navigSpinner.getValue()).intValue() - 1;
1299
1301 }
1302 }
1303
1304//-----------------------------------------------------------------------------
1305
1307 {
1308 //btnSaveEdits.setEnabled(false);
1309 btnAddVrtx.setEnabled(true);
1310 btnOpenVrtxs.setEnabled(true);
1311 btnOpenSMILES.setEnabled(true);
1312 btnOpenMol.setEnabled(true);
1313 btnEmptFrag.setEnabled(true);
1314 if (vertex == null || vertex instanceof Fragment == false)
1315 {
1316 btnDelSel.setEnabled(false);
1317 btnAtmToAP.setEnabled(false);
1318 } else {
1319 btnDelSel.setEnabled(true);
1320 btnAtmToAP.setEnabled(true);
1321 }
1322
1323 ((DefaultEditor) navigSpinner.getEditor())
1324 .getTextField().setEditable(true);
1325 ((DefaultEditor) navigSpinner.getEditor())
1326 .getTextField().setForeground(Color.BLACK);
1329
1331 }
1332
1333//-----------------------------------------------------------------------------
1334
1335 private void protectEditedSystem()
1336 {
1337 btnSaveEdits.setEnabled(true);
1338 btnAddVrtx.setEnabled(false);
1339 btnOpenVrtxs.setEnabled(false);
1340 btnOpenSMILES.setEnabled(false);
1341 btnOpenMol.setEnabled(false);
1342 btnEmptFrag.setEnabled(false);
1343 //btnDelSel.setEnabled(false);
1344 //btnAtmToAP.setEnabled(false);
1345
1346 navigSpinner.setModel(new SpinnerNumberModel(currVrtxIdx+1,
1347 currVrtxIdx+1, currVrtxIdx+1, 1));
1348 ((DefaultEditor) navigSpinner.getEditor())
1349 .getTextField().setEditable(false);
1350 ((DefaultEditor) navigSpinner.getEditor())
1351 .getTextField().setForeground(Color.GRAY);
1352
1354
1356 }
1357
1358//-----------------------------------------------------------------------------
1359
1360 private void activateTabEditsListener(boolean var)
1361 {
1363 }
1364
1365//-----------------------------------------------------------------------------
1366
1368 {
1370 {
1371 String[] options = new String[]{"Yes","No"};
1372 int res = JOptionPane.showOptionDialog(this,
1373 "<html>Removing unsaved vertex?",
1374 "Warning",
1375 JOptionPane.DEFAULT_OPTION,
1376 JOptionPane.QUESTION_MESSAGE,
1377 UIManager.getIcon("OptionPane.warningIcon"),
1378 options,
1379 options[1]);
1380 if (res == 1)
1381 {
1382 return;
1383 }
1384 }
1385
1387
1388 // Actual removal from the library
1389 if (verticesLibrary.size()>0)
1390 {
1392 int libSize = verticesLibrary.size();
1393
1394 if (currVrtxIdx>=0 && currVrtxIdx<libSize)
1395 {
1396 //we keep currFrgIdx as it will correspond to the next item
1397 }
1398 else
1399 {
1401 }
1402
1403 if (currVrtxIdx==-1 || verticesLibrary.size()==0)
1404 {
1405 // The viewer gets hidden so we do not need to clear it
1406 // with 'zap' (which is a very slow operation)
1408 currVrtxIdx = 0;
1409 navigSpinner.setModel(new SpinnerNumberModel(0,0,0,1));
1410 totalVrtxsLabel.setText(Integer.toString(0));
1412 }
1413 else
1414 {
1417 navigSpinner.setModel(new SpinnerNumberModel(currVrtxIdx+1, 1,
1418 verticesLibrary.size(), 1));
1420 }
1421 }
1422 }
1423
1424//-----------------------------------------------------------------------------
1425
1426 private void saveUnsavedChanges()
1427 {
1428 this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
1429
1431 {
1432 DefaultTableModel tabModel = vertexViewer.getAPTableModel();
1433 // Import changes from AP table into molecular representation
1434 for (int i=0; i<tabModel.getRowCount(); i++)
1435 {
1436 int apId = ((Integer) tabModel.getValueAt(i, 0)).intValue();
1437 String currApClass = tabModel.getValueAt(i, 1).toString();
1438
1439 // Make sure the new class has a proper syntax
1440 GUIAPClassDefinitionDialog apcDefiner =
1442 apcDefiner.setTitle("Confirm APClass on AP #"+i);
1443 apcDefiner.setPreDefinedAPClass(currApClass);
1444 Object chosen = apcDefiner.showDialog();
1445 if (chosen != null)
1446 {
1447 Object[] pair = (Object[]) chosen;
1448 currApClass = pair[0].toString();
1449 } else {
1450 currApClass = "dafaultAPClass:0";
1451 }
1452
1453 Map<Integer, AttachmentPoint> mapAPs =
1455
1456 if (mapAPs.containsKey(apId))
1457 {
1458 String origApClass = mapAPs.get(apId).getAPClass().toString();
1459 if (!origApClass.equals(currApClass))
1460 {
1461 try {
1462 mapAPs.get(apId).setAPClass(currApClass);
1463 } catch (DENOPTIMException e) {
1464 // We made sure the class is valid, so this
1465 // should never happen, though one never knows
1466 e.printStackTrace();
1467 JOptionPane.showMessageDialog(btnSaveEdits,
1468 "<html>Could not save due to errors setting a "
1469 + "new APClass.<br>Please report this to the "
1470 + "DENOPTIM team.</html>",
1471 "Error",
1472 JOptionPane.PLAIN_MESSAGE,
1473 UIManager.getIcon("OptionPane.errorIcon"));
1474 return;
1475 }
1476 }
1477 }
1478 else
1479 {
1480 JOptionPane.showMessageDialog(btnSaveEdits,
1481 "<html>Could not save due to mistmatch between AP "
1482 + "table and map.<br>Please report this to the "
1483 + "DENOPTIM team.</html>",
1484 "Error",
1485 JOptionPane.PLAIN_MESSAGE,
1486 UIManager.getIcon("OptionPane.errorIcon"));
1487 return;
1488 }
1489 }
1490 }
1491
1492 // Retrieve chemical object from the viewer, if edited, otherwise
1493 // we get what is already in 'vertex'
1495 if (verticesLibrary.size()==0
1496 && (vertex==null || vertex.getNumberOfAPs()==0))
1497 {
1498 //Nothing to same
1499 this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1500 return;
1501 }
1503
1504 // Reload fragment from library to refresh table and viewer
1506 try {
1508 } catch (Throwable t) {
1509 //This can happen if the viewer has been started but is empty
1510 // E.G:, if the cactvs server is down).
1511 // We just keep going, and make sure we get the default cursor back.
1512 }
1513 // Release constraints
1515 navigSpinner.setModel(new SpinnerNumberModel(currVrtxIdx+1, 1,
1516 verticesLibrary.size(), 1));
1518
1519 this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1520 }
1521
1522//----------------------------------------------------------------------------
1523
1532 public static List<APClass> choseOrCreateNewAPClass(JComponent parent,
1533 boolean singleSelection)
1534 {
1535 // To facilitate selection of existing APCs we offer a list...
1536 DefaultListModel<String> apClassLstModel =
1537 new DefaultListModel<String>();
1538 JList<String> apClassList = new JList<String>(apClassLstModel);
1539 for (String apc : APClass.getAllAPClassesAsString())
1540 {
1541 apClassLstModel.addElement(apc);
1542 }
1543 //...and to the list we add the option to create a new APClass.
1544 apClassLstModel.addElement(
1545 "<html><b><i>Define a new APClass...<i></b></html>");
1546 if (singleSelection)
1547 {
1548 apClassList.setSelectionMode(
1549 ListSelectionModel.SINGLE_SELECTION);
1550 } else {
1551 apClassList.setSelectionMode(
1552 ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
1553 }
1554 if (apClassList.getModel().getSize() == 1)
1555 {
1556 apClassList.setSelectedIndex(0);
1557 } else {
1558 apClassList.setSelectedIndex(apClassLstModel.getSize()-1);
1559 }
1560
1561 //Make and launch dialog for the user to make the selection
1562 JPanel chooseApPanel = new JPanel();
1563 JLabel header = new JLabel("Choose APClass:");
1564 JScrollPane apClassScroll = new JScrollPane(apClassList);
1565 chooseApPanel.add(header);
1566 chooseApPanel.add(apClassScroll);
1567
1568 int res = JOptionPane.showConfirmDialog(parent,
1569 chooseApPanel,
1570 "Choose APClasses to Add",
1571 JOptionPane.OK_CANCEL_OPTION,
1572 JOptionPane.PLAIN_MESSAGE,
1573 null);
1574 if (res != JOptionPane.OK_OPTION)
1575 {
1576 return new ArrayList<APClass>();
1577 }
1578
1579 // Interpret the selection made by the user
1580 ArrayList<APClass> selectedSPCs = new ArrayList<APClass>();
1581 int[] selectedIds = apClassList.getSelectedIndices();
1582 if (selectedIds.length > 0)
1583 {
1584 for (int ii=0; ii<selectedIds.length; ii++)
1585 {
1586 APClass apc = null;
1587 Integer idAPC = selectedIds[ii];
1588
1589 try {
1590 if (idAPC.intValue() == (apClassLstModel.size()-1))
1591 {
1592 // We chose to create a new class
1593 GUIAPClassDefinitionDialog apcDefiner =
1594 new GUIAPClassDefinitionDialog(parent, false);
1595 Object chosen = apcDefiner.showDialog();
1596 if (chosen != null)
1597 {
1598 Object[] pair = (Object[]) chosen;
1599 apc = APClass.make(pair[0].toString(),
1600 (BondType) pair[1]);
1601 }
1602 } else {
1603 apc = APClass.make(apClassLstModel.getElementAt(idAPC));
1604 }
1605 } catch (Exception e1) {
1606 // We have pressed cancel or closed the dialog: abandon
1607 continue;
1608 }
1609 selectedSPCs.add(apc);
1610 }
1611 }
1612 return selectedSPCs;
1613 }
1614
1615//-----------------------------------------------------------------------------
1616
1625 public static String ensureGoodAPRuleString(String currApRule,
1626 String title, boolean mustReply, JComponent parent)
1627 throws DENOPTIMException
1628 {
1629 String preStr = "";
1630 while (!APClass.isValidAPRuleString(currApRule))
1631 {
1632 if (currApRule != "")
1633 {
1634 preStr = "APRule '" + currApRule + "' is not valid!<br>"
1635 + "The valid syntax for APClass is:<br><br><code>APRule"
1636 + DENOPTIMConstants.SEPARATORAPPROPSCL
1637 + "subClass</code><br><br> where "
1638 + "<ul><li><code>APRule</code>"
1639 + " is the string you should provide now, and is "
1640 + "typically any string with no spaces,</li>"
1641 + "<li><code>subClass</code> is an integer.</ul>";
1642 }
1643
1644 currApRule = JOptionPane.showInputDialog(parent,String.format(
1645 "<html><body width='%1s'>" + preStr
1646 + " Please, provide a valid APClass rule string: </html>",
1647 300),
1648 title,
1649 JOptionPane.PLAIN_MESSAGE);
1650
1651 if (currApRule == null)
1652 {
1653 currApRule = "";
1654 if (!mustReply)
1655 {
1656 throw new DENOPTIMException();
1657 }
1658 }
1659 }
1660
1661 return currApRule;
1662 }
1663
1664//-----------------------------------------------------------------------------
1665
1671 public boolean hasUnsavedChanges()
1672 {
1673 return unsavedChanges;
1674 }
1675
1676//-----------------------------------------------------------------------------
1677
1678 /*
1679 * This is needed to stop Jmol threads upon closure of this gui card.
1680 */
1681 public void dispose()
1682 {
1684 }
1685
1686//-----------------------------------------------------------------------------
1687
1688}
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:303
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:396
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:136
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:1120
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< 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.
Parameters controlling execution of the fragmenter.
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.