JAL-1563 house keeping/updated API doc for new method added for FTS pagination
[jalview.git] / src / jalview / fts / core / GFTSPanel.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ 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.fts.core;
23
24 import jalview.fts.api.FTSDataColumnI;
25 import jalview.fts.api.GFTSPanelI;
26 import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource;
27 import jalview.gui.Desktop;
28 import jalview.gui.IProgressIndicator;
29 import jalview.gui.JvSwingUtils;
30 import jalview.gui.SequenceFetcher;
31 import jalview.util.MessageManager;
32
33 import java.awt.BorderLayout;
34 import java.awt.CardLayout;
35 import java.awt.Dimension;
36 import java.awt.event.ActionEvent;
37 import java.awt.event.ActionListener;
38 import java.awt.event.FocusEvent;
39 import java.awt.event.FocusListener;
40 import java.awt.event.KeyAdapter;
41 import java.awt.event.KeyEvent;
42 import java.awt.event.MouseAdapter;
43 import java.awt.event.MouseEvent;
44 import java.util.ArrayList;
45 import java.util.Arrays;
46 import java.util.Collection;
47 import java.util.Collections;
48 import java.util.Comparator;
49 import java.util.List;
50
51 import javax.swing.ImageIcon;
52 import javax.swing.JButton;
53 import javax.swing.JComboBox;
54 import javax.swing.JFrame;
55 import javax.swing.JInternalFrame;
56 import javax.swing.JLabel;
57 import javax.swing.JPanel;
58 import javax.swing.JScrollPane;
59 import javax.swing.JTabbedPane;
60 import javax.swing.JTable;
61 import javax.swing.JTextField;
62 import javax.swing.Timer;
63 import javax.swing.event.ChangeEvent;
64 import javax.swing.event.ChangeListener;
65 import javax.swing.event.DocumentEvent;
66 import javax.swing.event.DocumentListener;
67 import javax.swing.table.DefaultTableModel;
68 import javax.swing.table.TableColumn;
69
70 /**
71  * This class provides the swing GUI layout for FTS Panel and implements most of
72  * the contracts defined in GFSPanelI
73  * 
74  * @author tcnofoegbu
75  *
76  */
77
78 @SuppressWarnings("serial")
79 public abstract class GFTSPanel extends JPanel implements GFTSPanelI
80 {
81   protected JInternalFrame mainFrame = new JInternalFrame(
82           getFTSFrameTitle());
83
84   protected IProgressIndicator progressIdicator;
85
86   protected JComboBox<FTSDataColumnI> cmb_searchTarget = new JComboBox<FTSDataColumnI>();
87
88   protected JButton btn_ok = new JButton();
89
90   protected JButton btn_back = new JButton();
91
92   protected JButton btn_cancel = new JButton();
93
94   protected JTextField txt_search = new JTextField(35);
95
96   protected SequenceFetcher seqFetcher;
97
98   protected Collection<FTSDataColumnI> wantedFields;
99
100   private String lastSearchTerm = "";
101
102   protected JButton btn_next_page = new JButton();
103
104   protected JButton btn_prev_page = new JButton();
105
106   private JTable tbl_summary = new JTable()
107   {
108     private boolean inLayout;
109
110     @Override
111     public boolean getScrollableTracksViewportWidth()
112     {
113       return hasExcessWidth();
114
115     }
116
117     @Override
118     public void doLayout()
119     {
120       if (hasExcessWidth())
121       {
122         autoResizeMode = AUTO_RESIZE_SUBSEQUENT_COLUMNS;
123       }
124       inLayout = true;
125       super.doLayout();
126       inLayout = false;
127       autoResizeMode = AUTO_RESIZE_OFF;
128     }
129
130     protected boolean hasExcessWidth()
131     {
132       return getPreferredSize().width < getParent().getWidth();
133     }
134
135     @Override
136     public void columnMarginChanged(ChangeEvent e)
137     {
138       if (isEditing())
139       {
140         removeEditor();
141       }
142       TableColumn resizingColumn = getTableHeader().getResizingColumn();
143       // Need to do this here, before the parent's
144       // layout manager calls getPreferredSize().
145       if (resizingColumn != null && autoResizeMode == AUTO_RESIZE_OFF
146               && !inLayout)
147       {
148         resizingColumn.setPreferredWidth(resizingColumn.getWidth());
149       }
150       resizeAndRepaint();
151     }
152
153     @Override
154     public String getToolTipText(MouseEvent evt)
155     {
156       String toolTipText = null;
157       java.awt.Point pnt = evt.getPoint();
158       int rowIndex = rowAtPoint(pnt);
159       int colIndex = columnAtPoint(pnt);
160
161       try
162       {
163         if (getValueAt(rowIndex, colIndex) == null)
164         {
165           return null;
166         }
167         toolTipText = getValueAt(rowIndex, colIndex).toString();
168
169       } catch (Exception e)
170       {
171         e.printStackTrace();
172       }
173       toolTipText = (toolTipText == null ? null
174               : (toolTipText.length() > 500 ? JvSwingUtils.wrapTooltip(
175                       true, toolTipText.subSequence(0, 500) + "...")
176                       : JvSwingUtils.wrapTooltip(true, toolTipText)));
177
178       return toolTipText;
179     }
180   };
181
182   protected StringBuilder errorWarning = new StringBuilder();
183
184   protected JScrollPane scrl_searchResult = new JScrollPane(tbl_summary);
185
186   protected ImageIcon warningImage = new ImageIcon(getClass().getResource(
187           "/images/warning.gif"));
188
189   protected ImageIcon loadingImage = new ImageIcon(getClass().getResource(
190           "/images/loading.gif"));
191
192   protected JLabel lbl_warning = new JLabel(warningImage);
193
194   protected JLabel lbl_loading = new JLabel(loadingImage);
195
196   private JTabbedPane tabbedPane = new JTabbedPane();
197
198   private JPanel pnl_actions = new JPanel();
199
200   private JPanel pnl_results = new JPanel(new CardLayout());
201
202   private JPanel pnl_inputs = new JPanel();
203
204   private BorderLayout mainLayout = new BorderLayout();
205
206   protected Object[] previousWantedFields;
207
208   public GFTSPanel()
209   {
210     try
211     {
212       jbInit();
213       mainFrame.invalidate();
214       mainFrame.pack();
215     } catch (Exception e)
216     {
217       e.printStackTrace();
218     }
219   }
220
221   /**
222    * Initializes the GUI default properties
223    * 
224    * @throws Exception
225    */
226   private void jbInit() throws Exception
227   {
228     lbl_warning.setVisible(false);
229     lbl_warning.setFont(new java.awt.Font("Verdana", 0, 12));
230     lbl_loading.setVisible(false);
231     lbl_loading.setFont(new java.awt.Font("Verdana", 0, 12));
232
233     tbl_summary.setAutoCreateRowSorter(true);
234     tbl_summary.getTableHeader().setReorderingAllowed(false);
235     tbl_summary.addMouseListener(new MouseAdapter()
236     {
237       @Override
238       public void mouseClicked(MouseEvent e)
239       {
240         validateSelection();
241       }
242
243       @Override
244       public void mouseReleased(MouseEvent e)
245       {
246         validateSelection();
247       }
248     });
249     tbl_summary.addKeyListener(new KeyAdapter()
250     {
251       @Override
252       public void keyPressed(KeyEvent evt)
253       {
254         validateSelection();
255         switch (evt.getKeyCode())
256         {
257         case KeyEvent.VK_ESCAPE: // escape key
258           btn_back_ActionPerformed();
259           break;
260         case KeyEvent.VK_ENTER: // enter key
261           if (btn_ok.isEnabled())
262           {
263             okAction();
264           }
265           evt.consume();
266           break;
267         case KeyEvent.VK_TAB: // tab key
268           if (evt.isShiftDown())
269           {
270             tabbedPane.requestFocus();
271           }
272           else
273           {
274             btn_back.requestFocus();
275           }
276           evt.consume();
277           break;
278         default:
279           return;
280         }
281       }
282     });
283
284     btn_back.setFont(new java.awt.Font("Verdana", 0, 12));
285     btn_back.setText(MessageManager.getString("action.back"));
286     btn_back.addActionListener(new java.awt.event.ActionListener()
287     {
288       @Override
289       public void actionPerformed(ActionEvent e)
290       {
291         btn_back_ActionPerformed();
292       }
293     });
294     btn_back.addKeyListener(new KeyAdapter()
295     {
296       @Override
297       public void keyPressed(KeyEvent evt)
298       {
299         if (evt.getKeyCode() == KeyEvent.VK_ENTER)
300         {
301           btn_back_ActionPerformed();
302         }
303       }
304     });
305
306     btn_ok.setEnabled(false);
307     btn_ok.setFont(new java.awt.Font("Verdana", 0, 12));
308     btn_ok.setText(MessageManager.getString("action.ok"));
309     btn_ok.addActionListener(new java.awt.event.ActionListener()
310     {
311       @Override
312       public void actionPerformed(ActionEvent e)
313       {
314         okAction();
315       }
316     });
317     btn_ok.addKeyListener(new KeyAdapter()
318     {
319       @Override
320       public void keyPressed(KeyEvent evt)
321       {
322         if (evt.getKeyCode() == KeyEvent.VK_ENTER)
323         {
324           okAction();
325         }
326       }
327     });
328
329     btn_next_page.setEnabled(false);
330     btn_next_page.setFont(new java.awt.Font("Verdana", 0, 12));
331     btn_next_page.setText(MessageManager.getString("action.next_page"));
332     btn_next_page.addActionListener(new java.awt.event.ActionListener()
333     {
334       @Override
335       public void actionPerformed(ActionEvent e)
336       {
337         nextPageAction();
338       }
339     });
340     btn_next_page.addKeyListener(new KeyAdapter()
341     {
342       @Override
343       public void keyPressed(KeyEvent evt)
344       {
345         if (evt.getKeyCode() == KeyEvent.VK_ENTER)
346         {
347           nextPageAction();
348         }
349       }
350     });
351
352     btn_prev_page.setEnabled(false);
353     btn_prev_page.setFont(new java.awt.Font("Verdana", 0, 12));
354     btn_prev_page.setText(MessageManager.getString("action.prev_page"));
355     btn_prev_page.addActionListener(new java.awt.event.ActionListener()
356     {
357       @Override
358       public void actionPerformed(ActionEvent e)
359       {
360         prevPageAction();
361       }
362     });
363     btn_prev_page.addKeyListener(new KeyAdapter()
364     {
365       @Override
366       public void keyPressed(KeyEvent evt)
367       {
368         if (evt.getKeyCode() == KeyEvent.VK_ENTER)
369         {
370           prevPageAction();
371         }
372       }
373     });
374
375     if (isPaginationEnabled())
376     {
377       btn_prev_page.setVisible(true);
378       btn_next_page.setVisible(true);
379     }
380     else
381     {
382       btn_prev_page.setVisible(false);
383       btn_next_page.setVisible(false);
384     }
385
386     btn_cancel.setFont(new java.awt.Font("Verdana", 0, 12));
387     btn_cancel.setText(MessageManager.getString("action.cancel"));
388     btn_cancel.addActionListener(new java.awt.event.ActionListener()
389     {
390       @Override
391       public void actionPerformed(ActionEvent e)
392       {
393         btn_cancel_ActionPerformed();
394       }
395     });
396     btn_cancel.addKeyListener(new KeyAdapter()
397     {
398       @Override
399       public void keyPressed(KeyEvent evt)
400       {
401         if (evt.getKeyCode() == KeyEvent.VK_ENTER)
402         {
403           btn_cancel_ActionPerformed();
404         }
405       }
406     });
407     scrl_searchResult.setPreferredSize(new Dimension(800, 400));
408
409     cmb_searchTarget.setFont(new java.awt.Font("Verdana", 0, 12));
410     cmb_searchTarget.addActionListener(new ActionListener()
411     {
412       @Override
413       public void actionPerformed(ActionEvent e)
414       {
415         String tooltipText;
416         if ("all".equalsIgnoreCase(getCmbSearchTarget().getSelectedItem()
417                 .toString()))
418         {
419           tooltipText = MessageManager.getString("label.search_all");
420         }
421         else if ("pdb id".equalsIgnoreCase(getCmbSearchTarget()
422                 .getSelectedItem().toString()))
423         {
424           tooltipText = MessageManager
425                   .getString("label.separate_multiple_accession_ids");
426         }
427         else
428         {
429           tooltipText = MessageManager.formatMessage(
430                   "label.separate_multiple_query_values",
431                   new Object[] { getCmbSearchTarget().getSelectedItem()
432                           .toString() });
433         }
434         txt_search.setToolTipText(JvSwingUtils.wrapTooltip(true,
435                 tooltipText));
436         searchAction();
437       }
438     });
439
440     populateCmbSearchTargetOptions();
441
442     txt_search.setFont(new java.awt.Font("Verdana", 0, 12));
443
444     txt_search.addKeyListener(new KeyAdapter()
445     {
446       @Override
447       public void keyPressed(KeyEvent e)
448       {
449         if (e.getKeyCode() == KeyEvent.VK_ENTER)
450         {
451           if (txt_search.getText() == null
452                   || txt_search.getText().isEmpty())
453           {
454             return;
455           }
456           String primaryKeyName = getFTSRestClient().getPrimaryKeyColumn()
457                   .getName();
458           if (primaryKeyName.equalsIgnoreCase(getCmbSearchTarget()
459                   .getSelectedItem().toString()))
460           {
461             transferToSequenceFetcher(txt_search.getText());
462           }
463         }
464       }
465     });
466
467     final DeferredTextInputListener listener = new DeferredTextInputListener(
468             2500,
469             new ActionListener()
470             {
471               @Override
472               public void actionPerformed(ActionEvent e)
473               {
474                 if (!getTypedText().equalsIgnoreCase(lastSearchTerm))
475                 {
476                   searchAction();
477                   lastSearchTerm = getTypedText();
478                 }
479               }
480             }, false);
481     txt_search.getDocument().addDocumentListener(listener);
482     txt_search.addFocusListener(new FocusListener()
483     {
484       @Override
485       public void focusGained(FocusEvent e)
486       {
487         listener.start();
488       }
489
490       @Override
491       public void focusLost(FocusEvent e)
492       {
493         listener.stop();
494       }
495     });
496
497     final String searchTabTitle = MessageManager
498             .getString("label.search_result");
499     final String configureCols = MessageManager
500             .getString("label.configure_displayed_columns");
501     ChangeListener changeListener = new ChangeListener()
502     {
503       @Override
504       public void stateChanged(ChangeEvent changeEvent)
505       {
506         JTabbedPane sourceTabbedPane = (JTabbedPane) changeEvent
507                 .getSource();
508         int index = sourceTabbedPane.getSelectedIndex();
509
510         btn_back.setVisible(true);
511         btn_cancel.setVisible(true);
512         btn_ok.setVisible(true);
513         if (sourceTabbedPane.getTitleAt(index).equals(configureCols))
514         {
515           btn_back.setVisible(false);
516           btn_cancel.setVisible(false);
517           btn_ok.setVisible(false);
518           btn_back.setEnabled(false);
519           btn_cancel.setEnabled(false);
520           btn_ok.setEnabled(false);
521           previousWantedFields = getFTSRestClient()
522                   .getAllDefaulDisplayedDataColumns()
523                   .toArray(new Object[0]);
524         }
525         if (sourceTabbedPane.getTitleAt(index).equals(searchTabTitle))
526         {
527           btn_back.setEnabled(true);
528           btn_cancel.setEnabled(true);
529           if (wantedFieldsUpdated())
530           {
531             searchAction();
532           }
533           else
534           {
535             validateSelection();
536           }
537         }
538       }
539     };
540     tabbedPane.addChangeListener(changeListener);
541     tabbedPane.setPreferredSize(new Dimension(800, 400));
542     tabbedPane.add(searchTabTitle, scrl_searchResult);
543     tabbedPane.add(configureCols, new FTSDataColumnPreferences(
544             PreferenceSource.SEARCH_SUMMARY, getFTSRestClient()));
545
546     pnl_actions.add(btn_back);
547     pnl_actions.add(btn_ok);
548     pnl_actions.add(btn_cancel);
549
550     pnl_results.add(tabbedPane);
551     pnl_inputs.add(cmb_searchTarget);
552     pnl_inputs.add(txt_search);
553     pnl_inputs.add(lbl_loading);
554     pnl_inputs.add(lbl_warning);
555     pnl_inputs.add(btn_prev_page);
556     pnl_inputs.add(btn_next_page);
557
558     this.setLayout(mainLayout);
559     this.add(pnl_inputs, java.awt.BorderLayout.NORTH);
560     this.add(pnl_results, java.awt.BorderLayout.CENTER);
561     this.add(pnl_actions, java.awt.BorderLayout.SOUTH);
562     mainFrame.setVisible(true);
563     mainFrame.setContentPane(this);
564     mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
565     Desktop.addInternalFrame(mainFrame, getFTSFrameTitle(), 900, 500);
566   }
567
568   public class DeferredTextInputListener implements DocumentListener
569   {
570     private final Timer swingTimer;
571
572     public DeferredTextInputListener(int timeOut, ActionListener listener,
573             boolean repeats)
574     {
575       swingTimer = new Timer(timeOut, listener);
576       swingTimer.setRepeats(repeats);
577     }
578
579     public void start()
580     {
581       swingTimer.start();
582     }
583
584     public void stop()
585     {
586       swingTimer.stop();
587     }
588
589     @Override
590     public void insertUpdate(DocumentEvent e)
591     {
592       swingTimer.restart();
593     }
594
595     @Override
596     public void removeUpdate(DocumentEvent e)
597     {
598       swingTimer.restart();
599     }
600
601     @Override
602     public void changedUpdate(DocumentEvent e)
603     {
604       swingTimer.restart();
605     }
606
607   }
608
609   public boolean wantedFieldsUpdated()
610   {
611     if (previousWantedFields == null)
612     {
613       return true;
614     }
615
616     return Arrays.equals(getFTSRestClient()
617             .getAllDefaulDisplayedDataColumns()
618             .toArray(new Object[0]), previousWantedFields) ? false
619             : true;
620
621   }
622
623   public void validateSelection()
624   {
625     if (tbl_summary.getSelectedRows().length > 0)
626     {
627       btn_ok.setEnabled(true);
628     }
629     else
630     {
631       btn_ok.setEnabled(false);
632     }
633   }
634
635   public JComboBox<FTSDataColumnI> getCmbSearchTarget()
636   {
637     return cmb_searchTarget;
638   }
639
640   public JTextField getTxtSearch()
641   {
642     return txt_search;
643   }
644
645   public JInternalFrame getMainFrame()
646   {
647     return mainFrame;
648   }
649
650   protected void delayAndEnableActionButtons()
651   {
652     new Thread()
653     {
654       @Override
655       public void run()
656       {
657         try
658         {
659           Thread.sleep(1500);
660         } catch (InterruptedException e)
661         {
662           e.printStackTrace();
663         }
664         btn_ok.setEnabled(true);
665         btn_back.setEnabled(true);
666         btn_cancel.setEnabled(true);
667       }
668     }.start();
669   }
670
671   protected void checkForErrors()
672   {
673     lbl_warning.setVisible(false);
674     if (errorWarning.length() > 0)
675     {
676       lbl_loading.setVisible(false);
677       lbl_warning.setToolTipText(JvSwingUtils.wrapTooltip(true,
678               errorWarning.toString()));
679       lbl_warning.setVisible(true);
680     }
681   }
682
683   protected void btn_back_ActionPerformed()
684   {
685     mainFrame.dispose();
686     new SequenceFetcher(progressIdicator);
687   }
688
689   protected void disableActionButtons()
690   {
691     btn_ok.setEnabled(false);
692     btn_back.setEnabled(false);
693     btn_cancel.setEnabled(false);
694   }
695
696   protected void btn_cancel_ActionPerformed()
697   {
698     mainFrame.dispose();
699   }
700
701   /**
702    * Populates search target combo-box options
703    */
704   public void populateCmbSearchTargetOptions()
705   {
706     List<FTSDataColumnI> searchableTargets = new ArrayList<FTSDataColumnI>();
707     try
708     {
709       Collection<FTSDataColumnI> foundFTSTargets = getFTSRestClient()
710               .getSearchableDataColumns();
711       searchableTargets.addAll(foundFTSTargets);
712     } catch (Exception e)
713     {
714       e.printStackTrace();
715     }
716
717     Collections.sort(searchableTargets, new Comparator<FTSDataColumnI>()
718     {
719       @Override
720       public int compare(FTSDataColumnI o1, FTSDataColumnI o2)
721       {
722         return o1.getName().compareTo(o2.getName());
723       }
724     });
725
726     for (FTSDataColumnI searchTarget : searchableTargets)
727     {
728       cmb_searchTarget.addItem(searchTarget);
729     }
730   }
731
732
733   public void transferToSequenceFetcher(String ids)
734   {
735     // mainFrame.dispose();
736     seqFetcher.getTextArea().setText(ids);
737     Thread worker = new Thread(seqFetcher);
738     worker.start();
739   }
740
741   @Override
742   public String getTypedText()
743   {
744     return txt_search.getText().trim();
745   }
746
747   @Override
748   public JTable getResultTable()
749   {
750     return tbl_summary;
751   }
752
753   public void reset()
754   {
755     lbl_loading.setVisible(false);
756     errorWarning.setLength(0);
757     lbl_warning.setVisible(false);
758     btn_ok.setEnabled(false);
759     mainFrame.setTitle(getFTSFrameTitle());
760     referesh();
761     tbl_summary.setModel(new DefaultTableModel());
762     tbl_summary.setVisible(false);
763   }
764
765   @Override
766   public void setPrevPageButtonEnabled(boolean isEnabled)
767   {
768     btn_prev_page.setEnabled(isEnabled);
769   }
770
771   @Override
772   public void setNextPageButtonEnabled(boolean isEnabled)
773   {
774     btn_next_page.setEnabled(isEnabled);
775   }
776
777   @Override
778   public void setErrorMessage(String message)
779   {
780     errorWarning.append(message);
781   }
782
783   @Override
784   public void updateSearchFrameTitle(String title)
785   {
786     mainFrame.setTitle(title);
787   }
788
789   @Override
790   public void setSearchInProgress(Boolean isSearchInProgress)
791   {
792     lbl_loading.setVisible(isSearchInProgress);
793   }
794   public void referesh()
795   {
796     mainFrame.setTitle(getFTSFrameTitle());
797   }
798
799 }