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