JAL-3490 revised layout and search algorithm (more tests to be added)
[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.io.cache.JvCacheableInputBox;
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.Font;
38 import java.awt.event.ActionEvent;
39 import java.awt.event.ActionListener;
40 import java.awt.event.FocusAdapter;
41 import java.awt.event.FocusEvent;
42 import java.awt.event.FocusListener;
43 import java.awt.event.ItemEvent;
44 import java.awt.event.ItemListener;
45 import java.awt.event.KeyAdapter;
46 import java.awt.event.KeyEvent;
47 import java.awt.event.MouseAdapter;
48 import java.awt.event.MouseEvent;
49 import java.text.DecimalFormat;
50 import java.util.ArrayList;
51 import java.util.Arrays;
52 import java.util.Collection;
53 import java.util.Collections;
54 import java.util.Comparator;
55 import java.util.HashSet;
56 import java.util.List;
57
58 import javax.swing.ImageIcon;
59 import javax.swing.JButton;
60 import javax.swing.JCheckBox;
61 import javax.swing.JComboBox;
62 import javax.swing.JFrame;
63 import javax.swing.JInternalFrame;
64 import javax.swing.JLabel;
65 import javax.swing.JPanel;
66 import javax.swing.JScrollPane;
67 import javax.swing.JTabbedPane;
68 import javax.swing.JTable;
69 import javax.swing.Timer;
70 import javax.swing.event.ChangeEvent;
71 import javax.swing.event.ChangeListener;
72 import javax.swing.event.DocumentEvent;
73 import javax.swing.event.DocumentListener;
74 import javax.swing.event.InternalFrameEvent;
75 import javax.swing.table.DefaultTableModel;
76 import javax.swing.table.TableColumn;
77 import javax.swing.text.JTextComponent;
78
79 /**
80  * This class provides the swing GUI layout for FTS Panel and implements most of
81  * the contracts defined in GFSPanelI
82  * 
83  * @author tcnofoegbu
84  *
85  */
86
87 @SuppressWarnings("serial")
88 public abstract class GFTSPanel extends JPanel implements GFTSPanelI
89 {
90   private static final Font VERDANA_12 = new Font("Verdana", 0, 12);
91
92   protected JInternalFrame mainFrame = new JInternalFrame(
93           getFTSFrameTitle());
94
95   protected JTabbedPane tabs = new JTabbedPane();
96   protected IProgressIndicator progressIndicator;
97
98   protected JComboBox<FTSDataColumnI> cmb_searchTarget = new JComboBox<>();
99
100   protected JButton btn_ok = new JButton();
101
102   protected JButton btn_back = new JButton();
103
104   protected JButton btn_cancel = new JButton();
105
106   protected JCheckBox btn_autosearch = new JCheckBox();
107
108   protected JvCacheableInputBox<String> txt_search;
109
110   protected SequenceFetcher seqFetcher;
111
112   protected Collection<FTSDataColumnI> wantedFields;
113
114   private String lastSearchTerm = "";
115
116   protected JButton btn_next_page = new JButton();
117
118   protected JButton btn_prev_page = new JButton();
119
120   protected StringBuilder errorWarning = new StringBuilder();
121
122   protected ImageIcon warningImage = new ImageIcon(
123           getClass().getResource("/images/warning.gif"));
124
125   protected ImageIcon loadingImage = new ImageIcon(
126           getClass().getResource("/images/loading.gif"));
127
128   protected ImageIcon balnkPlaceholderImage = new ImageIcon(
129           getClass().getResource("/images/blank_16x16_placeholder.png"));
130
131   protected JLabel lbl_warning = new JLabel(warningImage);
132
133   protected JLabel lbl_loading = new JLabel(loadingImage);
134
135   protected JLabel lbl_blank = new JLabel(balnkPlaceholderImage);
136
137   private JTabbedPane tabbedPane = new JTabbedPane();
138
139   private JPanel pnl_actions = new JPanel();
140
141   private JPanel pnl_results = new JPanel(new CardLayout());
142
143   private JPanel pnl_inputs = new JPanel();
144
145   private BorderLayout mainLayout = new BorderLayout();
146
147   protected Object[] previousWantedFields;
148
149   protected int resultSetCount;
150
151   protected int totalResultSetCount;
152
153   protected int offSet;
154
155   protected int pageLimit;
156
157   protected HashSet<String> paginatorCart = new HashSet<>();
158
159   private static final int MIN_WIDTH = 670;
160
161   private static final int MIN_HEIGHT = 300;
162
163   protected static final DecimalFormat totalNumberformatter = new DecimalFormat(
164           "###,###");
165
166   private JTable tbl_summary = new JTable()
167   {
168     private boolean inLayout;
169
170     @Override
171     public boolean getScrollableTracksViewportWidth()
172     {
173       return hasExcessWidth();
174
175     }
176
177     @Override
178     public void doLayout()
179     {
180       if (hasExcessWidth())
181       {
182         autoResizeMode = AUTO_RESIZE_SUBSEQUENT_COLUMNS;
183       }
184       inLayout = true;
185       super.doLayout();
186       inLayout = false;
187       autoResizeMode = AUTO_RESIZE_OFF;
188     }
189
190     protected boolean hasExcessWidth()
191     {
192       return getPreferredSize().width < getParent().getWidth();
193     }
194
195     @Override
196     public void columnMarginChanged(ChangeEvent e)
197     {
198       if (isEditing())
199       {
200         removeEditor();
201       }
202       TableColumn resizingColumn = getTableHeader().getResizingColumn();
203       // Need to do this here, before the parent's
204       // layout manager calls getPreferredSize().
205       if (resizingColumn != null && autoResizeMode == AUTO_RESIZE_OFF
206               && !inLayout)
207       {
208         resizingColumn.setPreferredWidth(resizingColumn.getWidth());
209         String colHeader = resizingColumn.getHeaderValue().toString();
210         getTempUserPrefs().put(colHeader, resizingColumn.getWidth());
211       }
212       resizeAndRepaint();
213     }
214
215     @Override
216     public String getToolTipText(MouseEvent evt)
217     {
218       String toolTipText = null;
219       java.awt.Point pnt = evt.getPoint();
220       int rowIndex = rowAtPoint(pnt);
221       int colIndex = columnAtPoint(pnt);
222
223       try
224       {
225         if (getValueAt(rowIndex, colIndex) == null)
226         {
227           return null;
228         }
229         toolTipText = getValueAt(rowIndex, colIndex).toString();
230
231       } catch (Exception e)
232       {
233         e.printStackTrace();
234       }
235       toolTipText = (toolTipText == null ? null
236               : (toolTipText.length() > 500
237                       ? JvSwingUtils.wrapTooltip(true,
238                               toolTipText.subSequence(0, 500) + "...")
239                       : JvSwingUtils.wrapTooltip(true, toolTipText)));
240
241       return toolTipText;
242     }
243   };
244
245   protected JScrollPane scrl_searchResult = new JScrollPane(tbl_summary);
246
247   public GFTSPanel()
248   {
249     this(null);
250   }
251
252   public GFTSPanel(SequenceFetcher fetcher)
253   {
254     try
255     {
256       if (fetcher == null)
257       {
258         tabs = null;
259       }
260       jbInit();
261       if (fetcher != null)
262       {
263         tabs.addTab(MessageManager.getString("label.retrieve_ids"),
264                 fetcher);
265         fetcher.setDatabaseChooserVisible(false);
266         fetcher.embedWithFTSPanel(this);
267       }
268       mainFrame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
269       final JPanel ftsPanel = this;
270       mainFrame.addFocusListener(new FocusAdapter()
271       {
272         @Override
273         public void focusGained(FocusEvent e)
274         {
275           // TODO: make selected tab gain focus in correct widget
276           if (tabs != null
277                   && tabs.getSelectedComponent() == ftsPanel)
278           {
279             txt_search.requestFocusInWindow();
280           }
281         }
282       });
283       mainFrame.invalidate();
284       mainFrame.pack();
285     } catch (Exception e)
286     {
287       e.printStackTrace();
288     }
289   }
290
291   /**
292    * Initializes the GUI default properties
293    * 
294    * @throws Exception
295    */
296   private void jbInit() throws Exception
297   {
298
299     txt_search = new JvCacheableInputBox<>(getCacheKey(), 45);
300     populateCmbSearchTargetOptions();
301     Integer width = getTempUserPrefs().get("FTSPanel.width") == null ? 800
302             : getTempUserPrefs().get("FTSPanel.width");
303     Integer height = getTempUserPrefs().get("FTSPanel.height") == null ? 400
304             : getTempUserPrefs().get("FTSPanel.height");
305     lbl_warning.setVisible(false);
306     lbl_warning.setFont(VERDANA_12);
307     lbl_loading.setVisible(false);
308     lbl_loading.setFont(VERDANA_12);
309     lbl_blank.setVisible(true);
310     lbl_blank.setFont(VERDANA_12);
311
312     tbl_summary.setAutoCreateRowSorter(true);
313     tbl_summary.getTableHeader().setReorderingAllowed(false);
314     tbl_summary.addMouseListener(new MouseAdapter()
315     {
316       @Override
317       public void mouseClicked(MouseEvent e)
318       {
319         validateSelection();
320       }
321
322       @Override
323       public void mouseReleased(MouseEvent e)
324       {
325         validateSelection();
326       }
327     });
328     tbl_summary.addKeyListener(new KeyAdapter()
329     {
330       @Override
331       public void keyPressed(KeyEvent evt)
332       {
333         validateSelection();
334         switch (evt.getKeyCode())
335         {
336         case KeyEvent.VK_ESCAPE: // escape key
337           btn_back_ActionPerformed();
338           break;
339         case KeyEvent.VK_ENTER: // enter key
340           if (btn_ok.isEnabled())
341           {
342             okAction();
343           }
344           evt.consume();
345           break;
346         case KeyEvent.VK_TAB: // tab key
347           if (evt.isShiftDown())
348           {
349             tabbedPane.requestFocus();
350           }
351           else
352           {
353             btn_back.requestFocus();
354           }
355           evt.consume();
356           break;
357         default:
358           return;
359         }
360       }
361     });
362
363     JButton txt_help = new JButton("?");
364     txt_help.setFont(VERDANA_12);
365     txt_help.setPreferredSize(new Dimension(15, 15));
366     txt_help.setToolTipText(MessageManager.getString("action.help"));
367     txt_help.addActionListener(new ActionListener()
368     {
369       @Override
370       public void actionPerformed(ActionEvent e)
371       {
372         showHelp();
373       }
374     });
375
376     btn_autosearch.setText(MessageManager.getString("option.autosearch"));
377     btn_autosearch.setToolTipText(
378             MessageManager.getString("option.enable_disable_autosearch"));
379     btn_autosearch.setSelected(
380             jalview.bin.Cache.getDefault(getAutosearchPreference(), true));
381     btn_autosearch.addActionListener(new java.awt.event.ActionListener()
382     {
383       @Override
384       public void actionPerformed(ActionEvent e)
385       {
386         jalview.bin.Cache.setProperty(getAutosearchPreference(),
387                 Boolean.toString(btn_autosearch.isSelected()));
388       }
389     });
390     btn_back.setFont(VERDANA_12);
391     btn_back.setText(MessageManager.getString("action.back"));
392     btn_back.addActionListener(new java.awt.event.ActionListener()
393     {
394       @Override
395       public void actionPerformed(ActionEvent e)
396       {
397         btn_back_ActionPerformed();
398       }
399     });
400     btn_back.addKeyListener(new KeyAdapter()
401     {
402       @Override
403       public void keyPressed(KeyEvent evt)
404       {
405         if (evt.getKeyCode() == KeyEvent.VK_ENTER)
406         {
407           btn_back_ActionPerformed();
408         }
409       }
410     });
411
412     btn_ok.setEnabled(false);
413     btn_ok.setFont(VERDANA_12);
414     btn_ok.setText(MessageManager.getString("action.ok"));
415     btn_ok.addActionListener(new java.awt.event.ActionListener()
416     {
417       @Override
418       public void actionPerformed(ActionEvent e)
419       {
420         okAction();
421       }
422     });
423     btn_ok.addKeyListener(new KeyAdapter()
424     {
425       @Override
426       public void keyPressed(KeyEvent evt)
427       {
428         if (evt.getKeyCode() == KeyEvent.VK_ENTER)
429         {
430           okAction();
431         }
432       }
433     });
434     btn_next_page.setEnabled(false);
435     btn_next_page.setToolTipText(
436             MessageManager.getString("label.next_page_tooltip"));
437     btn_next_page.setFont(VERDANA_12);
438     btn_next_page.setText(MessageManager.getString("action.next_page"));
439     btn_next_page.addActionListener(new java.awt.event.ActionListener()
440     {
441       @Override
442       public void actionPerformed(ActionEvent e)
443       {
444         nextPageAction();
445       }
446     });
447     btn_next_page.addKeyListener(new KeyAdapter()
448     {
449       @Override
450       public void keyPressed(KeyEvent evt)
451       {
452         if (evt.getKeyCode() == KeyEvent.VK_ENTER)
453         {
454           nextPageAction();
455         }
456       }
457     });
458
459     btn_prev_page.setEnabled(false);
460     btn_prev_page.setToolTipText(
461             MessageManager.getString("label.prev_page_tooltip"));
462     btn_prev_page.setFont(VERDANA_12);
463     btn_prev_page.setText(MessageManager.getString("action.prev_page"));
464     btn_prev_page.addActionListener(new java.awt.event.ActionListener()
465     {
466       @Override
467       public void actionPerformed(ActionEvent e)
468       {
469         prevPageAction();
470       }
471     });
472     btn_prev_page.addKeyListener(new KeyAdapter()
473     {
474       @Override
475       public void keyPressed(KeyEvent evt)
476       {
477         if (evt.getKeyCode() == KeyEvent.VK_ENTER)
478         {
479           prevPageAction();
480         }
481       }
482     });
483
484     if (isPaginationEnabled())
485     {
486       btn_prev_page.setVisible(true);
487       btn_next_page.setVisible(true);
488     }
489     else
490     {
491       btn_prev_page.setVisible(false);
492       btn_next_page.setVisible(false);
493     }
494
495     btn_cancel.setFont(VERDANA_12);
496     btn_cancel.setText(MessageManager.getString("action.cancel"));
497     btn_cancel.addActionListener(new java.awt.event.ActionListener()
498     {
499       @Override
500       public void actionPerformed(ActionEvent e)
501       {
502         btn_cancel_ActionPerformed();
503       }
504     });
505     btn_cancel.addKeyListener(new KeyAdapter()
506     {
507       @Override
508       public void keyPressed(KeyEvent evt)
509       {
510         if (evt.getKeyCode() == KeyEvent.VK_ENTER)
511         {
512           btn_cancel_ActionPerformed();
513         }
514       }
515     });
516     scrl_searchResult.setPreferredSize(new Dimension(width, height));
517
518     cmb_searchTarget.setFont(VERDANA_12);
519     cmb_searchTarget.addItemListener(new ItemListener()
520     {
521       @Override
522       public void itemStateChanged(ItemEvent e)
523       {
524         if (e.getStateChange() == ItemEvent.SELECTED)
525         {
526           String tooltipText;
527           if ("all".equalsIgnoreCase(
528                   getCmbSearchTarget().getSelectedItem().toString()))
529           {
530             tooltipText = MessageManager.getString("label.search_all");
531           }
532           else if ("pdb id".equalsIgnoreCase(
533                   getCmbSearchTarget().getSelectedItem().toString()))
534           {
535             tooltipText = MessageManager
536                     .getString("label.separate_multiple_accession_ids");
537           }
538           else
539           {
540             tooltipText = MessageManager.formatMessage(
541                     "label.separate_multiple_query_values", new Object[]
542                     { getCmbSearchTarget().getSelectedItem().toString() });
543           }
544           txt_search.setToolTipText(
545                   JvSwingUtils.wrapTooltip(true, tooltipText));
546           searchAction(true);
547         }
548       }
549     });
550
551     txt_search.setFont(VERDANA_12);
552
553     txt_search.getEditor().getEditorComponent()
554             .addKeyListener(new KeyAdapter()
555             {
556               @Override
557               public void keyPressed(KeyEvent e)
558               {
559                 if (e.getKeyCode() == KeyEvent.VK_ENTER)
560                 {
561                   if (getTypedText() == null || getTypedText().isEmpty())
562                   {
563                     return;
564                   }
565                   String primaryKeyName = getFTSRestClient()
566                           .getPrimaryKeyColumn().getName();
567                   if (primaryKeyName.equalsIgnoreCase(getCmbSearchTarget()
568                           .getSelectedItem().toString()))
569                   {
570                     // TODO: nicer to show the list in the result set before
571                     // viewing in Jalview perhaps ?
572                     transferToSequenceFetcher(getTypedText());
573                   }
574                   else
575                   {
576                     performSearchAction();
577                   }
578                 }
579               }
580             });
581     final DeferredTextInputListener listener = new DeferredTextInputListener(
582             1500, new ActionListener()
583             {
584               @Override
585               public void actionPerformed(ActionEvent e)
586               {
587                 if (btn_autosearch.isSelected()
588                         || txt_search.wasEnterPressed())
589                 {
590                   performSearchAction();
591                 }
592               }
593             }, false);
594     ((JTextComponent) txt_search.getEditor().getEditorComponent())
595             .getDocument().addDocumentListener(listener);
596
597     txt_search.addFocusListener(new FocusListener()
598     {
599       @Override
600       public void focusGained(FocusEvent e)
601       {
602         listener.start();
603       }
604
605       @Override
606       public void focusLost(FocusEvent e)
607       {
608         // listener.stop();
609       }
610     });
611
612     txt_search.addActionListener(new ActionListener()
613     {
614
615       @Override
616       public void actionPerformed(ActionEvent e)
617       {
618         performSearchAction();
619       }
620     });
621     final String searchTabTitle = MessageManager
622             .getString("label.search_result");
623     final String configureCols = MessageManager
624             .getString("label.configure_displayed_columns");
625     ChangeListener changeListener = new ChangeListener()
626     {
627       @Override
628       public void stateChanged(ChangeEvent changeEvent)
629       {
630         JTabbedPane sourceTabbedPane = (JTabbedPane) changeEvent
631                 .getSource();
632         int index = sourceTabbedPane.getSelectedIndex();
633
634         btn_back.setVisible(true);
635         btn_cancel.setVisible(true);
636         btn_ok.setVisible(true);
637         if (sourceTabbedPane.getTitleAt(index).equals(configureCols))
638         {
639           btn_back.setVisible(false);
640           btn_cancel.setVisible(false);
641           btn_ok.setVisible(false);
642           btn_back.setEnabled(false);
643           btn_cancel.setEnabled(false);
644           btn_ok.setEnabled(false);
645           btn_next_page.setEnabled(false);
646           btn_prev_page.setEnabled(false);
647           txt_search.setEnabled(false);
648           cmb_searchTarget.setEnabled(false);
649           previousWantedFields = getFTSRestClient()
650                   .getAllDefaultDisplayedFTSDataColumns()
651                   .toArray(new Object[0]);
652         }
653         if (sourceTabbedPane.getTitleAt(index).equals(searchTabTitle))
654         {
655           btn_back.setEnabled(true);
656           btn_cancel.setEnabled(true);
657           refreshPaginatorState();
658           txt_search.setEnabled(true);
659           cmb_searchTarget.setEnabled(true);
660           if (wantedFieldsUpdated())
661           {
662             searchAction(true);
663             paginatorCart.clear();
664           }
665           else
666           {
667             validateSelection();
668           }
669         }
670       }
671     };
672     tabbedPane.addChangeListener(changeListener);
673     tabbedPane.setPreferredSize(new Dimension(width, height));
674     tabbedPane.add(searchTabTitle, scrl_searchResult);
675     tabbedPane.add(configureCols, new FTSDataColumnPreferences(
676             PreferenceSource.SEARCH_SUMMARY, getFTSRestClient()));
677
678     pnl_actions.add(btn_back);
679     pnl_actions.add(btn_ok);
680     pnl_actions.add(btn_cancel);
681
682     pnl_results.add(tabbedPane);
683     pnl_inputs.add(cmb_searchTarget);
684     pnl_inputs.add(txt_search);
685     pnl_inputs.add(txt_help);
686     pnl_inputs.add(btn_autosearch);
687     pnl_inputs.add(lbl_loading);
688     pnl_inputs.add(lbl_warning);
689     pnl_inputs.add(lbl_blank);
690     pnl_inputs.add(btn_prev_page);
691     pnl_inputs.add(btn_next_page);
692
693     this.setLayout(mainLayout);
694     this.add(pnl_inputs, java.awt.BorderLayout.NORTH);
695     this.add(pnl_results, java.awt.BorderLayout.CENTER);
696     this.add(pnl_actions, java.awt.BorderLayout.SOUTH);
697     mainFrame.setVisible(true);
698     if (tabs != null)
699     {
700       tabs.setOpaque(true);
701       tabs.insertTab(MessageManager.getString("label.free_text_search"),
702               null, this, "", 0);
703       mainFrame.setContentPane(tabs);
704       tabs.setVisible(true);
705     }
706     else
707     {
708       mainFrame.setContentPane(this);
709     }
710     mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
711     mainFrame.addInternalFrameListener(
712             new javax.swing.event.InternalFrameAdapter()
713             {
714               @Override
715               public void internalFrameClosing(InternalFrameEvent e)
716               {
717                 closeAction();
718               }
719             });
720     mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
721     Integer x = getTempUserPrefs().get("FTSPanel.x");
722     Integer y = getTempUserPrefs().get("FTSPanel.y");
723     if (x != null && y != null)
724     {
725       mainFrame.setLocation(x, y);
726     }
727     Desktop.addInternalFrame(mainFrame, getFTSFrameTitle(), width, height);
728   }
729
730   abstract protected void showHelp();
731
732   protected void closeAction()
733   {
734     getTempUserPrefs().put("FTSPanel.width", this.getWidth());
735     getTempUserPrefs().put("FTSPanel.height", pnl_results.getHeight());
736     getTempUserPrefs().put("FTSPanel.x", mainFrame.getX());
737     getTempUserPrefs().put("FTSPanel.y", mainFrame.getY());
738     mainFrame.dispose();
739     txt_search.persistCache();
740   }
741
742   public class DeferredTextInputListener implements DocumentListener
743   {
744     private final Timer swingTimer;
745
746     public DeferredTextInputListener(int timeOut, ActionListener listener,
747             boolean repeats)
748     {
749       swingTimer = new Timer(timeOut, listener);
750       swingTimer.setRepeats(repeats);
751     }
752
753     public void start()
754     {
755       swingTimer.start();
756     }
757
758     public void stop()
759     {
760       swingTimer.stop();
761     }
762
763     @Override
764     public void insertUpdate(DocumentEvent e)
765     {
766       swingTimer.restart();
767     }
768
769     @Override
770     public void removeUpdate(DocumentEvent e)
771     {
772       swingTimer.restart();
773     }
774
775     @Override
776     public void changedUpdate(DocumentEvent e)
777     {
778       swingTimer.restart();
779     }
780
781   }
782
783   void performSearchAction()
784   {
785     String typed = getTypedText();
786     if (typed != null && typed.length() > 0
787             && !typed.equalsIgnoreCase(lastSearchTerm))
788     {
789       searchAction(true);
790       paginatorCart.clear();
791       lastSearchTerm = typed;
792     }
793   }
794
795   public boolean wantedFieldsUpdated()
796   {
797     if (previousWantedFields == null)
798     {
799       return false;
800     }
801
802     return Arrays.equals(getFTSRestClient()
803             .getAllDefaultDisplayedFTSDataColumns().toArray(new Object[0]),
804             previousWantedFields) ? false : true;
805
806   }
807
808   public void validateSelection()
809   {
810     if (tbl_summary.getSelectedRows().length > 0
811             || !paginatorCart.isEmpty())
812     {
813       btn_ok.setEnabled(true);
814     }
815     else
816     {
817       btn_ok.setEnabled(false);
818     }
819   }
820
821   public JComboBox<FTSDataColumnI> getCmbSearchTarget()
822   {
823     return cmb_searchTarget;
824   }
825
826   public JComboBox<String> getTxtSearch()
827   {
828     return txt_search;
829   }
830
831   public JInternalFrame getMainFrame()
832   {
833     return mainFrame;
834   }
835
836   protected void delayAndEnableActionButtons()
837   {
838     new Thread()
839     {
840       @Override
841       public void run()
842       {
843         try
844         {
845           Thread.sleep(1500);
846         } catch (InterruptedException e)
847         {
848           e.printStackTrace();
849         }
850         btn_ok.setEnabled(true);
851         btn_back.setEnabled(true);
852         btn_cancel.setEnabled(true);
853       }
854     }.start();
855   }
856
857   protected void checkForErrors()
858   {
859     lbl_warning.setVisible(false);
860     lbl_blank.setVisible(true);
861     if (errorWarning.length() > 0)
862     {
863       lbl_loading.setVisible(false);
864       lbl_blank.setVisible(false);
865       lbl_warning.setToolTipText(
866               JvSwingUtils.wrapTooltip(true, errorWarning.toString()));
867       lbl_warning.setVisible(true);
868     }
869   }
870
871   public void btn_back_ActionPerformed()
872   {
873     closeAction();
874     new SequenceFetcher(progressIndicator);
875   }
876
877   protected void disableActionButtons()
878   {
879     btn_ok.setEnabled(false);
880     btn_back.setEnabled(false);
881     btn_cancel.setEnabled(false);
882   }
883
884   public void btn_cancel_ActionPerformed()
885   {
886     closeAction();
887   }
888
889   /**
890    * Populates search target combo-box options
891    */
892   public void populateCmbSearchTargetOptions()
893   {
894     List<FTSDataColumnI> searchableTargets = new ArrayList<>();
895     try
896     {
897       Collection<FTSDataColumnI> foundFTSTargets = getFTSRestClient()
898               .getSearchableDataColumns();
899       searchableTargets.addAll(foundFTSTargets);
900     } catch (Exception e)
901     {
902       e.printStackTrace();
903     }
904
905     Collections.sort(searchableTargets, new Comparator<FTSDataColumnI>()
906     {
907       @Override
908       public int compare(FTSDataColumnI o1, FTSDataColumnI o2)
909       {
910         return o1.getName().compareTo(o2.getName());
911       }
912     });
913
914     for (FTSDataColumnI searchTarget : searchableTargets)
915     {
916       cmb_searchTarget.addItem(searchTarget);
917     }
918   }
919
920   public void transferToSequenceFetcher(String ids)
921   {
922     seqFetcher.getTextArea().setText(ids);
923     Thread worker = new Thread(seqFetcher);
924     worker.start();
925   }
926
927   @Override
928   public String getTypedText()
929   {
930     return txt_search.getUserInput();
931   }
932
933   @Override
934   public JTable getResultTable()
935   {
936     return tbl_summary;
937   }
938
939   public void reset()
940   {
941     lbl_loading.setVisible(false);
942     errorWarning.setLength(0);
943     lbl_warning.setVisible(false);
944     lbl_blank.setVisible(true);
945     btn_ok.setEnabled(false);
946     mainFrame.setTitle(getFTSFrameTitle());
947     referesh();
948     tbl_summary.setModel(new DefaultTableModel());
949     tbl_summary.setVisible(false);
950   }
951
952   @Override
953   public void setPrevPageButtonEnabled(boolean isEnabled)
954   {
955     btn_prev_page.setEnabled(isEnabled);
956   }
957
958   @Override
959   public void setNextPageButtonEnabled(boolean isEnabled)
960   {
961     btn_next_page.setEnabled(isEnabled);
962   }
963
964   @Override
965   public void setErrorMessage(String message)
966   {
967     errorWarning.append(message);
968   }
969
970   @Override
971   public void updateSearchFrameTitle(String title)
972   {
973     mainFrame.setTitle(title);
974   }
975
976   @Override
977   public void setSearchInProgress(Boolean isSearchInProgress)
978   {
979     lbl_blank.setVisible(!isSearchInProgress);
980     lbl_loading.setVisible(isSearchInProgress);
981     txt_search.setEditable(!isSearchInProgress);
982   }
983
984   @Override
985   public void prevPageAction()
986   {
987     updatePaginatorCart();
988     if (offSet >= pageLimit)
989     {
990       offSet = offSet - pageLimit;
991       searchAction(false);
992     }
993     else
994     {
995       refreshPaginatorState();
996     }
997   }
998
999   @Override
1000   public void nextPageAction()
1001   {
1002     updatePaginatorCart();
1003     offSet = offSet + pageLimit;
1004     searchAction(false);
1005   }
1006
1007   public void updatePaginatorCart()
1008   {
1009     int primaryKeyColIndex = 0;
1010     JTable resultTable = getResultTable();
1011     int totalRows = resultTable.getRowCount();
1012     try
1013     {
1014       primaryKeyColIndex = getFTSRestClient()
1015               .getPrimaryKeyColumIndex(wantedFields, false);
1016     } catch (Exception e)
1017     {
1018       e.printStackTrace();
1019     }
1020
1021     for (int row = 0; row < totalRows; row++)
1022     {
1023       String id = (String) resultTable.getValueAt(row, primaryKeyColIndex);
1024       if (paginatorCart.contains(id))
1025       {
1026         paginatorCart.remove(id);
1027       }
1028     }
1029     int[] selectedRows = resultTable.getSelectedRows();
1030     for (int summaryRow : selectedRows)
1031     {
1032       String idStr = resultTable.getValueAt(summaryRow, primaryKeyColIndex)
1033               .toString();
1034       paginatorCart.add(idStr);
1035     }
1036   }
1037
1038   public void updateSummaryTableSelections()
1039   {
1040     JTable resultTable = getResultTable();
1041     if (paginatorCart.isEmpty())
1042     {
1043       return;
1044     }
1045     int primaryKeyColIndex = 0;
1046     try
1047     {
1048       primaryKeyColIndex = getFTSRestClient()
1049               .getPrimaryKeyColumIndex(wantedFields, false);
1050     } catch (Exception e)
1051     {
1052       e.printStackTrace();
1053     }
1054     int totalRows = resultTable.getRowCount();
1055     for (int row = 0; row < totalRows; row++)
1056     {
1057       String id = (String) resultTable.getValueAt(row, primaryKeyColIndex);
1058       if (paginatorCart.contains(id))
1059       {
1060         resultTable.addRowSelectionInterval(row, row);
1061       }
1062     }
1063     validateSelection();
1064   }
1065
1066   public void refreshPaginatorState()
1067   {
1068     setPrevPageButtonEnabled(false);
1069     setNextPageButtonEnabled(false);
1070     if (resultSetCount == 0 && pageLimit == 0)
1071     {
1072       return;
1073     }
1074     if (resultSetCount >= pageLimit)
1075     {
1076       setNextPageButtonEnabled(true);
1077     }
1078     if (offSet >= pageLimit)
1079     {
1080       setPrevPageButtonEnabled(true);
1081     }
1082   }
1083
1084   public void referesh()
1085   {
1086     mainFrame.setTitle(getFTSFrameTitle());
1087   }
1088
1089 }