JAL-2071 architectural improvement for Plugable Free Text Search Services
[jalview.git] / src / jalview / fts / core / GFTSPanel.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.fts.core;
23
24 import jalview.fts.api.FTSDataColumnI;
25 import jalview.fts.api.FTSRestClientI;
26 import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource;
27 import jalview.fts.service.pdb.PDBFTSRestClient;
28 import jalview.gui.Desktop;
29 import jalview.gui.IProgressIndicator;
30 import jalview.gui.JvSwingUtils;
31 import jalview.gui.SequenceFetcher;
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.event.ActionEvent;
38 import java.awt.event.ActionListener;
39 import java.awt.event.KeyAdapter;
40 import java.awt.event.KeyEvent;
41 import java.awt.event.MouseAdapter;
42 import java.awt.event.MouseEvent;
43 import java.util.ArrayList;
44 import java.util.Arrays;
45 import java.util.Collection;
46 import java.util.Collections;
47 import java.util.Comparator;
48 import java.util.List;
49
50 import javax.swing.ImageIcon;
51 import javax.swing.JButton;
52 import javax.swing.JComboBox;
53 import javax.swing.JFrame;
54 import javax.swing.JInternalFrame;
55 import javax.swing.JLabel;
56 import javax.swing.JPanel;
57 import javax.swing.JScrollPane;
58 import javax.swing.JTabbedPane;
59 import javax.swing.JTable;
60 import javax.swing.JTextField;
61 import javax.swing.event.ChangeEvent;
62 import javax.swing.event.ChangeListener;
63 import javax.swing.event.DocumentEvent;
64 import javax.swing.event.DocumentListener;
65
66 /**
67  * GUI layout for PDB Fetch Panel
68  * 
69  * @author tcnofoegbu
70  *
71  */
72
73 @SuppressWarnings("serial")
74 public abstract class GFTSPanel extends JPanel
75 {
76   protected JInternalFrame mainFrame = new JInternalFrame(
77           getFTSFrameTitle());
78
79   protected IProgressIndicator progressIdicator;
80
81   protected JComboBox<FTSDataColumnI> cmb_searchTarget = new JComboBox<FTSDataColumnI>();
82
83   protected JButton btn_ok = new JButton();
84
85   protected JButton btn_back = new JButton();
86
87   protected JButton btn_cancel = new JButton();
88
89   protected JTextField txt_search = new JTextField(20);
90
91   protected SequenceFetcher seqFetcher;
92
93   protected Collection<FTSDataColumnI> wantedFields;
94
95   protected JTable tbl_summary = new JTable()
96   {
97     @Override
98     public String getToolTipText(MouseEvent evt)
99     {
100       String toolTipText = null;
101       java.awt.Point pnt = evt.getPoint();
102       int rowIndex = rowAtPoint(pnt);
103       int colIndex = columnAtPoint(pnt);
104
105       try
106       {
107         if (getValueAt(rowIndex, colIndex) == null)
108         {
109           return null;
110         }
111         toolTipText = getValueAt(rowIndex, colIndex).toString();
112
113       } catch (Exception e)
114       {
115         e.printStackTrace();
116       }
117       toolTipText = (toolTipText == null ? null
118               : (toolTipText.length() > 500 ? JvSwingUtils.wrapTooltip(
119                       true, toolTipText.subSequence(0, 500) + "...")
120                       : JvSwingUtils.wrapTooltip(true, toolTipText)));
121
122       return toolTipText;
123     }
124   };
125
126   protected StringBuilder errorWarning = new StringBuilder();
127
128   protected JScrollPane scrl_searchResult = new JScrollPane(tbl_summary);
129
130   protected ImageIcon warningImage = new ImageIcon(getClass().getResource(
131           "/images/warning.gif"));
132
133   protected ImageIcon loadingImage = new ImageIcon(getClass().getResource(
134           "/images/loading.gif"));
135
136   protected JLabel lbl_warning = new JLabel(warningImage);
137
138   protected JLabel lbl_loading = new JLabel(loadingImage);
139
140   private JTabbedPane tabbedPane = new JTabbedPane();
141
142   private JPanel pnl_actions = new JPanel();
143
144   private JPanel pnl_results = new JPanel(new CardLayout());
145
146   private JPanel pnl_inputs = new JPanel();
147
148   private BorderLayout mainLayout = new BorderLayout();
149
150   protected Object[] previousWantedFields;
151
152   public GFTSPanel()
153   {
154     try
155     {
156       jbInit();
157       mainFrame.invalidate();
158       mainFrame.pack();
159     } catch (Exception e)
160     {
161       e.printStackTrace();
162     }
163   }
164
165   /**
166    * Initializes the GUI default properties
167    * 
168    * @throws Exception
169    */
170   private void jbInit() throws Exception
171   {
172     lbl_warning.setVisible(false);
173     lbl_warning.setFont(new java.awt.Font("Verdana", 0, 12));
174     lbl_loading.setVisible(false);
175     lbl_loading.setFont(new java.awt.Font("Verdana", 0, 12));
176
177     tbl_summary.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
178     tbl_summary.setAutoCreateRowSorter(true);
179     tbl_summary.getTableHeader().setReorderingAllowed(false);
180     tbl_summary.addMouseListener(new MouseAdapter()
181     {
182       @Override
183       public void mouseClicked(MouseEvent e)
184       {
185         validateSelection();
186       }
187
188       @Override
189       public void mouseReleased(MouseEvent e)
190       {
191         validateSelection();
192       }
193     });
194     tbl_summary.addKeyListener(new KeyAdapter()
195     {
196       @Override
197       public void keyPressed(KeyEvent evt)
198       {
199         validateSelection();
200         switch (evt.getKeyCode())
201         {
202         case KeyEvent.VK_ESCAPE: // escape key
203           btn_back_ActionPerformed();
204           break;
205         case KeyEvent.VK_ENTER: // enter key
206           if (btn_ok.isEnabled())
207           {
208             btn_ok_ActionPerformed();
209           }
210           evt.consume();
211           break;
212         case KeyEvent.VK_TAB: // tab key
213           if (evt.isShiftDown())
214           {
215             tabbedPane.requestFocus();
216           }
217           else
218           {
219             btn_back.requestFocus();
220           }
221           evt.consume();
222           break;
223         default:
224           return;
225         }
226       }
227     });
228
229     btn_back.setFont(new java.awt.Font("Verdana", 0, 12));
230     btn_back.setText(MessageManager.getString("action.back"));
231     btn_back.addActionListener(new java.awt.event.ActionListener()
232     {
233       @Override
234       public void actionPerformed(ActionEvent e)
235       {
236         btn_back_ActionPerformed();
237       }
238     });
239     btn_back.addKeyListener(new KeyAdapter()
240     {
241       @Override
242       public void keyPressed(KeyEvent evt)
243       {
244         if (evt.getKeyCode() == KeyEvent.VK_ENTER)
245         {
246           btn_back_ActionPerformed();
247         }
248       }
249     });
250
251     btn_ok.setEnabled(false);
252     btn_ok.setFont(new java.awt.Font("Verdana", 0, 12));
253     btn_ok.setText(MessageManager.getString("action.ok"));
254     btn_ok.addActionListener(new java.awt.event.ActionListener()
255     {
256       @Override
257       public void actionPerformed(ActionEvent e)
258       {
259         btn_ok_ActionPerformed();
260       }
261     });
262     btn_ok.addKeyListener(new KeyAdapter()
263     {
264       @Override
265       public void keyPressed(KeyEvent evt)
266       {
267         if (evt.getKeyCode() == KeyEvent.VK_ENTER)
268         {
269           btn_ok_ActionPerformed();
270         }
271       }
272     });
273
274     btn_cancel.setFont(new java.awt.Font("Verdana", 0, 12));
275     btn_cancel.setText(MessageManager.getString("action.cancel"));
276     btn_cancel.addActionListener(new java.awt.event.ActionListener()
277     {
278       @Override
279       public void actionPerformed(ActionEvent e)
280       {
281         btn_cancel_ActionPerformed();
282       }
283     });
284     btn_cancel.addKeyListener(new KeyAdapter()
285     {
286       @Override
287       public void keyPressed(KeyEvent evt)
288       {
289         if (evt.getKeyCode() == KeyEvent.VK_ENTER)
290         {
291           btn_cancel_ActionPerformed();
292         }
293       }
294     });
295
296     scrl_searchResult.setPreferredSize(new Dimension(500, 300));
297     scrl_searchResult
298             .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
299
300     cmb_searchTarget.setFont(new java.awt.Font("Verdana", 0, 12));
301     cmb_searchTarget.addActionListener(new ActionListener()
302     {
303       @Override
304       public void actionPerformed(ActionEvent e)
305       {
306         String tooltipText;
307         if ("all".equalsIgnoreCase(getCmbSearchTarget().getSelectedItem()
308                 .toString()))
309         {
310           tooltipText = MessageManager.getString("label.search_all");
311         }
312         else if ("pdb id".equalsIgnoreCase(getCmbSearchTarget()
313                 .getSelectedItem().toString()))
314         {
315           tooltipText = MessageManager
316                   .getString("label.separate_multiple_accession_ids");
317         }
318         else
319         {
320           tooltipText = MessageManager.formatMessage(
321                   "label.separate_multiple_query_values",
322                   new Object[] { getCmbSearchTarget().getSelectedItem()
323                           .toString() });
324         }
325         txt_search.setToolTipText(JvSwingUtils.wrapTooltip(true,
326                 tooltipText));
327         txt_search_ActionPerformed();
328       }
329     });
330
331     populateCmbSearchTargetOptions();
332
333     txt_search.setFont(new java.awt.Font("Verdana", 0, 12));
334
335     txt_search.addKeyListener(new KeyAdapter()
336     {
337       @Override
338       public void keyPressed(KeyEvent e)
339       {
340         if (e.getKeyCode() == KeyEvent.VK_ENTER)
341         {
342           if (txt_search.getText() == null
343                   || txt_search.getText().isEmpty())
344           {
345             return;
346           }
347           if ("pdb id".equalsIgnoreCase(getCmbSearchTarget()
348                   .getSelectedItem().toString()))
349           {
350             transferToSequenceFetcher(txt_search.getText());
351           }
352         }
353       }
354     });
355
356     txt_search.getDocument().addDocumentListener(new DocumentListener()
357     {
358       @Override
359       public void insertUpdate(DocumentEvent e)
360       {
361         txt_search_ActionPerformed();
362       }
363
364       @Override
365       public void removeUpdate(DocumentEvent e)
366       {
367         txt_search_ActionPerformed();
368       }
369
370       @Override
371       public void changedUpdate(DocumentEvent e)
372       {
373         txt_search_ActionPerformed();
374       }
375     });
376
377     final String searchTabTitle = MessageManager
378             .getString("label.search_result");
379     final String configureCols = MessageManager
380             .getString("label.configure_displayed_columns");
381     ChangeListener changeListener = new ChangeListener()
382     {
383       @Override
384       public void stateChanged(ChangeEvent changeEvent)
385       {
386         JTabbedPane sourceTabbedPane = (JTabbedPane) changeEvent
387                 .getSource();
388         int index = sourceTabbedPane.getSelectedIndex();
389
390         btn_back.setVisible(true);
391         btn_cancel.setVisible(true);
392         btn_ok.setVisible(true);
393         if (sourceTabbedPane.getTitleAt(index).equals(configureCols))
394         {
395           btn_back.setVisible(false);
396           btn_cancel.setVisible(false);
397           btn_ok.setVisible(false);
398           btn_back.setEnabled(false);
399           btn_cancel.setEnabled(false);
400           btn_ok.setEnabled(false);
401           previousWantedFields = PDBFTSRestClient.getInstance()
402                   .getAllDefaulDisplayedDataColumns()
403                   .toArray(new Object[0]);
404         }
405         if (sourceTabbedPane.getTitleAt(index).equals(searchTabTitle))
406         {
407           btn_back.setEnabled(true);
408           btn_cancel.setEnabled(true);
409           if (wantedFieldsUpdated())
410           {
411             txt_search_ActionPerformed();
412           }
413           else
414           {
415             validateSelection();
416           }
417         }
418       }
419     };
420     tabbedPane.addChangeListener(changeListener);
421     tabbedPane.setPreferredSize(new Dimension(500, 300));
422     tabbedPane.add(searchTabTitle, scrl_searchResult);
423     tabbedPane.add(configureCols, new FTSDataColumnPreferences(
424             PreferenceSource.SEARCH_SUMMARY, getFTSRestClient()));
425
426     pnl_actions.add(btn_back);
427     pnl_actions.add(btn_ok);
428     pnl_actions.add(btn_cancel);
429
430     pnl_results.add(tabbedPane);
431     pnl_inputs.add(cmb_searchTarget);
432     pnl_inputs.add(txt_search);
433     pnl_inputs.add(lbl_loading);
434     pnl_inputs.add(lbl_warning);
435
436     this.setLayout(mainLayout);
437     this.add(pnl_inputs, java.awt.BorderLayout.NORTH);
438     this.add(pnl_results, java.awt.BorderLayout.CENTER);
439     this.add(pnl_actions, java.awt.BorderLayout.SOUTH);
440     mainFrame.setVisible(true);
441     mainFrame.setContentPane(this);
442     mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
443     Desktop.addInternalFrame(mainFrame, getFTSFrameTitle(), 800, 400);
444   }
445
446   public boolean wantedFieldsUpdated()
447   {
448     if (previousWantedFields == null)
449     {
450       return true;
451     }
452
453     return Arrays.equals(PDBFTSRestClient.getInstance()
454             .getAllDefaulDisplayedDataColumns()
455             .toArray(new Object[0]), previousWantedFields) ? false
456             : true;
457
458   }
459
460   public void validateSelection()
461   {
462     if (tbl_summary.getSelectedRows().length > 0)
463     {
464       btn_ok.setEnabled(true);
465     }
466     else
467     {
468       btn_ok.setEnabled(false);
469     }
470   }
471
472   public JComboBox<FTSDataColumnI> getCmbSearchTarget()
473   {
474     return cmb_searchTarget;
475   }
476
477   public JTextField getTxtSearch()
478   {
479     return txt_search;
480   }
481
482   public JInternalFrame getMainFrame()
483   {
484     return mainFrame;
485   }
486
487   protected void delayAndEnableActionButtons()
488   {
489     new Thread()
490     {
491       @Override
492       public void run()
493       {
494         try
495         {
496           Thread.sleep(1500);
497         } catch (InterruptedException e)
498         {
499           e.printStackTrace();
500         }
501         btn_ok.setEnabled(true);
502         btn_back.setEnabled(true);
503         btn_cancel.setEnabled(true);
504       }
505     }.start();
506   }
507
508   protected void checkForErrors()
509   {
510     lbl_warning.setVisible(false);
511     if (errorWarning.length() > 0)
512     {
513       lbl_loading.setVisible(false);
514       lbl_warning.setToolTipText(JvSwingUtils.wrapTooltip(true,
515               errorWarning.toString()));
516       lbl_warning.setVisible(true);
517     }
518   }
519
520   protected void btn_back_ActionPerformed()
521   {
522     mainFrame.dispose();
523     new SequenceFetcher(progressIdicator);
524   }
525
526   protected void disableActionButtons()
527   {
528     btn_ok.setEnabled(false);
529     btn_back.setEnabled(false);
530     btn_cancel.setEnabled(false);
531   }
532
533   protected void btn_cancel_ActionPerformed()
534   {
535     mainFrame.dispose();
536   }
537
538   /**
539    * Populates search target combo-box options
540    */
541   public void populateCmbSearchTargetOptions()
542   {
543     List<FTSDataColumnI> searchableTargets = new ArrayList<FTSDataColumnI>();
544     try
545     {
546       Collection<FTSDataColumnI> foundFTSTargets = getFTSRestClient()
547               .getSearchableDataColumns();
548       searchableTargets.addAll(foundFTSTargets);
549     } catch (Exception e)
550     {
551       e.printStackTrace();
552     }
553
554     Collections.sort(searchableTargets, new Comparator<FTSDataColumnI>()
555     {
556       @Override
557       public int compare(FTSDataColumnI o1, FTSDataColumnI o2)
558       {
559         return o1.getName().compareTo(o2.getName());
560       }
561     });
562
563     for (FTSDataColumnI searchTarget : searchableTargets)
564     {
565       cmb_searchTarget.addItem(searchTarget);
566     }
567   }
568
569
570   public void transferToSequenceFetcher(String ids)
571   {
572     // mainFrame.dispose();
573     seqFetcher.getTextArea().setText(ids);
574     Thread worker = new Thread(seqFetcher);
575     worker.start();
576   }
577
578   public abstract String getFTSFrameTitle();
579
580   public abstract FTSRestClientI getFTSRestClient();
581
582   public abstract void txt_search_ActionPerformed();
583
584   public abstract void btn_ok_ActionPerformed();
585
586 }