/* * Copyright 2008 the original author or authors. * Copyright 2005 Sun Microsystems, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.rioproject.watch; import net.jini.core.lookup.ServiceItem; import org.rioproject.ui.GlassPaneContainer; import javax.swing.*; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeCellRenderer; import java.awt.*; import java.awt.event.*; import java.awt.geom.Rectangle2D; import java.net.URL; import java.rmi.Remote; import java.rmi.RemoteException; import java.text.DateFormat; import java.text.NumberFormat; import java.text.SimpleDateFormat; import java.util.Date; /** * Accumulator Viewer */ @SuppressWarnings("PMD.ConstructorCallsOverridableMethod") public class AccumulatorViewer extends JPanel implements TreeSelectionListener { static final long serialVersionUID = 1L; private Watchable service; private WatchDataSource[] watchDataSources; private CalculableViewable defaultDataView = new DefaultCalculableView(); private CalculableViewable dataView = defaultDataView; private JTree tree; private DefaultMutableTreeNode root= new DefaultMutableTreeNode("Watches"); private JComponent graphPanel; boolean paintFlag = false; private ClassLoader loader; private javax.swing.Timer autoRefresh; private GlassPaneContainer glassPane; private CalculableViewable.PlottedCalculable mouseOverValue = null; //private final static Color fillBoxColor = new Color(110, 206, 227); private final static Color fillBoxColor = new Color(255, 255, 204); private NumberFormat numberFormatter = NumberFormat.getInstance(); private DateFormat dateFormat; private Point lastMousePoint; private boolean autoRefreshMode = true; /** * Creates new form AccumulatorViewer * * @param item the service proxy * * @throws Exception If communication errors happen interfacing with * the WatchDataSource */ public AccumulatorViewer(Object item) throws Exception { super(); loader = getClass().getClassLoader(); getAccessibleContext().setAccessibleName("Watchable Viewer"); if(((ServiceItem)item).service instanceof Watchable) { setService((Watchable)((ServiceItem)item).service); } initComponents(); } /** * Accessor for property service * * @return Value of property service. */ public Remote getService() { return(service); } /** * Mutator for property service. * * @param service New value of property services. * * @throws RemoteException If communication errors happen interfacing with * the WatchDataSource */ public void setService(Watchable service) throws RemoteException { this.service = service; if(this.service != null) { watchDataSources = this.service.fetch(); if(watchDataSources == null) return; if(watchDataSources.length > 0) { int wdsNbr = watchDataSources.length; String[] wdsIDs = new String[wdsNbr]; for(int i = 0; i < watchDataSources.length; i++) { wdsIDs[i] = watchDataSources[i].getID(); } setTreeWDSNodes(wdsIDs); } } } private void initComponents() { numberFormatter.setMinimumFractionDigits(1); numberFormatter.setMaximumFractionDigits(3); dateFormat = new SimpleDateFormat("MM/dd/yy hh:mm:ss aa"); setLayout(new BorderLayout()); setUI(this); graphPanel = createGraphPanel(); glassPane = createGlassPane(graphPanel); //glassPane.setVisible(true); glassPane.addMouseListener(new MouseAdapter() { @Override public void mouseExited(MouseEvent event) { mouseOverValue = null; lastMousePoint = null; glassPane.repaint(); } }); glassPane.addMouseMotionListener(new MouseMotionAdapter() { @Override public void mouseMoved(MouseEvent event) { lastMousePoint = event.getPoint(); mouseOverValue = dataView.getCalcForPoint(lastMousePoint); if(mouseOverValue!=null) { glassPane.repaint(); } } }); JSplitPane mainSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, createTreePanel(), glassPane); mainSplitPane.setDividerSize(2); mainSplitPane.setContinuousLayout(true); mainSplitPane.setDividerLocation(100); add(BorderLayout.CENTER, mainSplitPane); autoRefresh = new javax.swing.Timer( 5*1000, new ActionListener() { public void actionPerformed(ActionEvent evt) { try { if(!autoRefreshMode) return; runAccumulator(); if(lastMousePoint!=null) { mouseOverValue = dataView.getCalcForPoint(lastMousePoint); glassPane.repaint(); } } catch(RemoteException e) { e.printStackTrace(); autoRefresh.stop(); } } }); } public void removeNotify() { if(autoRefresh != null && autoRefresh.isRunning()) { autoRefresh.stop(); } super.removeNotify(); } public void addNotify() { super.addNotify(); if(autoRefresh != null) { if(isVisible() && !autoRefresh.isRunning()) { autoRefresh.start(); } } } public void setVisible(boolean flag) { if(autoRefresh != null) { if(flag && !autoRefresh.isRunning()) autoRefresh.start(); else if(autoRefresh.isRunning()) autoRefresh.stop(); } super.setVisible(flag); } private void setUI(Component component) { try { UIManager.setLookAndFeel(UIManager.getLookAndFeel()); UIManager.put("Label.foreground", Color.black); UIManager.put("TitledBorder.titleColor", Color.black); SwingUtilities.updateComponentTreeUI(component); } catch(Exception e) { e.printStackTrace(); } } private ImageIcon getScaledImageIcon(String location, int width, int height) { ImageIcon icon = null; URL url = this.getClass().getClassLoader().getResource(location); if (url != null) { icon = new ImageIcon(url); icon = new ImageIcon(icon.getImage().getScaledInstance(width, height, Image.SCALE_SMOOTH)); } return (icon); } private JComponent createTreePanel() { JPanel panel = new JPanel(new BorderLayout(0,0)); tree = new JTree(root); tree.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); tree.setBackground(new Color(202,202,202)); tree.setForeground(Color.green); tree.setRowHeight(-1); tree.addTreeSelectionListener(this); tree.setCellRenderer(new AVTreeCellRenderer()); JPanel bPanel = new JPanel(); bPanel.setLayout(new BoxLayout(bPanel, BoxLayout.X_AXIS)); final ImageIcon playIcon = getScaledImageIcon("images/Play24.gif", 16, 16); final ImageIcon pauseIcon = getScaledImageIcon("images/Pause24.gif", 16, 16); final JButton playPause = new JButton(pauseIcon); playPause.setToolTipText("Stop auto feed"); playPause.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { if(autoRefreshMode) { playPause.setIcon(playIcon); autoRefreshMode = false; playPause.setToolTipText("Enable auto feed"); } else { playPause.setIcon(pauseIcon); autoRefreshMode = true; playPause.setToolTipText("Stop auto feed"); } } }); JButton refreshB = new JButton("Refresh"); refreshB.setToolTipText("Refresh Watch Data Source Graph"); refreshB.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { try { runAccumulator(); } catch(Exception e) { e.printStackTrace(); } } }); JButton resetB = new JButton("Reset"); resetB.setToolTipText("Reset Watches"); resetB.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent evt) { try { getWatches(); } catch(RemoteException re) { Toolkit.getDefaultToolkit().beep(); JOptionPane.showMessageDialog(null, "Cannot reset watches\n" + "RemoteException: "+ re.getLocalizedMessage(), "Reset Watch Remote Error", JOptionPane.ERROR_MESSAGE); } } }); JButton clearB = new JButton("Clear"); clearB.setToolTipText("Clear all entries in the WatchDataSource"); clearB.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { clearWatch(); } }); bPanel.add(playPause); bPanel.add(refreshB); bPanel.add(resetB); bPanel.add(clearB); panel.add(BorderLayout.CENTER, /*sp*/tree); panel.add(BorderLayout.SOUTH, bPanel); JScrollPane sp = new JScrollPane(); sp.getViewport().add(panel); return(sp); } public void valueChanged(TreeSelectionEvent evt) { try { runAccumulator(); } catch(Exception e) { e.printStackTrace(); } } private void getWatches() throws RemoteException { root.removeAllChildren(); setService(service); ((DefaultTreeModel)tree.getModel()).nodeStructureChanged(root); tree.repaint(); } private void clearWatch() { int[] selRows = tree.getSelectionRows(); if(selRows == null || selRows.length == 0 || selRows[0] == 0) return; int wdsIdx = selRows[0] - 1; try { watchDataSources[wdsIdx].clear(); runAccumulator(); } catch(Exception ex) { ex.printStackTrace(); } } private void runAccumulator() throws RemoteException { int[] selRows = tree.getSelectionRows(); if(selRows == null || selRows.length == 0 || selRows[0] == 0) return; int wdsIdx = selRows[0] - 1; // assuming we only have one here, future enhancement would build the views // in the JTree and let the user select the view to see. dataView = defaultDataView; String view = watchDataSources[wdsIdx].getView(); if(view != null) { try { dataView = loadView(view); } catch(Exception e) { e.printStackTrace(); } } dataView.setWatchDataSource(watchDataSources[wdsIdx]); graphPanel.repaint(); } private void setTreeWDSNodes(String[] wdsNames) { for (String wdsName : wdsNames) root.add(new DefaultMutableTreeNode(wdsName)); //tree.repaint(); } static class AVTreeCellRenderer extends JLabel implements TreeCellRenderer { final Color SelectedBackgroundColor = Color.yellow;//new Color(0, 0, 128); protected boolean selected; public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { String stringValue = tree.convertValueToText(value, selected, expanded, leaf, row, hasFocus); setText(stringValue); this.selected = selected; return(this); } public void paint(Graphics g) { Color bColor; if(selected) bColor = SelectedBackgroundColor; else if(getParent() != null) bColor = getParent().getBackground(); else bColor = getBackground(); g.setColor(bColor); g.fillRect(0, 0, getWidth() - 1, getHeight() - 1); super.paint(g); } } private JComponent createGraphPanel() { final JPanel panel = new JPanel() { public Color getBackground() { return(Color.white); } public void paint(Graphics g) { paintFlag = true; dataView.paint(g,this.getSize()); paintFlag = false; } }; panel.setDoubleBuffered(true); return(panel); } /* * Draw mouse over details */ private GlassPaneContainer createGlassPane(JComponent comp) { return new GlassPaneContainer(comp) { @Override public void paint(Graphics g) { super.paint(g); if(mouseOverValue!=null) { g.setFont(DefaultCalculableView.defaultFont); Point p = mouseOverValue.getPoint(); String value = " "+numberFormatter.format( mouseOverValue.getCalculable().getValue())+" "; Graphics2D g2 = (Graphics2D)g; g2.setColor(Color.BLUE); Point p2 = new Point((int)p.getX()+15, (int)p.getY()-15); g2.drawLine((int)p.getX(), (int)p.getY(), (int)p2.getX(), (int)p2.getY()); Rectangle2D rect = g2.getFontMetrics().getStringBounds(value, g2); int startX = p2.x-(int)rect.getWidth()/2; int startY = p2.y-10; g2.setColor(fillBoxColor); g2.fillRect(startX, startY, (int)rect.getWidth(), (int)rect.getHeight()+g2.getFontMetrics().getMaxDescent()); g2.setColor(Color.BLACK); g2.drawRect(startX, startY, (int)rect.getWidth(), (int)rect.getHeight()+g2.getFontMetrics().getMaxDescent()); g2.drawString(value, startX, startY+(int)rect.getHeight()); Date date = new Date(mouseOverValue.getCalculable().getWhen()); value = dateFormat.format(date); rect = g2.getFontMetrics().getStringBounds(value, g2); startX = p.x-(int)rect.getWidth()/2; startY = getSize().height-10; g2.setColor(fillBoxColor); g2.fillRect(startX, startY-(int)rect.getHeight(), (int)rect.getWidth(), (int)rect.getHeight()+g2.getFontMetrics().getMaxDescent()); g2.setColor(Color.BLACK); g2.drawRect(startX, startY-(int)rect.getHeight(), (int)rect.getWidth(), (int)rect.getHeight()+g2.getFontMetrics().getMaxDescent()); g2.setColor(Color.BLACK); g2.drawLine(p.x, startY-(int)rect.getHeight(), p.x, p.y); g2.drawString(value, startX, startY); } } }; } private CalculableViewable loadView(String view) throws ClassNotFoundException, InstantiationException, IllegalAccessException { Class vuClass = loader.loadClass(view); return(CalculableViewable)vuClass.newInstance(); } static class GridBagPanel extends JPanel { private GridBagLayout layout = new GridBagLayout(); public GridBagConstraints constraints = new GridBagConstraints(); public GridBagPanel() { super(); setLayout(layout); constraints.fill = GridBagConstraints.HORIZONTAL; } public void addLabeledComp(String labelText, Component comp) { constraints.weightx = 0.0; constraints.gridwidth = GridBagConstraints.RELATIVE; layout.setConstraints(comp, constraints); super.add(new JLabel(labelText)); constraints.weightx = 1.0; constraints.gridwidth = GridBagConstraints.REMAINDER; layout.setConstraints(comp, constraints); super.add(comp); } } }