JAL-4034 button looks better also on other OSes with the 0 vertical insets
[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     }
605     btn_queryTDB.setMargin(new Insets(0, 16, 0, 20));
606     btn_queryTDB
607             .setText(MessageManager.getString("label.search_3dbeacons"));
608     btn_queryTDB.setIconTextGap(12);
609     btn_queryTDB.setIcon(tdbImage);
610     btn_queryTDB.setVerticalTextPosition(SwingConstants.CENTER);
611     btn_queryTDB.setHorizontalTextPosition(SwingConstants.TRAILING);
612     btn_queryTDB.setFont(VERDANA_12);
613     btn_queryTDB.setToolTipText(
614             MessageManager.getString("label.find_models_from_3dbeacons"));
615     // btn_queryTDB.setPreferredSize(new Dimension(200, 32));
616     btn_queryTDB.setVisible(false);
617
618     targetView.setVisible(false);
619
620     JPanel actionsPanel = new JPanel(new MigLayout());
621     actionsPanel.add(targetView, "left");
622     actionsPanel.add(btn_add, "wrap");
623     actionsPanel.add(chk_superpose, "left");
624     actionsPanel.add(btn_newView);
625     actionsPanel.add(btn_cancel, "right");
626
627     JPanel pnl_main = new JPanel(new BorderLayout());
628     JPanel pnl_controls = new JPanel();
629     pnl_queryTDB = new JPanel();
630     pnl_queryTDB.setLayout(new FlowLayout(FlowLayout.CENTER, 4, 4));
631     pnl_queryTDB.setBackground(getBackground());
632     pnl_queryTDB.add(btn_queryTDB);
633
634     pnl_queryTDB.setVisible(false);
635     pnl_main.add(pnl_queryTDB, BorderLayout.NORTH);
636     pnl_controls.add(cmb_filterOption);
637     pnl_controls.add(lbl_loading);
638     pnl_controls.add(chk_invertFilter);
639     pnl_main.add(pnl_controls, BorderLayout.CENTER);
640     lbl_loading.setVisible(false);
641
642     JPanel pnl_fileChooser = new JPanel(new FlowLayout());
643     pnl_fileChooser.add(btn_pdbFromFile);
644     pnl_fileChooser.add(lbl_fromFileStatus);
645     JPanel pnl_fileChooserBL = new JPanel(new BorderLayout());
646     pnl_fileChooserBL.add(fileChooserAssSeqPanel, BorderLayout.NORTH);
647     pnl_fileChooserBL.add(pnl_fileChooser, BorderLayout.CENTER);
648
649     JPanel pnl_idInput = new JPanel(new FlowLayout());
650     pnl_idInput.add(txt_search);
651     pnl_idInput.add(lbl_pdbManualFetchStatus);
652
653     JPanel pnl_idInputBL = new JPanel(new BorderLayout());
654     pnl_idInputBL.add(idInputAssSeqPanel, BorderLayout.NORTH);
655     pnl_idInputBL.add(pnl_idInput, BorderLayout.CENTER);
656
657     final String foundStructureSummary = MessageManager
658             .getString("label.found_structures_summary");
659     final String configureCols = MessageManager
660             .getString("label.configure_displayed_columns");
661     ChangeListener changeListener = new ChangeListener()
662     {
663       @Override
664       public void stateChanged(ChangeEvent changeEvent)
665       {
666         JTabbedPane sourceTabbedPane = (JTabbedPane) changeEvent
667                 .getSource();
668         int index = sourceTabbedPane.getSelectedIndex();
669         btn_add.setVisible(targetView.isVisible());
670         btn_newView.setVisible(true);
671         btn_cancel.setVisible(true);
672         if (sourceTabbedPane.getTitleAt(index).equals(configureCols))
673         {
674           btn_add.setEnabled(false);
675           btn_cancel.setEnabled(false);
676           btn_add.setVisible(false);
677           btn_newView.setEnabled(false);
678           btn_cancel.setVisible(false);
679           previousWantedFields = getFTSDocFieldPrefs()
680                   .getStructureSummaryFields()
681                   .toArray(new FTSDataColumnI[0]);
682         }
683         if (sourceTabbedPane.getTitleAt(index)
684                 .equals(foundStructureSummary))
685         {
686           btn_cancel.setEnabled(true);
687           if (wantedFieldsUpdated())
688           {
689             tabRefresh();
690           }
691           else
692           {
693             validateSelections();
694           }
695         }
696       }
697     };
698     pnl_filter.addChangeListener(changeListener);
699     pnl_filter.setPreferredSize(new Dimension(width, height));
700     pnl_filter.add(foundStructureSummary, scrl_foundStructures);
701     pnl_filter.add(configureCols, getFTSDocFieldPrefs());
702
703     JPanel pnl_locPDB = new JPanel(new BorderLayout());
704     pnl_locPDB.add(scrl_localPDB);
705
706     pnl_switchableViews.add(pnl_fileChooserBL, VIEWS_FROM_FILE);
707     pnl_switchableViews.add(pnl_idInputBL, VIEWS_ENTER_ID);
708     pnl_switchableViews.add(pnl_filter, VIEWS_FILTER);
709     pnl_switchableViews.add(pnl_locPDB, VIEWS_LOCAL_PDB);
710
711     this.setLayout(new BorderLayout());
712     this.add(pnl_main, java.awt.BorderLayout.NORTH);
713     this.add(pnl_switchableViews, java.awt.BorderLayout.CENTER);
714     // this.add(pnl_actions, java.awt.BorderLayout.SOUTH);
715     statusPanel.setLayout(new GridLayout());
716
717     JPanel pnl_actionsAndStatus = new JPanel(new BorderLayout());
718     pnl_actionsAndStatus.add(actionsPanel, BorderLayout.CENTER);
719     pnl_actionsAndStatus.add(statusPanel, BorderLayout.SOUTH);
720     statusPanel.add(statusBar, null);
721     this.add(pnl_actionsAndStatus, java.awt.BorderLayout.SOUTH);
722
723     mainFrame.addInternalFrameListener(
724             new javax.swing.event.InternalFrameAdapter()
725             {
726               @Override
727               public void internalFrameClosing(InternalFrameEvent e)
728               {
729                 closeAction(pnl_filter.getHeight());
730               }
731             });
732     mainFrame.setVisible(true);
733     mainFrame.setContentPane(this);
734     mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
735     Integer x = tempUserPrefs.get("structureChooser.x");
736     Integer y = tempUserPrefs.get("structureChooser.y");
737     if (x != null && y != null)
738     {
739       mainFrame.setLocation(x, y);
740     }
741     Desktop.addInternalFrame(mainFrame, frameTitle, width, height);
742   }
743
744   protected abstract boolean showPopupFor(int selectedRow, int x, int y);
745
746   protected void closeAction(int preferredHeight)
747   {
748     // System.out.println(">>>>>>>>>> closing internal frame!!!");
749     // System.out.println("width : " + mainFrame.getWidth());
750     // System.out.println("heigh : " + mainFrame.getHeight());
751     // System.out.println("x : " + mainFrame.getX());
752     // System.out.println("y : " + mainFrame.getY());
753     tempUserPrefs.put("structureChooser.width", pnl_filter.getWidth());
754     tempUserPrefs.put("structureChooser.height", preferredHeight);
755     tempUserPrefs.put("structureChooser.x", mainFrame.getX());
756     tempUserPrefs.put("structureChooser.y", mainFrame.getY());
757     mainFrame.dispose();
758   }
759
760   public boolean wantedFieldsUpdated()
761   {
762     if (previousWantedFields == null)
763     {
764       return true;
765     }
766
767     FTSDataColumnI[] currentWantedFields = getFTSDocFieldPrefs()
768             .getStructureSummaryFields().toArray(new FTSDataColumnI[0]);
769     return Arrays.equals(currentWantedFields, previousWantedFields) ? false
770             : true;
771
772   }
773
774   @Override
775   /**
776    * Event listener for the 'filter' combo-box and 'invert' check-box
777    */
778   public void itemStateChanged(ItemEvent e)
779   {
780     stateChanged(e);
781   }
782
783   /**
784    * This inner class provides the provides the data model for associate
785    * sequence combo-box - cmb_assSeq
786    * 
787    * @author tcnofoegbu
788    *
789    */
790   public class AssociateSeqOptions
791   {
792     private SequenceI sequence;
793
794     private String name;
795
796     public AssociateSeqOptions(SequenceI seq)
797     {
798       this.sequence = seq;
799       this.name = (seq.getName().length() >= 23)
800               ? seq.getName().substring(0, 23)
801               : seq.getName();
802     }
803
804     public AssociateSeqOptions(String name, SequenceI seq)
805     {
806       this.name = name;
807       this.sequence = seq;
808     }
809
810     @Override
811     public String toString()
812     {
813       return name;
814     }
815
816     public String getName()
817     {
818       return name;
819     }
820
821     public void setName(String name)
822     {
823       this.name = name;
824     }
825
826     public SequenceI getSequence()
827     {
828       return sequence;
829     }
830
831     public void setSequence(SequenceI sequence)
832     {
833       this.sequence = sequence;
834     }
835
836   }
837
838   /**
839    * This inner class holds the Layout and configuration of the panel which
840    * handles association of manually fetched structures to a unique sequence
841    * when more than one sequence selection is made
842    * 
843    * @author tcnofoegbu
844    *
845    */
846   public class AssociateSeqPanel extends JPanel implements ItemListener
847   {
848     private JComboBox<AssociateSeqOptions> cmb_assSeq = new JComboBox<>();
849
850     private JLabel lbl_associateSeq = new JLabel();
851
852     public AssociateSeqPanel()
853     {
854       this.setLayout(new FlowLayout());
855       this.add(cmb_assSeq);
856       this.add(lbl_associateSeq);
857       cmb_assSeq.setToolTipText(
858               MessageManager.getString("info.associate_wit_sequence"));
859       cmb_assSeq.addItemListener(this);
860     }
861
862     public void loadCmbAssSeq()
863     {
864       populateCmbAssociateSeqOptions(cmb_assSeq, lbl_associateSeq);
865     }
866
867     public JComboBox<AssociateSeqOptions> getCmb_assSeq()
868     {
869       return cmb_assSeq;
870     }
871
872     public void setCmb_assSeq(JComboBox<AssociateSeqOptions> cmb_assSeq)
873     {
874       this.cmb_assSeq = cmb_assSeq;
875     }
876
877     @Override
878     public void itemStateChanged(ItemEvent e)
879     {
880       if (e.getStateChange() == ItemEvent.SELECTED)
881       {
882         cmbAssSeqStateChanged();
883       }
884     }
885   }
886
887   public JTable getResultTable()
888   {
889     return tbl_summary;
890   }
891
892   public JComboBox<FilterOption> getCmbFilterOption()
893   {
894     return cmb_filterOption;
895   }
896
897   /**
898    * Custom ListCellRenderer for adding a separator between different categories
899    * of structure chooser filter option drop-down.
900    * 
901    * @author tcnofoegbu
902    *
903    */
904   public abstract class CustomComboSeparatorsRenderer
905           implements ListCellRenderer<Object>
906   {
907     private ListCellRenderer<Object> regent;
908
909     private JPanel separatorPanel = new JPanel(new BorderLayout());
910
911     private JSeparator jSeparator = new JSeparator();
912
913     public CustomComboSeparatorsRenderer(
914             ListCellRenderer<Object> listCellRenderer)
915     {
916       this.regent = listCellRenderer;
917     }
918
919     @Override
920     public Component getListCellRendererComponent(JList list, Object value,
921             int index, boolean isSelected, boolean cellHasFocus)
922     {
923
924       Component comp = regent.getListCellRendererComponent(list, value,
925               index, isSelected, cellHasFocus);
926       if (index != -1
927               && addSeparatorAfter(list, (FilterOption) value, index))
928       {
929         separatorPanel.removeAll();
930         separatorPanel.add(comp, BorderLayout.CENTER);
931         separatorPanel.add(jSeparator, BorderLayout.SOUTH);
932         return separatorPanel;
933       }
934       else
935       {
936         return comp;
937       }
938     }
939
940     protected abstract boolean addSeparatorAfter(JList list,
941             FilterOption value, int index);
942   }
943
944   protected abstract void stateChanged(ItemEvent e);
945
946   protected abstract void add_ActionPerformed();
947
948   protected abstract void newView_ActionPerformed();
949
950   protected abstract void pdbFromFile_actionPerformed();
951
952   protected abstract void txt_search_ActionPerformed();
953
954   protected abstract void populateCmbAssociateSeqOptions(
955           JComboBox<AssociateSeqOptions> cmb_assSeq,
956           JLabel lbl_associateSeq);
957
958   protected abstract void cmbAssSeqStateChanged();
959
960   protected abstract void tabRefresh();
961
962   protected abstract void validateSelections();
963
964   public JInternalFrame getFrame()
965   {
966     return mainFrame;
967   }
968 }