JS optimization for too aggressive dynamic operation when entering PDB
[jalview.git] / src / jalview / jbgui / GStructureChooser.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.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.gui.StructureViewer;
33 import jalview.util.MessageManager;
34
35 import java.awt.BorderLayout;
36 import java.awt.CardLayout;
37 import java.awt.Component;
38 import java.awt.Dimension;
39 import java.awt.FlowLayout;
40 import java.awt.Font;
41 import java.awt.GridLayout;
42 import java.awt.event.ActionEvent;
43 import java.awt.event.ActionListener;
44 import java.awt.event.ItemEvent;
45 import java.awt.event.ItemListener;
46 import java.awt.event.KeyAdapter;
47 import java.awt.event.KeyEvent;
48 import java.awt.event.MouseAdapter;
49 import java.awt.event.MouseEvent;
50 import java.beans.PropertyChangeEvent;
51 import java.beans.PropertyChangeListener;
52 import java.util.Arrays;
53 import java.util.HashMap;
54 import java.util.Map;
55
56 import javax.swing.ImageIcon;
57 import javax.swing.JButton;
58 import javax.swing.JCheckBox;
59 import javax.swing.JComboBox;
60 import javax.swing.JFrame;
61 import javax.swing.JInternalFrame;
62 import javax.swing.JLabel;
63 import javax.swing.JList;
64 import javax.swing.JPanel;
65 import javax.swing.JScrollPane;
66 import javax.swing.JSeparator;
67 import javax.swing.JTabbedPane;
68 import javax.swing.JTable;
69 import javax.swing.JTextField;
70 import javax.swing.ListCellRenderer;
71 import javax.swing.SwingUtilities;
72 import javax.swing.Timer;
73 import javax.swing.event.ChangeEvent;
74 import javax.swing.event.ChangeListener;
75 import javax.swing.event.DocumentEvent;
76 import javax.swing.event.DocumentListener;
77 import javax.swing.event.InternalFrameEvent;
78 import javax.swing.table.TableColumn;
79
80 import net.miginfocom.swing.MigLayout;
81
82 @SuppressWarnings("serial")
83 /**
84  * GUI layout for structure chooser
85  * 
86  * @author tcnofoegbu
87  *
88  */
89 public abstract class GStructureChooser extends JPanel
90         implements ItemListener
91 {
92   private static final Font VERDANA_12 = new Font("Verdana", 0, 12);
93
94   protected static final String VIEWS_FILTER = "VIEWS_FILTER";
95
96   protected static final String VIEWS_FROM_FILE = "VIEWS_FROM_FILE";
97
98   protected static final String VIEWS_ENTER_ID = "VIEWS_ENTER_ID";
99
100   /*
101    * 'cached' structure view
102    */
103   protected static final String VIEWS_LOCAL_PDB = "VIEWS_LOCAL_PDB";
104
105   protected JPanel statusPanel = new JPanel();
106
107   public JLabel statusBar = new JLabel();
108
109   protected String frameTitle = MessageManager
110           .getString("label.structure_chooser");
111
112   protected JInternalFrame mainFrame = new JInternalFrame(frameTitle);
113
114   protected JComboBox<FilterOption> cmb_filterOption = new JComboBox<>();
115
116   protected AlignmentPanel ap;
117
118   protected StringBuilder errorWarning = new StringBuilder();
119
120   protected JButton btn_add;
121
122   protected JButton btn_newView;
123
124   protected JButton btn_pdbFromFile = new JButton();
125
126   protected JCheckBox chk_superpose = new JCheckBox(
127           MessageManager.getString("label.superpose_structures"));
128
129   protected JTextField txt_search = new JTextField(14);
130
131   protected JPanel pnl_switchableViews = new JPanel(new CardLayout());
132
133   protected CardLayout layout_switchableViews = (CardLayout) (pnl_switchableViews
134           .getLayout());
135
136   protected JCheckBox chk_invertFilter = new JCheckBox(
137           MessageManager.getString("label.invert"));
138
139   protected ImageIcon loadingImage = new ImageIcon(
140           getClass().getResource("/images/loading.gif"));
141
142   protected ImageIcon goodImage = new ImageIcon(
143           getClass().getResource("/images/good.png"));
144
145   protected ImageIcon errorImage = new ImageIcon(
146           getClass().getResource("/images/error.png"));
147
148   protected ImageIcon warningImage = new ImageIcon(
149           getClass().getResource("/images/warning.gif"));
150
151   protected JLabel lbl_loading = new JLabel(loadingImage);
152
153   protected JLabel lbl_pdbManualFetchStatus = new JLabel(errorImage);
154
155   protected JLabel lbl_fromFileStatus = new JLabel(errorImage);
156
157   protected AssociateSeqPanel idInputAssSeqPanel = new AssociateSeqPanel();
158
159   protected AssociateSeqPanel fileChooserAssSeqPanel = new AssociateSeqPanel();
160
161   protected JComboBox<StructureViewer> targetView = new JComboBox<>();
162
163   protected JTable tbl_local_pdb = new JTable();
164
165   protected JTabbedPane pnl_filter = jalview.jbgui.GDesktop.createTabbedPane();
166
167   protected FTSDataColumnPreferences pdbDocFieldPrefs = new FTSDataColumnPreferences(
168           PreferenceSource.STRUCTURE_CHOOSER,
169           PDBFTSRestClient.getInstance());
170
171   protected FTSDataColumnI[] previousWantedFields;
172
173   protected static Map<String, Integer> tempUserPrefs = new HashMap<>();
174
175   private JTable tbl_summary = new JTable()
176   {
177     private boolean inLayout;
178
179     @Override
180     public boolean getScrollableTracksViewportWidth()
181     {
182       return hasExcessWidth();
183
184     }
185
186     @Override
187     public void doLayout()
188     {
189       if (hasExcessWidth())
190       {
191         autoResizeMode = AUTO_RESIZE_SUBSEQUENT_COLUMNS;
192       }
193       inLayout = true;
194       super.doLayout();
195       inLayout = false;
196       autoResizeMode = AUTO_RESIZE_OFF;
197     }
198
199     protected boolean hasExcessWidth()
200     {
201       return getPreferredSize().width < getParent().getWidth();
202     }
203
204     @Override
205     public void columnMarginChanged(ChangeEvent e)
206     {
207       if (isEditing())
208       {
209         removeEditor();
210       }
211       TableColumn resizingColumn = getTableHeader().getResizingColumn();
212       // Need to do this here, before the parent's
213       // layout manager calls getPreferredSize().
214       if (resizingColumn != null && autoResizeMode == AUTO_RESIZE_OFF
215               && !inLayout)
216       {
217         resizingColumn.setPreferredWidth(resizingColumn.getWidth());
218         String colHeader = resizingColumn.getHeaderValue().toString();
219         tempUserPrefs.put(colHeader, resizingColumn.getWidth());
220       }
221       resizeAndRepaint();
222     }
223
224     @Override
225     public String getToolTipText(MouseEvent evt)
226     {
227       String toolTipText = null;
228       java.awt.Point pnt = evt.getPoint();
229       int rowIndex = rowAtPoint(pnt);
230       int colIndex = columnAtPoint(pnt);
231
232       try
233       {
234         if (getValueAt(rowIndex, colIndex) == null)
235         {
236           return null;
237         }
238         toolTipText = getValueAt(rowIndex, colIndex).toString();
239       } catch (Exception e)
240       {
241         // e.printStackTrace();
242       }
243       toolTipText = (toolTipText == null ? null
244               : (toolTipText.length() > 500
245                       ? JvSwingUtils.wrapTooltip(true,
246                               "\"" + toolTipText.subSequence(0, 500)
247                                       + "...\"")
248                       : JvSwingUtils.wrapTooltip(true, toolTipText)));
249       return toolTipText;
250     }
251   };
252
253   public GStructureChooser()
254   {
255     try
256     {
257       jbInit();
258       mainFrame.setVisible(false);
259       mainFrame.invalidate();
260       mainFrame.pack();
261     } catch (Exception e)
262     {
263       System.out.println(e); // for JavaScript TypeError
264       e.printStackTrace();
265     }
266   }
267
268   // BH SwingJS optimization
269   // (a) 100-ms interruptable timer for text entry -- BH 1/10/2019
270   // (b) two-character minimum, at least for JavaScript.
271   
272   private Timer timer; 
273   
274   protected void txt_search_ActionPerformedDelayed() {
275           if (timer != null) {
276                   timer.stop();
277           }
278           timer = new Timer(300, new ActionListener() {
279
280                 @Override
281                 public void actionPerformed(ActionEvent e) {
282                         txt_search_ActionPerformed();
283                 }                 
284           });
285           timer.setRepeats(false);
286           timer.start();
287   }
288   //
289   
290   /**
291    * Initializes the GUI default properties
292    * 
293    * @throws Exception
294    */
295   private void jbInit() throws Exception
296   {
297     Integer width = tempUserPrefs.get("structureChooser.width") == null
298             ? 800
299             : tempUserPrefs.get("structureChooser.width");
300     Integer height = tempUserPrefs.get("structureChooser.height") == null
301             ? 400
302             : tempUserPrefs.get("structureChooser.height");
303     tbl_summary.setAutoCreateRowSorter(true);
304     tbl_summary.getTableHeader().setReorderingAllowed(false);
305     tbl_summary.addMouseListener(new MouseAdapter()
306     {
307       @Override
308       public void mouseClicked(MouseEvent e)
309       {
310         validateSelections();
311       }
312
313       @Override
314       public void mouseReleased(MouseEvent e)
315       {
316         validateSelections();
317       }
318     });
319     tbl_summary.addKeyListener(new KeyAdapter()
320     {
321       @Override
322       public void keyPressed(KeyEvent evt)
323       {
324         validateSelections();
325         switch (evt.getKeyCode())
326         {
327         case KeyEvent.VK_ESCAPE: // escape key
328           mainFrame.dispose();
329           break;
330         case KeyEvent.VK_ENTER: // enter key
331           if (btn_add.isEnabled())
332           {
333             add_ActionPerformed();
334           }
335           break;
336         case KeyEvent.VK_TAB: // tab key
337           if (evt.isShiftDown())
338           {
339             pnl_filter.requestFocus();
340           }
341           else
342           {
343             btn_add.requestFocus();
344           }
345           evt.consume();
346           break;
347         default:
348           return;
349         }
350       }
351     });
352
353     JButton btn_cancel = new JButton(
354             MessageManager.getString("action.cancel"));
355     btn_cancel.setFont(VERDANA_12);
356     btn_cancel.addActionListener(new java.awt.event.ActionListener()
357     {
358       @Override
359       public void actionPerformed(ActionEvent e)
360       {
361         closeAction(pnl_filter.getHeight());
362       }
363     });
364     btn_cancel.addKeyListener(new KeyAdapter()
365     {
366       @Override
367       public void keyPressed(KeyEvent evt)
368       {
369         if (evt.getKeyCode() == KeyEvent.VK_ENTER)
370         {
371           closeAction(pnl_filter.getHeight());
372         }
373       }
374     });
375
376     tbl_local_pdb.setAutoCreateRowSorter(true);
377     tbl_local_pdb.getTableHeader().setReorderingAllowed(false);
378     tbl_local_pdb.addMouseListener(new MouseAdapter()
379     {
380       @Override
381       public void mouseClicked(MouseEvent e)
382       {
383         validateSelections();
384       }
385
386       @Override
387       public void mouseReleased(MouseEvent e)
388       {
389         validateSelections();
390       }
391     });
392     tbl_local_pdb.addKeyListener(new KeyAdapter()
393     {
394       @Override
395       public void keyPressed(KeyEvent evt)
396       {
397         validateSelections();
398         switch (evt.getKeyCode())
399         {
400         case KeyEvent.VK_ESCAPE: // escape key
401           mainFrame.dispose();
402           break;
403         case KeyEvent.VK_ENTER: // enter key
404           if (btn_add.isEnabled())
405           {
406             add_ActionPerformed();
407           }
408           break;
409         case KeyEvent.VK_TAB: // tab key
410           if (evt.isShiftDown())
411           {
412             cmb_filterOption.requestFocus();
413           }
414           else
415           {
416             if (btn_add.isEnabled())
417             {
418               btn_add.requestFocus();
419             }
420             else
421             {
422               btn_cancel.requestFocus();
423             }
424           }
425           evt.consume();
426           break;
427         default:
428           return;
429         }
430       }
431     });
432
433     btn_newView = new JButton(MessageManager.getString("action.new_view"));
434     btn_newView.setFont(VERDANA_12);
435     btn_newView.addActionListener(new java.awt.event.ActionListener()
436     {
437       @Override
438       public void actionPerformed(ActionEvent e)
439       {
440         newView_ActionPerformed();
441       }
442     });
443     btn_newView.addKeyListener(new KeyAdapter()
444     {
445       @Override
446       public void keyPressed(KeyEvent evt)
447       {
448         if (evt.getKeyCode() == KeyEvent.VK_ENTER)
449         {
450           newView_ActionPerformed();
451         }
452       }
453     });
454
455     btn_add = new JButton(MessageManager.getString("action.add"));
456     btn_add.setFont(VERDANA_12);
457     btn_add.addActionListener(new java.awt.event.ActionListener()
458     {
459       @Override
460       public void actionPerformed(ActionEvent e)
461       {
462         add_ActionPerformed();
463       }
464     });
465     btn_add.addKeyListener(new KeyAdapter()
466     {
467       @Override
468       public void keyPressed(KeyEvent evt)
469       {
470         if (evt.getKeyCode() == KeyEvent.VK_ENTER)
471         {
472           add_ActionPerformed();
473         }
474       }
475     });
476
477     btn_pdbFromFile.setFont(VERDANA_12);
478     String btn_title = MessageManager.getString("label.select_pdb_file");
479     btn_pdbFromFile.setText(btn_title + "              ");
480     btn_pdbFromFile.addActionListener(new java.awt.event.ActionListener()
481     {
482       @Override
483       public void actionPerformed(ActionEvent e)
484       {
485         pdbFromFile_actionPerformed();
486       }
487     });
488     btn_pdbFromFile.addKeyListener(new KeyAdapter()
489     {
490       @Override
491       public void keyPressed(KeyEvent evt)
492       {
493         if (evt.getKeyCode() == KeyEvent.VK_ENTER)
494         {
495           pdbFromFile_actionPerformed();
496         }
497       }
498     });
499
500     JScrollPane scrl_foundStructures = new JScrollPane(tbl_summary);
501     scrl_foundStructures.setPreferredSize(new Dimension(width, height));
502
503     JScrollPane scrl_localPDB = new JScrollPane(tbl_local_pdb);
504     scrl_localPDB.setPreferredSize(new Dimension(width, height));
505     scrl_localPDB.setHorizontalScrollBarPolicy(
506             JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
507
508     chk_invertFilter.setFont(VERDANA_12);
509     txt_search.setToolTipText(JvSwingUtils.wrapTooltip(true,
510             MessageManager.getString("label.enter_pdb_id_tip")));
511     txt_search.getDocument().addDocumentListener(new DocumentListener()
512     {
513       @Override
514       public void insertUpdate(DocumentEvent e)
515       {
516         txt_search_ActionPerformedDelayed();
517       }
518
519       @Override
520       public void removeUpdate(DocumentEvent e)
521       {
522         txt_search_ActionPerformedDelayed();
523       }
524
525       @Override
526       public void changedUpdate(DocumentEvent e)
527       {
528         txt_search_ActionPerformedDelayed();
529       }
530     });
531
532     cmb_filterOption.setFont(VERDANA_12);
533     cmb_filterOption.setToolTipText(
534             MessageManager.getString("info.select_filter_option"));
535     cmb_filterOption.addItemListener(this);
536     // add CustomComboSeparatorsRenderer to filter option combo-box
537     cmb_filterOption.setRenderer(new CustomComboSeparatorsRenderer(
538             (ListCellRenderer<Object>) cmb_filterOption.getRenderer())
539     {
540       @Override
541       protected boolean addSeparatorAfter(JList list, FilterOption value,
542               int index)
543       {
544         return value.isAddSeparatorAfter();
545       }
546     });
547
548     chk_invertFilter.addItemListener(this);
549
550     targetView.setVisible(false);
551
552     JPanel actionsPanel = new JPanel(new MigLayout());
553     actionsPanel.add(targetView, "left");
554     actionsPanel.add(btn_add, "wrap");
555     actionsPanel.add(chk_superpose, "left");
556     actionsPanel.add(btn_newView);
557     actionsPanel.add(btn_cancel, "right");
558
559     JPanel pnl_main = new JPanel();
560     pnl_main.add(cmb_filterOption);
561     pnl_main.add(lbl_loading);
562     pnl_main.add(chk_invertFilter);
563     lbl_loading.setVisible(false);
564
565     JPanel pnl_fileChooser = new JPanel(new FlowLayout());
566     pnl_fileChooser.add(btn_pdbFromFile);
567     pnl_fileChooser.add(lbl_fromFileStatus);
568     JPanel pnl_fileChooserBL = new JPanel(new BorderLayout());
569     pnl_fileChooserBL.add(fileChooserAssSeqPanel, BorderLayout.NORTH);
570     pnl_fileChooserBL.add(pnl_fileChooser, BorderLayout.CENTER);
571
572     JPanel pnl_idInput = new JPanel(new FlowLayout());
573     pnl_idInput.add(txt_search);
574     pnl_idInput.add(lbl_pdbManualFetchStatus);
575
576     JPanel pnl_idInputBL = new JPanel(new BorderLayout());
577     pnl_idInputBL.add(idInputAssSeqPanel, BorderLayout.NORTH);
578     pnl_idInputBL.add(pnl_idInput, BorderLayout.CENTER);
579
580     final String foundStructureSummary = MessageManager
581             .getString("label.found_structures_summary");
582     final String configureCols = MessageManager
583             .getString("label.configure_displayed_columns");
584     ChangeListener changeListener = new ChangeListener()
585     {
586       @Override
587       public void stateChanged(ChangeEvent changeEvent)
588       {
589         JTabbedPane sourceTabbedPane = (JTabbedPane) changeEvent
590                 .getSource();
591         int index = sourceTabbedPane.getSelectedIndex();
592         btn_add.setVisible(targetView.isVisible());
593         btn_newView.setVisible(true);
594         btn_cancel.setVisible(true);
595         if (sourceTabbedPane.getTitleAt(index).equals(configureCols))
596         {
597           btn_add.setEnabled(false);
598           btn_cancel.setEnabled(false);
599           btn_add.setVisible(false);
600           btn_newView.setEnabled(false);
601           btn_cancel.setVisible(false);
602           previousWantedFields = pdbDocFieldPrefs
603                   .getStructureSummaryFields()
604                   .toArray(new FTSDataColumnI[0]);
605         }
606         if (sourceTabbedPane.getTitleAt(index)
607                 .equals(foundStructureSummary))
608         {
609           btn_cancel.setEnabled(true);
610           if (wantedFieldsUpdated())
611           {
612             tabRefresh();
613           }
614           else
615           {
616             validateSelections();
617           }
618         }
619       }
620     };
621     pnl_filter.addChangeListener(changeListener);
622     pnl_filter.setPreferredSize(new Dimension(width, height));
623     pnl_filter.add(foundStructureSummary, scrl_foundStructures);
624     pnl_filter.add(configureCols, pdbDocFieldPrefs);
625
626     JPanel pnl_locPDB = new JPanel(new BorderLayout());
627     pnl_locPDB.add(scrl_localPDB);
628
629     pnl_switchableViews.add(pnl_fileChooserBL, VIEWS_FROM_FILE);
630     pnl_switchableViews.add(pnl_idInputBL, VIEWS_ENTER_ID);
631     pnl_switchableViews.add(pnl_filter, VIEWS_FILTER);
632     pnl_switchableViews.add(pnl_locPDB, VIEWS_LOCAL_PDB);
633
634     this.setLayout(new BorderLayout());
635     this.add(pnl_main, java.awt.BorderLayout.NORTH);
636     this.add(pnl_switchableViews, java.awt.BorderLayout.CENTER);
637     // this.add(pnl_actions, java.awt.BorderLayout.SOUTH);
638     statusPanel.setLayout(new GridLayout());
639
640     JPanel pnl_actionsAndStatus = new JPanel(new BorderLayout());
641     pnl_actionsAndStatus.add(actionsPanel, BorderLayout.CENTER);
642     pnl_actionsAndStatus.add(statusPanel, BorderLayout.SOUTH);
643     statusPanel.add(statusBar, null);
644     this.add(pnl_actionsAndStatus, java.awt.BorderLayout.SOUTH);
645
646     mainFrame.addInternalFrameListener(
647             new javax.swing.event.InternalFrameAdapter()
648             {
649               @Override
650               public void internalFrameClosing(InternalFrameEvent e)
651               {
652                 closeAction(pnl_filter.getHeight());
653               }
654             });
655     mainFrame.setVisible(true);
656     mainFrame.setContentPane(this);
657     mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
658     Integer x = tempUserPrefs.get("structureChooser.x");
659     Integer y = tempUserPrefs.get("structureChooser.y");
660     if (x != null && y != null)
661     {
662       mainFrame.setLocation(x, y);
663     }
664     Desktop.addInternalFrame(mainFrame, frameTitle, width, height);
665   }
666
667
668 protected void closeAction(int preferredHeight)
669   {
670     // System.out.println(">>>>>>>>>> closing internal frame!!!");
671     // System.out.println("width : " + mainFrame.getWidth());
672     // System.out.println("heigh : " + mainFrame.getHeight());
673     // System.out.println("x : " + mainFrame.getX());
674     // System.out.println("y : " + mainFrame.getY());
675     tempUserPrefs.put("structureChooser.width", pnl_filter.getWidth());
676     tempUserPrefs.put("structureChooser.height", preferredHeight);
677     tempUserPrefs.put("structureChooser.x", mainFrame.getX());
678     tempUserPrefs.put("structureChooser.y", mainFrame.getY());
679     mainFrame.dispose();
680   }
681
682   public boolean wantedFieldsUpdated()
683   {
684     if (previousWantedFields == null)
685     {
686       return true;
687     }
688
689     FTSDataColumnI[] currentWantedFields = pdbDocFieldPrefs
690             .getStructureSummaryFields().toArray(new FTSDataColumnI[0]);
691     return Arrays.equals(currentWantedFields, previousWantedFields) ? false
692             : true;
693
694   }
695
696   @Override
697   /**
698    * Event listener for the 'filter' combo-box and 'invert' check-box
699    */
700   public void itemStateChanged(ItemEvent e)
701   {
702     stateChanged(e);
703   }
704
705   /**
706    * This inner class provides the data model for the structure filter combo-box
707    * 
708    * @author tcnofoegbu
709    *
710    */
711   public class FilterOption
712   {
713     private String name;
714
715     private String value;
716
717     private String view;
718
719     private boolean addSeparatorAfter;
720
721     /**
722      * Model for structure filter option
723      * 
724      * @param name
725      *          - the name of the Option
726      * @param value
727      *          - the value of the option
728      * @param view
729      *          - the category of the filter option
730      * @param addSeparatorAfter
731      *          - if true, a horizontal separator is rendered immediately after
732      *          this filter option, otherwise
733      */
734     public FilterOption(String name, String value, String view,
735             boolean addSeparatorAfter)
736     {
737       this.name = name;
738       this.value = value;
739       this.view = view;
740       this.addSeparatorAfter = addSeparatorAfter;
741     }
742
743     public String getName()
744     {
745       return name;
746     }
747
748     public void setName(String name)
749     {
750       this.name = name;
751     }
752
753     public String getValue()
754     {
755       return value;
756     }
757
758     public void setValue(String value)
759     {
760       this.value = value;
761     }
762
763     public String getView()
764     {
765       return view;
766     }
767
768     public void setView(String view)
769     {
770       this.view = view;
771     }
772
773     @Override
774     public String toString()
775     {
776       return this.name;
777     }
778
779     public boolean isAddSeparatorAfter()
780     {
781       return addSeparatorAfter;
782     }
783
784     public void setAddSeparatorAfter(boolean addSeparatorAfter)
785     {
786       this.addSeparatorAfter = addSeparatorAfter;
787     }
788   }
789
790   /**
791    * This inner class provides the provides the data model for associate
792    * sequence combo-box - cmb_assSeq
793    * 
794    * @author tcnofoegbu
795    *
796    */
797   public class AssociateSeqOptions
798   {
799     private SequenceI sequence;
800
801     private String name;
802
803     public AssociateSeqOptions(SequenceI seq)
804     {
805       this.sequence = seq;
806       this.name = (seq.getName().length() >= 23)
807               ? seq.getName().substring(0, 23)
808               : seq.getName();
809     }
810
811     public AssociateSeqOptions(String name, SequenceI seq)
812     {
813       this.name = name;
814       this.sequence = seq;
815     }
816
817     @Override
818     public String toString()
819     {
820       return name;
821     }
822
823     public String getName()
824     {
825       return name;
826     }
827
828     public void setName(String name)
829     {
830       this.name = name;
831     }
832
833     public SequenceI getSequence()
834     {
835       return sequence;
836     }
837
838     public void setSequence(SequenceI sequence)
839     {
840       this.sequence = sequence;
841     }
842
843   }
844
845   /**
846    * This inner class holds the Layout and configuration of the panel which
847    * handles association of manually fetched structures to a unique sequence
848    * when more than one sequence selection is made
849    * 
850    * @author tcnofoegbu
851    *
852    */
853   public class AssociateSeqPanel extends JPanel implements ItemListener
854   {
855     private JComboBox<AssociateSeqOptions> cmb_assSeq = new JComboBox<>();
856
857     private JLabel lbl_associateSeq = new JLabel();
858
859     public AssociateSeqPanel()
860     {
861       this.setLayout(new FlowLayout());
862       this.add(cmb_assSeq);
863       this.add(lbl_associateSeq);
864       cmb_assSeq.setToolTipText(
865               MessageManager.getString("info.associate_wit_sequence"));
866       cmb_assSeq.addItemListener(this);
867     }
868
869     public void loadCmbAssSeq()
870     {
871       populateCmbAssociateSeqOptions(cmb_assSeq, lbl_associateSeq);
872     }
873
874     public JComboBox<AssociateSeqOptions> getCmb_assSeq()
875     {
876       return cmb_assSeq;
877     }
878
879     public void setCmb_assSeq(JComboBox<AssociateSeqOptions> cmb_assSeq)
880     {
881       this.cmb_assSeq = cmb_assSeq;
882     }
883
884     @Override
885     public void itemStateChanged(ItemEvent e)
886     {
887       if (e.getStateChange() == ItemEvent.SELECTED)
888       {
889         cmbAssSeqStateChanged();
890       }
891     }
892   }
893
894   public JTable getResultTable()
895   {
896     return tbl_summary;
897   }
898
899   public JComboBox<FilterOption> getCmbFilterOption()
900   {
901     return cmb_filterOption;
902   }
903
904   /**
905    * Custom ListCellRenderer for adding a separator between different categories
906    * of structure chooser filter option drop-down.
907    * 
908    * @author tcnofoegbu
909    *
910    */
911   public abstract class CustomComboSeparatorsRenderer
912           implements ListCellRenderer<Object>
913   {
914     private ListCellRenderer<Object> regent;
915
916     private JPanel separatorPanel = new JPanel(new BorderLayout());
917
918     private JSeparator jSeparator = new JSeparator();
919
920     public CustomComboSeparatorsRenderer(
921             ListCellRenderer<Object> listCellRenderer)
922     {
923       this.regent = listCellRenderer;
924     }
925
926     @Override
927     public Component getListCellRendererComponent(JList list, Object value,
928             int index, boolean isSelected, boolean cellHasFocus)
929     {
930
931       Component comp = regent.getListCellRendererComponent(list, value,
932               index, isSelected, cellHasFocus);
933       if (index != -1
934               && addSeparatorAfter(list, (FilterOption) value, index))
935       {
936         separatorPanel.removeAll();
937         separatorPanel.add(comp, BorderLayout.CENTER);
938         separatorPanel.add(jSeparator, BorderLayout.SOUTH);
939         return separatorPanel;
940       }
941       else
942       {
943         return comp;
944       }
945     }
946
947     protected abstract boolean addSeparatorAfter(JList list,
948             FilterOption value, int index);
949   }
950
951   protected abstract void stateChanged(ItemEvent e);
952
953   protected abstract void add_ActionPerformed();
954
955   protected abstract void newView_ActionPerformed();
956
957   protected abstract void pdbFromFile_actionPerformed();
958
959   protected abstract void txt_search_ActionPerformed();
960
961   protected abstract void populateCmbAssociateSeqOptions(
962           JComboBox<AssociateSeqOptions> cmb_assSeq,
963           JLabel lbl_associateSeq);
964
965   protected abstract void cmbAssSeqStateChanged();
966
967   protected abstract void tabRefresh();
968
969   protected abstract void validateSelections();
970 }