/*
* ALMA - Atacama Large Millimiter Array (c) European Southern Observatory, 2007
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library 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 Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
package alma.acsplugins.alarmsystem.gui.toolbar;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.JTextField;
import javax.swing.JToggleButton;
import javax.swing.ListCellRenderer;
import javax.swing.SwingConstants;
import javax.swing.border.EtchedBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import alma.acsplugins.alarmsystem.gui.CernSysPanel;
import alma.acsplugins.alarmsystem.gui.sound.AlarmSound;
import alma.acsplugins.alarmsystem.gui.table.AlarmGUIType;
import alma.acsplugins.alarmsystem.gui.table.AlarmTable;
import alma.acsplugins.alarmsystem.gui.table.AlarmTableModel;
import alma.acsplugins.alarmsystem.gui.table.AlarmTableModel.PriorityLabel;
import alma.acs.gui.util.threadsupport.EDTExecutor;
/**
* The toolbar for the alarm panel
*
* @author acaproni
*
*/
public class Toolbar extends JPanel implements ActionListener, DocumentListener {
/**
* The rendered for the auto acknowledge combo box
* This renderer shows each level with it color as defined in CellColor
*
* @author acaproni
*
*/
public class ComboBoxRenderer implements ListCellRenderer {
// The label shown by the combo box (i.e. the text of the
// selected item)
private JLabel selectedLabel=new JLabel();
/**
* Constructor
*/
public ComboBoxRenderer() {
selectedLabel.setOpaque(false);
Dimension d = new Dimension(ComboBoxValues.getWidth(),ComboBoxValues.getHeight());
selectedLabel.setMinimumSize(d);
}
/**
* @see ListCellRenderer
*/
public Component getListCellRendererComponent(JList list, Object value,
int index, boolean isSelected, boolean cellHasFocus) {
if (index==-1) {
ComboBoxValues val = (ComboBoxValues)value;
selectedLabel.setText(val.title);
selectedLabel.setBackground(val.normalRenderer.getBackground());
selectedLabel.setForeground(val.normalRenderer.getForeground());
return selectedLabel;
}
if (isSelected) {
return ((ComboBoxValues)value).selectedRenderer;
} else {
return ((ComboBoxValues)value).normalRenderer;
}
}
}
/**
* The values shown in the ComboBox.
* It contains the labels to use as renderer for each cell.
* One label is for the normal situation and the second one
* is used when the cell has focus (inverted colors)
*
* @author acaproni
*
*/
public enum ComboBoxValues {
NONE("None",AlarmGUIType.INACTIVE),
PRIORITY3(PriorityLabel.LOW.description,AlarmGUIType.PRIORITY_3),
PRIORITY2(PriorityLabel.MEDIUM.description,AlarmGUIType.PRIORITY_2),
PRIORITY1(PriorityLabel.HIGH.description,AlarmGUIType.PRIORITY_1);
/**
* The title of the value
*/
public final String title;
/**
* Normal renderer
*/
public final JLabel normalRenderer;
/**
* Normal renderer
*/
public final JLabel selectedRenderer;
/**
* The AlarmGUIType related to this ComboBox value
*/
public final AlarmGUIType guiType;
// Width and height of the label
// They are calculated from the dimension of the strings
// to show
private static int height=0;
private static int width=0;
/**
* Constructor
*
* @param title
* @param guiType
*/
private ComboBoxValues(String title,AlarmGUIType guiType) {
this.guiType=guiType;
this.title=title;
normalRenderer = new JLabel(title);
normalRenderer.setBackground(guiType.backg);
normalRenderer.setForeground(guiType.foreg);
normalRenderer.setHorizontalAlignment(SwingConstants.CENTER);
normalRenderer.setVerticalAlignment(SwingConstants.CENTER);
normalRenderer.setOpaque(true);
Font fnt = normalRenderer.getFont();
Font newFont = fnt.deriveFont(fnt.getSize()*80/100);
normalRenderer.setFont(newFont);
selectedRenderer = new JLabel(title);
selectedRenderer.setBackground(guiType.foreg);
selectedRenderer.setForeground(guiType.backg);
selectedRenderer.setHorizontalAlignment(SwingConstants.CENTER);
selectedRenderer.setVerticalAlignment(SwingConstants.CENTER);
selectedRenderer.setFont(newFont);
selectedRenderer.setOpaque(true);
selectedRenderer.setBorder(BorderFactory.createLineBorder(guiType.backg));
}
/**
* Init the sizes of all the labels
*/
public static void initSizes() {
for (ComboBoxValues val: ComboBoxValues.values()) {
Font f = val.normalRenderer.getFont();
FontMetrics fm = val.normalRenderer.getFontMetrics(f);
int h = fm.getHeight()+5;
int w = fm.charsWidth(val.title.toCharArray(), 0, val.title.length())+10;
setHeight(h);
setWidth(w);
}
Dimension d = new Dimension(ComboBoxValues.getWidth(),ComboBoxValues.getHeight());
for (ComboBoxValues val: ComboBoxValues.values()) {
val.normalRenderer.setPreferredSize(d);
val.selectedRenderer.setPreferredSize(d);
val.normalRenderer.setMinimumSize(d);
val.selectedRenderer.setMinimumSize(d);
}
System.out.println(d);
}
/**
* Return the height of each label
*
* @return The height of each lable
*/
public static int getHeight() {
return height;
}
/**
* Return the width of each label
*
* @return The width of each label
*/
public static int getWidth() {
return width;
}
/**
* Set the height (static fields can't be
* called directly by the constructor)
*
* @param height The new height
*/
private static void setHeight(int height) {
if (height>ComboBoxValues.height) {
ComboBoxValues.height = height;
}
}
/**
* Set the width (static fields can't be
* called directly by the constructor)
*
* @param width The new width
*/
private static void setWidth(int width) {
if (width>ComboBoxValues.width) {
ComboBoxValues.width = width;
}
}
}
/**
* The panel showing the toolbar
*/
private final CernSysPanel alarmPanel;
/**
* The combo box for auto-acknowledgment of alarms
*/
private JComboBox autoAckLevelCB=new JComboBox(ComboBoxValues.values());
/**
* The icon shown in the button when reduction is active
*/
private ImageIcon activeReductionIcon;
/**
* The icon shown in the button when reduction is inactive
*/
private ImageIcon inactiveReductionIcon;
/**
* The check box to activate/deactivate the reduction of alarms
*/
private JToggleButton reductionRulesBtn;
/**
* The icon shown by <code>pauseBtn</code> when the application is paused
*/
private ImageIcon pausedIcon = new ImageIcon(Toolbar.class.getResource(AlarmGUIType.iconFolder+"play.png"));
/**
* The icon shown by <code>pauseBtn</code> when the application is not paused
*/
private ImageIcon notPausedIcon = new ImageIcon(Toolbar.class.getResource(AlarmGUIType.iconFolder+"pause.png"));
/**
* The icon for the filter button
*/
private ImageIcon filterIcon = new ImageIcon(Toolbar.class.getResource(AlarmGUIType.iconFolder+"filters.png"));
/**
* The button to select the alarms matching the content of the <code>searchTF</code> text field
*/
private JToggleButton showBtn = new JToggleButton("Show",filterIcon, false);
/**
* The button to hide the alarms matching the content of the <code>searchTF</code> text field
*/
private JToggleButton hideBtn = new JToggleButton("Hide",filterIcon, false);
/**
* The button to pause/unpause the application
*/
private JButton pauseBtn = new JButton("Pause",notPausedIcon);
/**
* The label box for auto-acknowledgement of alarms
*/
private JLabel autoAckLbl = new JLabel("Auto ack: ");
/**
* The text field to write the text to search for in the table
*/
private JTextField searchTF = new JTextField(16);
/**
* The button to search for the next item
*/
private JButton nextSearchBtn = new JButton(new ImageIcon(Toolbar.class.getResource(AlarmGUIType.iconFolder+"resultset_next.png")));
/**
* The button to search for the next item
*/
private JButton prevSearchBtn = new JButton(new ImageIcon(Toolbar.class.getResource(AlarmGUIType.iconFolder+"resultset_previous.png")));
/**
* The table of alarms
*/
private final AlarmTable table;
/**
* The table model
*/
private final AlarmTableModel model;
private final SoundWidget soundComponent;
/**
* Constructor
*
* @param table The table of alarms
* @param model The table model
* @param alarmSound The object playing audibles
* @param reduce <code>true</code> if the reduction rules are applied at startup
* @param panel The panel showing the toolbar
*/
public Toolbar(AlarmTable table, AlarmTableModel model, AlarmSound alarmSound, boolean reduce, CernSysPanel panel) {
super();
if (table==null) {
throw new IllegalArgumentException("The table can't be null");
}
if (model==null) {
throw new IllegalArgumentException("The model can't be null");
}
if (panel==null) {
throw new IllegalArgumentException("The panel can't be null");
}
soundComponent=new SoundWidget(alarmSound);
this.table=table;
this.model=model;
this.alarmPanel=panel;
initialize(reduce);
}
/**
* Initialize the toolbar
*
* @param <code>true</code> if the reduction rules are applied at startup
*/
private void initialize(boolean reduce) {
setLayout(new BoxLayout(this,BoxLayout.LINE_AXIS));
setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
// Add the button to inhibit sounds
add(soundComponent);
// Add the label and the combobox for auto ack
Font fnt = autoAckLbl.getFont();
Font newFont = fnt.deriveFont(fnt.getSize()*80/100);
autoAckLbl.setFont(newFont);
add(Box.createHorizontalStrut(2));
add(autoAckLbl);
autoAckLevelCB.setFont(newFont);
autoAckLevelCB.setEditable(false);
autoAckLevelCB.setOpaque(true);
// Set the colors of the renderers
ComboBoxValues.initSizes();
autoAckLevelCB.setRenderer(new ComboBoxRenderer());
autoAckLevelCB.setSelectedIndex(ComboBoxValues.NONE.ordinal());
autoAckLevelCB.setMaximumRowCount(ComboBoxValues.values().length);
autoAckLevelCB.setEditable(false);
autoAckLevelCB.addActionListener(this);
Dimension d = new Dimension(ComboBoxValues.getWidth(),ComboBoxValues.getHeight());
autoAckLevelCB.setMinimumSize(d);
add(Box.createHorizontalStrut(5));
add(autoAckLevelCB);
activeReductionIcon=new ImageIcon(this.getClass().getResource("/alma/acsplugins/alarmsystem/gui/resources/arrow_in.png"));
inactiveReductionIcon=new ImageIcon(this.getClass().getResource("/alma/acsplugins/alarmsystem/gui/resources/arrow_out.png"));
reductionRulesBtn= new JToggleButton("Reduce",activeReductionIcon, reduce);
reductionRulesBtn.setFont(newFont);
add(Box.createHorizontalStrut(5));
add(reductionRulesBtn);
reductionRulesBtn.addActionListener(this);
add(Box.createHorizontalStrut(5));
add(pauseBtn);
pauseBtn.addActionListener(this);
pauseBtn.setFont(newFont);
add(Box.createHorizontalStrut(5));
add(new JSeparator(JSeparator.VERTICAL));
add(Box.createHorizontalStrut(5));
JLabel searchLbl=new JLabel("Search");
add(searchLbl);
searchLbl.setFont(newFont);
add(Box.createHorizontalStrut(5));
add(searchTF);
searchTF.setEditable(true);
searchTF.getDocument().addDocumentListener(this);
searchTF.setToolTipText("Search");
add(Box.createHorizontalStrut(3));
add(prevSearchBtn);
prevSearchBtn.setToolTipText("Search prev");
prevSearchBtn.addActionListener(this);
add(Box.createHorizontalStrut(3));
add(nextSearchBtn);
nextSearchBtn.setToolTipText("Search next");
nextSearchBtn.addActionListener(this);
add(Box.createHorizontalStrut(3));
add(showBtn);
showBtn.setFont(newFont);
showBtn.setToolTipText("Filter in");
showBtn.addActionListener(this);
add(Box.createHorizontalStrut(3));
add(hideBtn);
hideBtn.setFont(newFont);
hideBtn.setToolTipText("Filter out");
hideBtn.addActionListener(this);
add(Box.createHorizontalStrut(2));
ratioSearchBtns();
}
/**
* Set the auto acknowledge level to the passed priority.
* <P>
* The priority must be set accordingly to the rules explained in {@link CernSysPanel#AutoAckLevelPropName},
* so the only valid values are -1,1,2,3
*
*
* @param newLevel The new priority of the auto acknowledge
* @see CernSysPanel#AutoAckLevelPropName
*/
public void setAutoAckLevel(int newLevel) {
ComboBoxValues ackLvl=null;
switch (newLevel) {
case 1: {
ackLvl=ComboBoxValues.PRIORITY1;
break;
}
case 2: {
ackLvl=ComboBoxValues.PRIORITY2;
break;
}
case 3: {
ackLvl=ComboBoxValues.PRIORITY3;
break;
}
default: {
ackLvl=ComboBoxValues.NONE;
}
}
final ComboBoxValues ackLvlToSet=ackLvl;
EDTExecutor.instance().execute(new Runnable() {
public void run() {
// This trigger an ActionEvent too.
autoAckLevelCB.setSelectedItem(ackLvlToSet);
}
});
}
/**
* @see ActionListener
*/
public void actionPerformed(ActionEvent e) {
if (e.getSource()==autoAckLevelCB) {
model.setAutoAckLevel((ComboBoxValues)autoAckLevelCB.getSelectedItem());
} else if (e.getSource()==reductionRulesBtn) {
model.applyReductions(reductionRulesBtn.isSelected());
if (reductionRulesBtn.isSelected()) {
reductionRulesBtn.setIcon(activeReductionIcon);
} else {
reductionRulesBtn.setIcon(inactiveReductionIcon);
}
} else if (e.getSource()==pauseBtn) {
try {
if (pauseBtn.getIcon()==notPausedIcon) {
alarmPanel.pause();
} else {
alarmPanel.resume();
}
} catch (Throwable t) {
t.printStackTrace(System.err);
JOptionPane.showMessageDialog(this, t.getMessage(), "Error pausing/unpausing", JOptionPane.ERROR_MESSAGE);
}
} else if (e.getSource()==prevSearchBtn) {
table.search(searchTF.getText(), false);
} else if (e.getSource()==nextSearchBtn) {
table.search(searchTF.getText(), true);
} else if (e.getSource()==showBtn) {
searchTF.setEnabled(!showBtn.isSelected());
hideBtn.setEnabled(!showBtn.isSelected());
if (showBtn.isSelected()) {
table.filter(searchTF.getText(),false);
} else {
table.filter(null,false);
}
} else if (e.getSource()==hideBtn) {
searchTF.setEnabled(!hideBtn.isSelected());
showBtn.setEnabled(!hideBtn.isSelected());
if (hideBtn.isSelected()) {
table.filter(searchTF.getText(),true);
} else {
table.filter(null,false);
}
} else {
System.err.println("Invalid source of event: "+e.getSource());
}
}
/**
* Update the state of the pause button depending on the state
* paused/unpaused of the application
* <P>
* This method is not called directly by <code>actionPerformed</code> when the button
* is pressed.
* It is executed when the application is started/paused.
*
* @param paused <code>true</code> if the application is paused
*/
public void updatePauseBtn(final boolean paused) {
EDTExecutor.instance().execute(new Runnable() {
public void run() {
if (paused) {
pauseBtn.setIcon(pausedIcon);
pauseBtn.setText("Play");
} else {
pauseBtn.setIcon(notPausedIcon);
pauseBtn.setText("Pause");
}
}
});
}
/**
* The document listener for the text in the search TF
*
* @see DocumentListener
*/
@Override
public void changedUpdate(DocumentEvent e) {
ratioSearchBtns();
}
/**
* The document listener for the text in the search TF
*
* @see DocumentListener
*/
@Override
public void insertUpdate(DocumentEvent e) {
ratioSearchBtns();
}
/**
* The document listener for the text in the search TF
*
* @see DocumentListener
*/
@Override
public void removeUpdate(DocumentEvent e) {
ratioSearchBtns();
}
/**
* Enable/disable the buttons for searching depending
* on the content of the text field.
* <P>
* This is the logic:
* <UL>
* <LI>if the TF is empty the buttons are all disabled
* <LI>if the TF contains test, the buttons are all enabled
* <LI>if the user filters then the TF is disabled
* (this is done while catching the <code>filterBtn</code> event)
*</UL>
*/
private void ratioSearchBtns() {
EDTExecutor.instance().execute(new Runnable() {
public void run() {
String text = searchTF.getText();
prevSearchBtn.setEnabled(text!=null && !text.isEmpty());
nextSearchBtn.setEnabled(text!=null && !text.isEmpty());
showBtn.setEnabled(text!=null && !text.isEmpty());
hideBtn.setEnabled(text!=null && !text.isEmpty());
}
});
}
}