JAL-2028 improvment to the displayed column customisation
[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         toolTipText = getValueAt(rowIndex, colIndex).toString();
173       } catch (Exception e)
174       {
175         // e.printStackTrace();
176       }
177       toolTipText = (toolTipText == null ? null
178               : (toolTipText.length() > 500 ? JvSwingUtils.wrapTooltip(
179                       true, "\"" + toolTipText.subSequence(0, 500)
180                               + "...\"") : JvSwingUtils.wrapTooltip(true,
181                       toolTipText)));
182       return toolTipText;
183     }
184   };
185
186   protected JScrollPane scrl_foundStructures = new JScrollPane(tbl_summary);
187
188   protected JTable tbl_local_pdb = new JTable();
189
190   protected JScrollPane scrl_localPDB = new JScrollPane(tbl_local_pdb);
191
192   private JTabbedPane pnl_filter = new JTabbedPane();
193
194   private PDBDocFieldPreferences pdbDocFieldPrefs = new PDBDocFieldPreferences(
195           PreferenceSource.STRUCTURE_CHOOSER);
196
197   protected PDBDocField[] previousWantedFields;
198
199   public GStructureChooser()
200   {
201     try
202     {
203       jbInit();
204       mainFrame.setVisible(false);
205       mainFrame.invalidate();
206       mainFrame.pack();
207     } catch (Exception e)
208     {
209       e.printStackTrace();
210     }
211   }
212
213   /**
214    * Initializes the GUI default properties
215    * 
216    * @throws Exception
217    */
218   private void jbInit() throws Exception
219   {
220     tbl_summary.setAutoCreateRowSorter(true);
221     tbl_summary.getTableHeader().setReorderingAllowed(false);
222     tbl_summary.addMouseListener(new MouseAdapter()
223     {
224       @Override
225       public void mouseClicked(MouseEvent e)
226       {
227         validateSelections();
228       }
229
230       @Override
231       public void mouseReleased(MouseEvent e)
232       {
233         validateSelections();
234       }
235     });
236     tbl_summary.addKeyListener(new KeyAdapter()
237     {
238       @Override
239       public void keyPressed(KeyEvent evt)
240       {
241         validateSelections();
242         switch (evt.getKeyCode())
243         {
244         case KeyEvent.VK_ESCAPE: // escape key
245           mainFrame.dispose();
246           break;
247         case KeyEvent.VK_ENTER: // enter key
248           if (btn_view.isEnabled())
249           {
250             ok_ActionPerformed();
251           }
252           break;
253         case KeyEvent.VK_TAB: // tab key
254           if (evt.isShiftDown())
255           {
256             pnl_filter.requestFocus();
257           }
258           else
259           {
260             btn_view.requestFocus();
261           }
262           evt.consume();
263           break;
264         default:
265           return;
266         }
267       }
268     });
269     tbl_local_pdb.setAutoCreateRowSorter(true);
270     tbl_local_pdb.getTableHeader().setReorderingAllowed(false);
271     tbl_local_pdb.addMouseListener(new MouseAdapter()
272     {
273       @Override
274       public void mouseClicked(MouseEvent e)
275       {
276         validateSelections();
277       }
278
279       @Override
280       public void mouseReleased(MouseEvent e)
281       {
282         validateSelections();
283       }
284     });
285     tbl_local_pdb.addKeyListener(new KeyAdapter()
286     {
287       @Override
288       public void keyPressed(KeyEvent evt)
289       {
290         validateSelections();
291         switch (evt.getKeyCode())
292         {
293         case KeyEvent.VK_ESCAPE: // escape key
294           mainFrame.dispose();
295           break;
296         case KeyEvent.VK_ENTER: // enter key
297           if (btn_view.isEnabled())
298           {
299             ok_ActionPerformed();
300           }
301           break;
302         case KeyEvent.VK_TAB: // tab key
303           if (evt.isShiftDown())
304           {
305             cmb_filterOption.requestFocus();
306           }
307           else
308           {
309             if (btn_view.isEnabled())
310             {
311               btn_view.requestFocus();
312             }
313             else
314             {
315               btn_cancel.requestFocus();
316             }
317           }
318           evt.consume();
319         default:
320           return;
321         }
322       }
323     });
324     btn_view.setFont(new java.awt.Font("Verdana", 0, 12));
325     btn_view.setText(MessageManager.getString("action.view"));
326     btn_view.addActionListener(new java.awt.event.ActionListener()
327     {
328       @Override
329       public void actionPerformed(ActionEvent e)
330       {
331         ok_ActionPerformed();
332       }
333     });
334     btn_view.addKeyListener(new KeyAdapter()
335     {
336       @Override
337       public void keyPressed(KeyEvent evt)
338       {
339         if (evt.getKeyCode() == KeyEvent.VK_ENTER)
340         {
341           ok_ActionPerformed();
342         }
343       }
344     });
345
346     btn_cancel.setFont(new java.awt.Font("Verdana", 0, 12));
347     btn_cancel.setText(MessageManager.getString("action.cancel"));
348     btn_cancel.addActionListener(new java.awt.event.ActionListener()
349     {
350       @Override
351       public void actionPerformed(ActionEvent e)
352       {
353         mainFrame.dispose();
354       }
355     });
356     btn_cancel.addKeyListener(new KeyAdapter()
357     {
358       @Override
359       public void keyPressed(KeyEvent evt)
360       {
361         if (evt.getKeyCode() == KeyEvent.VK_ENTER)
362         {
363           mainFrame.dispose();
364         }
365       }
366     });
367
368     btn_pdbFromFile.setFont(new java.awt.Font("Verdana", 0, 12));
369     String btn_title = MessageManager.getString("label.select_pdb_file");
370     btn_pdbFromFile.setText(btn_title + "              ");
371     btn_pdbFromFile.addActionListener(new java.awt.event.ActionListener()
372     {
373       @Override
374       public void actionPerformed(ActionEvent e)
375       {
376         pdbFromFile_actionPerformed();
377       }
378     });
379     btn_pdbFromFile.addKeyListener(new KeyAdapter()
380     {
381       @Override
382       public void keyPressed(KeyEvent evt)
383       {
384         if (evt.getKeyCode() == KeyEvent.VK_ENTER)
385         {
386           pdbFromFile_actionPerformed();
387         }
388       }
389     });
390
391     scrl_foundStructures.setPreferredSize(new Dimension(500, 300));
392     scrl_foundStructures
393             .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
394
395     scrl_localPDB.setPreferredSize(new Dimension(500, 300));
396     scrl_localPDB
397             .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
398
399     cmb_filterOption.setFont(new java.awt.Font("Verdana", 0, 12));
400     chk_invertFilter.setFont(new java.awt.Font("Verdana", 0, 12));
401     chk_rememberSettings.setFont(new java.awt.Font("Verdana", 0, 12));
402     chk_rememberSettings.setVisible(false);
403
404     txt_search.setToolTipText(MessageManager
405             .getString("label.enter_pdb_id"));
406     cmb_filterOption.setToolTipText(MessageManager
407             .getString("info.select_filter_option"));
408     txt_search.getDocument().addDocumentListener(new DocumentListener()
409     {
410       @Override
411       public void insertUpdate(DocumentEvent e)
412       {
413         txt_search_ActionPerformed();
414       }
415
416       @Override
417       public void removeUpdate(DocumentEvent e)
418       {
419         txt_search_ActionPerformed();
420       }
421
422       @Override
423       public void changedUpdate(DocumentEvent e)
424       {
425         txt_search_ActionPerformed();
426       }
427     });
428
429     cmb_filterOption.addItemListener(this);
430     chk_invertFilter.addItemListener(this);
431
432     pnl_actions.add(chk_rememberSettings);
433     pnl_actions.add(btn_view);
434     pnl_actions.add(btn_cancel);
435
436     // pnl_filter.add(lbl_result);
437     pnl_main.add(cmb_filterOption);
438     pnl_main.add(lbl_loading);
439     pnl_main.add(chk_invertFilter);
440     lbl_loading.setVisible(false);
441
442     pnl_fileChooser.add(btn_pdbFromFile);
443     pnl_fileChooser.add(lbl_fromFileStatus);
444     pnl_fileChooserBL.add(fileChooserAssSeqPanel, BorderLayout.NORTH);
445     pnl_fileChooserBL.add(pnl_fileChooser, BorderLayout.CENTER);
446
447     pnl_idInput.add(txt_search);
448     pnl_idInput.add(lbl_pdbManualFetchStatus);
449     pnl_idInputBL.add(idInputAssSeqPanel, BorderLayout.NORTH);
450     pnl_idInputBL.add(pnl_idInput, BorderLayout.CENTER);
451
452     final String foundStructureSummary = MessageManager
453             .getString("label.found_structures_summary");
454     final String configureCols = MessageManager
455             .getString("label.configure_displayed_columns");
456     ChangeListener changeListener = new ChangeListener()
457     {
458       @Override
459       public void stateChanged(ChangeEvent changeEvent)
460       {
461         JTabbedPane sourceTabbedPane = (JTabbedPane) changeEvent
462                 .getSource();
463         int index = sourceTabbedPane.getSelectedIndex();
464         btn_view.setVisible(true);
465         btn_cancel.setVisible(true);
466         if (sourceTabbedPane.getTitleAt(index).equals(configureCols))
467         {
468           btn_view.setEnabled(false);
469           btn_cancel.setEnabled(false);
470           btn_view.setVisible(false);
471           btn_cancel.setVisible(false);
472           previousWantedFields = PDBDocFieldPreferences
473                   .getStructureSummaryFields().toArray(
474                           new PDBRestClient.PDBDocField[0]);
475         }
476         if (sourceTabbedPane.getTitleAt(index)
477                 .equals(foundStructureSummary))
478         {
479           btn_cancel.setEnabled(true);
480           if (wantedFieldsUpdated())
481           {
482             tabRefresh();
483           }
484           else
485           {
486             validateSelections();
487           }
488         }
489       }
490     };
491     pnl_filter.addChangeListener(changeListener);
492     pnl_filter.setPreferredSize(new Dimension(500, 300));
493     pnl_filter.add(foundStructureSummary, scrl_foundStructures);
494     pnl_filter.add(configureCols, pdbDocFieldPrefs);
495
496     pnl_locPDB.add(scrl_localPDB);
497
498     pnl_switchableViews.add(pnl_fileChooserBL, VIEWS_FROM_FILE);
499     pnl_switchableViews.add(pnl_idInputBL, VIEWS_ENTER_ID);
500     pnl_switchableViews.add(pnl_filter, VIEWS_FILTER);
501     pnl_switchableViews.add(pnl_locPDB, VIEWS_LOCAL_PDB);
502
503     this.setLayout(mainLayout);
504     this.add(pnl_main, java.awt.BorderLayout.NORTH);
505     this.add(pnl_switchableViews, java.awt.BorderLayout.CENTER);
506     // this.add(pnl_actions, java.awt.BorderLayout.SOUTH);
507     statusPanel.setLayout(new GridLayout());
508     pnl_actionsAndStatus.add(pnl_actions, BorderLayout.CENTER);
509     pnl_actionsAndStatus.add(statusPanel, BorderLayout.SOUTH);
510     statusPanel.add(statusBar, null);
511     this.add(pnl_actionsAndStatus, java.awt.BorderLayout.SOUTH);
512
513     mainFrame.setVisible(true);
514     mainFrame.setContentPane(this);
515     mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
516     Desktop.addInternalFrame(mainFrame, frameTitle, 800, 400);
517   }
518
519   public boolean wantedFieldsUpdated()
520   {
521     if (previousWantedFields == null)
522     {
523       return true;
524     }
525
526     return Arrays.equals(PDBDocFieldPreferences.getStructureSummaryFields()
527             .toArray(new PDBRestClient.PDBDocField[0]),
528             previousWantedFields) ? false : true;
529
530   }
531
532   @Override
533   /**
534    * Event listener for the 'filter' combo-box and 'invert' check-box
535    */
536   public void itemStateChanged(ItemEvent e)
537   {
538     stateChanged(e);
539   }
540
541   /**
542    * This inner class provides the data model for the structure filter combo-box
543    * 
544    * @author tcnofoegbu
545    *
546    */
547   public class FilterOption
548   {
549     private String name;
550
551     private String value;
552
553     private String view;
554
555     public FilterOption(String name, String value, String view)
556     {
557       this.name = name;
558       this.value = value;
559       this.view = view;
560     }
561
562     public String getName()
563     {
564       return name;
565     }
566
567     public void setName(String name)
568     {
569       this.name = name;
570     }
571
572     public String getValue()
573     {
574       return value;
575     }
576
577     public void setValue(String value)
578     {
579       this.value = value;
580     }
581
582     public String getView()
583     {
584       return view;
585     }
586
587     public void setView(String view)
588     {
589       this.view = view;
590     }
591
592     @Override
593     public String toString()
594     {
595       return this.name;
596     }
597   }
598
599   /**
600    * This inner class provides the provides the data model for associate
601    * sequence combo-box - cmb_assSeq
602    * 
603    * @author tcnofoegbu
604    *
605    */
606   public class AssociateSeqOptions
607   {
608     private SequenceI sequence;
609
610     private String name;
611
612     public AssociateSeqOptions(SequenceI seq)
613     {
614       this.sequence = seq;
615       this.name = (seq.getName().length() >= 23) ? seq.getName().substring(
616               0, 23) : seq.getName();
617     }
618
619     public AssociateSeqOptions(String name, SequenceI seq)
620     {
621       this.name = name;
622       this.sequence = seq;
623     }
624
625     @Override
626     public String toString()
627     {
628       return name;
629     }
630
631     public String getName()
632     {
633       return name;
634     }
635
636     public void setName(String name)
637     {
638       this.name = name;
639     }
640
641     public SequenceI getSequence()
642     {
643       return sequence;
644     }
645
646     public void setSequence(SequenceI sequence)
647     {
648       this.sequence = sequence;
649     }
650
651   }
652
653   /**
654    * This inner class holds the Layout and configuration of the panel which
655    * handles association of manually fetched structures to a unique sequence
656    * when more than one sequence selection is made
657    * 
658    * @author tcnofoegbu
659    *
660    */
661   public class AssciateSeqPanel extends JPanel implements ItemListener
662   {
663     private JComboBox<AssociateSeqOptions> cmb_assSeq = new JComboBox<AssociateSeqOptions>();
664
665     private JLabel lbl_associateSeq = new JLabel();
666
667     public AssciateSeqPanel()
668     {
669       this.setLayout(new FlowLayout());
670       this.add(cmb_assSeq);
671       this.add(lbl_associateSeq);
672       cmb_assSeq.setToolTipText(MessageManager
673               .getString("info.associate_wit_sequence"));
674       cmb_assSeq.addItemListener(this);
675     }
676
677     public void loadCmbAssSeq()
678     {
679       populateCmbAssociateSeqOptions(cmb_assSeq, lbl_associateSeq);
680     }
681
682     public JComboBox<AssociateSeqOptions> getCmb_assSeq()
683     {
684       return cmb_assSeq;
685     }
686
687     public void setCmb_assSeq(JComboBox<AssociateSeqOptions> cmb_assSeq)
688     {
689       this.cmb_assSeq = cmb_assSeq;
690     }
691
692     @Override
693     public void itemStateChanged(ItemEvent e)
694     {
695       if (e.getStateChange() == ItemEvent.SELECTED)
696       {
697         cmbAssSeqStateChanged();
698       }
699     }
700   }
701
702   public JComboBox<FilterOption> getCmbFilterOption()
703   {
704     return cmb_filterOption;
705   }
706
707   protected abstract void stateChanged(ItemEvent e);
708
709   protected abstract void updateCurrentView();
710
711   protected abstract void populateFilterComboBox();
712
713   protected abstract void ok_ActionPerformed();
714
715   protected abstract void pdbFromFile_actionPerformed();
716
717   protected abstract void txt_search_ActionPerformed();
718
719   public abstract void populateCmbAssociateSeqOptions(
720           JComboBox<AssociateSeqOptions> cmb_assSeq, JLabel lbl_associateSeq);
721
722   public abstract void cmbAssSeqStateChanged();
723
724   public abstract void tabRefresh();
725
726   public abstract void validateSelections();
727 }