2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
3 * Copyright (C) 2014 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;
40 import java.awt.GridLayout;
41 import java.awt.event.ActionEvent;
42 import java.awt.event.ItemEvent;
43 import java.awt.event.ItemListener;
44 import java.awt.event.KeyAdapter;
45 import java.awt.event.KeyEvent;
46 import java.awt.event.MouseAdapter;
47 import java.awt.event.MouseEvent;
48 import java.util.Arrays;
49 import java.util.HashMap;
52 import javax.swing.ImageIcon;
53 import javax.swing.JButton;
54 import javax.swing.JCheckBox;
55 import javax.swing.JComboBox;
56 import javax.swing.JFrame;
57 import javax.swing.JInternalFrame;
58 import javax.swing.JLabel;
59 import javax.swing.JList;
60 import javax.swing.JPanel;
61 import javax.swing.JScrollPane;
62 import javax.swing.JSeparator;
63 import javax.swing.JTabbedPane;
64 import javax.swing.JTable;
65 import javax.swing.JTextField;
66 import javax.swing.ListCellRenderer;
67 import javax.swing.event.ChangeEvent;
68 import javax.swing.event.ChangeListener;
69 import javax.swing.event.DocumentEvent;
70 import javax.swing.event.DocumentListener;
71 import javax.swing.event.InternalFrameEvent;
72 import javax.swing.table.TableColumn;
74 import net.miginfocom.swing.MigLayout;
76 @SuppressWarnings("serial")
78 * GUI layout for structure chooser
83 public abstract class GStructureChooser extends JPanel
84 implements ItemListener
86 protected JPanel statusPanel = new JPanel();
88 public JLabel statusBar = new JLabel();
90 private JPanel pnl_actionsAndStatus = new JPanel(new BorderLayout());
92 protected String frameTitle = MessageManager
93 .getString("label.structure_chooser");
95 protected JInternalFrame mainFrame = new JInternalFrame(frameTitle);
97 protected JComboBox<FilterOption> cmb_filterOption = new JComboBox<>();
99 protected AlignmentPanel ap;
101 protected StringBuilder errorWarning = new StringBuilder();
103 protected JLabel lbl_result = new JLabel(
104 MessageManager.getString("label.select"));
106 protected JButton btn_view = new JButton();
108 protected JButton btn_newview = new JButton(
109 MessageManager.getString("label.new_view"));
111 protected JButton btn_cancel = new JButton();
113 protected JButton btn_pdbFromFile = new JButton();
115 protected JCheckBox chk_superpose = new JCheckBox(
116 MessageManager.getString("label.superpose_structures"));
118 protected JTextField txt_search = new JTextField(14);
120 private JPanel pnl_actions = new JPanel(new MigLayout());
122 private JPanel pnl_main = new JPanel();
124 private JPanel pnl_idInput = new JPanel(new FlowLayout());
126 private JPanel pnl_fileChooser = new JPanel(new FlowLayout());
128 private JPanel pnl_idInputBL = new JPanel(new BorderLayout());
130 private JPanel pnl_fileChooserBL = new JPanel(new BorderLayout());
132 private JPanel pnl_locPDB = new JPanel(new BorderLayout());
134 protected JPanel pnl_switchableViews = new JPanel(new CardLayout());
136 protected CardLayout layout_switchableViews = (CardLayout) (pnl_switchableViews
139 private BorderLayout mainLayout = new BorderLayout();
141 protected JCheckBox chk_rememberSettings = new JCheckBox(
142 MessageManager.getString("label.dont_ask_me_again"));
144 protected JCheckBox chk_invertFilter = new JCheckBox(
145 MessageManager.getString("label.invert"));
147 protected ImageIcon loadingImage = new ImageIcon(
148 getClass().getResource("/images/loading.gif"));
150 protected ImageIcon goodImage = new ImageIcon(
151 getClass().getResource("/images/good.png"));
153 protected ImageIcon errorImage = new ImageIcon(
154 getClass().getResource("/images/error.png"));
156 protected ImageIcon warningImage = new ImageIcon(
157 getClass().getResource("/images/warning.gif"));
159 protected JLabel lbl_warning = new JLabel(warningImage);
161 protected JLabel lbl_loading = new JLabel(loadingImage);
163 protected JLabel lbl_pdbManualFetchStatus = new JLabel(errorImage);
165 protected JLabel lbl_fromFileStatus = new JLabel(errorImage);
167 protected AssciateSeqPanel idInputAssSeqPanel = new AssciateSeqPanel();
169 protected AssciateSeqPanel fileChooserAssSeqPanel = new AssciateSeqPanel();
171 protected static final String VIEWS_FILTER = "VIEWS_FILTER";
173 protected static final String VIEWS_FROM_FILE = "VIEWS_FROM_FILE";
175 protected static final String VIEWS_ENTER_ID = "VIEWS_ENTER_ID";
177 protected JComboBox<StructureViewer> targetView = new JComboBox();
180 * 'cached' structure view
182 protected static final String VIEWS_LOCAL_PDB = "VIEWS_LOCAL_PDB";
184 protected JTable tbl_local_pdb = new JTable();
186 protected JScrollPane scrl_localPDB = new JScrollPane(tbl_local_pdb);
188 protected JTabbedPane pnl_filter = new JTabbedPane();
190 protected FTSDataColumnPreferences pdbDocFieldPrefs = new FTSDataColumnPreferences(
191 PreferenceSource.STRUCTURE_CHOOSER,
192 PDBFTSRestClient.getInstance());
194 protected FTSDataColumnI[] previousWantedFields;
196 protected static Map<String, Integer> tempUserPrefs = new HashMap<>();
198 private JTable tbl_summary = new JTable()
200 private boolean inLayout;
203 public boolean getScrollableTracksViewportWidth()
205 return hasExcessWidth();
210 public void doLayout()
212 if (hasExcessWidth())
214 autoResizeMode = AUTO_RESIZE_SUBSEQUENT_COLUMNS;
219 autoResizeMode = AUTO_RESIZE_OFF;
222 protected boolean hasExcessWidth()
224 return getPreferredSize().width < getParent().getWidth();
228 public void columnMarginChanged(ChangeEvent e)
234 TableColumn resizingColumn = getTableHeader().getResizingColumn();
235 // Need to do this here, before the parent's
236 // layout manager calls getPreferredSize().
237 if (resizingColumn != null && autoResizeMode == AUTO_RESIZE_OFF
240 resizingColumn.setPreferredWidth(resizingColumn.getWidth());
241 String colHeader = resizingColumn.getHeaderValue().toString();
242 tempUserPrefs.put(colHeader, resizingColumn.getWidth());
248 public String getToolTipText(MouseEvent evt)
250 String toolTipText = null;
251 java.awt.Point pnt = evt.getPoint();
252 int rowIndex = rowAtPoint(pnt);
253 int colIndex = columnAtPoint(pnt);
257 if (getValueAt(rowIndex, colIndex) == null)
261 toolTipText = getValueAt(rowIndex, colIndex).toString();
262 } catch (Exception e)
264 // e.printStackTrace();
266 toolTipText = (toolTipText == null ? null
267 : (toolTipText.length() > 500
268 ? JvSwingUtils.wrapTooltip(true,
269 "\"" + toolTipText.subSequence(0, 500)
271 : JvSwingUtils.wrapTooltip(true, toolTipText)));
276 protected JScrollPane scrl_foundStructures = new JScrollPane(tbl_summary);
278 public GStructureChooser()
283 mainFrame.setVisible(false);
284 mainFrame.invalidate();
286 } catch (Exception e)
293 * Initializes the GUI default properties
297 private void jbInit() throws Exception
299 Integer width = tempUserPrefs.get("structureChooser.width") == null
301 : tempUserPrefs.get("structureChooser.width");
302 Integer height = tempUserPrefs.get("structureChooser.height") == null
304 : tempUserPrefs.get("structureChooser.height");
305 tbl_summary.setAutoCreateRowSorter(true);
306 tbl_summary.getTableHeader().setReorderingAllowed(false);
307 tbl_summary.addMouseListener(new MouseAdapter()
310 public void mouseClicked(MouseEvent e)
312 validateSelections();
316 public void mouseReleased(MouseEvent e)
318 validateSelections();
321 tbl_summary.addKeyListener(new KeyAdapter()
324 public void keyPressed(KeyEvent evt)
326 validateSelections();
327 switch (evt.getKeyCode())
329 case KeyEvent.VK_ESCAPE: // escape key
332 case KeyEvent.VK_ENTER: // enter key
333 if (btn_view.isEnabled())
335 view_ActionPerformed();
338 case KeyEvent.VK_TAB: // tab key
339 if (evt.isShiftDown())
341 pnl_filter.requestFocus();
345 btn_view.requestFocus();
354 tbl_local_pdb.setAutoCreateRowSorter(true);
355 tbl_local_pdb.getTableHeader().setReorderingAllowed(false);
356 tbl_local_pdb.addMouseListener(new MouseAdapter()
359 public void mouseClicked(MouseEvent e)
361 validateSelections();
365 public void mouseReleased(MouseEvent e)
367 validateSelections();
370 tbl_local_pdb.addKeyListener(new KeyAdapter()
373 public void keyPressed(KeyEvent evt)
375 validateSelections();
376 switch (evt.getKeyCode())
378 case KeyEvent.VK_ESCAPE: // escape key
381 case KeyEvent.VK_ENTER: // enter key
382 if (btn_view.isEnabled())
384 view_ActionPerformed();
387 case KeyEvent.VK_TAB: // tab key
388 if (evt.isShiftDown())
390 cmb_filterOption.requestFocus();
394 if (btn_view.isEnabled())
396 btn_view.requestFocus();
400 btn_cancel.requestFocus();
410 btn_newview.setFont(new java.awt.Font("Verdana", 0, 12));
411 btn_newview.setText(MessageManager.getString("action.new_view"));
412 btn_newview.addActionListener(new java.awt.event.ActionListener()
415 public void actionPerformed(ActionEvent e)
417 newview_ActionPerformed();
420 btn_newview.addKeyListener(new KeyAdapter()
423 public void keyPressed(KeyEvent evt)
425 if (evt.getKeyCode() == KeyEvent.VK_ENTER)
427 newview_ActionPerformed();
432 btn_view.setFont(new java.awt.Font("Verdana", 0, 12));
433 btn_view.setText(MessageManager.getString("action.add"));
434 btn_view.addActionListener(new java.awt.event.ActionListener()
437 public void actionPerformed(ActionEvent e)
439 view_ActionPerformed();
442 btn_view.addKeyListener(new KeyAdapter()
445 public void keyPressed(KeyEvent evt)
447 if (evt.getKeyCode() == KeyEvent.VK_ENTER)
449 view_ActionPerformed();
454 btn_cancel.setFont(new java.awt.Font("Verdana", 0, 12));
455 btn_cancel.setText(MessageManager.getString("action.cancel"));
456 btn_cancel.addActionListener(new java.awt.event.ActionListener()
459 public void actionPerformed(ActionEvent e)
461 closeAction(pnl_filter.getHeight());
464 btn_cancel.addKeyListener(new KeyAdapter()
467 public void keyPressed(KeyEvent evt)
469 if (evt.getKeyCode() == KeyEvent.VK_ENTER)
471 closeAction(pnl_filter.getHeight());
476 btn_pdbFromFile.setFont(new java.awt.Font("Verdana", 0, 12));
477 String btn_title = MessageManager.getString("label.select_pdb_file");
478 btn_pdbFromFile.setText(btn_title + " ");
479 btn_pdbFromFile.addActionListener(new java.awt.event.ActionListener()
482 public void actionPerformed(ActionEvent e)
484 pdbFromFile_actionPerformed();
487 btn_pdbFromFile.addKeyListener(new KeyAdapter()
490 public void keyPressed(KeyEvent evt)
492 if (evt.getKeyCode() == KeyEvent.VK_ENTER)
494 pdbFromFile_actionPerformed();
499 scrl_foundStructures.setPreferredSize(new Dimension(width, height));
501 scrl_localPDB.setPreferredSize(new Dimension(width, height));
502 scrl_localPDB.setHorizontalScrollBarPolicy(
503 JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
505 cmb_filterOption.setFont(new java.awt.Font("Verdana", 0, 12));
506 chk_invertFilter.setFont(new java.awt.Font("Verdana", 0, 12));
507 chk_rememberSettings.setFont(new java.awt.Font("Verdana", 0, 12));
508 chk_rememberSettings.setVisible(false);
509 txt_search.setToolTipText(JvSwingUtils.wrapTooltip(true,
510 MessageManager.getString("label.enter_pdb_id_tip")));
511 cmb_filterOption.setToolTipText(
512 MessageManager.getString("info.select_filter_option"));
513 txt_search.getDocument().addDocumentListener(new DocumentListener()
516 public void insertUpdate(DocumentEvent e)
518 txt_search_ActionPerformed();
522 public void removeUpdate(DocumentEvent e)
524 txt_search_ActionPerformed();
528 public void changedUpdate(DocumentEvent e)
530 txt_search_ActionPerformed();
534 cmb_filterOption.addItemListener(this);
536 // add CustomComboSeparatorsRenderer to filter option combo-box
537 cmb_filterOption.setRenderer(new CustomComboSeparatorsRenderer(
538 (ListCellRenderer<Object>) cmb_filterOption.getRenderer())
541 protected boolean addSeparatorAfter(JList list, FilterOption value,
544 return value.isAddSeparatorAfter();
548 chk_invertFilter.addItemListener(this);
550 targetView.setVisible(false);
551 pnl_actions.add(targetView, "left");
552 pnl_actions.add(btn_view, "wrap");
553 pnl_actions.add(chk_superpose, "left");
554 pnl_actions.add(btn_newview);
555 pnl_actions.add(btn_cancel, "right");
557 // pnl_actions.add(chk_rememberSettings);
559 // pnl_filter.add(lbl_result);
560 pnl_main.add(cmb_filterOption);
561 pnl_main.add(lbl_loading);
562 pnl_main.add(chk_invertFilter);
563 lbl_loading.setVisible(false);
565 pnl_fileChooser.add(btn_pdbFromFile);
566 pnl_fileChooser.add(lbl_fromFileStatus);
567 pnl_fileChooserBL.add(fileChooserAssSeqPanel, BorderLayout.NORTH);
568 pnl_fileChooserBL.add(pnl_fileChooser, BorderLayout.CENTER);
570 pnl_idInput.add(txt_search);
571 pnl_idInput.add(lbl_pdbManualFetchStatus);
572 pnl_idInputBL.add(idInputAssSeqPanel, BorderLayout.NORTH);
573 pnl_idInputBL.add(pnl_idInput, BorderLayout.CENTER);
575 final String foundStructureSummary = MessageManager
576 .getString("label.found_structures_summary");
577 final String configureCols = MessageManager
578 .getString("label.configure_displayed_columns");
579 ChangeListener changeListener = new ChangeListener()
582 public void stateChanged(ChangeEvent changeEvent)
584 JTabbedPane sourceTabbedPane = (JTabbedPane) changeEvent
586 int index = sourceTabbedPane.getSelectedIndex();
587 btn_view.setVisible(targetView.isVisible());
588 btn_newview.setVisible(true);
589 btn_cancel.setVisible(true);
590 if (sourceTabbedPane.getTitleAt(index).equals(configureCols))
592 btn_view.setEnabled(false);
593 btn_cancel.setEnabled(false);
594 btn_view.setVisible(false);
595 btn_newview.setEnabled(false);
596 btn_cancel.setVisible(false);
597 previousWantedFields = pdbDocFieldPrefs
598 .getStructureSummaryFields()
599 .toArray(new FTSDataColumnI[0]);
601 if (sourceTabbedPane.getTitleAt(index)
602 .equals(foundStructureSummary))
604 btn_cancel.setEnabled(true);
605 if (wantedFieldsUpdated())
611 validateSelections();
616 pnl_filter.addChangeListener(changeListener);
617 pnl_filter.setPreferredSize(new Dimension(width, height));
618 pnl_filter.add(foundStructureSummary, scrl_foundStructures);
619 pnl_filter.add(configureCols, pdbDocFieldPrefs);
621 pnl_locPDB.add(scrl_localPDB);
623 pnl_switchableViews.add(pnl_fileChooserBL, VIEWS_FROM_FILE);
624 pnl_switchableViews.add(pnl_idInputBL, VIEWS_ENTER_ID);
625 pnl_switchableViews.add(pnl_filter, VIEWS_FILTER);
626 pnl_switchableViews.add(pnl_locPDB, VIEWS_LOCAL_PDB);
628 this.setLayout(mainLayout);
629 this.add(pnl_main, java.awt.BorderLayout.NORTH);
630 this.add(pnl_switchableViews, java.awt.BorderLayout.CENTER);
631 // this.add(pnl_actions, java.awt.BorderLayout.SOUTH);
632 statusPanel.setLayout(new GridLayout());
633 pnl_actionsAndStatus.add(pnl_actions, BorderLayout.CENTER);
634 pnl_actionsAndStatus.add(statusPanel, BorderLayout.SOUTH);
635 statusPanel.add(statusBar, null);
636 this.add(pnl_actionsAndStatus, java.awt.BorderLayout.SOUTH);
638 mainFrame.addInternalFrameListener(
639 new javax.swing.event.InternalFrameAdapter()
642 public void internalFrameClosing(InternalFrameEvent e)
644 closeAction(pnl_filter.getHeight());
647 mainFrame.setVisible(true);
648 mainFrame.setContentPane(this);
649 mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
650 Integer x = tempUserPrefs.get("structureChooser.x");
651 Integer y = tempUserPrefs.get("structureChooser.y");
652 if (x != null && y != null)
654 mainFrame.setLocation(x, y);
656 Desktop.addInternalFrame(mainFrame, frameTitle, width, height);
659 protected void closeAction(int preferredHeight)
661 // System.out.println(">>>>>>>>>> closing internal frame!!!");
662 // System.out.println("width : " + mainFrame.getWidth());
663 // System.out.println("heigh : " + mainFrame.getHeight());
664 // System.out.println("x : " + mainFrame.getX());
665 // System.out.println("y : " + mainFrame.getY());
666 tempUserPrefs.put("structureChooser.width", pnl_filter.getWidth());
667 tempUserPrefs.put("structureChooser.height", preferredHeight);
668 tempUserPrefs.put("structureChooser.x", mainFrame.getX());
669 tempUserPrefs.put("structureChooser.y", mainFrame.getY());
673 public boolean wantedFieldsUpdated()
675 if (previousWantedFields == null)
680 FTSDataColumnI[] currentWantedFields = pdbDocFieldPrefs
681 .getStructureSummaryFields().toArray(new FTSDataColumnI[0]);
682 return Arrays.equals(currentWantedFields, previousWantedFields) ? false
689 * Event listener for the 'filter' combo-box and 'invert' check-box
691 public void itemStateChanged(ItemEvent e)
697 * This inner class provides the data model for the structure filter combo-box
702 public class FilterOption
706 private String value;
710 private boolean addSeparatorAfter;
713 * Model for structure filter option
716 * - the name of the Option
718 * - the value of the option
720 * - the category of the filter option
721 * @param addSeparatorAfter
722 * - if true, a horizontal separator is rendered immediately after
723 * this filter option, otherwise
725 public FilterOption(String name, String value, String view,
726 boolean addSeparatorAfter)
731 this.addSeparatorAfter = addSeparatorAfter;
734 public String getName()
739 public void setName(String name)
744 public String getValue()
749 public void setValue(String value)
754 public String getView()
759 public void setView(String view)
765 public String toString()
770 public boolean isAddSeparatorAfter()
772 return addSeparatorAfter;
775 public void setAddSeparatorAfter(boolean addSeparatorAfter)
777 this.addSeparatorAfter = addSeparatorAfter;
782 * This inner class provides the provides the data model for associate
783 * sequence combo-box - cmb_assSeq
788 public class AssociateSeqOptions
790 private SequenceI sequence;
794 public AssociateSeqOptions(SequenceI seq)
797 this.name = (seq.getName().length() >= 23)
798 ? seq.getName().substring(0, 23)
802 public AssociateSeqOptions(String name, SequenceI seq)
809 public String toString()
814 public String getName()
819 public void setName(String name)
824 public SequenceI getSequence()
829 public void setSequence(SequenceI sequence)
831 this.sequence = sequence;
837 * This inner class holds the Layout and configuration of the panel which
838 * handles association of manually fetched structures to a unique sequence
839 * when more than one sequence selection is made
844 public class AssciateSeqPanel extends JPanel implements ItemListener
846 private JComboBox<AssociateSeqOptions> cmb_assSeq = new JComboBox<>();
848 private JLabel lbl_associateSeq = new JLabel();
850 public AssciateSeqPanel()
852 this.setLayout(new FlowLayout());
853 this.add(cmb_assSeq);
854 this.add(lbl_associateSeq);
855 cmb_assSeq.setToolTipText(
856 MessageManager.getString("info.associate_wit_sequence"));
857 cmb_assSeq.addItemListener(this);
860 public void loadCmbAssSeq()
862 populateCmbAssociateSeqOptions(cmb_assSeq, lbl_associateSeq);
865 public JComboBox<AssociateSeqOptions> getCmb_assSeq()
870 public void setCmb_assSeq(JComboBox<AssociateSeqOptions> cmb_assSeq)
872 this.cmb_assSeq = cmb_assSeq;
876 public void itemStateChanged(ItemEvent e)
878 if (e.getStateChange() == ItemEvent.SELECTED)
880 cmbAssSeqStateChanged();
885 public JTable getResultTable()
890 public JComboBox<FilterOption> getCmbFilterOption()
892 return cmb_filterOption;
896 * Custom ListCellRenderer for adding a separator between different categories
897 * of structure chooser filter option drop-down.
902 public abstract class CustomComboSeparatorsRenderer
903 implements ListCellRenderer<Object>
905 private ListCellRenderer<Object> regent;
907 private JPanel separatorPanel = new JPanel(new BorderLayout());
909 private JSeparator jSeparator = new JSeparator();
911 public CustomComboSeparatorsRenderer(
912 ListCellRenderer<Object> listCellRenderer)
914 this.regent = listCellRenderer;
918 public Component getListCellRendererComponent(JList list, Object value,
919 int index, boolean isSelected, boolean cellHasFocus)
922 Component comp = regent.getListCellRendererComponent(list, value,
923 index, isSelected, cellHasFocus);
925 && addSeparatorAfter(list, (FilterOption) value, index))
927 separatorPanel.removeAll();
928 separatorPanel.add(comp, BorderLayout.CENTER);
929 separatorPanel.add(jSeparator, BorderLayout.SOUTH);
930 return separatorPanel;
938 protected abstract boolean addSeparatorAfter(JList list,
939 FilterOption value, int index);
942 protected abstract void stateChanged(ItemEvent e);
944 protected abstract void view_ActionPerformed();
946 protected abstract void newview_ActionPerformed();
948 protected abstract void pdbFromFile_actionPerformed();
950 protected abstract void txt_search_ActionPerformed();
952 public abstract void populateCmbAssociateSeqOptions(
953 JComboBox<AssociateSeqOptions> cmb_assSeq,
954 JLabel lbl_associateSeq);
956 public abstract void cmbAssSeqStateChanged();
958 public abstract void tabRefresh();
960 public abstract void validateSelections();