Mercurial > hg > release > thermostat-1.0
changeset 1306:ac26a7ee871d
Make all timeline-related classes experimental
Reviewed-by: neugens
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-November/008640.html
line wrap: on
line diff
--- a/client/swing/pom.xml Thu Nov 07 19:10:06 2013 +0100 +++ b/client/swing/pom.xml Thu Nov 07 14:55:48 2013 -0500 @@ -168,7 +168,7 @@ com.redhat.thermostat.client.swing, com.redhat.thermostat.client.swing.components, com.redhat.thermostat.client.swing.components.models, - com.redhat.thermostat.client.swing.components.timeline, + com.redhat.thermostat.client.swing.components.experimental, com.redhat.thermostat.client.swing.vmlist, </Export-Package> <Private-Package>
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/BasicEventTimelineUI.java Thu Nov 07 19:10:06 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,419 +0,0 @@ -/* - * Copyright 2012, 2013 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 - * <http://www.gnu.org/licenses/>. - * - * 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.client.swing.components; - -import java.awt.BasicStroke; -import java.awt.Color; -import java.awt.Cursor; -import java.awt.FontMetrics; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.GridLayout; -import java.awt.Insets; -import java.awt.Paint; -import java.awt.RenderingHints; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.AdjustmentEvent; -import java.awt.event.AdjustmentListener; -import java.awt.event.HierarchyBoundsListener; -import java.awt.event.HierarchyEvent; -import java.awt.event.HierarchyListener; -import java.util.concurrent.TimeUnit; - -import javax.swing.JButton; -import javax.swing.JComponent; -import javax.swing.JPanel; - -import com.redhat.thermostat.client.swing.components.EventTimelineModel.Event; -import com.redhat.thermostat.client.swing.components.TimelineIntervalMouseHandler.TimeIntervalSelectorTarget; -import com.redhat.thermostat.client.swing.components.timeline.Timeline; -import com.redhat.thermostat.client.swing.internal.LocaleResources; -import com.redhat.thermostat.common.model.LongRangeNormalizer; -import com.redhat.thermostat.common.model.Range; -import com.redhat.thermostat.shared.locale.Translate; - -public class BasicEventTimelineUI extends EventTimelineUI { - - private static final Translate<LocaleResources> translate = LocaleResources.createLocalizer(); - - private static final Color DEFAULT_FILL_COLOR = new Color(0,0,0,255); - private static final Color DEFAULT_EDGE_COLOR = Color.BLACK; - private static final Color DEFAULT_MARKER_COLOR = Color.BLACK; - - private EventTimeline eventTimeline; - - private OverviewPanel overviewPanel = new OverviewPanel(); - private Timeline overviewRuler; - - private Refresher refresher = new Refresher(overviewPanel); - private JButton moveLeftButton; - private JButton moveRightButton; - private JButton zoomInButton; - private JButton zoomOutButton; - private JButton resetZoomButton; - - @Override - protected void installComponents(EventTimeline component) { - eventTimeline = component; - - overviewRuler = new Timeline(new Range<Long>(1l, 2l)); - - moveLeftButton = new JButton("<"); - moveLeftButton.setMargin(new Insets(0, 0, 0, 0)); - moveRightButton = new JButton(">"); - moveRightButton.setMargin(new Insets(0, 0, 0, 0)); - - JPanel buttonPanel = new JPanel(); - buttonPanel.setLayout(new GridLayout()); - - zoomInButton = new JButton("+"); - zoomInButton.setToolTipText(translate.localize(LocaleResources.ZOOM_IN).getContents()); - zoomInButton.setMargin(new Insets(2, 2, 2, 2)); - - buttonPanel.add(zoomInButton); - - zoomOutButton = new JButton("-"); - zoomOutButton.setToolTipText(translate.localize(LocaleResources.ZOOM_OUT).getContents()); - zoomOutButton.setMargin(new Insets(2, 2, 2, 2)); - buttonPanel.add(zoomOutButton); - - resetZoomButton = new JButton("R"); - resetZoomButton.setToolTipText(translate.localize(LocaleResources.RESET_ZOOM).getContents()); - resetZoomButton.setMargin(new Insets(2, 2, 2, 2)); - buttonPanel.add(resetZoomButton); - - GridBagLayout layout = new GridBagLayout(); - component.setLayout(layout); - overviewPanel.setLayout(layout); - - GridBagConstraints c = new GridBagConstraints(); - - c.gridx = 0; - c.gridy = 0; - c.fill = GridBagConstraints.VERTICAL; - c.weightx = 0; - c.weighty = 1; - - component.add(moveLeftButton, c); - - c.gridx = 1; - c.gridy = 0; - c.fill = GridBagConstraints.BOTH; - c.weighty = 1.0; - c.weightx = 1.0; - - component.add(overviewPanel, c); - - c.gridy++; - c.fill = GridBagConstraints.HORIZONTAL; - c.weighty = 0; - component.add(overviewRuler, c); - - c.gridx = 2; - c.gridy = 0; - c.fill = GridBagConstraints.VERTICAL; - c.weighty = 1; - c.weightx = 0; - component.add(moveRightButton, c); - - c.gridx = 0; - c.gridy = 3; - c.fill = GridBagConstraints.VERTICAL; - c.anchor = GridBagConstraints.LINE_START; - c.weightx = 1; - c.weighty = 0; - c.gridwidth = 4; - component.add(buttonPanel, c); - } - - @Override - protected void installDefaults(EventTimeline c) { - c.setSelectionEdgePaint(DEFAULT_EDGE_COLOR); - c.setSelectionFillPaint(DEFAULT_FILL_COLOR); - c.setEventPaint(DEFAULT_MARKER_COLOR); - } - - @Override - protected void installListeners(EventTimeline c) { - c.addHierarchyBoundsListener(refresher); - c.addHierarchyListener(refresher); - c.getModel().addDataChangeListener(refresher); - c.getModel().addRangeChangeListener(new EventTimelineRangeChangeListener() { - @Override - public void rangeChanged(Range<Long> overview, Range<Long> detail) { - overviewRuler.setRange(overview); - overviewRuler.repaint(); - - } - }); - moveRightButton.addActionListener(new DetailChangeListener() { - @Override - protected Range<Long> computeNewDetailRange(long min, long max) { - long diff = (long) ((max - min) * 0.1); - return new Range<>(min + diff, max + diff); - } - }); - moveLeftButton.addActionListener(new DetailChangeListener() { - protected Range<Long> computeNewDetailRange(long min, long max) { - long diff = (long) ((max - min) * 0.1); - return new Range<>(min - diff, max - diff); - }; - }); - zoomOutButton.addActionListener(new DetailChangeListener() { - protected Range<Long> computeNewDetailRange(long min, long max) { - long diff = max - min; - return new Range<>(min - diff / 2, max + diff / 2); - }; - }); - - zoomInButton.addActionListener(new DetailChangeListener() { - protected Range<Long> computeNewDetailRange(long min, long max) { - long diff = max - min; - return new Range<>(min + diff / 4, max - diff / 4); - }; - }); - - resetZoomButton.addActionListener(new DetailChangeListener() { - @Override - protected Range<Long> computeNewDetailRange(long min, long max) { - long timeDelta = max - min; - - long tenMinutesInMillis = TimeUnit.MINUTES.toMillis(10); - - if (timeDelta <= tenMinutesInMillis) { - return new Range<>(min, max); - } else { - return new Range<>(max - tenMinutesInMillis, max); - } - } - }); - } - - private abstract class DetailChangeListener implements ActionListener { - @Override - public void actionPerformed(ActionEvent arg0) { - Range<Long> range = eventTimeline.getModel().getDetailRange(); - - long min = range.getMin(); - long max = range.getMax(); - - eventTimeline.getModel().setDetailRange(computeNewDetailRange(min, max)); - - overviewPanel.refresh(); - } - - protected abstract Range<Long> computeNewDetailRange(long min, long max); - } - - protected void uninstallListeners(EventTimeline c) { - c.removeHierarchyBoundsListener(refresher); - c.removeHierarchyListener(refresher); - } - - @Override - protected void uninstallComponents(EventTimeline c) { - c.remove(overviewPanel); - - eventTimeline = null; - } - - private long positionToTimeStamp(int position) { - Range<Long> range = eventTimeline.getModel().getTotalRange(); - LongRangeNormalizer normalizer = new LongRangeNormalizer(new Range<>(0l, (long)overviewPanel.getWidth()), range); - long result = normalizer.getValueNormalized(position); - return result; - } - - private int timeStampToPosition(long timeStamp) { - Range<Long> range = eventTimeline.getModel().getTotalRange(); - LongRangeNormalizer normalizer = new LongRangeNormalizer(range, new Range<>(0l, (long)overviewPanel.getWidth())); - int result = (int) normalizer.getValueNormalized(timeStamp); - return result; - } - - private static class Refresher implements HierarchyBoundsListener, HierarchyListener, AdjustmentListener, EventTimelineDataChangeListener { - - private OverviewPanel toRefresh; - - public Refresher(OverviewPanel toRefresh) { - this.toRefresh = toRefresh; - } - - @Override - public void dataChanged() { - refresh(); - } - - @Override - public void adjustmentValueChanged(AdjustmentEvent e) { - refresh(); - } - - @Override - public void ancestorMoved(HierarchyEvent e) { - refresh(); - } - - @Override - public void ancestorResized(HierarchyEvent e) { - refresh(); - } - - @Override - public void hierarchyChanged(HierarchyEvent e) { - refresh(); - } - - private void refresh() { - toRefresh.refresh(); - } - } - - private class OverviewPanel extends JPanel implements TimeIntervalSelectorTarget { - - private int MOUSE_MARGIN = 10; - - private int left; - private int right; - - public OverviewPanel() { - TimelineIntervalMouseHandler chartMotionListener = new TimelineIntervalMouseHandler(this); - addMouseMotionListener(chartMotionListener); - addMouseListener(chartMotionListener); - } - - public void refresh() { - recomputeBars(); - repaint(); - } - - private void recomputeBars() { - Range<Long> range = eventTimeline.getModel().getDetailRange(); - if (range != null) { - left = timeStampToPosition(range.getMin()); - right = timeStampToPosition(range.getMax()); - } - } - - @Override - protected void paintComponent(Graphics g) { - super.paintComponent(g); - - int width = (right - left); - g.clearRect(0, 0, getWidth(), getHeight()); - - Graphics2D g2 = (Graphics2D) g; - g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - - EventTimelineModel model = eventTimeline.getModel(); - - int i = 0; - for (Event event : model.getEvents()) { - paintEvent(g2, event, i); - i++; - } - - Paint fillColor = eventTimeline.getSelectionFillPaint(); - g2.setPaint(fillColor); - g2.fillRect(left, 1, width, getHeight() - 2); - - g2.setStroke(new BasicStroke(2)); - Paint edgeColor = eventTimeline.getSelectionEdgePaint(); - g2.setPaint(edgeColor); - g2.drawRect(left, 1, width, getHeight() - 2); - - g2.dispose(); - } - - private void paintEvent(Graphics2D g, Event event, int count) { - int y = getYBandPosition(count); - int x = timeStampToPosition(event.getTimeStamp()); - paintEvent(g, event.getDescription(), x, y); - } - - private int getYBandPosition(int step) { - // TODO can we do better in determining the number of 'bands' ? - int TOTAL_STEPS = 10; - step = step % TOTAL_STEPS + 1; - - return Math.round(1.0f * step * getHeight() / TOTAL_STEPS); - } - - private void paintEvent(Graphics2D g, String text, int x, int y) { - g = (Graphics2D) g.create(); - - FontMetrics metrics = g.getFontMetrics(); - int descent = metrics.getDescent(); - int stringWidth = (int) Math.round(metrics.getStringBounds(text, g).getWidth()); - - g.setPaint(eventTimeline.getEventPaint()); - g.drawLine(x, getHeight(), x, y + descent); - g.drawLine(x, y + descent, x + stringWidth, y + descent); - g.drawString(text, x, y); - - g.dispose(); - } - - @Override - public int getSelectionMargin() { - return MOUSE_MARGIN; - } - - @Override - public int getLeftSelectionPosition() { - return left; - } - - @Override - public int getRightSelectionPosition() { - return right; - } - - @Override - public void updateSelectionPosition(int newLeft, int newRight) { - left = newLeft; - right = newRight; - Range<Long> range = new Range<Long>(positionToTimeStamp(left), positionToTimeStamp(right)); - eventTimeline.getModel().setDetailRange(range); - refresh(); - } - } - -}
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/EventTimeline.java Thu Nov 07 19:10:06 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,105 +0,0 @@ -/* - * Copyright 2012, 2013 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 - * <http://www.gnu.org/licenses/>. - * - * 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.client.swing.components; - -import java.awt.Paint; - -import javax.swing.JComponent; -import javax.swing.UIManager; - -public class EventTimeline extends JComponent { - - private static final String uiClassID = "EventTimelineUI"; - - private EventTimelineModel model = new EventTimelineModel(); - - private Paint selectionEdgePaint = null; - private Paint selectionFillPaint = null; - private Paint eventPaint; - - public EventTimeline() { - updateUI(); - } - - public void setUI(EventTimelineUI newUI) { - super.setUI(newUI); - } - - @Override - public void updateUI() { - if (UIManager.get(getUIClassID()) != null) { - setUI((EventTimelineUI) UIManager.getUI(this)); - } else { - setUI(new BasicEventTimelineUI()); - } - } - - @Override - public String getUIClassID() { - return uiClassID; - } - - public EventTimelineModel getModel() { - return model; - } - - public Paint getSelectionEdgePaint() { - return selectionEdgePaint; - } - - public void setSelectionEdgePaint(Paint edgePaint) { - this.selectionEdgePaint = edgePaint; - } - - public Paint getSelectionFillPaint() { - return selectionFillPaint; - } - - public void setSelectionFillPaint(Paint fillPaint) { - this.selectionFillPaint = fillPaint; - } - - public void setEventPaint(Paint eventPaint) { - this.eventPaint = eventPaint; - } - - public Paint getEventPaint() { - return eventPaint; - } - - -}
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/EventTimelineDataChangeListener.java Thu Nov 07 19:10:06 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ -/* - * Copyright 2012, 2013 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 - * <http://www.gnu.org/licenses/>. - * - * 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.client.swing.components; - -public interface EventTimelineDataChangeListener { - - void dataChanged(); - -}
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/EventTimelineModel.java Thu Nov 07 19:10:06 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,167 +0,0 @@ -/* - * Copyright 2012, 2013 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 - * <http://www.gnu.org/licenses/>. - * - * 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.client.swing.components; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.TimeUnit; - -import com.redhat.thermostat.common.model.Range; - -/** - * A time line with an total and a detail range. - */ -public class EventTimelineModel { - - private final List<EventTimelineRangeChangeListener> rangeListeners = new CopyOnWriteArrayList<>(); - private final List<EventTimelineDataChangeListener> dataListeners = new CopyOnWriteArrayList<>(); - - private Range<Long> totalRange; - private Range<Long> detailRange;; - private List<Event> events = new ArrayList<>(); - - private boolean isAdjusting = false; - - public Range<Long> getTotalRange() { - return totalRange; - } - - private void setTotalRange(Range<Long> newRange) { - if (totalRange != null && totalRange.equals(newRange)) { - return; - } - - this.totalRange = newRange; - fireRangeChanged(); - } - - public void addEvent(long eventTimeStamp, String description) { - addEvent(new Event(eventTimeStamp, description)); - } - - public void addEvent(Event event) { - long eventTimeStamp = event.getTimeStamp(); - - if (totalRange == null) { - // some heuristics to get sane initial ranges automagically - setTotalRange(new Range<>(eventTimeStamp - TimeUnit.MINUTES.toMillis(1), eventTimeStamp + TimeUnit.MINUTES.toMillis(1))); - setDetailRange(new Range<>(eventTimeStamp - TimeUnit.MINUTES.toMillis(1), eventTimeStamp + TimeUnit.MINUTES.toMillis(1))); - } else { - long delta = (long) ((totalRange.getMax() - totalRange.getMin()) * 0.1); - - if (totalRange.getMax() < eventTimeStamp + delta) { - setTotalRange(new Range<>(totalRange.getMin(), eventTimeStamp + delta)); - } else if (totalRange.getMin() + delta > eventTimeStamp) { - setTotalRange(new Range<>(eventTimeStamp - delta, totalRange.getMax())); - } - } - - events.add(event); - fireEventDataChanged(); - } - - public List<Event> getEvents() { - return events; - } - - public void clearEvents() { - events.clear(); - } - - public void setDetailRange(Range<Long> range) { - if (detailRange != null && detailRange.equals(range)) { - return; - } - - this.detailRange = range; - - fireRangeChanged(); - } - - public Range<Long> getDetailRange() { - return detailRange; - } - - public void addRangeChangeListener(EventTimelineRangeChangeListener listener) { - rangeListeners.add(listener); - } - - public void removeRangeChangeListener(EventTimelineRangeChangeListener listener) { - rangeListeners.remove(listener); - } - - private void fireRangeChanged() { - for (EventTimelineRangeChangeListener listener : rangeListeners) { - listener.rangeChanged(this.totalRange, this.detailRange); - } - } - - public void addDataChangeListener(EventTimelineDataChangeListener listener) { - dataListeners.add(listener); - } - - public void removeEventDataChangeListener(EventTimelineDataChangeListener listener) { - dataListeners.remove(listener); - } - - private void fireEventDataChanged() { - for (EventTimelineDataChangeListener listener : dataListeners) { - listener.dataChanged(); - } - } - - public static class Event { - - private final long timeStamp; - private final String description; - - public Event(long timeStamp, String description) { - this.timeStamp = timeStamp; - this.description = description; - } - - public long getTimeStamp() { - return timeStamp; - } - - public String getDescription() { - return description; - } - } - -}
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/EventTimelineRangeChangeListener.java Thu Nov 07 19:10:06 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +0,0 @@ -/* - * Copyright 2012, 2013 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 - * <http://www.gnu.org/licenses/>. - * - * 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.client.swing.components; - -import com.redhat.thermostat.common.model.Range; - -public interface EventTimelineRangeChangeListener { - - void rangeChanged(Range<Long> overview, Range<Long> detail); - -} \ No newline at end of file
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/EventTimelineUI.java Thu Nov 07 19:10:06 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,71 +0,0 @@ -/* - * Copyright 2012, 2013 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 - * <http://www.gnu.org/licenses/>. - * - * 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.client.swing.components; - -import javax.swing.JComponent; -import javax.swing.plaf.ComponentUI; - -public abstract class EventTimelineUI extends ComponentUI { - - public void installUI(JComponent c) { - EventTimeline zc = (EventTimeline) c; - installComponents(zc); - installDefaults(zc); - installListeners(zc); - } - - protected void installComponents(EventTimeline c) {} - - protected void installDefaults(EventTimeline c) {} - - protected void installListeners(EventTimeline c) {} - - public void uninstallUI(JComponent c) { - EventTimeline zc = (EventTimeline) c; - - uninstallListeners(zc); - uninstallDefaults(zc); - uninstallComponents(zc); - } - - protected void uninstallListeners(EventTimeline c) {} - - protected void uninstallDefaults(EventTimeline c) {} - - protected void uninstallComponents(EventTimeline c) {} - -}
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/TimelineIntervalMouseHandler.java Thu Nov 07 19:10:06 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,118 +0,0 @@ -/* - * Copyright 2012, 2013 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 <http://www.gnu.org/licenses/>. - * - * 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.client.swing.components; - -import java.awt.Cursor; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; - -class TimelineIntervalMouseHandler extends MouseAdapter { - - static interface TimeIntervalSelectorTarget { - void setCursor(Cursor cursor); - int getSelectionMargin(); - int getLeftSelectionPosition(); - int getRightSelectionPosition(); - void updateSelectionPosition(int left, int right); - } - - private final TimeIntervalSelectorTarget target; - - private boolean moving = false; - private boolean movingLeft = false; - private boolean movingRight = false; - - private int oldX = -1; - private int oldLeft = -1; - private int oldRight = -1; - - public TimelineIntervalMouseHandler(TimeIntervalSelectorTarget target) { - this.target = target; - } - - @Override - public void mouseMoved(MouseEvent e) { - if (Math.abs(e.getX() - target.getLeftSelectionPosition()) < target.getSelectionMargin()) { - target.setCursor(Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR)); - } else if (Math.abs(e.getX() - target.getRightSelectionPosition()) < target.getSelectionMargin()) { - target.setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR)); - } else if ((e.getX() > target.getSelectionMargin() + target.getLeftSelectionPosition()) && (e.getX() < target.getRightSelectionPosition() - target.getSelectionMargin())) { - target.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); - } else { - target.setCursor(Cursor.getDefaultCursor()); - } - - } - - @Override - public void mouseDragged(MouseEvent e) { - if (moving || movingLeft || movingRight) { - - int newLeft = oldLeft; - int newRight = oldRight; - if (movingLeft) { - newLeft = e.getX(); - } else if (movingRight) { - newRight = e.getX(); - } else if (moving) { - long delta = e.getX() - oldX; - newLeft += delta; - newRight += delta; - } - - target.updateSelectionPosition(newLeft, newRight); - } - } - - @Override - public void mouseReleased(MouseEvent e) { - moving = movingLeft = movingRight = false; - oldLeft = oldRight = oldX = -1; - } - - @Override - public void mousePressed(MouseEvent e) { - if (Math.abs(e.getX() - target.getLeftSelectionPosition()) < target.getSelectionMargin()) { - movingLeft = true; - } else if (Math.abs(e.getX() - target.getRightSelectionPosition()) < target.getSelectionMargin()) { - movingRight = true; - } else if ((e.getX() > target.getLeftSelectionPosition() + target.getSelectionMargin()) && (e.getX() < target.getRightSelectionPosition() - target.getSelectionMargin())) { - moving = true; - } - oldLeft = target.getLeftSelectionPosition(); - oldRight = target.getRightSelectionPosition(); - oldX = e.getX(); - } - - // TODO implement wheel scrolling -} \ No newline at end of file
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/TimelineIntervalSelector.java Thu Nov 07 19:10:06 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,100 +0,0 @@ -/* - * Copyright 2012, 2013 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 <http://www.gnu.org/licenses/>. - * - * 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.client.swing.components; - -import java.awt.Paint; - -import javax.swing.JComponent; -import javax.swing.UIManager; - -/** - * A component that allows specifying a time range to select - */ -public class TimelineIntervalSelector extends JComponent { - - private static final String uiClassID = "TimelineIntervalSelectorUI"; - - private TimelineIntervalSelectorModel model; - - private Paint linePaint; - - private Paint fillPaint; - - public TimelineIntervalSelector() { - model = new TimelineIntervalSelectorModel(); - - updateUI(); - } - - public TimelineIntervalSelectorModel getModel() { - return model; - } - - public void setUI(TimelineIntervalSelectorUI ui) { - super.setUI(ui); - } - - @Override - public void updateUI() { - if (UIManager.get(getUIClassID()) != null) { - setUI((TimelineIntervalSelectorUI) UIManager.getUI(this)); - } else { - setUI(new TimelineIntervalSelectorUIBasic()); - } - } - - public TimelineIntervalSelectorUI getUI() { - return (TimelineIntervalSelectorUI) ui; - } - - @Override - public String getUIClassID() { - return uiClassID; - } - - public void setSelectionLinePaint(Paint paint) { - this.linePaint = paint; - } - - public Paint getSelectionLinePaint() { - return this.linePaint; - } - - public void setSelectionFillPaint(Paint paint) { - this.fillPaint = paint; - } - - public Paint getSelectionFillPaint() { - return this.fillPaint; - } -}
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/TimelineIntervalSelectorModel.java Thu Nov 07 19:10:06 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,167 +0,0 @@ -/* - * Copyright 2012, 2013 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 <http://www.gnu.org/licenses/>. - * - * 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.client.swing.components; - -import java.util.EventListener; -import java.util.concurrent.TimeUnit; - -import javax.swing.event.EventListenerList; - -public class TimelineIntervalSelectorModel { - - public static interface ChangeListener extends EventListener { - public void changed(); - } - - private final EventListenerList listeners = new EventListenerList(); - - private final long CREATION_TIME = System.currentTimeMillis(); - - private long totalMinimum = CREATION_TIME; - private long totalMaximum = CREATION_TIME + TimeUnit.HOURS.toMillis(1); - - private long selectedMinimum = CREATION_TIME; - private long selectedMaximum = CREATION_TIME + TimeUnit.MINUTES.toMillis(10); - - public long getTotalMinimum() { - return totalMinimum; - } - - public void setTotalMinimum(long totalMinimum) { - setTotalMinimum(totalMinimum, true); - } - public void setTotalMinimum(long totalMinimum, boolean notify) { - if (this.totalMinimum != totalMinimum) { - this.totalMinimum = totalMinimum; - - if (this.totalMaximum < this.totalMinimum) { - this.totalMaximum = this.totalMinimum; - } - - if (this.selectedMaximum < this.totalMinimum){ - this.selectedMaximum = this.totalMinimum; - } - - if (this.selectedMinimum < this.totalMinimum) { - this.selectedMinimum = this.totalMinimum; - } - - if (notify) { - fireModelChanged(); - } - } - } - - public long getTotalMaximum() { - return totalMaximum; - } - - public void setTotalMaximum(long totalMaximum) { - setTotalMaximum(totalMaximum, true); - } - - public void setTotalMaximum(long totalMaximum, boolean notify) { - if (this.totalMaximum != totalMaximum) { - this.totalMaximum = totalMaximum; - - if (this.totalMinimum > this.totalMaximum) { - this.totalMinimum = this.totalMaximum; - } - - if (this.selectedMaximum > this.totalMaximum) { - this.selectedMaximum = this.totalMaximum; - } - - if (this.selectedMinimum > this.totalMaximum) { - this.selectedMinimum = this.totalMaximum; - } - - if (notify) { - fireModelChanged(); - } - } - } - - public long getSelectedMinimum() { - return selectedMinimum; - } - - public void setSelectedMinimum(long selectedMinimum) { - setSelectedMinimum(selectedMinimum, true); - } - - public void setSelectedMinimum(long selectedMinimum, boolean notify) { - if(this.selectedMinimum != selectedMinimum) { - this.selectedMinimum = selectedMinimum; - if (notify) { - fireModelChanged(); - } - } - } - - public long getSelectedMaximum() { - return selectedMaximum; - } - - public void setSelectedMaximum(long selectedMaximum) { - setSelectedMaximum(selectedMaximum, true); - } - - public void setSelectedMaximum(long selectedMaximum, boolean notify) { - if (this.selectedMaximum != selectedMaximum) { - this.selectedMaximum = selectedMaximum; - if (notify) { - fireModelChanged(); - } - } - } - - public void addChangeListener(ChangeListener l) { - listeners.add(ChangeListener.class, l); - } - - public void removeChangeListener(ChangeListener l) { - listeners.remove(ChangeListener.class, l); - } - - private void fireModelChanged() { - Object[] listeners = this.listeners.getListenerList(); - - for (int i = listeners.length - 2; i >= 0; i -= 2) { - if (listeners[i] == ChangeListener.class) { - ((ChangeListener) listeners[i + 1]).changed(); - } - } - } - -}
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/TimelineIntervalSelectorUI.java Thu Nov 07 19:10:06 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,39 +0,0 @@ -/* - * Copyright 2012, 2013 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 <http://www.gnu.org/licenses/>. - * - * 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.client.swing.components; - -import javax.swing.plaf.ComponentUI; - -public class TimelineIntervalSelectorUI extends ComponentUI { - // a marker class -}
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/TimelineIntervalSelectorUIBasic.java Thu Nov 07 19:10:06 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,292 +0,0 @@ -/* - * Copyright 2012, 2013 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 <http://www.gnu.org/licenses/>. - * - * 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.client.swing.components; - -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Component; -import java.awt.Cursor; -import java.awt.Dimension; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.geom.Rectangle2D; -import java.awt.geom.RoundRectangle2D; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; - -import javax.swing.Box; -import javax.swing.BoxLayout; -import javax.swing.JComponent; -import javax.swing.JPanel; -import javax.swing.border.EmptyBorder; - -import com.redhat.thermostat.client.swing.components.TimelineIntervalMouseHandler.TimeIntervalSelectorTarget; -import com.redhat.thermostat.client.swing.components.TimelineIntervalSelectorModel.ChangeListener; -import com.redhat.thermostat.client.swing.components.timeline.Timeline; -import com.redhat.thermostat.client.ui.Palette; -import com.redhat.thermostat.common.model.Range; - -public class TimelineIntervalSelectorUIBasic extends TimelineIntervalSelectorUI implements TimeIntervalSelectorTarget { - - private static final int SIDE_PADDING = 10; - - // the extra gap below the timeline header itself - private static final int GAP_BELOW = 15; /* pixels */ - - private TimelineIntervalSelector component; - - private TimelineIntervalMouseHandler mouseListener = new TimelineIntervalMouseHandler(this); - private Timeline timeline = new Timeline(new Range<Long>(0l, 100l)); - private JPanel customPaintingPanel = new CustomPaintPanel(); - - private Component topGlue; - private Component bottomGlue; - - private ChangeListener timelineSelectionPainter = new ChangeListener() { - @Override - public void changed() { - component.repaint(); - } - }; - private ChangeListener timelineRangeUpdater = new ChangeListener() { - @Override - public void changed() { - TimelineIntervalSelectorModel model = component.getModel(); - timeline.setRange(new Range<>(model.getTotalMinimum(), model.getTotalMaximum())); - } - }; - private PropertyChangeListener enabledListener = new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - if (evt.getPropertyName().equals("enabled")) { - boolean enabled = (Boolean) evt.getNewValue(); - timeline.setEnabled(enabled); - if (enabled) { - addUserInputListeners(); - } else { - removeUserInputListeners(); - } - } - } - }; - - public TimelineIntervalSelectorUIBasic() { - customPaintingPanel.setLayout(new BorderLayout()); - customPaintingPanel.setBorder(new EmptyBorder(0, SIDE_PADDING, 0, SIDE_PADDING)); - } - - @Override - public void installUI(JComponent c) { - super.installUI(c); - - component = (TimelineIntervalSelector) c; - - installDefaults(); - installComponents(); - installListeners(); - - } - - protected void installDefaults() { - component.setLayout(new BoxLayout(component, BoxLayout.PAGE_AXIS)); - - component.setSelectionLinePaint(Color.BLACK); - } - - protected void installComponents() { - topGlue = Box.createVerticalGlue(); - bottomGlue = Box.createVerticalGlue(); - - customPaintingPanel.add(timeline, BorderLayout.CENTER); - - component.add(topGlue); - component.add(customPaintingPanel); - component.add(bottomGlue); - } - - protected void installListeners() { - component.getModel().addChangeListener(timelineSelectionPainter); - component.getModel().addChangeListener(timelineRangeUpdater); - - component.addPropertyChangeListener(enabledListener); - - addUserInputListeners(); - } - - @Override - public void uninstallUI(JComponent c) { - uninstallListeners(); - uninstallComponents(); - uninstallDefaults(); - - component = null; - - super.uninstallUI(c); - } - - protected void uninstallComponents() { - customPaintingPanel.remove(timeline); - - component.remove(bottomGlue); - component.remove(customPaintingPanel); - component.remove(topGlue); - } - - protected void uninstallListeners() { - removeUserInputListeners(); - - component.getModel().removeChangeListener(timelineRangeUpdater); - component.getModel().removeChangeListener(timelineSelectionPainter); - } - - protected void uninstallDefaults() { - component.setSelectionLinePaint(null); - - component.setLayout(null); - } - - private void removeUserInputListeners() { - component.removeMouseWheelListener(mouseListener); - component.removeMouseMotionListener(mouseListener); - component.removeMouseListener(mouseListener); - } - - private void addUserInputListeners() { - component.addMouseListener(mouseListener); - component.addMouseMotionListener(mouseListener); - component.addMouseWheelListener(mouseListener); - } - - @Override - public int getLeftSelectionPosition() { - return domainToX(component.getModel().getSelectedMinimum()); - } - - @Override - public int getRightSelectionPosition() { - return domainToX(component.getModel().getSelectedMaximum()); - } - - @Override - public int getSelectionMargin() { - return 20; - } - - @Override - public void setCursor(Cursor cursor) { - component.setCursor(cursor); - } - - @Override - public void updateSelectionPosition(int left, int right) { - long min = xToDomain(left); - long max = xToDomain(right); - component.getModel().setSelectedMaximum(max); - component.getModel().setSelectedMinimum(min); - - component.repaint(); - } - - private int domainToX(long domainValue) { - long domainMin = component.getModel().getTotalMinimum(); - long domainMax = component.getModel().getTotalMaximum(); - int width = timeline.getWidth(); - return (int) (1.0 * (domainValue - domainMin) / (domainMax - domainMin) * (width - 1)); - } - - private long xToDomain(int x) { - long domainMin = component.getModel().getTotalMinimum(); - long domainMax = component.getModel().getTotalMaximum(); - int width = timeline.getWidth(); - return (long) ((1.0 * x / (width - 1) * (domainMax - domainMin)) + domainMin); - } - - private class CustomPaintPanel extends JPanel { - - @Override - public Dimension getPreferredSize() { - return new Dimension(super.getPreferredSize().width, timeline.getPreferredSize().height + GAP_BELOW); - } - - @Override - public void paint(Graphics g) { - super.paint(g); - - Graphics2D g2 = (Graphics2D) g.create(); - - int left = domainToX(component.getModel().getSelectedMinimum()) + SIDE_PADDING ; - int right = domainToX(component.getModel().getSelectedMaximum()) + SIDE_PADDING; - int height = getHeight(); - int width = getWidth() - 1; - - boolean enabled = component.isEnabled(); - if (enabled) { - g2.setPaint(component.getSelectionLinePaint()); - } else { - g2.setPaint(Color.LIGHT_GRAY); - } - - int pinchHeight = getHeight() - (GAP_BELOW / 2); - - g2.drawLine(0, height, 0, pinchHeight); - g2.drawLine(0, pinchHeight, left, pinchHeight); - g2.drawLine(left, pinchHeight, left, 0); - paintHandle(g2, left, pinchHeight/2); - - g2.drawLine(width, height, width, pinchHeight); - g2.drawLine(width, pinchHeight, right, pinchHeight); - g2.drawLine(right, pinchHeight, right, 0); - paintHandle(g2, right, pinchHeight/2); - - g2.dispose(); - } - - private void paintHandle(Graphics2D g, int x, int y) { - g = (Graphics2D) g.create(); - g.translate(x, y); - - g.setColor(Palette.LIGHT_GRAY.getColor()); - g.fill(new RoundRectangle2D.Float(-2, -10, 4, 20, 2, 2)); - - if (component.isEnabled()) { - g.setPaint(component.getSelectionLinePaint()); - } else { - g.setPaint(Color.LIGHT_GRAY); - } - - g.draw(new RoundRectangle2D.Float(-2, -10, 4, 20, 2, 2)); - - g.dispose(); - } - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/experimental/BasicEventTimelineUI.java Thu Nov 07 14:55:48 2013 -0500 @@ -0,0 +1,418 @@ +/* + * Copyright 2012, 2013 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 + * <http://www.gnu.org/licenses/>. + * + * 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.client.swing.components.experimental; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.GridLayout; +import java.awt.Insets; +import java.awt.Paint; +import java.awt.RenderingHints; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.AdjustmentEvent; +import java.awt.event.AdjustmentListener; +import java.awt.event.HierarchyBoundsListener; +import java.awt.event.HierarchyEvent; +import java.awt.event.HierarchyListener; +import java.util.concurrent.TimeUnit; + +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JPanel; + +import com.redhat.thermostat.client.swing.components.experimental.EventTimelineModel.Event; +import com.redhat.thermostat.client.swing.components.experimental.TimelineIntervalMouseHandler.TimeIntervalSelectorTarget; +import com.redhat.thermostat.client.swing.internal.LocaleResources; +import com.redhat.thermostat.common.model.LongRangeNormalizer; +import com.redhat.thermostat.common.model.Range; +import com.redhat.thermostat.shared.locale.Translate; + +public class BasicEventTimelineUI extends EventTimelineUI { + + private static final Translate<LocaleResources> translate = LocaleResources.createLocalizer(); + + private static final Color DEFAULT_FILL_COLOR = new Color(0,0,0,255); + private static final Color DEFAULT_EDGE_COLOR = Color.BLACK; + private static final Color DEFAULT_MARKER_COLOR = Color.BLACK; + + private EventTimeline eventTimeline; + + private OverviewPanel overviewPanel = new OverviewPanel(); + private Timeline overviewRuler; + + private Refresher refresher = new Refresher(overviewPanel); + private JButton moveLeftButton; + private JButton moveRightButton; + private JButton zoomInButton; + private JButton zoomOutButton; + private JButton resetZoomButton; + + @Override + protected void installComponents(EventTimeline component) { + eventTimeline = component; + + overviewRuler = new Timeline(new Range<Long>(1l, 2l)); + + moveLeftButton = new JButton("<"); + moveLeftButton.setMargin(new Insets(0, 0, 0, 0)); + moveRightButton = new JButton(">"); + moveRightButton.setMargin(new Insets(0, 0, 0, 0)); + + JPanel buttonPanel = new JPanel(); + buttonPanel.setLayout(new GridLayout()); + + zoomInButton = new JButton("+"); + zoomInButton.setToolTipText(translate.localize(LocaleResources.ZOOM_IN).getContents()); + zoomInButton.setMargin(new Insets(2, 2, 2, 2)); + + buttonPanel.add(zoomInButton); + + zoomOutButton = new JButton("-"); + zoomOutButton.setToolTipText(translate.localize(LocaleResources.ZOOM_OUT).getContents()); + zoomOutButton.setMargin(new Insets(2, 2, 2, 2)); + buttonPanel.add(zoomOutButton); + + resetZoomButton = new JButton("R"); + resetZoomButton.setToolTipText(translate.localize(LocaleResources.RESET_ZOOM).getContents()); + resetZoomButton.setMargin(new Insets(2, 2, 2, 2)); + buttonPanel.add(resetZoomButton); + + GridBagLayout layout = new GridBagLayout(); + component.setLayout(layout); + overviewPanel.setLayout(layout); + + GridBagConstraints c = new GridBagConstraints(); + + c.gridx = 0; + c.gridy = 0; + c.fill = GridBagConstraints.VERTICAL; + c.weightx = 0; + c.weighty = 1; + + component.add(moveLeftButton, c); + + c.gridx = 1; + c.gridy = 0; + c.fill = GridBagConstraints.BOTH; + c.weighty = 1.0; + c.weightx = 1.0; + + component.add(overviewPanel, c); + + c.gridy++; + c.fill = GridBagConstraints.HORIZONTAL; + c.weighty = 0; + component.add(overviewRuler, c); + + c.gridx = 2; + c.gridy = 0; + c.fill = GridBagConstraints.VERTICAL; + c.weighty = 1; + c.weightx = 0; + component.add(moveRightButton, c); + + c.gridx = 0; + c.gridy = 3; + c.fill = GridBagConstraints.VERTICAL; + c.anchor = GridBagConstraints.LINE_START; + c.weightx = 1; + c.weighty = 0; + c.gridwidth = 4; + component.add(buttonPanel, c); + } + + @Override + protected void installDefaults(EventTimeline c) { + c.setSelectionEdgePaint(DEFAULT_EDGE_COLOR); + c.setSelectionFillPaint(DEFAULT_FILL_COLOR); + c.setEventPaint(DEFAULT_MARKER_COLOR); + } + + @Override + protected void installListeners(EventTimeline c) { + c.addHierarchyBoundsListener(refresher); + c.addHierarchyListener(refresher); + c.getModel().addDataChangeListener(refresher); + c.getModel().addRangeChangeListener(new EventTimelineRangeChangeListener() { + @Override + public void rangeChanged(Range<Long> overview, Range<Long> detail) { + overviewRuler.setRange(overview); + overviewRuler.repaint(); + + } + }); + moveRightButton.addActionListener(new DetailChangeListener() { + @Override + protected Range<Long> computeNewDetailRange(long min, long max) { + long diff = (long) ((max - min) * 0.1); + return new Range<>(min + diff, max + diff); + } + }); + moveLeftButton.addActionListener(new DetailChangeListener() { + protected Range<Long> computeNewDetailRange(long min, long max) { + long diff = (long) ((max - min) * 0.1); + return new Range<>(min - diff, max - diff); + }; + }); + zoomOutButton.addActionListener(new DetailChangeListener() { + protected Range<Long> computeNewDetailRange(long min, long max) { + long diff = max - min; + return new Range<>(min - diff / 2, max + diff / 2); + }; + }); + + zoomInButton.addActionListener(new DetailChangeListener() { + protected Range<Long> computeNewDetailRange(long min, long max) { + long diff = max - min; + return new Range<>(min + diff / 4, max - diff / 4); + }; + }); + + resetZoomButton.addActionListener(new DetailChangeListener() { + @Override + protected Range<Long> computeNewDetailRange(long min, long max) { + long timeDelta = max - min; + + long tenMinutesInMillis = TimeUnit.MINUTES.toMillis(10); + + if (timeDelta <= tenMinutesInMillis) { + return new Range<>(min, max); + } else { + return new Range<>(max - tenMinutesInMillis, max); + } + } + }); + } + + private abstract class DetailChangeListener implements ActionListener { + @Override + public void actionPerformed(ActionEvent arg0) { + Range<Long> range = eventTimeline.getModel().getDetailRange(); + + long min = range.getMin(); + long max = range.getMax(); + + eventTimeline.getModel().setDetailRange(computeNewDetailRange(min, max)); + + overviewPanel.refresh(); + } + + protected abstract Range<Long> computeNewDetailRange(long min, long max); + } + + protected void uninstallListeners(EventTimeline c) { + c.removeHierarchyBoundsListener(refresher); + c.removeHierarchyListener(refresher); + } + + @Override + protected void uninstallComponents(EventTimeline c) { + c.remove(overviewPanel); + + eventTimeline = null; + } + + private long positionToTimeStamp(int position) { + Range<Long> range = eventTimeline.getModel().getTotalRange(); + LongRangeNormalizer normalizer = new LongRangeNormalizer(new Range<>(0l, (long)overviewPanel.getWidth()), range); + long result = normalizer.getValueNormalized(position); + return result; + } + + private int timeStampToPosition(long timeStamp) { + Range<Long> range = eventTimeline.getModel().getTotalRange(); + LongRangeNormalizer normalizer = new LongRangeNormalizer(range, new Range<>(0l, (long)overviewPanel.getWidth())); + int result = (int) normalizer.getValueNormalized(timeStamp); + return result; + } + + private static class Refresher implements HierarchyBoundsListener, HierarchyListener, AdjustmentListener, EventTimelineDataChangeListener { + + private OverviewPanel toRefresh; + + public Refresher(OverviewPanel toRefresh) { + this.toRefresh = toRefresh; + } + + @Override + public void dataChanged() { + refresh(); + } + + @Override + public void adjustmentValueChanged(AdjustmentEvent e) { + refresh(); + } + + @Override + public void ancestorMoved(HierarchyEvent e) { + refresh(); + } + + @Override + public void ancestorResized(HierarchyEvent e) { + refresh(); + } + + @Override + public void hierarchyChanged(HierarchyEvent e) { + refresh(); + } + + private void refresh() { + toRefresh.refresh(); + } + } + + private class OverviewPanel extends JPanel implements TimeIntervalSelectorTarget { + + private int MOUSE_MARGIN = 10; + + private int left; + private int right; + + public OverviewPanel() { + TimelineIntervalMouseHandler chartMotionListener = new TimelineIntervalMouseHandler(this); + addMouseMotionListener(chartMotionListener); + addMouseListener(chartMotionListener); + } + + public void refresh() { + recomputeBars(); + repaint(); + } + + private void recomputeBars() { + Range<Long> range = eventTimeline.getModel().getDetailRange(); + if (range != null) { + left = timeStampToPosition(range.getMin()); + right = timeStampToPosition(range.getMax()); + } + } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + + int width = (right - left); + g.clearRect(0, 0, getWidth(), getHeight()); + + Graphics2D g2 = (Graphics2D) g; + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + EventTimelineModel model = eventTimeline.getModel(); + + int i = 0; + for (Event event : model.getEvents()) { + paintEvent(g2, event, i); + i++; + } + + Paint fillColor = eventTimeline.getSelectionFillPaint(); + g2.setPaint(fillColor); + g2.fillRect(left, 1, width, getHeight() - 2); + + g2.setStroke(new BasicStroke(2)); + Paint edgeColor = eventTimeline.getSelectionEdgePaint(); + g2.setPaint(edgeColor); + g2.drawRect(left, 1, width, getHeight() - 2); + + g2.dispose(); + } + + private void paintEvent(Graphics2D g, Event event, int count) { + int y = getYBandPosition(count); + int x = timeStampToPosition(event.getTimeStamp()); + paintEvent(g, event.getDescription(), x, y); + } + + private int getYBandPosition(int step) { + // TODO can we do better in determining the number of 'bands' ? + int TOTAL_STEPS = 10; + step = step % TOTAL_STEPS + 1; + + return Math.round(1.0f * step * getHeight() / TOTAL_STEPS); + } + + private void paintEvent(Graphics2D g, String text, int x, int y) { + g = (Graphics2D) g.create(); + + FontMetrics metrics = g.getFontMetrics(); + int descent = metrics.getDescent(); + int stringWidth = (int) Math.round(metrics.getStringBounds(text, g).getWidth()); + + g.setPaint(eventTimeline.getEventPaint()); + g.drawLine(x, getHeight(), x, y + descent); + g.drawLine(x, y + descent, x + stringWidth, y + descent); + g.drawString(text, x, y); + + g.dispose(); + } + + @Override + public int getSelectionMargin() { + return MOUSE_MARGIN; + } + + @Override + public int getLeftSelectionPosition() { + return left; + } + + @Override + public int getRightSelectionPosition() { + return right; + } + + @Override + public void updateSelectionPosition(int newLeft, int newRight) { + left = newLeft; + right = newRight; + Range<Long> range = new Range<Long>(positionToTimeStamp(left), positionToTimeStamp(right)); + eventTimeline.getModel().setDetailRange(range); + refresh(); + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/experimental/EventTimeline.java Thu Nov 07 14:55:48 2013 -0500 @@ -0,0 +1,105 @@ +/* + * Copyright 2012, 2013 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 + * <http://www.gnu.org/licenses/>. + * + * 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.client.swing.components.experimental; + +import java.awt.Paint; + +import javax.swing.JComponent; +import javax.swing.UIManager; + +public class EventTimeline extends JComponent { + + private static final String uiClassID = "EventTimelineUI"; + + private EventTimelineModel model = new EventTimelineModel(); + + private Paint selectionEdgePaint = null; + private Paint selectionFillPaint = null; + private Paint eventPaint; + + public EventTimeline() { + updateUI(); + } + + public void setUI(EventTimelineUI newUI) { + super.setUI(newUI); + } + + @Override + public void updateUI() { + if (UIManager.get(getUIClassID()) != null) { + setUI((EventTimelineUI) UIManager.getUI(this)); + } else { + setUI(new BasicEventTimelineUI()); + } + } + + @Override + public String getUIClassID() { + return uiClassID; + } + + public EventTimelineModel getModel() { + return model; + } + + public Paint getSelectionEdgePaint() { + return selectionEdgePaint; + } + + public void setSelectionEdgePaint(Paint edgePaint) { + this.selectionEdgePaint = edgePaint; + } + + public Paint getSelectionFillPaint() { + return selectionFillPaint; + } + + public void setSelectionFillPaint(Paint fillPaint) { + this.selectionFillPaint = fillPaint; + } + + public void setEventPaint(Paint eventPaint) { + this.eventPaint = eventPaint; + } + + public Paint getEventPaint() { + return eventPaint; + } + + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/experimental/EventTimelineDataChangeListener.java Thu Nov 07 14:55:48 2013 -0500 @@ -0,0 +1,43 @@ +/* + * Copyright 2012, 2013 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 + * <http://www.gnu.org/licenses/>. + * + * 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.client.swing.components.experimental; + +public interface EventTimelineDataChangeListener { + + void dataChanged(); + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/experimental/EventTimelineModel.java Thu Nov 07 14:55:48 2013 -0500 @@ -0,0 +1,167 @@ +/* + * Copyright 2012, 2013 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 + * <http://www.gnu.org/licenses/>. + * + * 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.client.swing.components.experimental; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.TimeUnit; + +import com.redhat.thermostat.common.model.Range; + +/** + * A time line with an total and a detail range. + */ +public class EventTimelineModel { + + private final List<EventTimelineRangeChangeListener> rangeListeners = new CopyOnWriteArrayList<>(); + private final List<EventTimelineDataChangeListener> dataListeners = new CopyOnWriteArrayList<>(); + + private Range<Long> totalRange; + private Range<Long> detailRange;; + private List<Event> events = new ArrayList<>(); + + private boolean isAdjusting = false; + + public Range<Long> getTotalRange() { + return totalRange; + } + + private void setTotalRange(Range<Long> newRange) { + if (totalRange != null && totalRange.equals(newRange)) { + return; + } + + this.totalRange = newRange; + fireRangeChanged(); + } + + public void addEvent(long eventTimeStamp, String description) { + addEvent(new Event(eventTimeStamp, description)); + } + + public void addEvent(Event event) { + long eventTimeStamp = event.getTimeStamp(); + + if (totalRange == null) { + // some heuristics to get sane initial ranges automagically + setTotalRange(new Range<>(eventTimeStamp - TimeUnit.MINUTES.toMillis(1), eventTimeStamp + TimeUnit.MINUTES.toMillis(1))); + setDetailRange(new Range<>(eventTimeStamp - TimeUnit.MINUTES.toMillis(1), eventTimeStamp + TimeUnit.MINUTES.toMillis(1))); + } else { + long delta = (long) ((totalRange.getMax() - totalRange.getMin()) * 0.1); + + if (totalRange.getMax() < eventTimeStamp + delta) { + setTotalRange(new Range<>(totalRange.getMin(), eventTimeStamp + delta)); + } else if (totalRange.getMin() + delta > eventTimeStamp) { + setTotalRange(new Range<>(eventTimeStamp - delta, totalRange.getMax())); + } + } + + events.add(event); + fireEventDataChanged(); + } + + public List<Event> getEvents() { + return events; + } + + public void clearEvents() { + events.clear(); + } + + public void setDetailRange(Range<Long> range) { + if (detailRange != null && detailRange.equals(range)) { + return; + } + + this.detailRange = range; + + fireRangeChanged(); + } + + public Range<Long> getDetailRange() { + return detailRange; + } + + public void addRangeChangeListener(EventTimelineRangeChangeListener listener) { + rangeListeners.add(listener); + } + + public void removeRangeChangeListener(EventTimelineRangeChangeListener listener) { + rangeListeners.remove(listener); + } + + private void fireRangeChanged() { + for (EventTimelineRangeChangeListener listener : rangeListeners) { + listener.rangeChanged(this.totalRange, this.detailRange); + } + } + + public void addDataChangeListener(EventTimelineDataChangeListener listener) { + dataListeners.add(listener); + } + + public void removeEventDataChangeListener(EventTimelineDataChangeListener listener) { + dataListeners.remove(listener); + } + + private void fireEventDataChanged() { + for (EventTimelineDataChangeListener listener : dataListeners) { + listener.dataChanged(); + } + } + + public static class Event { + + private final long timeStamp; + private final String description; + + public Event(long timeStamp, String description) { + this.timeStamp = timeStamp; + this.description = description; + } + + public long getTimeStamp() { + return timeStamp; + } + + public String getDescription() { + return description; + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/experimental/EventTimelineRangeChangeListener.java Thu Nov 07 14:55:48 2013 -0500 @@ -0,0 +1,45 @@ +/* + * Copyright 2012, 2013 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 + * <http://www.gnu.org/licenses/>. + * + * 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.client.swing.components.experimental; + +import com.redhat.thermostat.common.model.Range; + +public interface EventTimelineRangeChangeListener { + + void rangeChanged(Range<Long> overview, Range<Long> detail); + +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/experimental/EventTimelineUI.java Thu Nov 07 14:55:48 2013 -0500 @@ -0,0 +1,71 @@ +/* + * Copyright 2012, 2013 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 + * <http://www.gnu.org/licenses/>. + * + * 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.client.swing.components.experimental; + +import javax.swing.JComponent; +import javax.swing.plaf.ComponentUI; + +public abstract class EventTimelineUI extends ComponentUI { + + public void installUI(JComponent c) { + EventTimeline zc = (EventTimeline) c; + installComponents(zc); + installDefaults(zc); + installListeners(zc); + } + + protected void installComponents(EventTimeline c) {} + + protected void installDefaults(EventTimeline c) {} + + protected void installListeners(EventTimeline c) {} + + public void uninstallUI(JComponent c) { + EventTimeline zc = (EventTimeline) c; + + uninstallListeners(zc); + uninstallDefaults(zc); + uninstallComponents(zc); + } + + protected void uninstallListeners(EventTimeline c) {} + + protected void uninstallDefaults(EventTimeline c) {} + + protected void uninstallComponents(EventTimeline c) {} + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/experimental/Timeline.java Thu Nov 07 14:55:48 2013 -0500 @@ -0,0 +1,246 @@ +/* + * Copyright 2012, 2013 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 + * <http://www.gnu.org/licenses/>. + * + * 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.client.swing.components.experimental; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.GradientPaint; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Paint; +import java.awt.Rectangle; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import com.redhat.thermostat.client.swing.GraphicsUtils; +import com.redhat.thermostat.client.swing.components.GradientPanel; +import com.redhat.thermostat.client.ui.Palette; +import com.redhat.thermostat.common.model.LongRangeNormalizer; +import com.redhat.thermostat.common.model.Range; + +/** + * Displays a timeline between the specified start and stop ranges, dynamically + * adjusting itself as needed. The values of the ranges are interpreted as + * timestamps in milliseconds. + */ +@SuppressWarnings("serial") +public class Timeline extends GradientPanel { + + /** Default height of this component. Subclasses may use different values */ + public static final int DEFAULT_HEIGHT = 25; + + private Range<Long> range; + + public Timeline(Range<Long> range) { + + super(Palette.LIGHT_GRAY.getColor(), Palette.WHITE.getColor()); + setFont(TimelineUtils.FONT); + + this.range = range; + } + + public Range<Long> getRange() { + return range; + } + + public void setRange(Range<Long> newRange) { + this.range = newRange; + repaint(); + } + + @Override + public int getHeight() { + return DEFAULT_HEIGHT; + } + + @Override + public Dimension getMaximumSize() { + Dimension dim = super.getMaximumSize(); + dim.height = getHeight(); + return dim; + } + + @Override + public Dimension getMinimumSize() { + Dimension dim = super.getMinimumSize(); + dim.height = getHeight(); + return dim; + } + + @Override + public Dimension getPreferredSize() { + Dimension dim = super.getPreferredSize(); + dim.height = getHeight(); + return dim; + } + + @Override + public Dimension getSize() { + return getPreferredSize(); + } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + + if (range == null) { + return; + } + + Graphics2D graphics = GraphicsUtils.getInstance().createAAGraphics(g); + + Rectangle bounds = g.getClipBounds(); + + TimeUnit timeUnitForTicks = getBestTimeUnit(); + + drawTicks(graphics, bounds, timeUnitForTicks); + + if (isEnabled()) { + graphics.setColor(Palette.THERMOSTAT_BLU.getColor()); + } else { + graphics.setColor(Color.GRAY); + } + graphics.drawLine(bounds.x, bounds.height - 1, bounds.width, bounds.height - 1); + + graphics.dispose(); + } + + /** + * Returns a TimeUnit and the number of subticks needed for that TimeUnit + * need to display the current range + */ + private TimeUnit getBestTimeUnit() { + long min = range.getMin(); + long max = range.getMax(); + + List<TimeUnit> units = new ArrayList<>(); + units.add(TimeUnit.DAYS); + units.add(TimeUnit.HOURS); + units.add(TimeUnit.MINUTES); + units.add(TimeUnit.SECONDS); + units.add(TimeUnit.MILLISECONDS); + + /* Find the largest unit of time suitable for the range */ + for (TimeUnit unit: units) { + long millis = unit.toMillis(1); + if (Math.abs(max - min) >= millis) { + return unit; + } + } + + return null; + } + + private void drawTicks(Graphics2D graphics, Rectangle bounds, TimeUnit tickUnit) { + Font font = graphics.getFont(); + + int widthOfOneCharacter = (int) font.getStringBounds("0", graphics.getFontRenderContext()).getWidth(); + + long deltaInMilliseconds = Math.max(1, tickUnit.toMillis(1) / 10); + + // some heuristics for spacing + int targetNumberOfIntervals = (int) (getWidth() / (10 * widthOfOneCharacter) / 1.3); + + while ((range.getMax() - range.getMin()) / deltaInMilliseconds > targetNumberOfIntervals) { + deltaInMilliseconds *= 2; + + } + + long start = (range.getMin() / deltaInMilliseconds) * deltaInMilliseconds; + long end = range.getMax(); + + DateFormat df = null; + + switch (tickUnit) { + case DAYS: + df = new SimpleDateFormat("YY-MM-dd"); + break; + case HOURS: + df = new SimpleDateFormat("hh:mm a"); + break; + case MINUTES: + df = new SimpleDateFormat("hh:mm a"); + break; + case SECONDS: + df = new SimpleDateFormat("mm.ss"); + break; + default: + df = new SimpleDateFormat("hh:mm:ss a"); + } + + Paint gradient = new GradientPaint(0, 0, Palette.WHITE.getColor(), 0, + getHeight(), Palette.GRAY.getColor()); + + LongRangeNormalizer normalizer = new LongRangeNormalizer(range, new Range<Long>(0l, (long)bounds.width)); + + for (long i = start; i < end; i += deltaInMilliseconds) { + int x = (int) normalizer.getValueNormalized(i); + + if (isEnabled()) { + graphics.setColor(Palette.THERMOSTAT_BLU.getColor()); + } else { + graphics.setColor(Color.GRAY); + } + + graphics.drawLine(x, 0, x, bounds.height); + + graphics.setPaint(gradient); + String value = df.format(new Date(i)); + + int stringWidth = (int) font.getStringBounds(value, + graphics.getFontRenderContext()).getWidth() - 1; + int stringHeight = (int) font.getStringBounds(value, + graphics.getFontRenderContext()).getHeight(); + graphics.fillRect(x + 1, bounds.y + 5, stringWidth + 4, stringHeight + + 4); + + if (isEnabled()) { + graphics.setColor(Palette.THERMOSTAT_BLU.getColor()); + } else { + graphics.setColor(Color.GRAY); + } + + graphics.drawString(value, x + 1, bounds.y + stringHeight + 5); + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/experimental/TimelineIntervalMouseHandler.java Thu Nov 07 14:55:48 2013 -0500 @@ -0,0 +1,118 @@ +/* + * Copyright 2012, 2013 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 <http://www.gnu.org/licenses/>. + * + * 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.client.swing.components.experimental; + +import java.awt.Cursor; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +class TimelineIntervalMouseHandler extends MouseAdapter { + + static interface TimeIntervalSelectorTarget { + void setCursor(Cursor cursor); + int getSelectionMargin(); + int getLeftSelectionPosition(); + int getRightSelectionPosition(); + void updateSelectionPosition(int left, int right); + } + + private final TimeIntervalSelectorTarget target; + + private boolean moving = false; + private boolean movingLeft = false; + private boolean movingRight = false; + + private int oldX = -1; + private int oldLeft = -1; + private int oldRight = -1; + + public TimelineIntervalMouseHandler(TimeIntervalSelectorTarget target) { + this.target = target; + } + + @Override + public void mouseMoved(MouseEvent e) { + if (Math.abs(e.getX() - target.getLeftSelectionPosition()) < target.getSelectionMargin()) { + target.setCursor(Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR)); + } else if (Math.abs(e.getX() - target.getRightSelectionPosition()) < target.getSelectionMargin()) { + target.setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR)); + } else if ((e.getX() > target.getSelectionMargin() + target.getLeftSelectionPosition()) && (e.getX() < target.getRightSelectionPosition() - target.getSelectionMargin())) { + target.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); + } else { + target.setCursor(Cursor.getDefaultCursor()); + } + + } + + @Override + public void mouseDragged(MouseEvent e) { + if (moving || movingLeft || movingRight) { + + int newLeft = oldLeft; + int newRight = oldRight; + if (movingLeft) { + newLeft = e.getX(); + } else if (movingRight) { + newRight = e.getX(); + } else if (moving) { + long delta = e.getX() - oldX; + newLeft += delta; + newRight += delta; + } + + target.updateSelectionPosition(newLeft, newRight); + } + } + + @Override + public void mouseReleased(MouseEvent e) { + moving = movingLeft = movingRight = false; + oldLeft = oldRight = oldX = -1; + } + + @Override + public void mousePressed(MouseEvent e) { + if (Math.abs(e.getX() - target.getLeftSelectionPosition()) < target.getSelectionMargin()) { + movingLeft = true; + } else if (Math.abs(e.getX() - target.getRightSelectionPosition()) < target.getSelectionMargin()) { + movingRight = true; + } else if ((e.getX() > target.getLeftSelectionPosition() + target.getSelectionMargin()) && (e.getX() < target.getRightSelectionPosition() - target.getSelectionMargin())) { + moving = true; + } + oldLeft = target.getLeftSelectionPosition(); + oldRight = target.getRightSelectionPosition(); + oldX = e.getX(); + } + + // TODO implement wheel scrolling +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/experimental/TimelineIntervalSelector.java Thu Nov 07 14:55:48 2013 -0500 @@ -0,0 +1,100 @@ +/* + * Copyright 2012, 2013 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 <http://www.gnu.org/licenses/>. + * + * 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.client.swing.components.experimental; + +import java.awt.Paint; + +import javax.swing.JComponent; +import javax.swing.UIManager; + +/** + * A component that allows specifying a time range to select + */ +public class TimelineIntervalSelector extends JComponent { + + private static final String uiClassID = "TimelineIntervalSelectorUI"; + + private TimelineIntervalSelectorModel model; + + private Paint linePaint; + + private Paint fillPaint; + + public TimelineIntervalSelector() { + model = new TimelineIntervalSelectorModel(); + + updateUI(); + } + + public TimelineIntervalSelectorModel getModel() { + return model; + } + + public void setUI(TimelineIntervalSelectorUI ui) { + super.setUI(ui); + } + + @Override + public void updateUI() { + if (UIManager.get(getUIClassID()) != null) { + setUI((TimelineIntervalSelectorUI) UIManager.getUI(this)); + } else { + setUI(new TimelineIntervalSelectorUIBasic()); + } + } + + public TimelineIntervalSelectorUI getUI() { + return (TimelineIntervalSelectorUI) ui; + } + + @Override + public String getUIClassID() { + return uiClassID; + } + + public void setSelectionLinePaint(Paint paint) { + this.linePaint = paint; + } + + public Paint getSelectionLinePaint() { + return this.linePaint; + } + + public void setSelectionFillPaint(Paint paint) { + this.fillPaint = paint; + } + + public Paint getSelectionFillPaint() { + return this.fillPaint; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/experimental/TimelineIntervalSelectorModel.java Thu Nov 07 14:55:48 2013 -0500 @@ -0,0 +1,167 @@ +/* + * Copyright 2012, 2013 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 <http://www.gnu.org/licenses/>. + * + * 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.client.swing.components.experimental; + +import java.util.EventListener; +import java.util.concurrent.TimeUnit; + +import javax.swing.event.EventListenerList; + +public class TimelineIntervalSelectorModel { + + public static interface ChangeListener extends EventListener { + public void changed(); + } + + private final EventListenerList listeners = new EventListenerList(); + + private final long CREATION_TIME = System.currentTimeMillis(); + + private long totalMinimum = CREATION_TIME; + private long totalMaximum = CREATION_TIME + TimeUnit.HOURS.toMillis(1); + + private long selectedMinimum = CREATION_TIME; + private long selectedMaximum = CREATION_TIME + TimeUnit.MINUTES.toMillis(10); + + public long getTotalMinimum() { + return totalMinimum; + } + + public void setTotalMinimum(long totalMinimum) { + setTotalMinimum(totalMinimum, true); + } + public void setTotalMinimum(long totalMinimum, boolean notify) { + if (this.totalMinimum != totalMinimum) { + this.totalMinimum = totalMinimum; + + if (this.totalMaximum < this.totalMinimum) { + this.totalMaximum = this.totalMinimum; + } + + if (this.selectedMaximum < this.totalMinimum){ + this.selectedMaximum = this.totalMinimum; + } + + if (this.selectedMinimum < this.totalMinimum) { + this.selectedMinimum = this.totalMinimum; + } + + if (notify) { + fireModelChanged(); + } + } + } + + public long getTotalMaximum() { + return totalMaximum; + } + + public void setTotalMaximum(long totalMaximum) { + setTotalMaximum(totalMaximum, true); + } + + public void setTotalMaximum(long totalMaximum, boolean notify) { + if (this.totalMaximum != totalMaximum) { + this.totalMaximum = totalMaximum; + + if (this.totalMinimum > this.totalMaximum) { + this.totalMinimum = this.totalMaximum; + } + + if (this.selectedMaximum > this.totalMaximum) { + this.selectedMaximum = this.totalMaximum; + } + + if (this.selectedMinimum > this.totalMaximum) { + this.selectedMinimum = this.totalMaximum; + } + + if (notify) { + fireModelChanged(); + } + } + } + + public long getSelectedMinimum() { + return selectedMinimum; + } + + public void setSelectedMinimum(long selectedMinimum) { + setSelectedMinimum(selectedMinimum, true); + } + + public void setSelectedMinimum(long selectedMinimum, boolean notify) { + if(this.selectedMinimum != selectedMinimum) { + this.selectedMinimum = selectedMinimum; + if (notify) { + fireModelChanged(); + } + } + } + + public long getSelectedMaximum() { + return selectedMaximum; + } + + public void setSelectedMaximum(long selectedMaximum) { + setSelectedMaximum(selectedMaximum, true); + } + + public void setSelectedMaximum(long selectedMaximum, boolean notify) { + if (this.selectedMaximum != selectedMaximum) { + this.selectedMaximum = selectedMaximum; + if (notify) { + fireModelChanged(); + } + } + } + + public void addChangeListener(ChangeListener l) { + listeners.add(ChangeListener.class, l); + } + + public void removeChangeListener(ChangeListener l) { + listeners.remove(ChangeListener.class, l); + } + + private void fireModelChanged() { + Object[] listeners = this.listeners.getListenerList(); + + for (int i = listeners.length - 2; i >= 0; i -= 2) { + if (listeners[i] == ChangeListener.class) { + ((ChangeListener) listeners[i + 1]).changed(); + } + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/experimental/TimelineIntervalSelectorUI.java Thu Nov 07 14:55:48 2013 -0500 @@ -0,0 +1,39 @@ +/* + * Copyright 2012, 2013 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 <http://www.gnu.org/licenses/>. + * + * 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.client.swing.components.experimental; + +import javax.swing.plaf.ComponentUI; + +public class TimelineIntervalSelectorUI extends ComponentUI { + // a marker class +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/experimental/TimelineIntervalSelectorUIBasic.java Thu Nov 07 14:55:48 2013 -0500 @@ -0,0 +1,291 @@ +/* + * Copyright 2012, 2013 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 <http://www.gnu.org/licenses/>. + * + * 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.client.swing.components.experimental; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.geom.Rectangle2D; +import java.awt.geom.RoundRectangle2D; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.border.EmptyBorder; + +import com.redhat.thermostat.client.swing.components.experimental.TimelineIntervalMouseHandler.TimeIntervalSelectorTarget; +import com.redhat.thermostat.client.swing.components.experimental.TimelineIntervalSelectorModel.ChangeListener; +import com.redhat.thermostat.client.ui.Palette; +import com.redhat.thermostat.common.model.Range; + +public class TimelineIntervalSelectorUIBasic extends TimelineIntervalSelectorUI implements TimeIntervalSelectorTarget { + + private static final int SIDE_PADDING = 10; + + // the extra gap below the timeline header itself + private static final int GAP_BELOW = 15; /* pixels */ + + private TimelineIntervalSelector component; + + private TimelineIntervalMouseHandler mouseListener = new TimelineIntervalMouseHandler(this); + private Timeline timeline = new Timeline(new Range<Long>(0l, 100l)); + private JPanel customPaintingPanel = new CustomPaintPanel(); + + private Component topGlue; + private Component bottomGlue; + + private ChangeListener timelineSelectionPainter = new ChangeListener() { + @Override + public void changed() { + component.repaint(); + } + }; + private ChangeListener timelineRangeUpdater = new ChangeListener() { + @Override + public void changed() { + TimelineIntervalSelectorModel model = component.getModel(); + timeline.setRange(new Range<>(model.getTotalMinimum(), model.getTotalMaximum())); + } + }; + private PropertyChangeListener enabledListener = new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getPropertyName().equals("enabled")) { + boolean enabled = (Boolean) evt.getNewValue(); + timeline.setEnabled(enabled); + if (enabled) { + addUserInputListeners(); + } else { + removeUserInputListeners(); + } + } + } + }; + + public TimelineIntervalSelectorUIBasic() { + customPaintingPanel.setLayout(new BorderLayout()); + customPaintingPanel.setBorder(new EmptyBorder(0, SIDE_PADDING, 0, SIDE_PADDING)); + } + + @Override + public void installUI(JComponent c) { + super.installUI(c); + + component = (TimelineIntervalSelector) c; + + installDefaults(); + installComponents(); + installListeners(); + + } + + protected void installDefaults() { + component.setLayout(new BoxLayout(component, BoxLayout.PAGE_AXIS)); + + component.setSelectionLinePaint(Color.BLACK); + } + + protected void installComponents() { + topGlue = Box.createVerticalGlue(); + bottomGlue = Box.createVerticalGlue(); + + customPaintingPanel.add(timeline, BorderLayout.CENTER); + + component.add(topGlue); + component.add(customPaintingPanel); + component.add(bottomGlue); + } + + protected void installListeners() { + component.getModel().addChangeListener(timelineSelectionPainter); + component.getModel().addChangeListener(timelineRangeUpdater); + + component.addPropertyChangeListener(enabledListener); + + addUserInputListeners(); + } + + @Override + public void uninstallUI(JComponent c) { + uninstallListeners(); + uninstallComponents(); + uninstallDefaults(); + + component = null; + + super.uninstallUI(c); + } + + protected void uninstallComponents() { + customPaintingPanel.remove(timeline); + + component.remove(bottomGlue); + component.remove(customPaintingPanel); + component.remove(topGlue); + } + + protected void uninstallListeners() { + removeUserInputListeners(); + + component.getModel().removeChangeListener(timelineRangeUpdater); + component.getModel().removeChangeListener(timelineSelectionPainter); + } + + protected void uninstallDefaults() { + component.setSelectionLinePaint(null); + + component.setLayout(null); + } + + private void removeUserInputListeners() { + component.removeMouseWheelListener(mouseListener); + component.removeMouseMotionListener(mouseListener); + component.removeMouseListener(mouseListener); + } + + private void addUserInputListeners() { + component.addMouseListener(mouseListener); + component.addMouseMotionListener(mouseListener); + component.addMouseWheelListener(mouseListener); + } + + @Override + public int getLeftSelectionPosition() { + return domainToX(component.getModel().getSelectedMinimum()); + } + + @Override + public int getRightSelectionPosition() { + return domainToX(component.getModel().getSelectedMaximum()); + } + + @Override + public int getSelectionMargin() { + return 20; + } + + @Override + public void setCursor(Cursor cursor) { + component.setCursor(cursor); + } + + @Override + public void updateSelectionPosition(int left, int right) { + long min = xToDomain(left); + long max = xToDomain(right); + component.getModel().setSelectedMaximum(max); + component.getModel().setSelectedMinimum(min); + + component.repaint(); + } + + private int domainToX(long domainValue) { + long domainMin = component.getModel().getTotalMinimum(); + long domainMax = component.getModel().getTotalMaximum(); + int width = timeline.getWidth(); + return (int) (1.0 * (domainValue - domainMin) / (domainMax - domainMin) * (width - 1)); + } + + private long xToDomain(int x) { + long domainMin = component.getModel().getTotalMinimum(); + long domainMax = component.getModel().getTotalMaximum(); + int width = timeline.getWidth(); + return (long) ((1.0 * x / (width - 1) * (domainMax - domainMin)) + domainMin); + } + + private class CustomPaintPanel extends JPanel { + + @Override + public Dimension getPreferredSize() { + return new Dimension(super.getPreferredSize().width, timeline.getPreferredSize().height + GAP_BELOW); + } + + @Override + public void paint(Graphics g) { + super.paint(g); + + Graphics2D g2 = (Graphics2D) g.create(); + + int left = domainToX(component.getModel().getSelectedMinimum()) + SIDE_PADDING ; + int right = domainToX(component.getModel().getSelectedMaximum()) + SIDE_PADDING; + int height = getHeight(); + int width = getWidth() - 1; + + boolean enabled = component.isEnabled(); + if (enabled) { + g2.setPaint(component.getSelectionLinePaint()); + } else { + g2.setPaint(Color.LIGHT_GRAY); + } + + int pinchHeight = getHeight() - (GAP_BELOW / 2); + + g2.drawLine(0, height, 0, pinchHeight); + g2.drawLine(0, pinchHeight, left, pinchHeight); + g2.drawLine(left, pinchHeight, left, 0); + paintHandle(g2, left, pinchHeight/2); + + g2.drawLine(width, height, width, pinchHeight); + g2.drawLine(width, pinchHeight, right, pinchHeight); + g2.drawLine(right, pinchHeight, right, 0); + paintHandle(g2, right, pinchHeight/2); + + g2.dispose(); + } + + private void paintHandle(Graphics2D g, int x, int y) { + g = (Graphics2D) g.create(); + g.translate(x, y); + + g.setColor(Palette.LIGHT_GRAY.getColor()); + g.fill(new RoundRectangle2D.Float(-2, -10, 4, 20, 2, 2)); + + if (component.isEnabled()) { + g.setPaint(component.getSelectionLinePaint()); + } else { + g.setPaint(Color.LIGHT_GRAY); + } + + g.draw(new RoundRectangle2D.Float(-2, -10, 4, 20, 2, 2)); + + g.dispose(); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/experimental/TimelineRulerHeader.java Thu Nov 07 14:55:48 2013 -0500 @@ -0,0 +1,196 @@ +/* + * Copyright 2012, 2013 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 + * <http://www.gnu.org/licenses/>. + * + * 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.client.swing.components.experimental; + +import java.awt.Dimension; +import java.awt.Font; +import java.awt.GradientPaint; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Paint; +import java.awt.Rectangle; +import java.beans.Transient; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; + +import com.redhat.thermostat.client.swing.GraphicsUtils; +import com.redhat.thermostat.client.swing.components.GradientPanel; +import com.redhat.thermostat.client.ui.Palette; +import com.redhat.thermostat.common.model.Range; + +@SuppressWarnings("serial") +public abstract class TimelineRulerHeader extends GradientPanel { + + /** Default height of this component. Subclasses may use different values */ + public static final int DEFAULT_HEIGHT = 25; + + /** + * Default increment is 20 pixels per units. + * Subclasses may use different values. + * + * @see #DEFAULT_INCREMENT_IN_MILLIS + */ + public static final int DEFAULT_INCREMENT_IN_PIXELS = 20; + + /** + * Default increments is 1 second (1000 ms) per pixel unit. + * Subclasses may use different values. + * + * @see #DEFAULT_INCREMENT_IN_PIXELS + */ + public static final long DEFAULT_INCREMENT_IN_MILLIS = 1_000; + + private Range<Long> range; + + public TimelineRulerHeader(Range<Long> range) { + + super(Palette.LIGHT_GRAY.getColor(), Palette.WHITE.getColor()); + setFont(TimelineUtils.FONT); + + this.range = range; + } + + public Range<Long> getRange() { + return range; + } + + public void setRange(Range<Long> range) { + this.range = range; + } + + @Override + public int getHeight() { + return DEFAULT_HEIGHT; + } + + @Override + @Transient + public Dimension getPreferredSize() { + Dimension dim = super.getPreferredSize(); + dim.height = getHeight(); + return dim; + } + + @Override + public Dimension getSize() { + return getPreferredSize(); + } + + /** + * Defines the distance, in pixels, between one tick mark and the other. + */ + public int getUnitIncrementInPixels() { + return DEFAULT_INCREMENT_IN_PIXELS; + } + + /** + * Defines how many milliseconds pass between two tick marks. + */ + public long getUnitIncrementInMillis() { + return DEFAULT_INCREMENT_IN_MILLIS; + } + + protected abstract int getCurrentDisplayValue(); + + @Override + protected void paintComponent(Graphics g) { + + super.paintComponent(g); + + Graphics2D graphics = GraphicsUtils.getInstance().createAAGraphics(g); + + int currentValue = getCurrentDisplayValue(); + + Rectangle bounds = g.getClipBounds(); + + int unitIncrement = getUnitIncrementInPixels(); + + TimelineUtils.drawMarks(graphics, bounds, currentValue, false, unitIncrement); + drawTimelineStrings(graphics, currentValue, bounds, unitIncrement); + + graphics.setColor(Palette.THERMOSTAT_BLU.getColor()); + graphics.drawLine(bounds.x, bounds.height - 1, bounds.width, bounds.height - 1); + + graphics.dispose(); + } + + private void drawTimelineStrings(Graphics2D graphics, int currentValue, Rectangle bounds, int totalInc) { + + Font font = graphics.getFont(); + + DateFormat df = new SimpleDateFormat("HH:mm:ss"); + + Paint gradient = new GradientPaint(0, 0, Palette.WHITE.getColor(), 0, getHeight(), Palette.GRAY.getColor()); + + graphics.setColor(Palette.EARL_GRAY.getColor()); + + long incrementInMillis = getUnitIncrementInMillis(); + + long round = range.getMin() % (10 * incrementInMillis); + + int shift = (int) (round / incrementInMillis) * totalInc; + long currentTime = range.getMin() - round; + + int lowerBound = bounds.x - (4 * totalInc); + int x = ((bounds.x - currentValue) - shift); + + long increment = 0; + int height = getHeight(); + for (int i = x; i < bounds.width; i += totalInc) { + if (increment % 10 == 0 && i >= lowerBound) { + graphics.setColor(Palette.THERMOSTAT_BLU.getColor()); + graphics.drawLine(i, 0, i, height); + + graphics.setPaint(gradient); + + String value = df.format(new Date(currentTime)); + + int stringWidth = (int) font.getStringBounds(value, graphics.getFontRenderContext()).getWidth() - 1; + int stringHeight = (int) font.getStringBounds(value, graphics.getFontRenderContext()).getHeight(); + graphics.fillRect(i + 1, bounds.y + 5, stringWidth + 4, stringHeight + 4); + + graphics.setColor(Palette.THERMOSTAT_BLU.getColor()); + graphics.drawString(value, i + 1, bounds.y + stringHeight + 5); + } + currentTime += incrementInMillis; + increment++; + } + } + +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/experimental/TimelineUtils.java Thu Nov 07 14:55:48 2013 -0500 @@ -0,0 +1,68 @@ +/* + * Copyright 2012, 2013 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 + * <http://www.gnu.org/licenses/>. + * + * 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.client.swing.components.experimental; + +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.Rectangle; + +import com.redhat.thermostat.client.ui.Palette; + +public class TimelineUtils { + public static final Font FONT = new Font("SansSerif", Font.PLAIN, 10); + + public static void drawMarks(Graphics2D graphics, Rectangle bounds, + int currentValue, boolean darkerTop, int increment) + { + int inc = currentValue % increment; + int x = (bounds.x - inc); + + graphics.setColor(Palette.GRAY.getColor()); + int upperBound = (bounds.x + bounds.width); + + for (int i = x; i < upperBound; i += increment) { + graphics.drawLine(i, 0, i, bounds.height); + if (darkerTop) { + graphics.setColor(Palette.DARK_GRAY.getColor()); + graphics.drawLine(i, 0, i, 5); + graphics.setColor(Palette.GRAY.getColor()); + } + } + } + +} +
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/timeline/Timeline.java Thu Nov 07 19:10:06 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,246 +0,0 @@ -/* - * Copyright 2012, 2013 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 - * <http://www.gnu.org/licenses/>. - * - * 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.client.swing.components.timeline; - -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Font; -import java.awt.GradientPaint; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Paint; -import java.awt.Rectangle; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import com.redhat.thermostat.client.swing.GraphicsUtils; -import com.redhat.thermostat.client.swing.components.GradientPanel; -import com.redhat.thermostat.client.ui.Palette; -import com.redhat.thermostat.common.model.LongRangeNormalizer; -import com.redhat.thermostat.common.model.Range; - -/** - * Displays a timeline between the specified start and stop ranges, dynamically - * adjusting itself as needed. The values of the ranges are interpreted as - * timestamps in milliseconds. - */ -@SuppressWarnings("serial") -public class Timeline extends GradientPanel { - - /** Default height of this component. Subclasses may use different values */ - public static final int DEFAULT_HEIGHT = 25; - - private Range<Long> range; - - public Timeline(Range<Long> range) { - - super(Palette.LIGHT_GRAY.getColor(), Palette.WHITE.getColor()); - setFont(TimelineUtils.FONT); - - this.range = range; - } - - public Range<Long> getRange() { - return range; - } - - public void setRange(Range<Long> newRange) { - this.range = newRange; - repaint(); - } - - @Override - public int getHeight() { - return DEFAULT_HEIGHT; - } - - @Override - public Dimension getMaximumSize() { - Dimension dim = super.getMaximumSize(); - dim.height = getHeight(); - return dim; - } - - @Override - public Dimension getMinimumSize() { - Dimension dim = super.getMinimumSize(); - dim.height = getHeight(); - return dim; - } - - @Override - public Dimension getPreferredSize() { - Dimension dim = super.getPreferredSize(); - dim.height = getHeight(); - return dim; - } - - @Override - public Dimension getSize() { - return getPreferredSize(); - } - - @Override - protected void paintComponent(Graphics g) { - super.paintComponent(g); - - if (range == null) { - return; - } - - Graphics2D graphics = GraphicsUtils.getInstance().createAAGraphics(g); - - Rectangle bounds = g.getClipBounds(); - - TimeUnit timeUnitForTicks = getBestTimeUnit(); - - drawTicks(graphics, bounds, timeUnitForTicks); - - if (isEnabled()) { - graphics.setColor(Palette.THERMOSTAT_BLU.getColor()); - } else { - graphics.setColor(Color.GRAY); - } - graphics.drawLine(bounds.x, bounds.height - 1, bounds.width, bounds.height - 1); - - graphics.dispose(); - } - - /** - * Returns a TimeUnit and the number of subticks needed for that TimeUnit - * need to display the current range - */ - private TimeUnit getBestTimeUnit() { - long min = range.getMin(); - long max = range.getMax(); - - List<TimeUnit> units = new ArrayList<>(); - units.add(TimeUnit.DAYS); - units.add(TimeUnit.HOURS); - units.add(TimeUnit.MINUTES); - units.add(TimeUnit.SECONDS); - units.add(TimeUnit.MILLISECONDS); - - /* Find the largest unit of time suitable for the range */ - for (TimeUnit unit: units) { - long millis = unit.toMillis(1); - if (Math.abs(max - min) >= millis) { - return unit; - } - } - - return null; - } - - private void drawTicks(Graphics2D graphics, Rectangle bounds, TimeUnit tickUnit) { - Font font = graphics.getFont(); - - int widthOfOneCharacter = (int) font.getStringBounds("0", graphics.getFontRenderContext()).getWidth(); - - long deltaInMilliseconds = Math.max(1, tickUnit.toMillis(1) / 10); - - // some heuristics for spacing - int targetNumberOfIntervals = (int) (getWidth() / (10 * widthOfOneCharacter) / 1.3); - - while ((range.getMax() - range.getMin()) / deltaInMilliseconds > targetNumberOfIntervals) { - deltaInMilliseconds *= 2; - - } - - long start = (range.getMin() / deltaInMilliseconds) * deltaInMilliseconds; - long end = range.getMax(); - - DateFormat df = null; - - switch (tickUnit) { - case DAYS: - df = new SimpleDateFormat("YY-MM-dd"); - break; - case HOURS: - df = new SimpleDateFormat("hh:mm a"); - break; - case MINUTES: - df = new SimpleDateFormat("hh:mm a"); - break; - case SECONDS: - df = new SimpleDateFormat("mm.ss"); - break; - default: - df = new SimpleDateFormat("hh:mm:ss a"); - } - - Paint gradient = new GradientPaint(0, 0, Palette.WHITE.getColor(), 0, - getHeight(), Palette.GRAY.getColor()); - - LongRangeNormalizer normalizer = new LongRangeNormalizer(range, new Range<Long>(0l, (long)bounds.width)); - - for (long i = start; i < end; i += deltaInMilliseconds) { - int x = (int) normalizer.getValueNormalized(i); - - if (isEnabled()) { - graphics.setColor(Palette.THERMOSTAT_BLU.getColor()); - } else { - graphics.setColor(Color.GRAY); - } - - graphics.drawLine(x, 0, x, bounds.height); - - graphics.setPaint(gradient); - String value = df.format(new Date(i)); - - int stringWidth = (int) font.getStringBounds(value, - graphics.getFontRenderContext()).getWidth() - 1; - int stringHeight = (int) font.getStringBounds(value, - graphics.getFontRenderContext()).getHeight(); - graphics.fillRect(x + 1, bounds.y + 5, stringWidth + 4, stringHeight + - 4); - - if (isEnabled()) { - graphics.setColor(Palette.THERMOSTAT_BLU.getColor()); - } else { - graphics.setColor(Color.GRAY); - } - - graphics.drawString(value, x + 1, bounds.y + stringHeight + 5); - } - } - -}
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/timeline/TimelineRulerHeader.java Thu Nov 07 19:10:06 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,196 +0,0 @@ -/* - * Copyright 2012, 2013 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 - * <http://www.gnu.org/licenses/>. - * - * 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.client.swing.components.timeline; - -import java.awt.Dimension; -import java.awt.Font; -import java.awt.GradientPaint; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Paint; -import java.awt.Rectangle; -import java.beans.Transient; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Date; - -import com.redhat.thermostat.client.swing.GraphicsUtils; -import com.redhat.thermostat.client.swing.components.GradientPanel; -import com.redhat.thermostat.client.ui.Palette; -import com.redhat.thermostat.common.model.Range; - -@SuppressWarnings("serial") -public abstract class TimelineRulerHeader extends GradientPanel { - - /** Default height of this component. Subclasses may use different values */ - public static final int DEFAULT_HEIGHT = 25; - - /** - * Default increment is 20 pixels per units. - * Subclasses may use different values. - * - * @see #DEFAULT_INCREMENT_IN_MILLIS - */ - public static final int DEFAULT_INCREMENT_IN_PIXELS = 20; - - /** - * Default increments is 1 second (1000 ms) per pixel unit. - * Subclasses may use different values. - * - * @see #DEFAULT_INCREMENT_IN_PIXELS - */ - public static final long DEFAULT_INCREMENT_IN_MILLIS = 1_000; - - private Range<Long> range; - - public TimelineRulerHeader(Range<Long> range) { - - super(Palette.LIGHT_GRAY.getColor(), Palette.WHITE.getColor()); - setFont(TimelineUtils.FONT); - - this.range = range; - } - - public Range<Long> getRange() { - return range; - } - - public void setRange(Range<Long> range) { - this.range = range; - } - - @Override - public int getHeight() { - return DEFAULT_HEIGHT; - } - - @Override - @Transient - public Dimension getPreferredSize() { - Dimension dim = super.getPreferredSize(); - dim.height = getHeight(); - return dim; - } - - @Override - public Dimension getSize() { - return getPreferredSize(); - } - - /** - * Defines the distance, in pixels, between one tick mark and the other. - */ - public int getUnitIncrementInPixels() { - return DEFAULT_INCREMENT_IN_PIXELS; - } - - /** - * Defines how many milliseconds pass between two tick marks. - */ - public long getUnitIncrementInMillis() { - return DEFAULT_INCREMENT_IN_MILLIS; - } - - protected abstract int getCurrentDisplayValue(); - - @Override - protected void paintComponent(Graphics g) { - - super.paintComponent(g); - - Graphics2D graphics = GraphicsUtils.getInstance().createAAGraphics(g); - - int currentValue = getCurrentDisplayValue(); - - Rectangle bounds = g.getClipBounds(); - - int unitIncrement = getUnitIncrementInPixels(); - - TimelineUtils.drawMarks(graphics, bounds, currentValue, false, unitIncrement); - drawTimelineStrings(graphics, currentValue, bounds, unitIncrement); - - graphics.setColor(Palette.THERMOSTAT_BLU.getColor()); - graphics.drawLine(bounds.x, bounds.height - 1, bounds.width, bounds.height - 1); - - graphics.dispose(); - } - - private void drawTimelineStrings(Graphics2D graphics, int currentValue, Rectangle bounds, int totalInc) { - - Font font = graphics.getFont(); - - DateFormat df = new SimpleDateFormat("HH:mm:ss"); - - Paint gradient = new GradientPaint(0, 0, Palette.WHITE.getColor(), 0, getHeight(), Palette.GRAY.getColor()); - - graphics.setColor(Palette.EARL_GRAY.getColor()); - - long incrementInMillis = getUnitIncrementInMillis(); - - long round = range.getMin() % (10 * incrementInMillis); - - int shift = (int) (round / incrementInMillis) * totalInc; - long currentTime = range.getMin() - round; - - int lowerBound = bounds.x - (4 * totalInc); - int x = ((bounds.x - currentValue) - shift); - - long increment = 0; - int height = getHeight(); - for (int i = x; i < bounds.width; i += totalInc) { - if (increment % 10 == 0 && i >= lowerBound) { - graphics.setColor(Palette.THERMOSTAT_BLU.getColor()); - graphics.drawLine(i, 0, i, height); - - graphics.setPaint(gradient); - - String value = df.format(new Date(currentTime)); - - int stringWidth = (int) font.getStringBounds(value, graphics.getFontRenderContext()).getWidth() - 1; - int stringHeight = (int) font.getStringBounds(value, graphics.getFontRenderContext()).getHeight(); - graphics.fillRect(i + 1, bounds.y + 5, stringWidth + 4, stringHeight + 4); - - graphics.setColor(Palette.THERMOSTAT_BLU.getColor()); - graphics.drawString(value, i + 1, bounds.y + stringHeight + 5); - } - currentTime += incrementInMillis; - increment++; - } - } - -} -
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/timeline/TimelineUtils.java Thu Nov 07 19:10:06 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,68 +0,0 @@ -/* - * Copyright 2012, 2013 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 - * <http://www.gnu.org/licenses/>. - * - * 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.client.swing.components.timeline; - -import java.awt.Font; -import java.awt.Graphics2D; -import java.awt.Rectangle; - -import com.redhat.thermostat.client.ui.Palette; - -public class TimelineUtils { - public static final Font FONT = new Font("SansSerif", Font.PLAIN, 10); - - public static void drawMarks(Graphics2D graphics, Rectangle bounds, - int currentValue, boolean darkerTop, int increment) - { - int inc = currentValue % increment; - int x = (bounds.x - inc); - - graphics.setColor(Palette.GRAY.getColor()); - int upperBound = (bounds.x + bounds.width); - - for (int i = x; i < upperBound; i += increment) { - graphics.drawLine(i, 0, i, bounds.height); - if (darkerTop) { - graphics.setColor(Palette.DARK_GRAY.getColor()); - graphics.drawLine(i, 0, i, 5); - graphics.setColor(Palette.GRAY.getColor()); - } - } - } - -} -
--- a/client/swing/src/test/java/com/redhat/thermostat/client/swing/components/EventTimelineModelTest.java Thu Nov 07 19:10:06 2013 +0100 +++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/components/EventTimelineModelTest.java Thu Nov 07 14:55:48 2013 -0500 @@ -47,7 +47,10 @@ import org.junit.Test; -import com.redhat.thermostat.client.swing.components.EventTimelineModel.Event; +import com.redhat.thermostat.client.swing.components.experimental.EventTimelineDataChangeListener; +import com.redhat.thermostat.client.swing.components.experimental.EventTimelineModel; +import com.redhat.thermostat.client.swing.components.experimental.EventTimelineRangeChangeListener; +import com.redhat.thermostat.client.swing.components.experimental.EventTimelineModel.Event; import com.redhat.thermostat.common.model.Range; public class EventTimelineModelTest {
--- a/client/swing/src/test/java/com/redhat/thermostat/client/swing/components/TimelineIntervalSelectorModelTest.java Thu Nov 07 19:10:06 2013 +0100 +++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/components/TimelineIntervalSelectorModelTest.java Thu Nov 07 14:55:48 2013 -0500 @@ -37,6 +37,8 @@ import org.junit.Test; +import com.redhat.thermostat.client.swing.components.experimental.TimelineIntervalSelectorModel; + public class TimelineIntervalSelectorModelTest { @Test
--- a/client/swing/src/test/java/com/redhat/thermostat/client/swing/components/TimelineIntervalSelectorTest.java Thu Nov 07 19:10:06 2013 +0100 +++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/components/TimelineIntervalSelectorTest.java Thu Nov 07 14:55:48 2013 -0500 @@ -50,7 +50,8 @@ import javax.swing.WindowConstants; import javax.swing.border.EmptyBorder; -import com.redhat.thermostat.client.swing.components.TimelineIntervalSelectorModel.ChangeListener; +import com.redhat.thermostat.client.swing.components.experimental.TimelineIntervalSelector; +import com.redhat.thermostat.client.swing.components.experimental.TimelineIntervalSelectorModel.ChangeListener; public class TimelineIntervalSelectorTest {
--- a/client/swing/src/test/java/com/redhat/thermostat/client/swing/components/timeline/TimelineTest.java Thu Nov 07 19:10:06 2013 +0100 +++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/components/timeline/TimelineTest.java Thu Nov 07 14:55:48 2013 -0500 @@ -46,6 +46,7 @@ import javax.swing.SwingUtilities; import javax.swing.WindowConstants; +import com.redhat.thermostat.client.swing.components.experimental.Timeline; import com.redhat.thermostat.common.model.Range; public class TimelineTest {
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadTimelineView.java Thu Nov 07 19:10:06 2013 +0100 +++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadTimelineView.java Thu Nov 07 14:55:48 2013 -0500 @@ -51,7 +51,7 @@ import com.redhat.thermostat.client.swing.ComponentVisibleListener; import com.redhat.thermostat.client.swing.SwingComponent; -import com.redhat.thermostat.client.swing.components.timeline.TimelineRulerHeader; +import com.redhat.thermostat.client.swing.components.experimental.TimelineRulerHeader; import com.redhat.thermostat.common.model.Range; import com.redhat.thermostat.thread.client.common.Timeline; import com.redhat.thermostat.thread.client.common.view.ThreadTimelineView;
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/ThreadTimelineHeader.java Thu Nov 07 19:10:06 2013 +0100 +++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/ThreadTimelineHeader.java Thu Nov 07 14:55:48 2013 -0500 @@ -38,7 +38,7 @@ import javax.swing.JScrollPane; -import com.redhat.thermostat.client.swing.components.timeline.TimelineRulerHeader; +import com.redhat.thermostat.client.swing.components.experimental.TimelineRulerHeader; import com.redhat.thermostat.common.model.Range; /**
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/TimelineComponent.java Thu Nov 07 19:10:06 2013 +0100 +++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/TimelineComponent.java Thu Nov 07 14:55:48 2013 -0500 @@ -50,7 +50,7 @@ import com.redhat.thermostat.client.swing.GraphicsUtils; import com.redhat.thermostat.client.swing.components.GradientPanel; -import com.redhat.thermostat.client.swing.components.timeline.TimelineUtils; +import com.redhat.thermostat.client.swing.components.experimental.TimelineUtils; import com.redhat.thermostat.client.ui.Palette; import com.redhat.thermostat.common.model.LongRangeNormalizer; import com.redhat.thermostat.common.model.Range;
--- a/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/stats/OverlayComponent.java Thu Nov 07 19:10:06 2013 +0100 +++ b/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/stats/OverlayComponent.java Thu Nov 07 14:55:48 2013 -0500 @@ -52,7 +52,7 @@ import com.redhat.thermostat.client.swing.components.CompositeIcon; import com.redhat.thermostat.client.swing.components.Icon; import com.redhat.thermostat.client.swing.components.ShadowLabel; -import com.redhat.thermostat.client.swing.components.timeline.TimelineUtils; +import com.redhat.thermostat.client.swing.components.experimental.TimelineUtils; import com.redhat.thermostat.client.ui.Palette; import com.redhat.thermostat.shared.locale.LocalizedString; import com.redhat.thermostat.vm.heap.analysis.client.core.HeapIconResources;
--- a/vm-jmx/client-swing/src/main/java/com/redhat/thermostat/vm/jmx/client/swing/internal/JmxNotificationsSwingView.java Thu Nov 07 19:10:06 2013 +0100 +++ b/vm-jmx/client-swing/src/main/java/com/redhat/thermostat/vm/jmx/client/swing/internal/JmxNotificationsSwingView.java Thu Nov 07 14:55:48 2013 -0500 @@ -68,11 +68,11 @@ import com.redhat.thermostat.client.swing.IconResource; import com.redhat.thermostat.client.swing.SwingComponent; import com.redhat.thermostat.client.swing.components.ActionToggleButton; -import com.redhat.thermostat.client.swing.components.EventTimeline; -import com.redhat.thermostat.client.swing.components.EventTimelineRangeChangeListener; import com.redhat.thermostat.client.swing.components.HeaderPanel; import com.redhat.thermostat.client.swing.components.LocalizedLabel; -import com.redhat.thermostat.client.swing.components.timeline.Timeline; +import com.redhat.thermostat.client.swing.components.experimental.EventTimeline; +import com.redhat.thermostat.client.swing.components.experimental.EventTimelineRangeChangeListener; +import com.redhat.thermostat.client.swing.components.experimental.Timeline; import com.redhat.thermostat.client.ui.Palette; import com.redhat.thermostat.common.ActionEvent; import com.redhat.thermostat.common.ActionListener;