JAL-2045 JAL-2047 improvement for sorting PDB quality measure numerically, and improv...
[jalview.git] / src / jalview / jbgui / GStructureChooser.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
3  * Copyright (C) 2014 The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
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.
11  *  
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.
16  * 
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.
20  */
21
22 package jalview.jbgui;
23
24 import jalview.datamodel.SequenceI;
25 import jalview.gui.AlignmentPanel;
26 import jalview.gui.Desktop;
27 import jalview.gui.JvSwingUtils;
28 import jalview.jbgui.PDBDocFieldPreferences.PreferenceSource;
29 import jalview.util.MessageManager;
30 import jalview.ws.dbsources.PDBRestClient;
31 import jalview.ws.dbsources.PDBRestClient.PDBDocField;
32
33 import java.awt.BorderLayout;
34 import java.awt.CardLayout;
35 import java.awt.Dimension;
36 import java.awt.FlowLayout;
37 import java.awt.GridLayout;
38 import java.awt.event.ActionEvent;
39 import java.awt.event.ItemEvent;
40 import java.awt.event.ItemListener;
41 import java.awt.event.KeyAdapter;
42 import java.awt.event.KeyEvent;
43 import java.awt.event.MouseAdapter;
44 import java.awt.event.MouseEvent;
45 import java.util.Arrays;
46
47 import javax.swing.ImageIcon;
48 import javax.swing.JButton;
49 import javax.swing.JCheckBox;
50 import javax.swing.JComboBox;
51 import javax.swing.JFrame;
52 import javax.swing.JInternalFrame;
53 import javax.swing.JLabel;
54 import javax.swing.JPanel;
55 import javax.swing.JScrollPane;
56 import javax.swing.JTabbedPane;
57 import javax.swing.JTable;
58 import javax.swing.JTextField;
59 import javax.swing.event.ChangeEvent;
60 import javax.swing.event.ChangeListener;
61 import javax.swing.event.DocumentEvent;
62 import javax.swing.event.DocumentListener;
63
64 @SuppressWarnings("serial")
65 /**
66  * GUI layout for structure chooser 
67  * @author tcnofoegbu
68  *
69  */
70 public abstract class GStructureChooser extends JPanel implements
71         ItemListener
72 {
73   protected JPanel statusPanel = new JPanel();
74
75   public JLabel statusBar = new JLabel();
76
77   private JPanel pnl_actionsAndStatus = new JPanel(new BorderLayout());
78
79   protected String frameTitle = MessageManager
80           .getString("label.structure_chooser");
81
82   protected JInternalFrame mainFrame = new JInternalFrame(frameTitle);
83
84   protected JComboBox<FilterOption> cmb_filterOption = new JComboBox<FilterOption>();
85
86   protected AlignmentPanel ap;
87
88   protected StringBuilder errorWarning = new StringBuilder();
89
90   protected JLabel lbl_result = new JLabel(
91           MessageManager.getString("label.select"));
92
93   protected JButton btn_view = new JButton();
94
95   protected JButton btn_cancel = new JButton();
96
97   protected JButton btn_pdbFromFile = new JButton();
98
99   protected JTextField txt_search = new JTextField(14);
100
101   private JPanel pnl_actions = new JPanel();
102
103   private JPanel pnl_main = new JPanel();
104
105   private JPanel pnl_idInput = new JPanel(new FlowLayout());
106
107   private JPanel pnl_fileChooser = new JPanel(new FlowLayout());
108
109   private JPanel pnl_idInputBL = new JPanel(new BorderLayout());
110
111   private JPanel pnl_fileChooserBL = new JPanel(new BorderLayout());
112
113   private JPanel pnl_locPDB = new JPanel(new BorderLayout());
114
115   protected JPanel pnl_switchableViews = new JPanel(new CardLayout());
116
117   protected CardLayout layout_switchableViews = (CardLayout) (pnl_switchableViews
118           .getLayout());
119
120   private BorderLayout mainLayout = new BorderLayout();
121
122   protected JCheckBox chk_rememberSettings = new JCheckBox(
123           MessageManager.getString("label.dont_ask_me_again"));
124
125   protected JCheckBox chk_invertFilter = new JCheckBox(
126           MessageManager.getString("label.invert"));
127
128   protected ImageIcon loadingImage = new ImageIcon(getClass().getResource(
129           "/images/loading.gif"));
130
131   protected ImageIcon goodImage = new ImageIcon(getClass().getResource(
132           "/images/good.png"));
133
134   protected ImageIcon errorImage = new ImageIcon(getClass().getResource(
135           "/images/error.png"));
136
137   protected ImageIcon warningImage = new ImageIcon(getClass().getResource(
138           "/images/warning.gif"));
139
140   protected JLabel lbl_warning = new JLabel(warningImage);
141
142   protected JLabel lbl_loading = new JLabel(loadingImage);
143
144   protected JLabel lbl_pdbManualFetchStatus = new JLabel(errorImage);
145
146   protected JLabel lbl_fromFileStatus = new JLabel(errorImage);
147
148   protected AssciateSeqPanel idInputAssSeqPanel = new AssciateSeqPanel();
149
150   protected AssciateSeqPanel fileChooserAssSeqPanel = new AssciateSeqPanel();
151
152   protected static final String VIEWS_FILTER = "VIEWS_FILTER";
153
154   protected static final String VIEWS_FROM_FILE = "VIEWS_FROM_FILE";
155
156   protected static final String VIEWS_ENTER_ID = "VIEWS_ENTER_ID";
157
158   protected static final String VIEWS_LOCAL_PDB = "VIEWS_LOCAL_PDB";
159
160   protected JTable tbl_summary = new JTable()
161   {
162     @Override
163     public String getToolTipText(MouseEvent evt)
164     {
165       String toolTipText = null;
166       java.awt.Point pnt = evt.getPoint();
167       int rowIndex = rowAtPoint(pnt);
168       int colIndex = columnAtPoint(pnt);
169
170       try
171       {
172         if (getValueAt(rowIndex, colIndex) == null)
173         {
174           return null;
175         }
176         toolTipText = getValueAt(rowIndex, colIndex).toString();
177       } catch (Exception e)
178       {
179         // e.printStackTrace();
180       }
181       toolTipText = (toolTipText == null ? null
182               : (toolTipText.length() > 500 ? JvSwingUtils.wrapTooltip(
183                       true, "\"" + toolTipText.subSequence(0, 500)
184                               + "...\"") : JvSwingUtils.wrapTooltip(true,
185                       toolTipText)));
186       return toolTipText;
187     }
188   };
189
190   protected JScrollPane scrl_foundStructures = new JScrollPane(tbl_summary);
191
192   protected JTable tbl_local_pdb = new JTable();
193
194   protected JScrollPane scrl_localPDB = new JScrollPane(tbl_local_pdb);
195
196   private JTabbedPane pnl_filter = new JTabbedPane();
197
198   private PDBDocFieldPreferences pdbDocFieldPrefs = new PDBDocFieldPreferences(
199           PreferenceSource.STRUCTURE_CHOOSER);
200
201   protected PDBDocField[] previousWantedFields;
202
203   public GStructureChooser()
204   {
205     try
206     {
207       jbInit();
208       mainFrame.setVisible(false);
209       mainFrame.invalidate();
210       mainFrame.pack();
211     } catch (Exception e)
212     {
213       e.printStackTrace();
214     }
215   }
216
217   /**
218    * Initializes the GUI default properties
219    * 
220    * @throws Exception
221    */
222   private void jbInit() throws Exception
223   {
224     tbl_summary.setAutoCreateRowSorter(true);
225     tbl_summary.getTableHeader().setReorderingAllowed(false);
226     tbl_summary.addMouseListener(new MouseAdapter()
227     {
228       @Override
229       public void mouseClicked(MouseEvent e)
230       {
231         validateSelections();
232       }
233
234       @Override
235       public void mouseReleased(MouseEvent e)
236       {
237         validateSelections();
238       }
239     });
240     tbl_summary.addKeyListener(new KeyAdapter()
241     {
242       @Override
243       public void keyPressed(KeyEvent evt)
244       {
245         validateSelections();
246         switch (evt.getKeyCode())
247         {
248         case KeyEvent.VK_ESCAPE: // escape key
249           mainFrame.dispose();
250           break;
251         case KeyEvent.VK_ENTER: // enter key
252           if (btn_view.isEnabled())
253           {
254             ok_ActionPerformed();
255           }
256           break;
257         case KeyEvent.VK_TAB: // tab key
258           if (evt.isShiftDown())
259           {
260             pnl_filter.requestFocus();
261           }
262           else
263           {
264             btn_view.requestFocus();
265           }
266           evt.consume();
267           break;
268         default:
269           return;
270         }
271       }
272     });
273     tbl_local_pdb.setAutoCreateRowSorter(true);
274     tbl_local_pdb.getTableHeader().setReorderingAllowed(false);
275     tbl_local_pdb.addMouseListener(new MouseAdapter()
276     {
277       @Override
278       public void mouseClicked(MouseEvent e)
279       {
280         validateSelections();
281       }
282
283       @Override
284       public void mouseReleased(MouseEvent e)
285       {
286         validateSelections();
287       }
288     });
289     tbl_local_pdb.addKeyListener(new KeyAdapter()
290     {
291       @Override
292       public void keyPressed(KeyEvent evt)
293       {
294         validateSelections();
295         switch (evt.getKeyCode())
296         {
297         case KeyEvent.VK_ESCAPE: // escape key
298           mainFrame.dispose();
299           break;
300         case KeyEvent.VK_ENTER: // enter key
301           if (btn_view.isEnabled())
302           {
303             ok_ActionPerformed();
304           }
305           break;
306         case KeyEvent.VK_TAB: // tab key
307           if (evt.isShiftDown())
308           {
309             cmb_filterOption.requestFocus();
310           }
311           else
312           {
313             if (btn_view.isEnabled())
314             {
315               btn_view.requestFocus();
316             }
317             else
318             {
319               btn_cancel.requestFocus();
320             }
321           }
322           evt.consume();
323         default:
324           return;
325         }
326       }
327     });
328     btn_view.setFont(new java.awt.Font("Verdana", 0, 12));
329     btn_view.setText(MessageManager.getString("action.view"));
330     btn_view.addActionListener(new java.awt.event.ActionListener()
331     {
332       @Override
333       public void actionPerformed(ActionEvent e)
334       {
335         ok_ActionPerformed();
336       }
337     });
338     btn_view.addKeyListener(new KeyAdapter()
339     {
340       @Override
341       public void keyPressed(KeyEvent evt)
342       {
343         if (evt.getKeyCode() == KeyEvent.VK_ENTER)
344         {
345           ok_ActionPerformed();
346         }
347       }
348     });
349
350     btn_cancel.setFont(new java.awt.Font("Verdana", 0, 12));
351     btn_cancel.setText(MessageManager.getString("action.cancel"));
352     btn_cancel.addActionListener(new java.awt.event.ActionListener()
353     {
354       @Override
355       public void actionPerformed(ActionEvent e)
356       {
357         mainFrame.dispose();
358       }
359     });
360     btn_cancel.addKeyListener(new KeyAdapter()
361     {
362       @Override
363       public void keyPressed(KeyEvent evt)
364       {
365         if (evt.getKeyCode() == KeyEvent.VK_ENTER)
366         {
367           mainFrame.dispose();
368         }
369       }
370     });
371
372     btn_pdbFromFile.setFont(new java.awt.Font("Verdana", 0, 12));
373     String btn_title = MessageManager.getString("label.select_pdb_file");
374     btn_pdbFromFile.setText(btn_title + "              ");
375     btn_pdbFromFile.addActionListener(new java.awt.event.ActionListener()
376     {
377       @Override
378       public void actionPerformed(ActionEvent e)
379       {
380         pdbFromFile_actionPerformed();
381       }
382     });
383     btn_pdbFromFile.addKeyListener(new KeyAdapter()
384     {
385       @Override
386       public void keyPressed(KeyEvent evt)
387       {
388         if (evt.getKeyCode() == KeyEvent.VK_ENTER)
389         {
390           pdbFromFile_actionPerformed();
391         }
392       }
393     });
394
395     scrl_foundStructures.setPreferredSize(new Dimension(500, 300));
396     scrl_foundStructures
397             .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
398
399     scrl_localPDB.setPreferredSize(new Dimension(500, 300));
400     scrl_localPDB
401             .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
402
403     cmb_filterOption.setFont(new java.awt.Font("Verdana", 0, 12));
404     chk_invertFilter.setFont(new java.awt.Font("Verdana", 0, 12));
405     chk_rememberSettings.setFont(new java.awt.Font("Verdana", 0, 12));
406     chk_rememberSettings.setVisible(false);
407
408     txt_search.setToolTipText(MessageManager
409             .getString("label.enter_pdb_id"));
410     cmb_filterOption.setToolTipText(MessageManager
411             .getString("info.select_filter_option"));
412     txt_search.getDocument().addDocumentListener(new DocumentListener()
413     {
414       @Override
415       public void insertUpdate(DocumentEvent e)
416       {
417         txt_search_ActionPerformed();
418       }
419
420       @Override
421       public void removeUpdate(DocumentEvent e)
422       {
423         txt_search_ActionPerformed();
424       }
425
426       @Override
427       public void changedUpdate(DocumentEvent e)
428       {
429         txt_search_ActionPerformed();
430       }
431     });
432
433     cmb_filterOption.addItemListener(this);
434     chk_invertFilter.addItemListener(this);
435
436     pnl_actions.add(chk_rememberSettings);
437     pnl_actions.add(btn_view);
438     pnl_actions.add(btn_cancel);
439
440     // pnl_filter.add(lbl_result);
441     pnl_main.add(cmb_filterOption);
442     pnl_main.add(lbl_loading);
443     pnl_main.add(chk_invertFilter);
444     lbl_loading.setVisible(false);
445
446     pnl_fileChooser.add(btn_pdbFromFile);
447     pnl_fileChooser.add(lbl_fromFileStatus);
448     pnl_fileChooserBL.add(fileChooserAssSeqPanel, BorderLayout.NORTH);
449     pnl_fileChooserBL.add(pnl_fileChooser, BorderLayout.CENTER);
450
451     pnl_idInput.add(txt_search);
452     pnl_idInput.add(lbl_pdbManualFetchStatus);
453     pnl_idInputBL.add(idInputAssSeqPanel, BorderLayout.NORTH);
454     pnl_idInputBL.add(pnl_idInput, BorderLayout.CENTER);
455
456     final String foundStructureSummary = MessageManager
457             .getString("label.found_structures_summary");
458     final String configureCols = MessageManager
459             .getString("label.configure_displayed_columns");
460     ChangeListener changeListener = new ChangeListener()
461     {
462       @Override
463       public void stateChanged(ChangeEvent changeEvent)
464       {
465         JTabbedPane sourceTabbedPane = (JTabbedPane) changeEvent
466                 .getSource();
467         int index = sourceTabbedPane.getSelectedIndex();
468         btn_view.setVisible(true);
469         btn_cancel.setVisible(true);
470         if (sourceTabbedPane.getTitleAt(index).equals(configureCols))
471         {
472           btn_view.setEnabled(false);
473           btn_cancel.setEnabled(false);
474           btn_view.setVisible(false);
475           btn_cancel.setVisible(false);
476           previousWantedFields = PDBDocFieldPreferences
477                   .getStructureSummaryFields().toArray(
478                           new PDBRestClient.PDBDocField[0]);
479         }
480         if (sourceTabbedPane.getTitleAt(index)
481                 .equals(foundStructureSummary))
482         {
483           btn_cancel.setEnabled(true);
484           if (wantedFieldsUpdated())
485           {
486             tabRefresh();
487           }
488           else
489           {
490             validateSelections();
491           }
492         }
493       }
494     };
495     pnl_filter.addChangeListener(changeListener);
496     pnl_filter.setPreferredSize(new Dimension(500, 300));
497     pnl_filter.add(foundStructureSummary, scrl_foundStructures);
498     pnl_filter.add(configureCols, pdbDocFieldPrefs);
499
500     pnl_locPDB.add(scrl_localPDB);
501
502     pnl_switchableViews.add(pnl_fileChooserBL, VIEWS_FROM_FILE);
503     pnl_switchableViews.add(pnl_idInputBL, VIEWS_ENTER_ID);
504     pnl_switchableViews.add(pnl_filter, VIEWS_FILTER);
505     pnl_switchableViews.add(pnl_locPDB, VIEWS_LOCAL_PDB);
506
507     this.setLayout(mainLayout);
508     this.add(pnl_main, java.awt.BorderLayout.NORTH);
509     this.add(pnl_switchableViews, java.awt.BorderLayout.CENTER);
510     // this.add(pnl_actions, java.awt.BorderLayout.SOUTH);
511     statusPanel.setLayout(new GridLayout());
512     pnl_actionsAndStatus.add(pnl_actions, BorderLayout.CENTER);
513     pnl_actionsAndStatus.add(statusPanel, BorderLayout.SOUTH);
514     statusPanel.add(statusBar, null);
515     this.add(pnl_actionsAndStatus, java.awt.BorderLayout.SOUTH);
516
517     mainFrame.setVisible(true);
518     mainFrame.setContentPane(this);
519     mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
520     Desktop.addInternalFrame(mainFrame, frameTitle, 800, 400);
521   }
522
523   public boolean wantedFieldsUpdated()
524   {
525     if (previousWantedFields == null)
526     {
527       return true;
528     }
529
530     return Arrays.equals(PDBDocFieldPreferences.getStructureSummaryFields()
531             .toArray(new PDBRestClient.PDBDocField[0]),
532             previousWantedFields) ? false : true;
533
534   }
535
536   @Override
537   /**
538    * Event listener for the 'filter' combo-box and 'invert' check-box
539    */
540   public void itemStateChanged(ItemEvent e)
541   {
542     stateChanged(e);
543   }
544
545   /**
546    * This inner class provides the data model for the structure filter combo-box
547    * 
548    * @author tcnofoegbu
549    *
550    */
551   public class FilterOption
552   {
553     private String name;
554
555     private String value;
556
557     private String view;
558
559     public FilterOption(String name, String value, String view)
560     {
561       this.name = name;
562       this.value = value;
563       this.view = view;
564     }
565
566     public String getName()
567     {
568       return name;
569     }
570
571     public void setName(String name)
572     {
573       this.name = name;
574     }
575
576     public String getValue()
577     {
578       return value;
579     }
580
581     public void setValue(String value)
582     {
583       this.value = value;
584     }
585
586     public String getView()
587     {
588       return view;
589     }
590
591     public void setView(String view)
592     {
593       this.view = view;
594     }
595
596     @Override
597     public String toString()
598     {
599       return this.name;
600     }
601   }
602
603   /**
604    * This inner class provides the provides the data model for associate
605    * sequence combo-box - cmb_assSeq
606    * 
607    * @author tcnofoegbu
608    *
609    */
610   public class AssociateSeqOptions
611   {
612     private SequenceI sequence;
613
614     private String name;
615
616     public AssociateSeqOptions(SequenceI seq)
617     {
618       this.sequence = seq;
619       this.name = (seq.getName().length() >= 23) ? seq.getName().substring(
620               0, 23) : seq.getName();
621     }
622
623     public AssociateSeqOptions(String name, SequenceI seq)
624     {
625       this.name = name;
626       this.sequence = seq;
627     }
628
629     @Override
630     public String toString()
631     {
632       return name;
633     }
634
635     public String getName()
636     {
637       return name;
638     }
639
640     public void setName(String name)
641     {
642       this.name = name;
643     }
644
645     public SequenceI getSequence()
646     {
647       return sequence;
648     }
649
650     public void setSequence(SequenceI sequence)
651     {
652       this.sequence = sequence;
653     }
654
655   }
656
657   /**
658    * This inner class holds the Layout and configuration of the panel which
659    * handles association of manually fetched structures to a unique sequence
660    * when more than one sequence selection is made
661    * 
662    * @author tcnofoegbu
663    *
664    */
665   public class AssciateSeqPanel extends JPanel implements ItemListener
666   {
667     private JComboBox<AssociateSeqOptions> cmb_assSeq = new JComboBox<AssociateSeqOptions>();
668
669     private JLabel lbl_associateSeq = new JLabel();
670
671     public AssciateSeqPanel()
672     {
673       this.setLayout(new FlowLayout());
674       this.add(cmb_assSeq);
675       this.add(lbl_associateSeq);
676       cmb_assSeq.setToolTipText(MessageManager
677               .getString("info.associate_wit_sequence"));
678       cmb_assSeq.addItemListener(this);
679     }
680
681     public void loadCmbAssSeq()
682     {
683       populateCmbAssociateSeqOptions(cmb_assSeq, lbl_associateSeq);
684     }
685
686     public JComboBox<AssociateSeqOptions> getCmb_assSeq()
687     {
688       return cmb_assSeq;
689     }
690
691     public void setCmb_assSeq(JComboBox<AssociateSeqOptions> cmb_assSeq)
692     {
693       this.cmb_assSeq = cmb_assSeq;
694     }
695
696     @Override
697     public void itemStateChanged(ItemEvent e)
698     {
699       if (e.getStateChange() == ItemEvent.SELECTED)
700       {
701         cmbAssSeqStateChanged();
702       }
703     }
704   }
705
706   public JComboBox<FilterOption> getCmbFilterOption()
707   {
708     return cmb_filterOption;
709   }
710
711   protected abstract void stateChanged(ItemEvent e);
712
713   protected abstract void updateCurrentView();
714
715   protected abstract void populateFilterComboBox();
716
717   protected abstract void ok_ActionPerformed();
718
719   protected abstract void pdbFromFile_actionPerformed();
720
721   protected abstract void txt_search_ActionPerformed();
722
723   public abstract void populateCmbAssociateSeqOptions(
724           JComboBox<AssociateSeqOptions> cmb_assSeq, JLabel lbl_associateSeq);
725
726   public abstract void cmbAssSeqStateChanged();
727
728   public abstract void tabRefresh();
729
730   public abstract void validateSelections();
731 }