/* * Copyright (c) 2012 Sam Harwell, Tunnel Vision Laboratories LLC * All rights reserved. * * The source code of this document is proprietary work, and is not licensed for * distribution. For information about licensing, contact Sam Harwell at: * sam@tunnelvisionlabs.com */ package org.antlr.netbeans.editor.navigation; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Point; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.lang.ref.WeakReference; import javax.swing.BorderFactory; import javax.swing.Icon; import javax.swing.JPanel; import javax.swing.UIManager; public final class CollapsiblePanel extends JPanel { public static final int UP = 0; public static final int DOWN = 1; public static final String EXPANDED_PROPERTY = "CollapsiblePanel.expanded"; public static final String PROP_ORIENTATION = "orientation"; private static WeakReference<MouseAdapterImpl> mouseAdapter; private int orientation; private boolean armed; private boolean expanded; private final int minimumHeight = 8; private final Icon up = new UpIcon (); private final Icon down = new DownIcon (); private final int ICON_SIZE = 8; public CollapsiblePanel() { setLayout(new TrivialLayout()); } private static MouseAdapterImpl getMouseAdapter() { MouseAdapterImpl result = null; if (mouseAdapter != null) { result = mouseAdapter.get(); } if (result == null) { result = new MouseAdapterImpl(); mouseAdapter = new WeakReference<>(result); } return result; } private void setSecondaryMouseListener(MouseListener listener) { getMouseAdapter().setSecondaryListener(listener); } @Override public void addNotify() { addMouseMotionListener(getMouseAdapter()); addMouseListener(getMouseAdapter()); super.addNotify(); } @Override public void removeNotify() { super.removeNotify(); removeMouseMotionListener(getMouseAdapter()); removeMouseListener(getMouseAdapter()); } public int getOrientation() { return orientation; } public void setOrientation(int value) { if (orientation == value) { return; } int oldValue = orientation; orientation = value; firePropertyChange(PROP_ORIENTATION, oldValue, value); } private void setArmed(boolean value) { if (armed == value) return; armed = value; repaint(); } public boolean isExpanded() { return expanded; } public void setExpanded(boolean value) { if (expanded == value) { return; } Dimension dimension = getPreferredSize(); expanded = value; Dimension dimension2 = getPreferredSize(); if (isDisplayable()) { revalidate(); } firePropertyChange(EXPANDED_PROPERTY, !expanded, expanded); } @Override public Dimension getPreferredSize() { return getLayout().preferredLayoutSize(this); } @Override public Dimension getMinimumSize() { Dimension d = getPreferredSize(); d.width = 20; return d; } @Override public Dimension getMaximumSize() { return getPreferredSize(); } private boolean isArmPoint(Point p) { if ( !expanded ) { return p.y > 0 && p.y < getHeight (); } else { if ( orientation == UP ) { return p.y > getHeight () - minimumHeight; } else { return p.y < minimumHeight; } } } public void updateBorder () { if ( orientation == UP ) { super.setBorder ( BorderFactory.createEmptyBorder ( 0, 0, minimumHeight, 0 ) ); } else { super.setBorder ( BorderFactory.createEmptyBorder ( minimumHeight, 0, 0, 0 ) ); } } public int getMinimumHeight () { return minimumHeight; } public void setBorder () { //do nothing } @Override public void paintBorder (Graphics g) { Color c = armed ? UIManager.getColor ( "List.selectionBackground" ) : getBackground (); //NOI18N if (c==null) { c=getBackground(); } int x = 0; int y = orientation == UP ? 1 + ( getHeight () - minimumHeight ) : 0; int w = getWidth (); int h = minimumHeight - 1; g.setColor ( c ); g.fillRect ( x, y, w, h ); int pos = orientation == UP ? getHeight () - 1 : 0; int dir = orientation == UP ? -1 : 1; g.setColor ( armed ? c.darker () : UIManager.getColor ( "controlShadow" ) ); //NOI18N g.drawLine ( 0, pos, w, pos ); pos += dir; if ( ( orientation == UP ) == expanded ) { up.paintIcon ( this, g, ( getWidth () / 2 ) - ( up.getIconWidth () / 2 ), getHeight () - ( minimumHeight + ( expanded ? 0 : -1 ) ) ); } else { down.paintIcon ( this, g, ( getWidth () / 2 ) - ( up.getIconWidth () / 2 ), expanded ? 2 : 1 ); } } @Override public void paintChildren (Graphics g) { if ( !expanded ) return; super.paintChildren(g); } private static class MouseAdapterImpl extends MouseAdapter implements MouseMotionListener { private MouseListener secondaryListener; public void setSecondaryListener(MouseListener value) { secondaryListener = value; } @Override public void mouseEntered(MouseEvent e) { ((CollapsiblePanel)e.getSource()).setArmed(true); } @Override public void mouseExited(MouseEvent e) { ((CollapsiblePanel)e.getSource()).setArmed(false); } @Override public void mouseMoved(MouseEvent e) { ((CollapsiblePanel)e.getSource()).setArmed(((CollapsiblePanel)e.getSource()).isArmPoint(e.getPoint())); } @Override public void mousePressed(MouseEvent e) { if (((CollapsiblePanel)e.getSource()).isArmPoint(e.getPoint())) { ((CollapsiblePanel)e.getSource()).setExpanded(!((CollapsiblePanel)e.getSource()).isExpanded()); e.consume(); } else if (secondaryListener != null) { secondaryListener.mousePressed(e); } } @Override public void mouseDragged(MouseEvent e) { // do nothing } } private class UpIcon implements Icon { @Override public int getIconHeight () { return ICON_SIZE - 2; } @Override public int getIconWidth () { return ICON_SIZE + 2; } @Override public void paintIcon (java.awt.Component c, Graphics g, int x, int y) { g.setColor ( armed ? UIManager.getColor ( "List.selectionForeground" ) : //NOI18N UIManager.getColor ( "controlShadow" ) ); //NOI18N /* int[] xPoints = new int[] {x+getIconWidth()/2, x+getIconWidth(), x}; int[] yPoints = new int[] {y, y+getIconHeight()-1, y+getIconHeight()-1}; */ int[] xPoints = new int[]{x, x + 8, x + 4}; int[] yPoints = new int[]{y + 5, y + 5, y}; g.fillPolygon ( xPoints, yPoints, 3 ); } } private class DownIcon implements Icon { @Override public int getIconHeight () { return ICON_SIZE - 3; } @Override public int getIconWidth () { return ICON_SIZE + 2; } @Override public void paintIcon (java.awt.Component c, Graphics g, int x, int y) { x++; g.setColor ( armed ? UIManager.getColor ( "List.selectionForeground" ) : //NOI18N UIManager.getColor ( "controlShadow" ) ); //NOI18N /* int[] xPoints = new int[] {(x+getIconWidth()/2), x+getIconWidth()-1, x}; int[] yPoints = new int[] {y+getIconHeight()-2, y, y}; */ int[] xPoints = new int[]{x, x + 8, x + 4}; int[] yPoints = new int[]{y, y, y + 4}; g.fillPolygon ( xPoints, yPoints, 3 ); } } }