package org.chartsy.main; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Insets; import java.awt.LayoutManager; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.geom.RoundRectangle2D; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JLayeredPane; import javax.swing.JPanel; import javax.swing.JToolBar; import javax.swing.SwingConstants; import javax.swing.border.Border; import org.chartsy.main.chart.Chart; import org.chartsy.main.chart.Overlay; import org.chartsy.main.data.Stock; import org.chartsy.main.dialogs.SettingsPanel; import org.chartsy.main.resources.ResourcesUtils; import org.chartsy.main.utils.ColorGenerator; import org.chartsy.main.utils.GraphicsUtils; import org.chartsy.main.utils.Range; import org.chartsy.main.utils.SerialVersion; import org.openide.util.NbBundle; /** * * @author viorel.gheba */ public class ChartPanel extends JLayeredPane implements Serializable { private static final long serialVersionUID = SerialVersion.APPVERSION; private ChartFrame chartFrame; private AnnotationPanel annotationPanel; private JLabel stockInfo; private JToolBar overlayToolboxes; private List<Overlay> overlays; private boolean overlayToolboxesUpdated = false; public ChartPanel(ChartFrame frame) { chartFrame = frame; overlays = new ArrayList<Overlay>(); initializeUIElements(); } private void initializeUIElements() { setOpaque(false); setDoubleBuffered(true); annotationPanel = new AnnotationPanel(chartFrame); overlayToolboxes = new JToolBar(JToolBar.HORIZONTAL); overlayToolboxes.setBorder(BorderFactory.createEmptyBorder()); overlayToolboxes.setOpaque(false); overlayToolboxes.setFloatable(false); stockInfo = new JLabel(); stockInfo.setOpaque(false); stockInfo.setHorizontalAlignment(SwingConstants.LEFT); stockInfo.setVerticalAlignment(SwingConstants.TOP); stockInfo.setFont(chartFrame.getChartProperties().getFont()); stockInfo.setForeground(chartFrame.getChartProperties().getFontColor()); if (!chartFrame.getChartData().isStockNull()) { Stock stock = chartFrame.getChartData().getStock(); String stockInfoText = ""; if (stock.hasCompanyName()) { stockInfoText = NbBundle.getMessage( ChartPanel.class, "LBL_StockInfo", new String[] { stock.getKey(), stock.getCompanyName(), chartFrame.getChartData().getInterval().getName(), chartFrame.getChartData().getDataProvider().getName() }); } else { stockInfoText = NbBundle.getMessage( ChartPanel.class, "LBL_StockInfoNoCompany", new String[] { stock.getKey(), chartFrame.getChartData().getInterval().getName(), chartFrame.getChartData().getDataProvider().getName() }); } stockInfo.setText(stockInfoText); } else { String stockInfoText = NbBundle.getMessage( ChartPanel.class, "LBL_StockInfoNoData"); stockInfo.setText(stockInfoText); } ChartFrameAdapter frameAdapter = new ChartFrameAdapter() { @Override public void stockChanged(Stock newStock) { updateStockInfo(newStock); } @Override public void chartChanged(Chart newChart) { repaint(); } @Override public void overlayAdded(Overlay overlay) { addOverlay(overlay); chartFrame.getChartData().calculateRange(chartFrame, overlays); chartFrame.revalidate(); chartFrame.repaint(); } @Override public void overlayRemoved(Overlay overlay) { removeOverlay(overlay); chartFrame.getChartData().calculateRange(chartFrame, overlays); chartFrame.revalidate(); chartFrame.repaint(); } }; chartFrame.addChartFrameListener(frameAdapter); setLayout(new LayoutManager() { @Override public void addLayoutComponent(String name, Component comp) {} @Override public void removeLayoutComponent(Component comp) {} @Override public Dimension preferredLayoutSize(Container parent) { return new Dimension(0, 0); } @Override public Dimension minimumLayoutSize(Container parent) { return new Dimension(0, 0); } @Override public void layoutContainer(Container parent) { int width = parent.getWidth(); int height = parent.getHeight(); stockInfo.setBounds(0, 0, width, stockInfo.getPreferredSize().height); annotationPanel.setBounds(0, 2, width - 4, height - 4); overlayToolboxes.setLocation(0, stockInfo.getPreferredSize().height + 1); } }); setFont(chartFrame.getChartProperties().getFont()); setForeground(chartFrame.getChartProperties().getFontColor()); add(overlayToolboxes); add(annotationPanel); add(stockInfo); setComponentZOrder(overlayToolboxes, 0); setComponentZOrder(annotationPanel, 1); setComponentZOrder(stockInfo, 2); } public ChartFrame getChartFrame() { return chartFrame; } public void setChartFrame(ChartFrame frame) { chartFrame = frame; } public AnnotationPanel getAnnotationPanel() { return annotationPanel; } public void setAnnotationPanel(AnnotationPanel panel) { annotationPanel = panel; } public Range getRange() { return chartFrame.getChartData().getVisibleRange(); } public @Override void paint(Graphics g) { Graphics2D g2 = GraphicsUtils.prepareGraphics(g); if (!overlayToolboxesUpdated) updateOverlayToolbar(); chartFrame.getChartData().calculateRange(chartFrame, overlays); if (!chartFrame.getChartData().isChartNull()) chartFrame.getChartData().getChart().paint(g2, chartFrame); if (!overlays.isEmpty()) { Rectangle bounds = getBounds(); bounds.grow(-2, -2); for (Overlay overlay : overlays) overlay.paint(g2, chartFrame, bounds); } super.paint(g); } @Override public void update(Graphics g) { paint(g); } private void updateStockInfo(Stock stock) { String stockInfoText = ""; if (stock.hasCompanyName()) { stockInfoText = NbBundle.getMessage( ChartPanel.class, "LBL_StockInfo", new String[] { stock.getKey(), stock.getCompanyName(), chartFrame.getChartData().getInterval().getName(), chartFrame.getChartData().getDataProvider().getName() }); } else { stockInfoText = NbBundle.getMessage( ChartPanel.class, "LBL_StockInfoNoCompany", new String[] { stock.getKey(), chartFrame.getChartData().getInterval().getName(), chartFrame.getChartData().getDataProvider().getName() }); } if (!stockInfo.getText().equals(stockInfoText)) stockInfo.setText(stockInfoText); } public void setOverlays(List<Overlay> list) { clearOverlays(); chartFrame.getChartData().removeAllOverlaysDatasetListeners(); for (Overlay o : list) { o.setDatasetKey(chartFrame.getChartData().getDatasetKey()); o.calculate(); addOverlay(o); } updateOverlayToolbar(); } public List<Overlay> getOverlays() { List<Overlay> list = new ArrayList<Overlay>(); for (Overlay overlay : overlays) { list.add(overlay); } return list; } public Overlay getOverlay(int index) { if (index < 0 || index > overlays.size()) { return null; } return overlays.get(index); } public boolean hasOverlays() { return !overlays.isEmpty(); } public int getOverlaysCount() { return overlays.size(); } public void addOverlay(Overlay overlay) { overlays.add(overlay); updateOverlayToolbar(); } public void removeOverlay(Overlay overlay) { overlays.remove(overlay); updateOverlayToolbar(); } public void clearOverlays() { overlays.clear(); overlays = new ArrayList<Overlay>(); updateOverlayToolbar(); } public void updateOverlayToolbar() { int width = 0; int height = 0; overlayToolboxes.removeAll(); for (Overlay overlay : overlays) { OverlayToolbox overlayToolbox = new OverlayToolbox(overlay); overlayToolboxes.add(overlayToolbox); overlayToolbox.update(); width += overlayToolbox.getWidth() + 16; height = overlayToolbox.getHeight() + 4; } overlayToolboxes.validate(); overlayToolboxes.repaint(); overlayToolboxes.setBounds(overlayToolboxes.getX(), overlayToolboxes.getY(), width, height); } @Override public Rectangle getBounds() { return new Rectangle(0, 0, getWidth(), getHeight()); } public final class OverlayToolbox extends JToolBar implements Serializable { private static final long serialVersionUID = SerialVersion.APPVERSION; private Overlay overlay; private JLabel overlayLabel; private JComponent container; public boolean mouseOver = false; private final Color backColor = ColorGenerator.getTransparentColor(new Color(0x1C2331), 50); public OverlayToolbox(Overlay overlay) { super(JToolBar.HORIZONTAL); this.overlay = overlay; setOpaque(false); setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); overlayLabel = new JLabel(overlay.getLabel()); overlayLabel.setHorizontalTextPosition(SwingConstants.LEFT); overlayLabel.setVerticalTextPosition(SwingConstants.CENTER); overlayLabel.setOpaque(false); overlayLabel.setBorder(BorderFactory.createEmptyBorder()); overlayLabel.setFont(ChartPanel.this.getFont()); overlayLabel.setForeground(ChartPanel.this.getForeground()); add(overlayLabel); container = new JPanel(); container.setOpaque(false); container.setLayout(new BoxLayout(container, BoxLayout.X_AXIS)); add(container); update(); addMouseListener(new MouseAdapter() { public @Override void mouseEntered(MouseEvent e) { mouseOver = true; revalidate(); repaint(); } public @Override void mouseExited(MouseEvent e) { setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); mouseOver = false; revalidate(); repaint(); } }); } public @Override int getWidth() { return getLayout().preferredLayoutSize(this).width; } public @Override int getHeight() { return getLayout().preferredLayoutSize(this).height; } public void update() { // remove all buttons container.removeAll(); OverlayToolboxButton button; // Settings container.add(button = new OverlayToolboxButton(overlaySettings(overlay))); button.setText(""); button.setToolTipText("Settings"); // Remove container.add(button = new OverlayToolboxButton(removeAction(overlay))); button.setText(""); button.setToolTipText("Remove"); revalidate(); repaint(); } public @Override void paint(Graphics g) { if (!overlayLabel.getFont().equals(chartFrame.getChartProperties().getFont())) overlayLabel.setFont(chartFrame.getChartProperties().getFont()); if (!overlayLabel.getForeground().equals(chartFrame.getChartProperties().getFontColor())) overlayLabel.setForeground(chartFrame.getChartProperties().getFontColor()); if (!overlayLabel.getText().equals(overlay.getLabel())) overlayLabel.setText(overlay.getLabel()); Graphics2D g2 = GraphicsUtils.prepareGraphics(g); g2.setPaintMode(); if (mouseOver) { g2.setColor(backColor); int x = overlayLabel.getLocation().x - getInsets().left; int y = overlayLabel.getLocation().y - getInsets().top; RoundRectangle2D roundRectangle = new RoundRectangle2D.Double(x, y, getWidth(), getHeight(), 10, 10); g2.fill(roundRectangle); } super.paint(g); g2.dispose(); } @Override public void update(Graphics g) { paint(g); } public class OverlayToolboxButton extends JButton implements Serializable { private static final long serialVersionUID = SerialVersion.APPVERSION; public OverlayToolboxButton(Action action) { super(action); setOpaque(false); setFocusPainted(false); setBorderPainted(false); setMargin(new Insets(0, 0, 0, 0)); setBorder(new Border() { public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { } public Insets getBorderInsets(Component c) { return new Insets(0, 2, 0, 2); } public boolean isBorderOpaque() { return true; } }); addMouseListener(new MouseAdapter() { public @Override void mouseExited(MouseEvent e) { setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); mouseOver = false; } public @Override void mouseEntered(MouseEvent e) { setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); mouseOver = true; } }); } } } private AbstractAction overlaySettings(final Overlay overlay) { return new AbstractAction("Overlay Settings", ResourcesUtils.getIcon("settings")) { public void actionPerformed(ActionEvent e) { SettingsPanel.getDefault().openSettingsWindow(overlay); } }; } private AbstractAction removeAction(final Overlay overlay) { return new AbstractAction("Remove Indicator", ResourcesUtils.getIcon("remove")) { public void actionPerformed(ActionEvent e) { chartFrame.overlayRemoved(overlay); } }; } }