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