# HG changeset patch # User Mario Torre # Date 1348254683 -7200 # Node ID 2e7690c2c441dadf6e219278aadc54816260366f # Parent 9e4437930235af462e4967ab8d6d7223bc91207b Thread timeline improvements review-thread: http://icedtea.classpath.org/pipermail/thermostat/2012-September/003355.html reviewed-by: rkennke diff -r 9e4437930235 -r 2e7690c2c441 client/swing-components/src/main/java/com/redhat/thermostat/swing/Palette.java --- a/client/swing-components/src/main/java/com/redhat/thermostat/swing/Palette.java Fri Sep 21 18:17:41 2012 +0200 +++ b/client/swing-components/src/main/java/com/redhat/thermostat/swing/Palette.java Fri Sep 21 21:11:23 2012 +0200 @@ -40,6 +40,9 @@ public enum Palette { + THERMOSTAT_BLU(new Color(74, 93, 117)), + THERMOSTAT_RED(new Color(226, 46, 42)), + RED(new Color(192, 0, 0)), PALE_RED(new Color(192, 80, 77)), diff -r 9e4437930235 -r 2e7690c2c441 thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/ThreadTimelineBean.java --- a/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/ThreadTimelineBean.java Fri Sep 21 18:17:41 2012 +0200 +++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/ThreadTimelineBean.java Fri Sep 21 21:11:23 2012 +0200 @@ -38,13 +38,23 @@ import java.util.Date; -public class ThreadTimelineBean { +public class ThreadTimelineBean implements Cloneable { private long startTime; private long stopTime; private String name; private Thread.State state; + private boolean highlight; + + public boolean isHighlight() { + return highlight; + } + + public void setHighlight(boolean highlight) { + this.highlight = highlight; + } + public Thread.State getState() { return state; } @@ -83,4 +93,68 @@ + ", startTime=" + startTime + " (" + new Date(startTime) + ")" + ", stopTime=" + stopTime + " (" + new Date(stopTime) + ")]"; } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + (int) (startTime ^ (startTime >>> 32)); + result = prime * result + ((state == null) ? 0 : state.hashCode()); + result = prime * result + (int) (stopTime ^ (stopTime >>> 32)); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ThreadTimelineBean other = (ThreadTimelineBean) obj; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + if (startTime != other.startTime) + return false; + if (state != other.state) + return false; + if (stopTime != other.stopTime) + return false; + return true; + } + + /** + * NOTE: A {@link ThreadTimelineBean} is contains another if they are + * either equals, or the the name, state and start time are the same + * (in other words, this method does not check the stop time, and is not a + * strict Set operation). + */ + public boolean contains(ThreadTimelineBean other) { + if (equals(other)) { + return true; + } + if (getName().equals(other.getName()) && + getState().equals(other.getState()) && + getStartTime() == other.getStartTime()) + { + return true; + } + + return false; + } + + @Override + public ThreadTimelineBean clone() { + ThreadTimelineBean copy = new ThreadTimelineBean(); + copy.name = this.name; + copy.startTime = this.startTime; + copy.stopTime = this.stopTime; + copy.state = this.state; + return copy; + } } diff -r 9e4437930235 -r 2e7690c2c441 thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/ThreadTimelineView.java --- a/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/ThreadTimelineView.java Fri Sep 21 18:17:41 2012 +0200 +++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/ThreadTimelineView.java Fri Sep 21 21:11:23 2012 +0200 @@ -40,10 +40,30 @@ import java.util.Map; import com.redhat.thermostat.client.osgi.service.BasicView; +import com.redhat.thermostat.common.ActionListener; +import com.redhat.thermostat.common.ActionNotifier; import com.redhat.thermostat.thread.model.ThreadInfoData; public abstract class ThreadTimelineView extends BasicView { + public static enum ThreadTimelineViewAction { + THREAD_TIMELINE_SELECTED + } + + protected final ActionNotifier threadTimelineNotifier; + public ThreadTimelineView() { + threadTimelineNotifier = new ActionNotifier<>(this); + } + + public void addThreadSelectionActionListener(ActionListener listener) { + threadTimelineNotifier.addActionListener(listener); + } + + public void removeThreadSelectionActionListener(ActionListener listener) { + threadTimelineNotifier.removeActionListener(listener); + } + public abstract void displayStats(Map> timelines, long start, long stop); - + public abstract void setMarkersMessage(String left, String right); + public abstract void resetMarkerMessage(); } diff -r 9e4437930235 -r 2e7690c2c441 thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTimelineController.java --- a/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTimelineController.java Fri Sep 21 18:17:41 2012 +0200 +++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTimelineController.java Fri Sep 21 21:11:23 2012 +0200 @@ -36,14 +36,18 @@ package com.redhat.thermostat.thread.client.controller.impl; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Stack; +import com.redhat.thermostat.common.ActionEvent; +import com.redhat.thermostat.common.ActionListener; import com.redhat.thermostat.common.Timer; import com.redhat.thermostat.thread.client.common.ThreadTimelineBean; import com.redhat.thermostat.thread.client.common.ThreadTimelineView; +import com.redhat.thermostat.thread.client.common.ThreadTimelineView.ThreadTimelineViewAction; import com.redhat.thermostat.thread.client.common.collector.ThreadCollector; import com.redhat.thermostat.thread.model.ThreadInfoData; @@ -52,16 +56,37 @@ private ThreadTimelineView view; private ThreadCollector collector; + private final String lock = new String("ThreadTimelineController"); + private ThreadTimelineBean latestSelected; + public ThreadTimelineController(ThreadTimelineView view, ThreadCollector collector, Timer timer) { super(timer, view); timer.setAction(new ThreadTimelineControllerAction()); this.view = view; + this.view.addThreadSelectionActionListener(new ThreadTimelineSelectedAction()); this.collector = collector; } + private class ThreadTimelineSelectedAction implements ActionListener { + + @Override + public void actionPerformed(ActionEvent actionEvent) { + synchronized (lock) { + latestSelected = (ThreadTimelineBean) actionEvent.getPayload(); + } + } + } + private class ThreadTimelineControllerAction implements Runnable { @Override public void run() { + ThreadTimelineBean _latestSelected = null; + synchronized (lock) { + if (latestSelected != null) { + _latestSelected = latestSelected.clone(); + } + } + List infos = collector.getThreadInfo(); if(infos.size() > 0) { @@ -88,6 +113,10 @@ Stack threadTimelines = new Stack(); timelines.put(lastThreadInfo, threadTimelines); + + if (_latestSelected != null && _latestSelected.contains(timeline)) { + timeline.setHighlight(true); + } threadTimelines.push(timeline); for (int i = beanList.size() - 1; i >= 0; i--) { @@ -100,6 +129,10 @@ timeline = threadTimelines.pop(); timeline.setStopTime(threadInfo.getTimeStamp()); + if (_latestSelected != null && _latestSelected.contains(timeline)) { + timeline.setHighlight(true); + } + threadTimelines.push(timeline); timeline = new ThreadTimelineBean(); @@ -111,6 +144,10 @@ lastThreadInfo = threadInfo; lastState = currentState; + if (_latestSelected != null && _latestSelected.contains(timeline)) { + timeline.setHighlight(true); + } + // add the new thread stat threadTimelines.push(timeline); } @@ -118,6 +155,13 @@ } view.displayStats(timelines, infos.get(infos.size() - 1).getTimeStamp(), infos.get(0).getTimeStamp()); + + if (_latestSelected != null) { + view.setMarkersMessage(new Date(_latestSelected.getStartTime()).toString(), + new Date(_latestSelected.getStopTime()).toString()); + } else { + view.resetMarkerMessage(); + } } } } diff -r 9e4437930235 -r 2e7690c2c441 thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadTimelineChart.java --- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadTimelineChart.java Fri Sep 21 18:17:41 2012 +0200 +++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadTimelineChart.java Fri Sep 21 21:11:23 2012 +0200 @@ -38,13 +38,19 @@ import java.awt.BasicStroke; import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; import java.awt.GradientPaint; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Paint; +import java.awt.Point; +import java.awt.Rectangle; import java.awt.Shape; -import java.awt.geom.Area; +import java.awt.Stroke; +import java.awt.geom.Rectangle2D; import java.util.ArrayList; +import java.util.Date; import java.util.List; import javax.swing.JFrame; @@ -57,14 +63,23 @@ import com.redhat.thermostat.swing.models.LongRange; import com.redhat.thermostat.swing.models.LongRangeNormalizer; import com.redhat.thermostat.thread.client.common.ThreadTimelineBean; +import com.redhat.thermostat.thread.client.common.chart.ChartColors; @SuppressWarnings("serial") public class SwingThreadTimelineChart extends JPanel { + private String leftMarkerMessage; + private String rightMarkerMessage; + + public static final String HIGHLIGHT_THREAD_STATE_PROPERTY = "highlightThreadThreadProperty"; + private ThreadTimelineBean selectedThread; + private List timeline; private LongRangeNormalizer normalizer; + private Point clickArea; + public SwingThreadTimelineChart(List timeline, long rangeStart, long rangeStop) { this.timeline = timeline; @@ -75,10 +90,23 @@ setBorder(new GradientRoundBorder()); normalizer = new LongRangeNormalizer(range); } + + public void clickAndHighlightArea(Point point) { + clickArea = point; + repaint(); + } + + public void unsetHighlightArea() { + clickArea = null; + selectedThread = null; + repaint(); + } @Override protected void paintComponent(Graphics g) { + ThreadTimelineBean oldSelectedThread = selectedThread; + normalizer.setMinNormalized(0); normalizer.setMaxNormalized(getWidth() - 3); @@ -86,72 +114,95 @@ Graphics2D graphics = utils.createAAGraphics(g); + Font font = graphics.getFont(); + graphics.setFont(font.deriveFont(font.getSize() - 2)); + int y = getHeight()/3; graphics.clearRect(0, 0, getWidth(), getHeight()); graphics.drawString(timeline.get(0).getName(), 2, y); y = getHeight()/2; + + graphics.translate(2, y); + if (clickArea != null) { + clickArea.translate(0, -y); + } int w = getWidth() - 4; - int h = 10; + int h = 15; Shape shape = utils.getRoundShape(w, h); - - graphics.translate(2, y); Paint paint = new GradientPaint(0, 0, Palette.DARK_GRAY.getColor(), 0, h, Palette.WHITE.getColor()); graphics.setPaint(paint); graphics.fill(shape); + for (ThreadTimelineBean thread : timeline) { + + boolean isSelected = false; normalizer.setValue(thread.getStartTime()); int x0 = (int) normalizer.getValueNormalized(); normalizer.setValue(thread.getStopTime()); int x1 = (int) normalizer.getValueNormalized(); - graphics.setColor(getColor(thread)); + Color currentThreadColour = ChartColors.getColor(thread.getState()); + Rectangle currentArea = new Rectangle(x0, 0, x1 - x0, h); + if (clickArea != null && currentArea.contains(clickArea)) { + currentThreadColour = Palette.THERMOSTAT_BLU.getColor(); + selectedThread = thread; + isSelected = true; + + } else if (thread.isHighlight()) { + currentThreadColour = Palette.THERMOSTAT_BLU.getColor(); + isSelected = true; + } + + graphics.setColor(currentThreadColour); graphics.fillRect(x0, 1, x1 - x0, h - 1); + + if (isSelected) { + paintMarks(graphics, x0, x1, h, thread.getStartTime(), thread.getStopTime(), ChartColors.getColor(thread.getState())); + String tooltipString = ""; + if (leftMarkerMessage != null) { + tooltipString = leftMarkerMessage + " - "; + } + if (rightMarkerMessage != null) { + tooltipString += " " + rightMarkerMessage; + } + setToolTipText(tooltipString); + } } - graphics.setColor(getColor(timeline.get(timeline.size() - 1))); - graphics.draw(shape); - graphics.dispose(); - } - - private Color getColor(ThreadTimelineBean thread) { - Color result = null; - - switch (thread.getState()) { - case TIMED_WAITING: - result = Palette.PALE_RED.getColor(); - break; - - case NEW: - result = Palette.POMP_AND_POWER_VIOLET.getColor(); - break; + graphics.setColor(ChartColors.getColor(timeline.get(timeline.size() - 1).getState())); + graphics.draw(shape); - case RUNNABLE: - result = Palette.PRUSSIAN_BLUE.getColor(); - break; - - case TERMINATED: - result = Palette.GRAY.getColor(); - break; - - case BLOCKED: - result = Palette.RED.getColor(); - break; - - case WAITING: - result = Palette.GRANITA_ORANGE.getColor(); - break; - - default: - result = Color.BLACK; - break; + graphics.dispose(); + + if (selectedThread != null || oldSelectedThread != null) { + firePropertyChange(HIGHLIGHT_THREAD_STATE_PROPERTY, oldSelectedThread, selectedThread); } - return result; + } + + private void paintMarks(Graphics2D graphics, int x0, int x1, int y, long start, long stop, Color colour) { + + graphics.setColor(colour); + graphics.fillRect(x0, y + 5, x1 - x0, 2); + + GraphicsUtils utils = GraphicsUtils.getInstance(); + FontMetrics metrics = utils.getFontMetrics(this, graphics.getFont()); + + graphics.setColor(getForeground()); + if (leftMarkerMessage != null) { + Rectangle2D rect = metrics.getStringBounds(leftMarkerMessage, graphics); + graphics.drawString(leftMarkerMessage, x0 - (int) rect.getWidth(), y - (int) rect.getY()); + } + + if (rightMarkerMessage != null) { + Rectangle2D rect = metrics.getStringBounds(leftMarkerMessage, graphics); + graphics.drawString(rightMarkerMessage, x1, y - (int) rect.getY()); + } } public static void main(String[] args) { @@ -185,7 +236,10 @@ timeline.add(bean2); timeline.add(bean3); + bean3.setHighlight(true); + SwingThreadTimelineChart chart = new SwingThreadTimelineChart(timeline, 0, 2500); + chart.setMarkersMessage(new Date(bean3.getStartTime()).toString(), new Date(bean3.getStopTime()).toString()); frame.add(chart); frame.setSize(800, 150); @@ -194,4 +248,9 @@ } }); } + + public void setMarkersMessage(String leftMarkerMessage, String rightMarkerMessage) { + this.leftMarkerMessage = leftMarkerMessage; + this.rightMarkerMessage = rightMarkerMessage; + } } diff -r 9e4437930235 -r 2e7690c2c441 thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadTimelineView.java --- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadTimelineView.java Fri Sep 21 18:17:41 2012 +0200 +++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadTimelineView.java Fri Sep 21 21:11:23 2012 +0200 @@ -39,6 +39,12 @@ import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; +import java.awt.GridLayout; +import java.awt.Point; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.util.List; import java.util.Map; @@ -47,8 +53,8 @@ import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.ListCellRenderer; -import javax.swing.ListSelectionModel; import javax.swing.SwingUtilities; +import javax.swing.SwingWorker; import com.redhat.thermostat.client.ui.ComponentVisibleListener; import com.redhat.thermostat.client.ui.SwingComponent; @@ -58,8 +64,14 @@ public class SwingThreadTimelineView extends ThreadTimelineView implements SwingComponent { + private final String lock = new String("SwingThreadTimelineViewLock"); + private JPanel timeLinePanel; - private DefaultListModel model; + private JList chartList; + private DefaultListModel chartModel; + + private String leftMarkerMessage; + private String rightMarkerMessage; public SwingThreadTimelineView() { timeLinePanel = new JPanel(); @@ -76,22 +88,21 @@ }); timeLinePanel.setLayout(new BorderLayout(0, 0)); - model = new DefaultListModel<>(); - JList chartList = new JList<>(model); + - chartList.setLayoutOrientation(JList.VERTICAL); - chartList.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); + chartModel = new DefaultListModel<>(); + chartList = new JList<>(chartModel); + + chartList.setCellRenderer(new ChartRenderer()); + chartList.addMouseListener(new ChartListListener()); + JScrollPane scrollPane = new JScrollPane(chartList); - scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); - timeLinePanel.add(scrollPane); - chartList.setCellRenderer(new ListCellRenderer() { - @Override - public Component getListCellRendererComponent( - JList list, SwingThreadTimelineChart value, - int index, boolean isSelected, boolean cellHasFocus) { - return value; - } - }); + scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); + + timeLinePanel.add(scrollPane, BorderLayout.CENTER); + ThreadTimelineLegendPanel timelineLegend = new ThreadTimelineLegendPanel(); + timeLinePanel.add(timelineLegend, BorderLayout.SOUTH); } @Override @@ -101,18 +112,94 @@ @Override public void run() { - model.clear(); + String _leftMarkerMessage = null; + String _rightMarkerMessage = null; + synchronized (lock) { + _leftMarkerMessage = leftMarkerMessage; + _rightMarkerMessage = rightMarkerMessage; + } + + chartModel.clear(); for (List timeline : timelines.values()) { SwingThreadTimelineChart panel = new SwingThreadTimelineChart(timeline, start, stop); - panel.setPreferredSize(new Dimension(timeLinePanel.getWidth(), 50)); - model.addElement(panel); + panel.setPreferredSize(new Dimension(chartList.getWidth(), 75)); + panel.setMarkersMessage(_leftMarkerMessage, _rightMarkerMessage); + panel.addPropertyChangeListener(SwingThreadTimelineChart.HIGHLIGHT_THREAD_STATE_PROPERTY, + new SelectedThreadListener()); + panel.setMarkersMessage(_leftMarkerMessage, _rightMarkerMessage); + chartModel.addElement(panel); } } }); } + private class SelectedThreadListener implements PropertyChangeListener { + @Override + public void propertyChange(final PropertyChangeEvent evt) { + SwingWorker notifier = new SwingWorker() { + @Override + protected Void doInBackground() throws Exception { + SwingThreadTimelineView.this. + threadTimelineNotifier.fireAction(ThreadTimelineView.ThreadTimelineViewAction.THREAD_TIMELINE_SELECTED, + evt.getNewValue()); + return null; + } + }; + notifier.execute(); + } + } + + private class ChartRenderer implements ListCellRenderer { + @Override + public Component getListCellRendererComponent(JList list, + SwingThreadTimelineChart chart, + int index, boolean isSelected, + boolean cellHasFocus) + { + if (!isSelected) { + chart.unsetHighlightArea(); + } + return chart; + } + } + + private class ChartListListener extends MouseAdapter { + + @Override + public void mouseClicked(MouseEvent e) { + int index = chartList.getSelectedIndex(); + if (index > 0) { + Point listIndex = chartList.indexToLocation(index); + Point absoluteLocation = e.getPoint(); + listIndex.x = absoluteLocation.x; + if (index != 0) { + listIndex.y = absoluteLocation.y - listIndex.y; + } + + SwingThreadTimelineChart chart = chartModel.get(index); + chart.clickAndHighlightArea(listIndex); + } + } + } + @Override public Component getUiComponent() { return timeLinePanel; } + + @Override + public void resetMarkerMessage() { + synchronized (lock) { + this.leftMarkerMessage = null; + this.rightMarkerMessage = null; + } + } + + @Override + public void setMarkersMessage(String left, String right) { + synchronized (lock) { + this.leftMarkerMessage = left; + this.rightMarkerMessage = right; + } + } } diff -r 9e4437930235 -r 2e7690c2c441 thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/ThreadTimelineChart.java --- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/ThreadTimelineChart.java Fri Sep 21 18:17:41 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,89 +0,0 @@ -/* - * Copyright 2012 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat 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 2, or (at your - * option) any later version. - * - * Thermostat 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 Thermostat; see the file COPYING. If not see - * . - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.thread.client.swing.impl; - -import java.awt.Graphics; -import java.awt.Graphics2D; - -import javax.swing.JFrame; -import javax.swing.JPanel; -import javax.swing.SwingUtilities; -import javax.swing.plaf.ColorUIResource; - -import com.redhat.thermostat.swing.GraphicsUtils; - -@SuppressWarnings("serial") -public class ThreadTimelineChart extends JPanel { - - private static final ColorUIResource TICK_COLOR = new ColorUIResource(0xa8aca8); - - public ThreadTimelineChart() { - } - - @Override - protected void paintComponent(Graphics g) { - Graphics2D graphics = GraphicsUtils.getInstance().createAAGraphics(g); - - graphics.setColor(TICK_COLOR); - - int x = 1; - int y = 2; - int w = getWidth() - 2; - int h = getHeight(); - - graphics.drawLine(x, y, w, y); - - graphics.dispose(); - } - - public static void main(String[] args) { - SwingUtilities.invokeLater(new Runnable() { - - @Override - public void run() { - JFrame frame = new JFrame(); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - ThreadTimelineChart chart = new ThreadTimelineChart(); - - frame.add(chart); - frame.setSize(500, 500); - frame.setVisible(true); - } - }); - } -} diff -r 9e4437930235 -r 2e7690c2c441 thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/ThreadTimelineLegendPanel.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/ThreadTimelineLegendPanel.java Fri Sep 21 21:11:23 2012 +0200 @@ -0,0 +1,118 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat 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 2, or (at your + * option) any later version. + * + * Thermostat 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 Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.thread.client.swing.impl; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Graphics; +import java.awt.Graphics2D; + +import javax.swing.Icon; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; + +import com.redhat.thermostat.swing.GraphicsUtils; +import com.redhat.thermostat.thread.client.common.chart.ChartColors; + +@SuppressWarnings("serial") +public class ThreadTimelineLegendPanel extends JPanel { + + public ThreadTimelineLegendPanel() { + setLayout(new FlowLayout(FlowLayout.RIGHT, 10, 10)); + setPreferredSize(new Dimension(getWidth(), 40)); + + for (Thread.State state : Thread.State.values()) { + + Color color = ChartColors.getColor(state); + // no chart is black, it's just the default colour + if (!color.equals(Color.BLACK)) { + JLabel label = new JLabel(new ColorIcon(color), SwingConstants.LEFT); + label.setText(state.toString()); + add(label); + } + } + } + + private class ColorIcon implements Icon { + + private Color color; + private ColorIcon(Color color) { + this.color = color; + } + + @Override + public int getIconHeight() { + return 16; + } + + @Override + public int getIconWidth() { + return 16; + } + + @Override + public void paintIcon(Component c, Graphics g, int x, int y) { + Graphics2D graphics = GraphicsUtils.getInstance().createAAGraphics(g); + graphics.setColor(color); + graphics.fillRect(x, y, getIconWidth(), getIconHeight()); + graphics.dispose(); + } + } + + public static void main(String[] args) { + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + JFrame frame = new JFrame(); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + ThreadTimelineLegendPanel panel = new ThreadTimelineLegendPanel(); + + frame.add(panel); + frame.setSize(800, 150); + + frame.setVisible(true); + } + }); + } +}