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