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 = new JTabbedPane();
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)
263 * Initializes the GUI default properties
267 private void jbInit() throws Exception
269 Integer width = tempUserPrefs.get("structureChooser.width") == null
271 : tempUserPrefs.get("structureChooser.width");
272 Integer height = tempUserPrefs.get("structureChooser.height") == null
274 : tempUserPrefs.get("structureChooser.height");
275 tbl_summary.setAutoCreateRowSorter(true);
276 tbl_summary.getTableHeader().setReorderingAllowed(false);
277 tbl_summary.addMouseListener(new MouseAdapter()
280 public void mouseClicked(MouseEvent e)
282 validateSelections();
286 public void mouseReleased(MouseEvent e)
288 validateSelections();
291 tbl_summary.addKeyListener(new KeyAdapter()
294 public void keyPressed(KeyEvent evt)
296 validateSelections();
297 switch (evt.getKeyCode())
299 case KeyEvent.VK_ESCAPE: // escape key
302 case KeyEvent.VK_ENTER: // enter key
303 if (btn_add.isEnabled())
305 add_ActionPerformed();
308 case KeyEvent.VK_TAB: // tab key
309 if (evt.isShiftDown())
311 pnl_filter.requestFocus();
315 btn_add.requestFocus();
325 JButton btn_cancel = new JButton(
326 MessageManager.getString("action.cancel"));
327 btn_cancel.setFont(VERDANA_12);
328 btn_cancel.addActionListener(new java.awt.event.ActionListener()
331 public void actionPerformed(ActionEvent e)
333 closeAction(pnl_filter.getHeight());
336 btn_cancel.addKeyListener(new KeyAdapter()
339 public void keyPressed(KeyEvent evt)
341 if (evt.getKeyCode() == KeyEvent.VK_ENTER)
343 closeAction(pnl_filter.getHeight());
348 tbl_local_pdb.setAutoCreateRowSorter(true);
349 tbl_local_pdb.getTableHeader().setReorderingAllowed(false);
350 tbl_local_pdb.addMouseListener(new MouseAdapter()
353 public void mouseClicked(MouseEvent e)
355 validateSelections();
359 public void mouseReleased(MouseEvent e)
361 validateSelections();
364 tbl_local_pdb.addKeyListener(new KeyAdapter()
367 public void keyPressed(KeyEvent evt)
369 validateSelections();
370 switch (evt.getKeyCode())
372 case KeyEvent.VK_ESCAPE: // escape key
375 case KeyEvent.VK_ENTER: // enter key
376 if (btn_add.isEnabled())
378 add_ActionPerformed();
381 case KeyEvent.VK_TAB: // tab key
382 if (evt.isShiftDown())
384 cmb_filterOption.requestFocus();
388 if (btn_add.isEnabled())
390 btn_add.requestFocus();
394 btn_cancel.requestFocus();
405 btn_newView = new JButton(MessageManager.getString("action.new_view"));
406 btn_newView.setFont(VERDANA_12);
407 btn_newView.addActionListener(new java.awt.event.ActionListener()
410 public void actionPerformed(ActionEvent e)
412 newView_ActionPerformed();
415 btn_newView.addKeyListener(new KeyAdapter()
418 public void keyPressed(KeyEvent evt)
420 if (evt.getKeyCode() == KeyEvent.VK_ENTER)
422 newView_ActionPerformed();
427 btn_add = new JButton(MessageManager.getString("action.add"));
428 btn_add.setFont(VERDANA_12);
429 btn_add.addActionListener(new java.awt.event.ActionListener()
432 public void actionPerformed(ActionEvent e)
434 add_ActionPerformed();
437 btn_add.addKeyListener(new KeyAdapter()
440 public void keyPressed(KeyEvent evt)
442 if (evt.getKeyCode() == KeyEvent.VK_ENTER)
444 add_ActionPerformed();
449 btn_pdbFromFile.setFont(VERDANA_12);
450 String btn_title = MessageManager.getString("label.select_pdb_file");
451 btn_pdbFromFile.setText(btn_title + " ");
452 btn_pdbFromFile.addActionListener(new java.awt.event.ActionListener()
455 public void actionPerformed(ActionEvent e)
457 pdbFromFile_actionPerformed();
460 btn_pdbFromFile.addKeyListener(new KeyAdapter()
463 public void keyPressed(KeyEvent evt)
465 if (evt.getKeyCode() == KeyEvent.VK_ENTER)
467 pdbFromFile_actionPerformed();
472 JScrollPane scrl_foundStructures = new JScrollPane(tbl_summary);
473 scrl_foundStructures.setPreferredSize(new Dimension(width, height));
475 JScrollPane scrl_localPDB = new JScrollPane(tbl_local_pdb);
476 scrl_localPDB.setPreferredSize(new Dimension(width, height));
477 scrl_localPDB.setHorizontalScrollBarPolicy(
478 JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
480 chk_invertFilter.setFont(VERDANA_12);
481 txt_search.setToolTipText(JvSwingUtils.wrapTooltip(true,
482 MessageManager.getString("label.enter_pdb_id_tip")));
483 txt_search.getDocument().addDocumentListener(new DocumentListener()
486 public void insertUpdate(DocumentEvent e)
488 txt_search_ActionPerformed();
492 public void removeUpdate(DocumentEvent e)
494 txt_search_ActionPerformed();
498 public void changedUpdate(DocumentEvent e)
500 txt_search_ActionPerformed();
504 cmb_filterOption.setFont(VERDANA_12);
505 cmb_filterOption.setToolTipText(
506 MessageManager.getString("info.select_filter_option"));
507 cmb_filterOption.addItemListener(this);
508 // add CustomComboSeparatorsRenderer to filter option combo-box
509 cmb_filterOption.setRenderer(new CustomComboSeparatorsRenderer(
510 (ListCellRenderer<Object>) cmb_filterOption.getRenderer())
513 protected boolean addSeparatorAfter(JList list, FilterOption value,
516 return value.isAddSeparatorAfter();
520 chk_invertFilter.addItemListener(this);
522 targetView.setVisible(false);
524 JPanel actionsPanel = new JPanel(new MigLayout());
525 actionsPanel.add(targetView, "left");
526 actionsPanel.add(btn_add, "wrap");
527 actionsPanel.add(chk_superpose, "left");
528 actionsPanel.add(btn_newView);
529 actionsPanel.add(btn_cancel, "right");
531 JPanel pnl_main = new JPanel();
532 pnl_main.add(cmb_filterOption);
533 pnl_main.add(lbl_loading);
534 pnl_main.add(chk_invertFilter);
535 lbl_loading.setVisible(false);
537 JPanel pnl_fileChooser = new JPanel(new FlowLayout());
538 pnl_fileChooser.add(btn_pdbFromFile);
539 pnl_fileChooser.add(lbl_fromFileStatus);
540 JPanel pnl_fileChooserBL = new JPanel(new BorderLayout());
541 pnl_fileChooserBL.add(fileChooserAssSeqPanel, BorderLayout.NORTH);
542 pnl_fileChooserBL.add(pnl_fileChooser, BorderLayout.CENTER);
544 JPanel pnl_idInput = new JPanel(new FlowLayout());
545 pnl_idInput.add(txt_search);
546 pnl_idInput.add(lbl_pdbManualFetchStatus);
548 JPanel pnl_idInputBL = new JPanel(new BorderLayout());
549 pnl_idInputBL.add(idInputAssSeqPanel, BorderLayout.NORTH);
550 pnl_idInputBL.add(pnl_idInput, BorderLayout.CENTER);
552 final String foundStructureSummary = MessageManager
553 .getString("label.found_structures_summary");
554 final String configureCols = MessageManager
555 .getString("label.configure_displayed_columns");
556 ChangeListener changeListener = new ChangeListener()
559 public void stateChanged(ChangeEvent changeEvent)
561 JTabbedPane sourceTabbedPane = (JTabbedPane) changeEvent
563 int index = sourceTabbedPane.getSelectedIndex();
564 btn_add.setVisible(targetView.isVisible());
565 btn_newView.setVisible(true);
566 btn_cancel.setVisible(true);
567 if (sourceTabbedPane.getTitleAt(index).equals(configureCols))
569 btn_add.setEnabled(false);
570 btn_cancel.setEnabled(false);
571 btn_add.setVisible(false);
572 btn_newView.setEnabled(false);
573 btn_cancel.setVisible(false);
574 previousWantedFields = pdbDocFieldPrefs
575 .getStructureSummaryFields()
576 .toArray(new FTSDataColumnI[0]);
578 if (sourceTabbedPane.getTitleAt(index)
579 .equals(foundStructureSummary))
581 btn_cancel.setEnabled(true);
582 if (wantedFieldsUpdated())
588 validateSelections();
593 pnl_filter.addChangeListener(changeListener);
594 pnl_filter.setPreferredSize(new Dimension(width, height));
595 pnl_filter.add(foundStructureSummary, scrl_foundStructures);
596 pnl_filter.add(configureCols, pdbDocFieldPrefs);
598 JPanel pnl_locPDB = new JPanel(new BorderLayout());
599 pnl_locPDB.add(scrl_localPDB);
601 pnl_switchableViews.add(pnl_fileChooserBL, VIEWS_FROM_FILE);
602 pnl_switchableViews.add(pnl_idInputBL, VIEWS_ENTER_ID);
603 pnl_switchableViews.add(pnl_filter, VIEWS_FILTER);
604 pnl_switchableViews.add(pnl_locPDB, VIEWS_LOCAL_PDB);
606 this.setLayout(new BorderLayout());
607 this.add(pnl_main, java.awt.BorderLayout.NORTH);
608 this.add(pnl_switchableViews, java.awt.BorderLayout.CENTER);
609 // this.add(pnl_actions, java.awt.BorderLayout.SOUTH);
610 statusPanel.setLayout(new GridLayout());
612 JPanel pnl_actionsAndStatus = new JPanel(new BorderLayout());
613 pnl_actionsAndStatus.add(actionsPanel, BorderLayout.CENTER);
614 pnl_actionsAndStatus.add(statusPanel, BorderLayout.SOUTH);
615 statusPanel.add(statusBar, null);
616 this.add(pnl_actionsAndStatus, java.awt.BorderLayout.SOUTH);
618 mainFrame.addInternalFrameListener(
619 new javax.swing.event.InternalFrameAdapter()
622 public void internalFrameClosing(InternalFrameEvent e)
624 closeAction(pnl_filter.getHeight());
627 mainFrame.setVisible(true);
628 mainFrame.setContentPane(this);
629 mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
630 Integer x = tempUserPrefs.get("structureChooser.x");
631 Integer y = tempUserPrefs.get("structureChooser.y");
632 if (x != null && y != null)
634 mainFrame.setLocation(x, y);
636 Desktop.addInternalFrame(mainFrame, frameTitle, width, height);
639 protected void closeAction(int preferredHeight)
641 // System.out.println(">>>>>>>>>> closing internal frame!!!");
642 // System.out.println("width : " + mainFrame.getWidth());
643 // System.out.println("heigh : " + mainFrame.getHeight());
644 // System.out.println("x : " + mainFrame.getX());
645 // System.out.println("y : " + mainFrame.getY());
646 tempUserPrefs.put("structureChooser.width", pnl_filter.getWidth());
647 tempUserPrefs.put("structureChooser.height", preferredHeight);
648 tempUserPrefs.put("structureChooser.x", mainFrame.getX());
649 tempUserPrefs.put("structureChooser.y", mainFrame.getY());
653 public boolean wantedFieldsUpdated()
655 if (previousWantedFields == null)
660 FTSDataColumnI[] currentWantedFields = pdbDocFieldPrefs
661 .getStructureSummaryFields().toArray(new FTSDataColumnI[0]);
662 return Arrays.equals(currentWantedFields, previousWantedFields) ? false
669 * Event listener for the 'filter' combo-box and 'invert' check-box
671 public void itemStateChanged(ItemEvent e)
677 * This inner class provides the data model for the structure filter combo-box
682 public class FilterOption
686 private String value;
690 private boolean addSeparatorAfter;
693 * Model for structure filter option
696 * - the name of the Option
698 * - the value of the option
700 * - the category of the filter option
701 * @param addSeparatorAfter
702 * - if true, a horizontal separator is rendered immediately after
703 * this filter option, otherwise
705 public FilterOption(String name, String value, String view,
706 boolean addSeparatorAfter)
711 this.addSeparatorAfter = addSeparatorAfter;
714 public String getName()
719 public void setName(String name)
724 public String getValue()
729 public void setValue(String value)
734 public String getView()
739 public void setView(String view)
745 public String toString()
750 public boolean isAddSeparatorAfter()
752 return addSeparatorAfter;
755 public void setAddSeparatorAfter(boolean addSeparatorAfter)
757 this.addSeparatorAfter = addSeparatorAfter;
762 * This inner class provides the provides the data model for associate
763 * sequence combo-box - cmb_assSeq
768 public class AssociateSeqOptions
770 private SequenceI sequence;
774 public AssociateSeqOptions(SequenceI seq)
777 this.name = (seq.getName().length() >= 23)
778 ? seq.getName().substring(0, 23)
782 public AssociateSeqOptions(String name, SequenceI seq)
789 public String toString()
794 public String getName()
799 public void setName(String name)
804 public SequenceI getSequence()
809 public void setSequence(SequenceI sequence)
811 this.sequence = sequence;
817 * This inner class holds the Layout and configuration of the panel which
818 * handles association of manually fetched structures to a unique sequence
819 * when more than one sequence selection is made
824 public class AssociateSeqPanel extends JPanel implements ItemListener
826 private JComboBox<AssociateSeqOptions> cmb_assSeq = new JComboBox<>();
828 private JLabel lbl_associateSeq = new JLabel();
830 public AssociateSeqPanel()
832 this.setLayout(new FlowLayout());
833 this.add(cmb_assSeq);
834 this.add(lbl_associateSeq);
835 cmb_assSeq.setToolTipText(
836 MessageManager.getString("info.associate_wit_sequence"));
837 cmb_assSeq.addItemListener(this);
840 public void loadCmbAssSeq()
842 populateCmbAssociateSeqOptions(cmb_assSeq, lbl_associateSeq);
845 public JComboBox<AssociateSeqOptions> getCmb_assSeq()
850 public void setCmb_assSeq(JComboBox<AssociateSeqOptions> cmb_assSeq)
852 this.cmb_assSeq = cmb_assSeq;
856 public void itemStateChanged(ItemEvent e)
858 if (e.getStateChange() == ItemEvent.SELECTED)
860 cmbAssSeqStateChanged();
865 public JTable getResultTable()
870 public JComboBox<FilterOption> getCmbFilterOption()
872 return cmb_filterOption;
876 * Custom ListCellRenderer for adding a separator between different categories
877 * of structure chooser filter option drop-down.
882 public abstract class CustomComboSeparatorsRenderer
883 implements ListCellRenderer<Object>
885 private ListCellRenderer<Object> regent;
887 private JPanel separatorPanel = new JPanel(new BorderLayout());
889 private JSeparator jSeparator = new JSeparator();
891 public CustomComboSeparatorsRenderer(
892 ListCellRenderer<Object> listCellRenderer)
894 this.regent = listCellRenderer;
898 public Component getListCellRendererComponent(JList list, Object value,
899 int index, boolean isSelected, boolean cellHasFocus)
902 Component comp = regent.getListCellRendererComponent(list, value,
903 index, isSelected, cellHasFocus);
905 && addSeparatorAfter(list, (FilterOption) value, index))
907 separatorPanel.removeAll();
908 separatorPanel.add(comp, BorderLayout.CENTER);
909 separatorPanel.add(jSeparator, BorderLayout.SOUTH);
910 return separatorPanel;
918 protected abstract boolean addSeparatorAfter(JList list,
919 FilterOption value, int index);
922 protected abstract void stateChanged(ItemEvent e);
924 protected abstract void add_ActionPerformed();
926 protected abstract void newView_ActionPerformed();
928 protected abstract void pdbFromFile_actionPerformed();
930 protected abstract void txt_search_ActionPerformed();
932 protected abstract void populateCmbAssociateSeqOptions(
933 JComboBox<AssociateSeqOptions> cmb_assSeq,
934 JLabel lbl_associateSeq);
936 protected abstract void cmbAssSeqStateChanged();
938 protected abstract void tabRefresh();
940 protected abstract void validateSelections();