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