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