2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
22 package jalview.jbgui;
24 import jalview.datamodel.SequenceI;
25 import jalview.fts.api.FTSDataColumnI;
26 import jalview.fts.core.FTSDataColumnPreferences;
27 import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource;
28 import jalview.fts.service.pdb.PDBFTSRestClient;
29 import jalview.gui.AlignmentPanel;
30 import jalview.gui.Desktop;
31 import jalview.gui.JvSwingUtils;
32 import jalview.gui.StructureViewer;
33 import jalview.util.MessageManager;
35 import java.awt.BorderLayout;
36 import java.awt.CardLayout;
37 import java.awt.Component;
38 import java.awt.Dimension;
39 import java.awt.FlowLayout;
41 import java.awt.GridLayout;
42 import java.awt.event.ActionEvent;
43 import java.awt.event.ItemEvent;
44 import java.awt.event.ItemListener;
45 import java.awt.event.KeyAdapter;
46 import java.awt.event.KeyEvent;
47 import java.awt.event.MouseAdapter;
48 import java.awt.event.MouseEvent;
49 import java.util.Arrays;
50 import java.util.HashMap;
53 import javax.swing.ImageIcon;
54 import javax.swing.JButton;
55 import javax.swing.JCheckBox;
56 import javax.swing.JComboBox;
57 import javax.swing.JFrame;
58 import javax.swing.JInternalFrame;
59 import javax.swing.JLabel;
60 import javax.swing.JList;
61 import javax.swing.JPanel;
62 import javax.swing.JScrollPane;
63 import javax.swing.JSeparator;
64 import javax.swing.JTabbedPane;
65 import javax.swing.JTable;
66 import javax.swing.JTextField;
67 import javax.swing.ListCellRenderer;
68 import javax.swing.event.ChangeEvent;
69 import javax.swing.event.ChangeListener;
70 import javax.swing.event.DocumentEvent;
71 import javax.swing.event.DocumentListener;
72 import javax.swing.event.InternalFrameEvent;
73 import javax.swing.table.TableColumn;
75 import net.miginfocom.swing.MigLayout;
77 @SuppressWarnings("serial")
79 * GUI layout for structure chooser
84 public abstract class GStructureChooser extends JPanel
85 implements ItemListener
87 private static final Font VERDANA_12 = new Font("Verdana", 0, 12);
89 protected static final String VIEWS_FILTER = "VIEWS_FILTER";
91 protected static final String VIEWS_FROM_FILE = "VIEWS_FROM_FILE";
93 protected static final String VIEWS_ENTER_ID = "VIEWS_ENTER_ID";
96 * 'cached' structure view
98 protected static final String VIEWS_LOCAL_PDB = "VIEWS_LOCAL_PDB";
100 protected JPanel statusPanel = new JPanel();
102 public JLabel statusBar = new JLabel();
104 protected String frameTitle = MessageManager
105 .getString("label.structure_chooser");
107 protected JInternalFrame mainFrame = new JInternalFrame(frameTitle);
109 protected JComboBox<FilterOption> cmb_filterOption = new JComboBox<>();
111 protected AlignmentPanel ap;
113 protected StringBuilder errorWarning = new StringBuilder();
115 protected JButton btn_add;
117 protected JButton btn_newView;
119 protected JButton btn_pdbFromFile = new JButton();
121 protected JCheckBox chk_superpose = new JCheckBox(
122 MessageManager.getString("label.superpose_structures"));
124 protected JTextField txt_search = new JTextField(14);
126 protected JPanel pnl_switchableViews = new JPanel(new CardLayout());
128 protected CardLayout layout_switchableViews = (CardLayout) (pnl_switchableViews
131 protected JCheckBox chk_invertFilter = new JCheckBox(
132 MessageManager.getString("label.invert"));
134 protected ImageIcon loadingImage = new ImageIcon(
135 getClass().getResource("/images/loading.gif"));
137 protected ImageIcon goodImage = new ImageIcon(
138 getClass().getResource("/images/good.png"));
140 protected ImageIcon errorImage = new ImageIcon(
141 getClass().getResource("/images/error.png"));
143 protected ImageIcon warningImage = new ImageIcon(
144 getClass().getResource("/images/warning.gif"));
146 protected JLabel lbl_loading = new JLabel(loadingImage);
148 protected JLabel lbl_pdbManualFetchStatus = new JLabel(errorImage);
150 protected JLabel lbl_fromFileStatus = new JLabel(errorImage);
152 protected AssociateSeqPanel idInputAssSeqPanel = new AssociateSeqPanel();
154 protected AssociateSeqPanel fileChooserAssSeqPanel = new AssociateSeqPanel();
156 protected JComboBox<StructureViewer> targetView = new JComboBox<>();
158 protected JTable tbl_local_pdb = new JTable();
160 protected JTabbedPane pnl_filter = jalview.jbgui.GDesktop.createTabbedPane();
162 protected FTSDataColumnPreferences pdbDocFieldPrefs = new FTSDataColumnPreferences(
163 PreferenceSource.STRUCTURE_CHOOSER,
164 PDBFTSRestClient.getInstance());
166 protected FTSDataColumnI[] previousWantedFields;
168 protected static Map<String, Integer> tempUserPrefs = new HashMap<>();
170 private JTable tbl_summary = new JTable()
172 private boolean inLayout;
175 public boolean getScrollableTracksViewportWidth()
177 return hasExcessWidth();
182 public void doLayout()
184 if (hasExcessWidth())
186 autoResizeMode = AUTO_RESIZE_SUBSEQUENT_COLUMNS;
191 autoResizeMode = AUTO_RESIZE_OFF;
194 protected boolean hasExcessWidth()
196 return getPreferredSize().width < getParent().getWidth();
200 public void columnMarginChanged(ChangeEvent e)
206 TableColumn resizingColumn = getTableHeader().getResizingColumn();
207 // Need to do this here, before the parent's
208 // layout manager calls getPreferredSize().
209 if (resizingColumn != null && autoResizeMode == AUTO_RESIZE_OFF
212 resizingColumn.setPreferredWidth(resizingColumn.getWidth());
213 String colHeader = resizingColumn.getHeaderValue().toString();
214 tempUserPrefs.put(colHeader, resizingColumn.getWidth());
220 public String getToolTipText(MouseEvent evt)
222 String toolTipText = null;
223 java.awt.Point pnt = evt.getPoint();
224 int rowIndex = rowAtPoint(pnt);
225 int colIndex = columnAtPoint(pnt);
229 if (getValueAt(rowIndex, colIndex) == null)
233 toolTipText = getValueAt(rowIndex, colIndex).toString();
234 } catch (Exception e)
236 // e.printStackTrace();
238 toolTipText = (toolTipText == null ? null
239 : (toolTipText.length() > 500
240 ? JvSwingUtils.wrapTooltip(true,
241 "\"" + toolTipText.subSequence(0, 500)
243 : JvSwingUtils.wrapTooltip(true, toolTipText)));
248 public GStructureChooser()
253 mainFrame.setVisible(false);
254 mainFrame.invalidate();
256 } catch (Exception e)
258 System.out.println(e); // for JavaScript TypeError
264 * Initializes the GUI default properties
268 private void jbInit() throws Exception
270 Integer width = tempUserPrefs.get("structureChooser.width") == null
272 : tempUserPrefs.get("structureChooser.width");
273 Integer height = tempUserPrefs.get("structureChooser.height") == null
275 : tempUserPrefs.get("structureChooser.height");
276 tbl_summary.setAutoCreateRowSorter(true);
277 tbl_summary.getTableHeader().setReorderingAllowed(false);
278 tbl_summary.addMouseListener(new MouseAdapter()
281 public void mouseClicked(MouseEvent e)
283 validateSelections();
287 public void mouseReleased(MouseEvent e)
289 validateSelections();
292 tbl_summary.addKeyListener(new KeyAdapter()
295 public void keyPressed(KeyEvent evt)
297 validateSelections();
298 switch (evt.getKeyCode())
300 case KeyEvent.VK_ESCAPE: // escape key
303 case KeyEvent.VK_ENTER: // enter key
304 if (btn_add.isEnabled())
306 add_ActionPerformed();
309 case KeyEvent.VK_TAB: // tab key
310 if (evt.isShiftDown())
312 pnl_filter.requestFocus();
316 btn_add.requestFocus();
326 JButton btn_cancel = new JButton(
327 MessageManager.getString("action.cancel"));
328 btn_cancel.setFont(VERDANA_12);
329 btn_cancel.addActionListener(new java.awt.event.ActionListener()
332 public void actionPerformed(ActionEvent e)
334 closeAction(pnl_filter.getHeight());
337 btn_cancel.addKeyListener(new KeyAdapter()
340 public void keyPressed(KeyEvent evt)
342 if (evt.getKeyCode() == KeyEvent.VK_ENTER)
344 closeAction(pnl_filter.getHeight());
349 tbl_local_pdb.setAutoCreateRowSorter(true);
350 tbl_local_pdb.getTableHeader().setReorderingAllowed(false);
351 tbl_local_pdb.addMouseListener(new MouseAdapter()
354 public void mouseClicked(MouseEvent e)
356 validateSelections();
360 public void mouseReleased(MouseEvent e)
362 validateSelections();
365 tbl_local_pdb.addKeyListener(new KeyAdapter()
368 public void keyPressed(KeyEvent evt)
370 validateSelections();
371 switch (evt.getKeyCode())
373 case KeyEvent.VK_ESCAPE: // escape key
376 case KeyEvent.VK_ENTER: // enter key
377 if (btn_add.isEnabled())
379 add_ActionPerformed();
382 case KeyEvent.VK_TAB: // tab key
383 if (evt.isShiftDown())
385 cmb_filterOption.requestFocus();
389 if (btn_add.isEnabled())
391 btn_add.requestFocus();
395 btn_cancel.requestFocus();
406 btn_newView = new JButton(MessageManager.getString("action.new_view"));
407 btn_newView.setFont(VERDANA_12);
408 btn_newView.addActionListener(new java.awt.event.ActionListener()
411 public void actionPerformed(ActionEvent e)
413 newView_ActionPerformed();
416 btn_newView.addKeyListener(new KeyAdapter()
419 public void keyPressed(KeyEvent evt)
421 if (evt.getKeyCode() == KeyEvent.VK_ENTER)
423 newView_ActionPerformed();
428 btn_add = new JButton(MessageManager.getString("action.add"));
429 btn_add.setFont(VERDANA_12);
430 btn_add.addActionListener(new java.awt.event.ActionListener()
433 public void actionPerformed(ActionEvent e)
435 add_ActionPerformed();
438 btn_add.addKeyListener(new KeyAdapter()
441 public void keyPressed(KeyEvent evt)
443 if (evt.getKeyCode() == KeyEvent.VK_ENTER)
445 add_ActionPerformed();
450 btn_pdbFromFile.setFont(VERDANA_12);
451 String btn_title = MessageManager.getString("label.select_pdb_file");
452 btn_pdbFromFile.setText(btn_title + " ");
453 btn_pdbFromFile.addActionListener(new java.awt.event.ActionListener()
456 public void actionPerformed(ActionEvent e)
458 pdbFromFile_actionPerformed();
461 btn_pdbFromFile.addKeyListener(new KeyAdapter()
464 public void keyPressed(KeyEvent evt)
466 if (evt.getKeyCode() == KeyEvent.VK_ENTER)
468 pdbFromFile_actionPerformed();
473 JScrollPane scrl_foundStructures = new JScrollPane(tbl_summary);
474 scrl_foundStructures.setPreferredSize(new Dimension(width, height));
476 JScrollPane scrl_localPDB = new JScrollPane(tbl_local_pdb);
477 scrl_localPDB.setPreferredSize(new Dimension(width, height));
478 scrl_localPDB.setHorizontalScrollBarPolicy(
479 JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
481 chk_invertFilter.setFont(VERDANA_12);
482 txt_search.setToolTipText(JvSwingUtils.wrapTooltip(true,
483 MessageManager.getString("label.enter_pdb_id_tip")));
484 txt_search.getDocument().addDocumentListener(new DocumentListener()
487 public void insertUpdate(DocumentEvent e)
489 txt_search_ActionPerformed();
493 public void removeUpdate(DocumentEvent e)
495 txt_search_ActionPerformed();
499 public void changedUpdate(DocumentEvent e)
501 txt_search_ActionPerformed();
505 cmb_filterOption.setFont(VERDANA_12);
506 cmb_filterOption.setToolTipText(
507 MessageManager.getString("info.select_filter_option"));
508 cmb_filterOption.addItemListener(this);
509 // add CustomComboSeparatorsRenderer to filter option combo-box
510 cmb_filterOption.setRenderer(new CustomComboSeparatorsRenderer(
511 (ListCellRenderer<Object>) cmb_filterOption.getRenderer())
514 protected boolean addSeparatorAfter(JList list, FilterOption value,
517 return value.isAddSeparatorAfter();
521 chk_invertFilter.addItemListener(this);
523 targetView.setVisible(false);
525 JPanel actionsPanel = new JPanel(new MigLayout());
526 actionsPanel.add(targetView, "left");
527 actionsPanel.add(btn_add, "wrap");
528 actionsPanel.add(chk_superpose, "left");
529 actionsPanel.add(btn_newView);
530 actionsPanel.add(btn_cancel, "right");
532 JPanel pnl_main = new JPanel();
533 pnl_main.add(cmb_filterOption);
534 pnl_main.add(lbl_loading);
535 pnl_main.add(chk_invertFilter);
536 lbl_loading.setVisible(false);
538 JPanel pnl_fileChooser = new JPanel(new FlowLayout());
539 pnl_fileChooser.add(btn_pdbFromFile);
540 pnl_fileChooser.add(lbl_fromFileStatus);
541 JPanel pnl_fileChooserBL = new JPanel(new BorderLayout());
542 pnl_fileChooserBL.add(fileChooserAssSeqPanel, BorderLayout.NORTH);
543 pnl_fileChooserBL.add(pnl_fileChooser, BorderLayout.CENTER);
545 JPanel pnl_idInput = new JPanel(new FlowLayout());
546 pnl_idInput.add(txt_search);
547 pnl_idInput.add(lbl_pdbManualFetchStatus);
549 JPanel pnl_idInputBL = new JPanel(new BorderLayout());
550 pnl_idInputBL.add(idInputAssSeqPanel, BorderLayout.NORTH);
551 pnl_idInputBL.add(pnl_idInput, BorderLayout.CENTER);
553 final String foundStructureSummary = MessageManager
554 .getString("label.found_structures_summary");
555 final String configureCols = MessageManager
556 .getString("label.configure_displayed_columns");
557 ChangeListener changeListener = new ChangeListener()
560 public void stateChanged(ChangeEvent changeEvent)
562 JTabbedPane sourceTabbedPane = (JTabbedPane) changeEvent
564 int index = sourceTabbedPane.getSelectedIndex();
565 btn_add.setVisible(targetView.isVisible());
566 btn_newView.setVisible(true);
567 btn_cancel.setVisible(true);
568 if (sourceTabbedPane.getTitleAt(index).equals(configureCols))
570 btn_add.setEnabled(false);
571 btn_cancel.setEnabled(false);
572 btn_add.setVisible(false);
573 btn_newView.setEnabled(false);
574 btn_cancel.setVisible(false);
575 previousWantedFields = pdbDocFieldPrefs
576 .getStructureSummaryFields()
577 .toArray(new FTSDataColumnI[0]);
579 if (sourceTabbedPane.getTitleAt(index)
580 .equals(foundStructureSummary))
582 btn_cancel.setEnabled(true);
583 if (wantedFieldsUpdated())
589 validateSelections();
594 pnl_filter.addChangeListener(changeListener);
595 pnl_filter.setPreferredSize(new Dimension(width, height));
596 pnl_filter.add(foundStructureSummary, scrl_foundStructures);
597 pnl_filter.add(configureCols, pdbDocFieldPrefs);
599 JPanel pnl_locPDB = new JPanel(new BorderLayout());
600 pnl_locPDB.add(scrl_localPDB);
602 pnl_switchableViews.add(pnl_fileChooserBL, VIEWS_FROM_FILE);
603 pnl_switchableViews.add(pnl_idInputBL, VIEWS_ENTER_ID);
604 pnl_switchableViews.add(pnl_filter, VIEWS_FILTER);
605 pnl_switchableViews.add(pnl_locPDB, VIEWS_LOCAL_PDB);
607 this.setLayout(new BorderLayout());
608 this.add(pnl_main, java.awt.BorderLayout.NORTH);
609 this.add(pnl_switchableViews, java.awt.BorderLayout.CENTER);
610 // this.add(pnl_actions, java.awt.BorderLayout.SOUTH);
611 statusPanel.setLayout(new GridLayout());
613 JPanel pnl_actionsAndStatus = new JPanel(new BorderLayout());
614 pnl_actionsAndStatus.add(actionsPanel, BorderLayout.CENTER);
615 pnl_actionsAndStatus.add(statusPanel, BorderLayout.SOUTH);
616 statusPanel.add(statusBar, null);
617 this.add(pnl_actionsAndStatus, java.awt.BorderLayout.SOUTH);
619 mainFrame.addInternalFrameListener(
620 new javax.swing.event.InternalFrameAdapter()
623 public void internalFrameClosing(InternalFrameEvent e)
625 closeAction(pnl_filter.getHeight());
628 mainFrame.setVisible(true);
629 mainFrame.setContentPane(this);
630 mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
631 Integer x = tempUserPrefs.get("structureChooser.x");
632 Integer y = tempUserPrefs.get("structureChooser.y");
633 if (x != null && y != null)
635 mainFrame.setLocation(x, y);
637 Desktop.addInternalFrame(mainFrame, frameTitle, width, height);
640 protected void closeAction(int preferredHeight)
642 // System.out.println(">>>>>>>>>> closing internal frame!!!");
643 // System.out.println("width : " + mainFrame.getWidth());
644 // System.out.println("heigh : " + mainFrame.getHeight());
645 // System.out.println("x : " + mainFrame.getX());
646 // System.out.println("y : " + mainFrame.getY());
647 tempUserPrefs.put("structureChooser.width", pnl_filter.getWidth());
648 tempUserPrefs.put("structureChooser.height", preferredHeight);
649 tempUserPrefs.put("structureChooser.x", mainFrame.getX());
650 tempUserPrefs.put("structureChooser.y", mainFrame.getY());
654 public boolean wantedFieldsUpdated()
656 if (previousWantedFields == null)
661 FTSDataColumnI[] currentWantedFields = pdbDocFieldPrefs
662 .getStructureSummaryFields().toArray(new FTSDataColumnI[0]);
663 return Arrays.equals(currentWantedFields, previousWantedFields) ? false
670 * Event listener for the 'filter' combo-box and 'invert' check-box
672 public void itemStateChanged(ItemEvent e)
678 * This inner class provides the data model for the structure filter combo-box
683 public class FilterOption
687 private String value;
691 private boolean addSeparatorAfter;
694 * Model for structure filter option
697 * - the name of the Option
699 * - the value of the option
701 * - the category of the filter option
702 * @param addSeparatorAfter
703 * - if true, a horizontal separator is rendered immediately after
704 * this filter option, otherwise
706 public FilterOption(String name, String value, String view,
707 boolean addSeparatorAfter)
712 this.addSeparatorAfter = addSeparatorAfter;
715 public String getName()
720 public void setName(String name)
725 public String getValue()
730 public void setValue(String value)
735 public String getView()
740 public void setView(String view)
746 public String toString()
751 public boolean isAddSeparatorAfter()
753 return addSeparatorAfter;
756 public void setAddSeparatorAfter(boolean addSeparatorAfter)
758 this.addSeparatorAfter = addSeparatorAfter;
763 * This inner class provides the provides the data model for associate
764 * sequence combo-box - cmb_assSeq
769 public class AssociateSeqOptions
771 private SequenceI sequence;
775 public AssociateSeqOptions(SequenceI seq)
778 this.name = (seq.getName().length() >= 23)
779 ? seq.getName().substring(0, 23)
783 public AssociateSeqOptions(String name, SequenceI seq)
790 public String toString()
795 public String getName()
800 public void setName(String name)
805 public SequenceI getSequence()
810 public void setSequence(SequenceI sequence)
812 this.sequence = sequence;
818 * This inner class holds the Layout and configuration of the panel which
819 * handles association of manually fetched structures to a unique sequence
820 * when more than one sequence selection is made
825 public class AssociateSeqPanel extends JPanel implements ItemListener
827 private JComboBox<AssociateSeqOptions> cmb_assSeq = new JComboBox<>();
829 private JLabel lbl_associateSeq = new JLabel();
831 public AssociateSeqPanel()
833 this.setLayout(new FlowLayout());
834 this.add(cmb_assSeq);
835 this.add(lbl_associateSeq);
836 cmb_assSeq.setToolTipText(
837 MessageManager.getString("info.associate_wit_sequence"));
838 cmb_assSeq.addItemListener(this);
841 public void loadCmbAssSeq()
843 populateCmbAssociateSeqOptions(cmb_assSeq, lbl_associateSeq);
846 public JComboBox<AssociateSeqOptions> getCmb_assSeq()
851 public void setCmb_assSeq(JComboBox<AssociateSeqOptions> cmb_assSeq)
853 this.cmb_assSeq = cmb_assSeq;
857 public void itemStateChanged(ItemEvent e)
859 if (e.getStateChange() == ItemEvent.SELECTED)
861 cmbAssSeqStateChanged();
866 public JTable getResultTable()
871 public JComboBox<FilterOption> getCmbFilterOption()
873 return cmb_filterOption;
877 * Custom ListCellRenderer for adding a separator between different categories
878 * of structure chooser filter option drop-down.
883 public abstract class CustomComboSeparatorsRenderer
884 implements ListCellRenderer<Object>
886 private ListCellRenderer<Object> regent;
888 private JPanel separatorPanel = new JPanel(new BorderLayout());
890 private JSeparator jSeparator = new JSeparator();
892 public CustomComboSeparatorsRenderer(
893 ListCellRenderer<Object> listCellRenderer)
895 this.regent = listCellRenderer;
899 public Component getListCellRendererComponent(JList list, Object value,
900 int index, boolean isSelected, boolean cellHasFocus)
903 Component comp = regent.getListCellRendererComponent(list, value,
904 index, isSelected, cellHasFocus);
906 && addSeparatorAfter(list, (FilterOption) value, index))
908 separatorPanel.removeAll();
909 separatorPanel.add(comp, BorderLayout.CENTER);
910 separatorPanel.add(jSeparator, BorderLayout.SOUTH);
911 return separatorPanel;
919 protected abstract boolean addSeparatorAfter(JList list,
920 FilterOption value, int index);
923 protected abstract void stateChanged(ItemEvent e);
925 protected abstract void add_ActionPerformed();
927 protected abstract void newView_ActionPerformed();
929 protected abstract void pdbFromFile_actionPerformed();
931 protected abstract void txt_search_ActionPerformed();
933 protected abstract void populateCmbAssociateSeqOptions(
934 JComboBox<AssociateSeqOptions> cmb_assSeq,
935 JLabel lbl_associateSeq);
937 protected abstract void cmbAssSeqStateChanged();
939 protected abstract void tabRefresh();
941 protected abstract void validateSelections();