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