package viz.panel; import java.awt.Color; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JColorChooser; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JTextField; import javax.swing.SpinnerNumberModel; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import viz.DensiTree; import viz.GridDrawer.GridMode; import viz.graphics.JFontChooser; import viz.util.Util; import javax.swing.BoxLayout; import javax.swing.border.LineBorder; import javax.swing.JSpinner; import javax.swing.event.ChangeListener; import javax.swing.event.ChangeEvent; import javax.swing.JSeparator; public class GridPanel extends JPanel { /** * */ private static final long serialVersionUID = 1L; final public static String HELP_GRID = "Show lines indicating timescale. Options are to show none, " + "short lines at the side of the panel, or full lines over the complete tree set."; final public static String HELP_DIGITS = "Set number of significant digits for the grid labels."; final public static String HELP_REVERSE = "By setting reverse, the time scale will be drawn forward in time. " + "By default, time scale is drawn backward in time, so that the height of a tree is a positive " + "number. Also, set 'origin' to the date of the youngest tip."; final public static String HELP_FONT = "Set font of the grid labels."; final public static String HELP_COLOR = "Set colour of the grid labels."; final public static String HELP_ORIGIN = "Set date of the youngest tip."; final public static String HELP_AUTOMATIC = "Automatically determine the number of ticks."; final public static String HELP_TICKS = "Interval between two ticks."; final public static String HELP_OFFSET = "Time added to the ticks. This can be usefull when the youngest tip of the " + "tree is on a number that is not quite a round number, for example 2003.4. Setting the offset to " + "-3.4 ensures the grid lines will be drawn on 2000 instead of through 2003.4."; final public static String HELP_SCALE = "Scale time, which can be handy when the tree is in substitutions " + "and a clock rate is available from the literature. A negative scale has the same effect as " + "selecting 'reverse' with a positive scale."; DensiTree m_dt; ButtonGroup m_modeGroup = new ButtonGroup(); private JTextField m_originTextField; private JTextField m_ticksTextField; private JTextField m_offsetTextField; SpinnerNumberModel significantDigitsModel; private JTextField txtScale; /** * Create the panel. */ public GridPanel(DensiTree dt) { // setBorder(new BevelBorder(BevelBorder.LOWERED, null, null, null, // null)); m_dt = dt; GridBagLayout gridBagLayout = new GridBagLayout(); gridBagLayout.columnWidths = new int[] { 10, 0, 20, 0, 0 }; gridBagLayout.rowWeights = new double[] { 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; gridBagLayout.columnWeights = new double[] { 1.0, 1.0, 1.0, 0.0, 0.0 }; setLayout(gridBagLayout); JPanel panel = new JPanel(); panel.setBorder(new LineBorder(Color.gray)); GridBagConstraints gbc_panel = new GridBagConstraints(); gbc_panel.weightx = 0.95; gbc_panel.gridwidth = 3; gbc_panel.gridheight = 3; gbc_panel.insets = new Insets(0, 0, 5, 5); gbc_panel.fill = GridBagConstraints.BOTH; gbc_panel.gridx = 0; gbc_panel.gridy = 1; add(panel, gbc_panel); panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); JRadioButton rdbtnNewRadioButton_1 = new JRadioButton("No grid"); panel.add(rdbtnNewRadioButton_1); rdbtnNewRadioButton_1.addActionListener(e-> { m_dt.m_gridDrawer.m_nGridMode = GridMode.NONE; m_dt.makeDirty(); }); m_modeGroup.add(rdbtnNewRadioButton_1); JRadioButton rdbtnNewRadioButton = new JRadioButton("Short grid"); panel.add(rdbtnNewRadioButton); rdbtnNewRadioButton.addActionListener(e-> { m_dt.m_gridDrawer.m_nGridMode = GridMode.SHORT; m_dt.makeDirty(); }); m_modeGroup.add(rdbtnNewRadioButton); JRadioButton radioButton_2 = new JRadioButton("Full grid"); panel.add(radioButton_2); radioButton_2.addActionListener(e-> { m_dt.m_gridDrawer.m_nGridMode = GridMode.FULL; m_dt.makeDirty(); }); m_modeGroup.add(radioButton_2); m_modeGroup.setSelected(rdbtnNewRadioButton_1.getModel(), true); rdbtnNewRadioButton.setToolTipText(Util.formatToolTipAsHtml(HELP_GRID)); rdbtnNewRadioButton_1.setToolTipText(Util.formatToolTipAsHtml(HELP_GRID)); radioButton_2.setToolTipText(Util.formatToolTipAsHtml(HELP_GRID)); JLabel lblDigits = new JLabel("Digits"); lblDigits.setToolTipText(Util.formatToolTipAsHtml(HELP_DIGITS)); GridBagConstraints gbc_lblDigits = new GridBagConstraints(); gbc_lblDigits.anchor = GridBagConstraints.SOUTHWEST; gbc_lblDigits.insets = new Insets(0, 0, 5, 5); gbc_lblDigits.gridx = 0; gbc_lblDigits.gridy = 4; add(lblDigits, gbc_lblDigits); significantDigitsModel = new SpinnerNumberModel(m_dt.m_gridDrawer.m_nGridDigits, 0, 5, 1); JSpinner spinner = new JSpinner(significantDigitsModel); spinner.setToolTipText(Util.formatToolTipAsHtml(HELP_DIGITS)); significantDigitsModel.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { m_dt.m_gridDrawer.m_nGridDigits = (Integer) significantDigitsModel.getValue(); m_dt.makeDirty(); } }); GridBagConstraints gbc_spinner = new GridBagConstraints(); gbc_spinner.anchor = GridBagConstraints.WEST; gbc_spinner.gridwidth = 2; gbc_spinner.insets = new Insets(0, 0, 5, 5); gbc_spinner.gridx = 1; gbc_spinner.gridy = 4; add(spinner, gbc_spinner); JCheckBox reverseGrid = new JCheckBox("Reverse"); reverseGrid.setToolTipText(Util.formatToolTipAsHtml(HELP_REVERSE)); GridBagConstraints c5 = new GridBagConstraints(); c5.insets = new Insets(0, 0, 5, 5); c5.gridx = 0; c5.gridy = 5; c5.weightx = 0.5; c5.gridwidth = 3; c5.fill = GridBagConstraints.HORIZONTAL; add(reverseGrid, c5); reverseGrid.addActionListener(e-> { JCheckBox button = (JCheckBox) e.getSource(); m_dt.m_gridDrawer.m_bReverseGrid = button.isSelected(); m_dt.m_Panel.clearImage(); m_dt.repaint(); }); JButton btnGridFont = new RoundedButton("Font"); btnGridFont.setToolTipText(Util.formatToolTipAsHtml(HELP_FONT)); GridBagConstraints c7 = new GridBagConstraints(); c7.insets = new Insets(0, 0, 5, 5); c7.gridwidth = 2; c7.gridx = 0; c7.gridy = 6; c7.weightx = 0.5; //c7.insets = new Insets(3, 3, 5, 5); c7.fill = GridBagConstraints.HORIZONTAL; add(btnGridFont, c7); btnGridFont.addActionListener(ae-> { JFontChooser fontChooser = new JFontChooser(); if (m_dt.m_gridDrawer.m_gridfont != null) { fontChooser.setSelectedFont(m_dt.m_gridDrawer.m_gridfont); } int result = fontChooser.showDialog(null); if (result == JFontChooser.OK_OPTION) { m_dt.m_gridDrawer.m_gridfont = fontChooser.getSelectedFont(); m_dt.makeDirty(); m_dt.repaint(); } }); // actionPerformed JButton btnGridColor = new RoundedButton("Color"); btnGridColor.setToolTipText(Util.formatToolTipAsHtml(HELP_COLOR)); GridBagConstraints c6 = new GridBagConstraints(); c6.insets = new Insets(0, 0, 5, 5); c6.gridwidth = 2; c6.gridx = 2; c6.gridy = 6; c6.weightx = 0.5; //c6.insets = new Insets(3, 3, 5, 5); c6.fill = GridBagConstraints.HORIZONTAL; add(btnGridColor, c6); btnGridColor.addActionListener(ae-> { Color newColor = JColorChooser.showDialog(m_dt.m_Panel, getName(), m_dt.m_color[DensiTree.HEIGHTCOLOR]); if (newColor != null) { m_dt.m_color[DensiTree.HEIGHTCOLOR] = newColor; m_dt.makeDirty(); } m_dt.repaint(); }); JLabel lblOffset = new JLabel("Origin"); lblOffset.setToolTipText(Util.formatToolTipAsHtml(HELP_ORIGIN)); GridBagConstraints c4 = new GridBagConstraints(); c4.insets = new Insets(0, 0, 5, 5); c4.gridx = 0; c4.gridy = 7; c4.weightx = 0.5; c4.gridwidth = 1; c4.fill = GridBagConstraints.HORIZONTAL; add(lblOffset, c4); m_originTextField = new JTextField(); m_originTextField.setToolTipText(Util.formatToolTipAsHtml(HELP_ORIGIN)); m_originTextField.setText(m_dt.m_gridDrawer.m_fGridOrigin + ""); GridBagConstraints c8 = new GridBagConstraints(); c8.gridwidth = 2; c8.fill = GridBagConstraints.HORIZONTAL; c8.insets = new Insets(0, 0, 5, 5); c8.gridx = 1; c8.gridy = 7; add(m_originTextField, c8); m_originTextField.setColumns(4); JCheckBox chckbxAutomatic = new JCheckBox("Automatic"); chckbxAutomatic.setToolTipText(Util.formatToolTipAsHtml(HELP_AUTOMATIC)); chckbxAutomatic.setSelected(m_dt.m_gridDrawer.m_bAutoGrid); chckbxAutomatic.addActionListener(e-> { boolean bPrev = m_dt.m_gridDrawer.m_bAutoGrid; m_dt.m_gridDrawer.m_bAutoGrid = ((JCheckBox) e.getSource()).isSelected(); m_ticksTextField.setEnabled(!m_dt.m_gridDrawer.m_bAutoGrid); m_offsetTextField.setEnabled(!m_dt.m_gridDrawer.m_bAutoGrid); if (bPrev != m_dt.m_gridDrawer.m_bAutoGrid) { m_dt.makeDirty(); m_dt.repaint(); } }); GridBagConstraints gbc_chckbxAutomatic = new GridBagConstraints(); gbc_chckbxAutomatic.gridwidth = 3; gbc_chckbxAutomatic.anchor = GridBagConstraints.WEST; gbc_chckbxAutomatic.insets = new Insets(0, 0, 5, 5); gbc_chckbxAutomatic.gridx = 0; gbc_chckbxAutomatic.gridy = 8; add(chckbxAutomatic, gbc_chckbxAutomatic); JLabel lblTicks = new JLabel("Ticks"); lblTicks.setToolTipText(Util.formatToolTipAsHtml(HELP_TICKS)); GridBagConstraints gbc_lblTicks = new GridBagConstraints(); gbc_lblTicks.anchor = GridBagConstraints.WEST; gbc_lblTicks.insets = new Insets(0, 0, 5, 5); gbc_lblTicks.gridx = 0; gbc_lblTicks.gridy = 9; add(lblTicks, gbc_lblTicks); m_ticksTextField = new JTextField(); m_ticksTextField.setToolTipText(Util.formatToolTipAsHtml(HELP_TICKS)); m_ticksTextField.setText(m_dt.m_gridDrawer.m_fGridTicks+""); m_ticksTextField.getDocument().addDocumentListener(new DocumentListener() { @Override public void removeUpdate(DocumentEvent e) { updateOffset(); } @Override public void insertUpdate(DocumentEvent e) { updateOffset(); } @Override public void changedUpdate(DocumentEvent e) { updateOffset(); } private void updateOffset() { try { float fGridTicks = Float.parseFloat(m_ticksTextField.getText()); if (fGridTicks > 0) { m_dt.m_gridDrawer.m_fGridTicks = fGridTicks; m_dt.makeDirty(); m_dt.repaint(); } } catch (NumberFormatException e) { // ignore } } }); GridBagConstraints gbc_txtScale = new GridBagConstraints(); gbc_txtScale.gridwidth = 2; gbc_txtScale.insets = new Insets(0, 0, 5, 5); gbc_txtScale.fill = GridBagConstraints.HORIZONTAL; gbc_txtScale.gridx = 1; gbc_txtScale.gridy = 9; add(m_ticksTextField, gbc_txtScale); m_ticksTextField.setColumns(4); JLabel lblOrigin = new JLabel("Offset"); lblOrigin.setToolTipText(Util.formatToolTipAsHtml(HELP_OFFSET)); GridBagConstraints gbc_lblOrigin = new GridBagConstraints(); gbc_lblOrigin.anchor = GridBagConstraints.WEST; gbc_lblOrigin.insets = new Insets(0, 0, 5, 5); gbc_lblOrigin.gridx = 0; gbc_lblOrigin.gridy = 10; add(lblOrigin, gbc_lblOrigin); m_offsetTextField = new JTextField(); m_offsetTextField.setToolTipText(Util.formatToolTipAsHtml(HELP_OFFSET)); m_offsetTextField.setText(m_dt.m_gridDrawer.m_fGridOffset+""); m_offsetTextField.getDocument().addDocumentListener(new DocumentListener() { @Override public void removeUpdate(DocumentEvent e) { updateOffset(); } @Override public void insertUpdate(DocumentEvent e) { updateOffset(); } @Override public void changedUpdate(DocumentEvent e) { updateOffset(); } private void updateOffset() { try { m_dt.m_gridDrawer.m_fGridOffset = Float.parseFloat(m_offsetTextField.getText()); m_dt.makeDirty(); m_dt.repaint(); } catch (NumberFormatException e) { // ignore } } }); GridBagConstraints gbc_textField_1 = new GridBagConstraints(); gbc_textField_1.gridwidth = 2; gbc_textField_1.insets = new Insets(0, 0, 5, 5); gbc_textField_1.fill = GridBagConstraints.HORIZONTAL; gbc_textField_1.gridx = 1; gbc_textField_1.gridy = 10; add(m_offsetTextField, gbc_textField_1); m_offsetTextField.setColumns(4); m_originTextField.getDocument().addDocumentListener(new DocumentListener() { @Override public void removeUpdate(DocumentEvent e) { updateOffset(); } @Override public void insertUpdate(DocumentEvent e) { updateOffset(); } @Override public void changedUpdate(DocumentEvent e) { updateOffset(); } private void updateOffset() { try { m_dt.m_gridDrawer.m_fGridOrigin = Float.parseFloat(m_originTextField.getText()); m_dt.m_Panel.clearImage(); m_dt.repaint(); } catch (NumberFormatException e) { // ignore } } }); m_ticksTextField.setEnabled(!m_dt.m_gridDrawer.m_bAutoGrid); m_offsetTextField.setEnabled(!m_dt.m_gridDrawer.m_bAutoGrid); JSeparator separator = new JSeparator(); GridBagConstraints gbc_separator = new GridBagConstraints(); gbc_separator.fill = GridBagConstraints.HORIZONTAL; gbc_separator.gridwidth = 3; gbc_separator.insets = new Insets(0, 0, 5, 5); gbc_separator.gridx = 0; gbc_separator.gridy = 11; add(separator, gbc_separator); JLabel lblS = new JLabel("Scale"); lblS.setToolTipText(Util.formatToolTipAsHtml(HELP_SCALE)); GridBagConstraints gbc_lblS = new GridBagConstraints(); gbc_lblS.anchor = GridBagConstraints.WEST; gbc_lblS.insets = new Insets(0, 0, 0, 5); gbc_lblS.gridx = 0; gbc_lblS.gridy = 12; add(lblS, gbc_lblS); txtScale = new JTextField(); txtScale.addActionListener(e-> { try { m_dt.m_fUserScale = Float.parseFloat(txtScale.getText()); m_dt.updateCladeModel(); m_dt.makeDirty(); m_dt.repaint(); } catch (Exception ex) { } }); txtScale.setText(m_dt.m_fUserScale + ""); txtScale.setToolTipText(Util.formatToolTipAsHtml(HELP_SCALE)); GridBagConstraints gbc_txtScale2 = new GridBagConstraints(); gbc_txtScale2.gridwidth = 2; gbc_txtScale2.insets = new Insets(0, 0, 0, 5); gbc_txtScale2.fill = GridBagConstraints.HORIZONTAL; gbc_txtScale2.gridx = 1; gbc_txtScale2.gridy = 12; add(txtScale, gbc_txtScale2); txtScale.setColumns(4); } }