/*
* Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
* Copyright (C) $$Year-Rel$$ The Jalview Authors
*
* This file is part of Jalview.
*
* Jalview is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* Jalview is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jalview. If not, see .
* The Jalview Authors are detailed in the 'AUTHORS' file.
*/
package jalview.jbgui;
import jalview.datamodel.SequenceI;
import jalview.fts.api.FTSDataColumnI;
import jalview.fts.core.FTSDataColumnPreferences;
import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource;
import jalview.fts.service.pdb.PDBFTSRestClient;
import jalview.gui.AlignmentPanel;
import jalview.gui.Desktop;
import jalview.gui.JvSwingUtils;
import jalview.gui.StructureViewer;
import jalview.util.MessageManager;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.ListCellRenderer;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.InternalFrameEvent;
import javax.swing.table.TableColumn;
import net.miginfocom.swing.MigLayout;
@SuppressWarnings("serial")
/**
* GUI layout for structure chooser
*
* @author tcnofoegbu
*
*/
public abstract class GStructureChooser extends JPanel
implements ItemListener
{
private static final Font VERDANA_12 = new Font("Verdana", 0, 12);
public static final String VIEWS_FILTER = "VIEWS_FILTER";
protected static final String VIEWS_FROM_FILE = "VIEWS_FROM_FILE";
protected static final String VIEWS_ENTER_ID = "VIEWS_ENTER_ID";
/*
* 'cached' structure view
*/
protected static final String VIEWS_LOCAL_PDB = "VIEWS_LOCAL_PDB";
protected JPanel statusPanel = new JPanel();
public JLabel statusBar = new JLabel();
protected String frameTitle = MessageManager
.getString("label.structure_chooser");
protected JInternalFrame mainFrame = new JInternalFrame(frameTitle);
protected JComboBox cmb_filterOption = new JComboBox<>();
protected AlignmentPanel ap;
protected StringBuilder errorWarning = new StringBuilder();
protected JButton btn_add;
protected JButton btn_newView;
protected JButton btn_pdbFromFile = new JButton();
protected JButton btn_queryTDB = new JButton();
protected JCheckBox chk_superpose = new JCheckBox(
MessageManager.getString("label.superpose_structures"));
protected JTextField txt_search = new JTextField(14);
protected JPanel pnl_switchableViews = new JPanel(new CardLayout());
protected CardLayout layout_switchableViews = (CardLayout) (pnl_switchableViews
.getLayout());
protected JCheckBox chk_invertFilter = new JCheckBox(
MessageManager.getString("label.invert"));
protected ImageIcon loadingImage = new ImageIcon(
getClass().getResource("/images/loading.gif"));
protected ImageIcon goodImage = new ImageIcon(
getClass().getResource("/images/good.png"));
protected ImageIcon errorImage = new ImageIcon(
getClass().getResource("/images/error.png"));
protected ImageIcon warningImage = new ImageIcon(
getClass().getResource("/images/warning.gif"));
protected ImageIcon tdbImage = new ImageIcon(getClass()
.getResource("/images/3d-beacons-logo-transparent.png"));
protected JLabel lbl_loading = new JLabel(loadingImage);
protected JLabel lbl_pdbManualFetchStatus = new JLabel(errorImage);
protected JLabel lbl_fromFileStatus = new JLabel(errorImage);
protected AssociateSeqPanel idInputAssSeqPanel = new AssociateSeqPanel();
protected AssociateSeqPanel fileChooserAssSeqPanel = new AssociateSeqPanel();
protected JComboBox targetView = new JComboBox<>();
protected JTable tbl_local_pdb = new JTable();
protected JTabbedPane pnl_filter = new JTabbedPane();
protected abstract FTSDataColumnPreferences getFTSDocFieldPrefs();
protected abstract void setFTSDocFieldPrefs(
FTSDataColumnPreferences newPrefs);
protected FTSDataColumnI[] previousWantedFields;
protected static Map tempUserPrefs = new HashMap<>();
private JTable tbl_summary = new JTable()
{
private boolean inLayout;
@Override
public boolean getScrollableTracksViewportWidth()
{
return hasExcessWidth();
}
@Override
public void doLayout()
{
if (hasExcessWidth())
{
autoResizeMode = AUTO_RESIZE_SUBSEQUENT_COLUMNS;
}
inLayout = true;
super.doLayout();
inLayout = false;
autoResizeMode = AUTO_RESIZE_OFF;
}
protected boolean hasExcessWidth()
{
return getPreferredSize().width < getParent().getWidth();
}
@Override
public void columnMarginChanged(ChangeEvent e)
{
if (isEditing())
{
removeEditor();
}
TableColumn resizingColumn = getTableHeader().getResizingColumn();
// Need to do this here, before the parent's
// layout manager calls getPreferredSize().
if (resizingColumn != null && autoResizeMode == AUTO_RESIZE_OFF
&& !inLayout)
{
resizingColumn.setPreferredWidth(resizingColumn.getWidth());
String colHeader = resizingColumn.getHeaderValue().toString();
tempUserPrefs.put(colHeader, resizingColumn.getWidth());
}
resizeAndRepaint();
}
@Override
public String getToolTipText(MouseEvent evt)
{
String toolTipText = null;
java.awt.Point pnt = evt.getPoint();
int rowIndex = rowAtPoint(pnt);
int colIndex = columnAtPoint(pnt);
try
{
if (getValueAt(rowIndex, colIndex) == null)
{
return null;
}
toolTipText = getValueAt(rowIndex, colIndex).toString();
} catch (Exception e)
{
// e.printStackTrace();
}
toolTipText = (toolTipText == null ? null
: (toolTipText.length() > 500
? JvSwingUtils.wrapTooltip(true,
"\"" + toolTipText.subSequence(0, 500)
+ "...\"")
: JvSwingUtils.wrapTooltip(true, toolTipText)));
return toolTipText;
}
};
public GStructureChooser()
{
}
protected void initDialog()
{
try
{
jbInit();
mainFrame.setVisible(false);
mainFrame.invalidate();
mainFrame.pack();
} catch (Exception e)
{
System.out.println(e); // for JavaScript TypeError
e.printStackTrace();
}
}
// BH SwingJS optimization
// (a) 100-ms interruptable timer for text entry -- BH 1/10/2019
// (b) two-character minimum, at least for JavaScript.
private Timer timer;
protected void txt_search_ActionPerformedDelayed()
{
if (timer != null)
{
timer.stop();
}
timer = new Timer(300, new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
txt_search_ActionPerformed();
}
});
timer.setRepeats(false);
timer.start();
}
//
/**
* Initializes the GUI default properties
*
* @throws Exception
*/
private void jbInit() throws Exception
{
Integer width = tempUserPrefs.get("structureChooser.width") == null
? 800
: tempUserPrefs.get("structureChooser.width");
Integer height = tempUserPrefs.get("structureChooser.height") == null
? 400
: tempUserPrefs.get("structureChooser.height");
tbl_summary.setAutoCreateRowSorter(true);
tbl_summary.getTableHeader().setReorderingAllowed(false);
tbl_summary.addMouseListener(new MouseAdapter()
{
@Override
public void mousePressed(MouseEvent e)
{
if (!popupAction(e))
{
super.mousePressed(e);
}
}
@Override
public void mouseClicked(MouseEvent e)
{
if (!popupAction(e))
{
validateSelections();
}
}
@Override
public void mouseReleased(MouseEvent e)
{
if (!popupAction(e))
{
validateSelections();
}
}
boolean popupAction(MouseEvent e)
{
if (e.isPopupTrigger())
{
Point pt = e.getPoint();
int selectedRow = tbl_summary.rowAtPoint(pt);
if (showPopupFor(selectedRow, pt.x, pt.y))
{
return true;
}
}
return false;
}
});
tbl_summary.addKeyListener(new KeyAdapter()
{
@Override
public void keyPressed(KeyEvent evt)
{
validateSelections();
switch (evt.getKeyCode())
{
case KeyEvent.VK_ESCAPE: // escape key
mainFrame.dispose();
break;
case KeyEvent.VK_ENTER: // enter key
if (btn_add.isEnabled())
{
add_ActionPerformed();
}
break;
case KeyEvent.VK_TAB: // tab key
if (evt.isShiftDown())
{
pnl_filter.requestFocus();
}
else
{
btn_add.requestFocus();
}
evt.consume();
break;
default:
return;
}
}
});
JButton btn_cancel = new JButton(
MessageManager.getString("action.cancel"));
btn_cancel.setFont(VERDANA_12);
btn_cancel.addActionListener(new java.awt.event.ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
closeAction(pnl_filter.getHeight());
}
});
btn_cancel.addKeyListener(new KeyAdapter()
{
@Override
public void keyPressed(KeyEvent evt)
{
if (evt.getKeyCode() == KeyEvent.VK_ENTER)
{
closeAction(pnl_filter.getHeight());
}
}
});
tbl_local_pdb.setAutoCreateRowSorter(true);
tbl_local_pdb.getTableHeader().setReorderingAllowed(false);
tbl_local_pdb.addMouseListener(new MouseAdapter()
{
@Override
public void mouseClicked(MouseEvent e)
{
validateSelections();
}
@Override
public void mouseReleased(MouseEvent e)
{
validateSelections();
}
});
tbl_local_pdb.addKeyListener(new KeyAdapter()
{
@Override
public void keyPressed(KeyEvent evt)
{
validateSelections();
switch (evt.getKeyCode())
{
case KeyEvent.VK_ESCAPE: // escape key
mainFrame.dispose();
break;
case KeyEvent.VK_ENTER: // enter key
if (btn_add.isEnabled())
{
add_ActionPerformed();
}
break;
case KeyEvent.VK_TAB: // tab key
if (evt.isShiftDown())
{
cmb_filterOption.requestFocus();
}
else
{
if (btn_add.isEnabled())
{
btn_add.requestFocus();
}
else
{
btn_cancel.requestFocus();
}
}
evt.consume();
break;
default:
return;
}
}
});
btn_newView = new JButton(
MessageManager.formatMessage("action.new_structure_view_with",
StructureViewer.getViewerType().toString()));
btn_newView.setFont(VERDANA_12);
btn_newView.addActionListener(new java.awt.event.ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
newView_ActionPerformed();
}
});
btn_newView.addKeyListener(new KeyAdapter()
{
@Override
public void keyPressed(KeyEvent evt)
{
if (evt.getKeyCode() == KeyEvent.VK_ENTER)
{
newView_ActionPerformed();
}
}
});
// TODO: JAL-3898 - get list of available external programs to view
// structures with
btn_add = new JButton(MessageManager.getString("action.add"));
btn_add.setFont(VERDANA_12);
btn_add.addActionListener(new java.awt.event.ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
add_ActionPerformed();
}
});
btn_add.addKeyListener(new KeyAdapter()
{
@Override
public void keyPressed(KeyEvent evt)
{
if (evt.getKeyCode() == KeyEvent.VK_ENTER)
{
add_ActionPerformed();
}
}
});
btn_pdbFromFile.setFont(VERDANA_12);
String btn_title = MessageManager.getString("label.select_pdb_file");
btn_pdbFromFile.setText(btn_title + " ");
btn_pdbFromFile.addActionListener(new java.awt.event.ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
pdbFromFile_actionPerformed();
}
});
btn_pdbFromFile.addKeyListener(new KeyAdapter()
{
@Override
public void keyPressed(KeyEvent evt)
{
if (evt.getKeyCode() == KeyEvent.VK_ENTER)
{
pdbFromFile_actionPerformed();
}
}
});
JScrollPane scrl_foundStructures = new JScrollPane(tbl_summary);
scrl_foundStructures.setPreferredSize(new Dimension(width, height));
JScrollPane scrl_localPDB = new JScrollPane(tbl_local_pdb);
scrl_localPDB.setPreferredSize(new Dimension(width, height));
scrl_localPDB.setHorizontalScrollBarPolicy(
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
chk_invertFilter.setFont(VERDANA_12);
txt_search.setToolTipText(JvSwingUtils.wrapTooltip(true,
MessageManager.getString("label.enter_pdb_id_tip")));
txt_search.getDocument().addDocumentListener(new DocumentListener()
{
@Override
public void insertUpdate(DocumentEvent e)
{
txt_search_ActionPerformedDelayed();
}
@Override
public void removeUpdate(DocumentEvent e)
{
txt_search_ActionPerformedDelayed();
}
@Override
public void changedUpdate(DocumentEvent e)
{
txt_search_ActionPerformedDelayed();
}
});
cmb_filterOption.setFont(VERDANA_12);
cmb_filterOption.setToolTipText(
MessageManager.getString("info.select_filter_option"));
cmb_filterOption.addItemListener(this);
// add CustomComboSeparatorsRenderer to filter option combo-box
cmb_filterOption.setRenderer(new CustomComboSeparatorsRenderer(
(ListCellRenderer