JAL-1990 added a progress indicator for Structure Chooser interface
[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         if (sourceTabbedPane.getTitleAt(index).equals(configureCols))
465         {
466           btn_view.setEnabled(false);
467           btn_cancel.setEnabled(false);
468           previousWantedFields = PDBDocFieldPreferences
469                   .getStructureSummaryFields().toArray(
470                           new PDBRestClient.PDBDocField[0]);
471         }
472         if (sourceTabbedPane.getTitleAt(index)
473                 .equals(foundStructureSummary))
474         {
475           btn_cancel.setEnabled(true);
476           if (wantedFieldsUpdated())
477           {
478             tabRefresh();
479           }
480           else
481           {
482             validateSelections();
483           }
484         }
485       }
486     };
487     pnl_filter.addChangeListener(changeListener);
488     pnl_filter.setPreferredSize(new Dimension(500, 300));
489     pnl_filter.add(foundStructureSummary, scrl_foundStructures);
490     pnl_filter.add(configureCols, pdbDocFieldPrefs);
491
492     pnl_locPDB.add(scrl_localPDB);
493
494     pnl_switchableViews.add(pnl_fileChooserBL, VIEWS_FROM_FILE);
495     pnl_switchableViews.add(pnl_idInputBL, VIEWS_ENTER_ID);
496     pnl_switchableViews.add(pnl_filter, VIEWS_FILTER);
497     pnl_switchableViews.add(pnl_locPDB, VIEWS_LOCAL_PDB);
498
499     this.setLayout(mainLayout);
500     this.add(pnl_main, java.awt.BorderLayout.NORTH);
501     this.add(pnl_switchableViews, java.awt.BorderLayout.CENTER);
502     // this.add(pnl_actions, java.awt.BorderLayout.SOUTH);
503     statusPanel.setLayout(new GridLayout());
504     pnl_actionsAndStatus.add(pnl_actions, BorderLayout.CENTER);
505     pnl_actionsAndStatus.add(statusPanel, BorderLayout.SOUTH);
506     statusPanel.add(statusBar, null);
507     this.add(pnl_actionsAndStatus, java.awt.BorderLayout.SOUTH);
508
509     mainFrame.setVisible(true);
510     mainFrame.setContentPane(this);
511     mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
512     Desktop.addInternalFrame(mainFrame, frameTitle, 800, 400);
513   }
514
515   public boolean wantedFieldsUpdated()
516   {
517     if (previousWantedFields == null)
518     {
519       return true;
520     }
521
522     return Arrays.equals(PDBDocFieldPreferences.getStructureSummaryFields()
523             .toArray(new PDBRestClient.PDBDocField[0]),
524             previousWantedFields) ? false : true;
525
526   }
527
528   @Override
529   /**
530    * Event listener for the 'filter' combo-box and 'invert' check-box
531    */
532   public void itemStateChanged(ItemEvent e)
533   {
534     stateChanged(e);
535   }
536
537   /**
538    * This inner class provides the data model for the structure filter combo-box
539    * 
540    * @author tcnofoegbu
541    *
542    */
543   public class FilterOption
544   {
545     private String name;
546
547     private String value;
548
549     private String view;
550
551     public FilterOption(String name, String value, String view)
552     {
553       this.name = name;
554       this.value = value;
555       this.view = view;
556     }
557
558     public String getName()
559     {
560       return name;
561     }
562
563     public void setName(String name)
564     {
565       this.name = name;
566     }
567
568     public String getValue()
569     {
570       return value;
571     }
572
573     public void setValue(String value)
574     {
575       this.value = value;
576     }
577
578     public String getView()
579     {
580       return view;
581     }
582
583     public void setView(String view)
584     {
585       this.view = view;
586     }
587
588     @Override
589     public String toString()
590     {
591       return this.name;
592     }
593   }
594
595   /**
596    * This inner class provides the provides the data model for associate
597    * sequence combo-box - cmb_assSeq
598    * 
599    * @author tcnofoegbu
600    *
601    */
602   public class AssociateSeqOptions
603   {
604     private SequenceI sequence;
605
606     private String name;
607
608     public AssociateSeqOptions(SequenceI seq)
609     {
610       this.sequence = seq;
611       this.name = (seq.getName().length() >= 23) ? seq.getName().substring(
612               0, 23) : seq.getName();
613     }
614
615     public AssociateSeqOptions(String name, SequenceI seq)
616     {
617       this.name = name;
618       this.sequence = seq;
619     }
620
621     @Override
622     public String toString()
623     {
624       return name;
625     }
626
627     public String getName()
628     {
629       return name;
630     }
631
632     public void setName(String name)
633     {
634       this.name = name;
635     }
636
637     public SequenceI getSequence()
638     {
639       return sequence;
640     }
641
642     public void setSequence(SequenceI sequence)
643     {
644       this.sequence = sequence;
645     }
646
647   }
648
649   /**
650    * This inner class holds the Layout and configuration of the panel which
651    * handles association of manually fetched structures to a unique sequence
652    * when more than one sequence selection is made
653    * 
654    * @author tcnofoegbu
655    *
656    */
657   public class AssciateSeqPanel extends JPanel implements ItemListener
658   {
659     private JComboBox<AssociateSeqOptions> cmb_assSeq = new JComboBox<AssociateSeqOptions>();
660
661     private JLabel lbl_associateSeq = new JLabel();
662
663     public AssciateSeqPanel()
664     {
665       this.setLayout(new FlowLayout());
666       this.add(cmb_assSeq);
667       this.add(lbl_associateSeq);
668       cmb_assSeq.setToolTipText(MessageManager
669               .getString("info.associate_wit_sequence"));
670       cmb_assSeq.addItemListener(this);
671     }
672
673     public void loadCmbAssSeq()
674     {
675       populateCmbAssociateSeqOptions(cmb_assSeq, lbl_associateSeq);
676     }
677
678     public JComboBox<AssociateSeqOptions> getCmb_assSeq()
679     {
680       return cmb_assSeq;
681     }
682
683     public void setCmb_assSeq(JComboBox<AssociateSeqOptions> cmb_assSeq)
684     {
685       this.cmb_assSeq = cmb_assSeq;
686     }
687
688     @Override
689     public void itemStateChanged(ItemEvent e)
690     {
691       if (e.getStateChange() == ItemEvent.SELECTED)
692       {
693         cmbAssSeqStateChanged();
694       }
695     }
696   }
697
698   public JComboBox<FilterOption> getCmbFilterOption()
699   {
700     return cmb_filterOption;
701   }
702
703   protected abstract void stateChanged(ItemEvent e);
704
705   protected abstract void updateCurrentView();
706
707   protected abstract void populateFilterComboBox();
708
709   protected abstract void ok_ActionPerformed();
710
711   protected abstract void pdbFromFile_actionPerformed();
712
713   protected abstract void txt_search_ActionPerformed();
714
715   public abstract void populateCmbAssociateSeqOptions(
716           JComboBox<AssociateSeqOptions> cmb_assSeq, JLabel lbl_associateSeq);
717
718   public abstract void cmbAssSeqStateChanged();
719
720   public abstract void tabRefresh();
721
722   public abstract void validateSelections();
723 }