/** * author: Marcel Genzmehr * 29.11.2011 */ package org.freeplane.core.ui.components; 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.event.MouseEvent; import java.awt.event.MouseListener; import java.util.LinkedHashSet; import java.util.Set; import javax.swing.JComponent; import javax.swing.JPanel; import org.freeplane.core.util.LogUtils; /** * */ public class OneTouchCollapseResizer extends JResizer { private static final long serialVersionUID = 3836146387249880446L; public static final String COLLAPSED = OneTouchCollapseResizer.class.getPackage().getName()+".collapsed"; private static final String ALREADY_IN_PAINT = OneTouchCollapseResizer.class.getPackage().getName()+".ALREADY_PAINTING"; public enum CollapseDirection {COLLAPSE_LEFT, COLLAPSE_RIGHT, COLLAPSE_UP, COLLAPSE_DOWN}; private Dimension lastComponentSize; protected boolean expanded = true; private JPanel hotspot; private CollapseDirection collapseDirection; private int inset = 2; private final Direction direction; private Integer resizeComponentIndex; private Set<ComponentCollapseListener> collapseListener = new LinkedHashSet<ComponentCollapseListener>(); /*********************************************************************************** * CONSTRUCTORS **********************************************************************************/ /** * @param d */ public OneTouchCollapseResizer(final Direction d, final CollapseDirection collapseDirection) { super(d); direction = d; this.setDividerSize(7); this.collapseDirection = collapseDirection; MouseListener listener = new MouseListener() { private void resetCursor() { if(d.equals(Direction.RIGHT)){ setCursor(Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR)); } else if(d.equals(Direction.LEFT)){ setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR)); } else if(d.equals(Direction.UP)){ setCursor(Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR)); } else /*Direction.DOWN*/ { setCursor(Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR)); } } public void mouseReleased(MouseEvent e) { } public void mousePressed(MouseEvent e) { } public void mouseExited(MouseEvent e) { if(e.getComponent() == getHotSpot()) { resetCursor(); } if(isExpanded()) { resetCursor(); } } public void mouseEntered(MouseEvent e) { if(e.getComponent() == getHotSpot()) { getHotSpot().setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); } if(!isExpanded() || sliderLock) { e.getComponent().setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); } } public void mouseClicked(MouseEvent e) { final Component resizedComponent = getResizedParent(); if((e.getComponent() == getHotSpot()) || sliderLock) { final Dimension size = new Dimension(resizedComponent.getPreferredSize()); if (isExpanded()) { getHotSpot().setEnabled(true); lastComponentSize = new Dimension(size); setExpanded(false); } else { setExpanded(true); } } else { if (!isExpanded()) { setExpanded(true); } } } }; getHotSpot().addMouseListener(listener); addMouseListener(listener); add(getHotSpot()); initDefaults(); } protected void initDefaults() { // TODO Auto-generated method stub } /*********************************************************************************** * METHODS **********************************************************************************/ public boolean isExpanded() { return this.expanded; } public void setDividerSize(int size) { final int w; final int h; if(direction.equals(Direction.RIGHT)){ w = size; h = 0; } else if(direction.equals(Direction.LEFT)){ h = 0; w = size; } else if(direction.equals(Direction.UP)){ h = size; w = 0; } else /*Direction.DOWN*/ { h = size; w = 0; } setPreferredSize(new Dimension(w, h)); } public int getDividerSize() { if(direction.equals(Direction.RIGHT) || direction.equals(Direction.LEFT)){ return getPreferredSize().width; } else /*Direction.DOWN || Direction.UP*/ { return getPreferredSize().height; } } public void setExpanded(boolean enabled) { if(this.expanded != enabled) { this.expanded = enabled; try { Component resizedComponent = getResizedParent(); if(resizedComponent instanceof JComponent) { ((JComponent) resizedComponent).putClientProperty(COLLAPSED, (enabled ? null : "true")); } if(enabled) { if(lastComponentSize != null) { resizedComponent.setPreferredSize(lastComponentSize); } } else { resizedComponent.setPreferredSize(new Dimension(0,0)); } fireCollapseStateChanged(resizedComponent, enabled); recalibrate(); } catch (Exception e) { LogUtils.warn("Exception in org.freeplane.core.ui.components.OneTouchCollapseResizer.setExpanded(enabled): "+e); } } } private Component getResizedParent() { final JComponent parent = (JComponent) getParent(); if(parent != null && resizeComponentIndex == null) { resizeComponentIndex = getIndex(); lastComponentSize = new Dimension(parent.getComponent(resizeComponentIndex).getPreferredSize()); } return parent.getComponent(resizeComponentIndex); } public void paint(Graphics g) { if(getClientProperty(ALREADY_IN_PAINT) != null) { return; } putClientProperty(ALREADY_IN_PAINT, "true"); super.paint(g); if((direction.equals(Direction.RIGHT) || direction.equals(Direction.LEFT))) { int center_y = getHeight()/2; int divSize = getDividerSize(); getHotSpot().setBounds(0, center_y-15, divSize, 30); } else { int center_x = getWidth()/2; int divSize = getDividerSize(); getHotSpot().setBounds(center_x-15, 0, 30, divSize); } Dimension size = getResizedParent().getPreferredSize(); if((direction.equals(Direction.RIGHT) || direction.equals(Direction.LEFT)) && size.width <= getDividerSize()) { setExpanded(false); } else if((direction.equals(Direction.UP) || direction.equals(Direction.DOWN)) && size.height <= getDividerSize()){ setExpanded(false); } else { setExpanded(true); //getHotSpot().setBounds(0, 0, getDividerSize(), 24); } if(getResizedParent() instanceof JComponent) { ((JComponent) getResizedParent()).putClientProperty(COLLAPSED, (isExpanded() ? null : "true")); } getHotSpot().paint(g.create(getHotSpot().getLocation().x, getHotSpot().getLocation().y, getHotSpot().getWidth(), getHotSpot().getHeight())); putClientProperty(ALREADY_IN_PAINT, null); } private Component getHotSpot() { if(hotspot == null) { hotspot = new JPanel() { private static final long serialVersionUID = -5321517835206976034L; public void paint(Graphics g) { if (isExpanded()) { drawCollapseLabel(g); } else { drawExpandLabel(g); } } public void updateUI() { try { super.updateUI(); } catch (Exception e) { } } }; hotspot.setBackground(Color.BLUE); } return hotspot; } private void drawCollapseLabel(Graphics g) { Dimension size = g.getClipBounds().getSize(); int half_length = Math.round(size.height*0.2f); int center_y = size.height / 2; int half_width = Math.round(size.width*0.2f); int center_x = size.width / 2; g.setColor(getBackground()); g.fillRect(0, 0, getWidth(), getHeight()); //g.setColor(); if(this.collapseDirection.equals(CollapseDirection.COLLAPSE_LEFT)) { arrowLeft(g, half_length, center_y); } else if(this.collapseDirection.equals(CollapseDirection.COLLAPSE_RIGHT)) { arrowRight(g, half_length, center_y); } else if(this.collapseDirection.equals(CollapseDirection.COLLAPSE_UP)) { arrowUp(g, half_width, center_x); } else if(this.collapseDirection.equals(CollapseDirection.COLLAPSE_DOWN)) { arrowDown(g, half_width, center_x); } } private void drawExpandLabel(Graphics g) { Dimension size = g.getClipBounds().getSize(); int half_length = (size.height-(inset*6))/2; int center_y = size.height / 2; int half_width = (size.width-(inset*6))/2; int center_x = size.width / 2; g.setColor(getBackground()); g.fillRect(0, 0, getWidth(), getHeight()); if(this.collapseDirection.equals(CollapseDirection.COLLAPSE_LEFT)) { arrowRight(g, half_length, center_y); } else if(this.collapseDirection.equals(CollapseDirection.COLLAPSE_RIGHT)) { arrowLeft(g, half_length, center_y); } else if(this.collapseDirection.equals(CollapseDirection.COLLAPSE_UP)) { arrowDown(g, half_width, center_x); } else if(this.collapseDirection.equals(CollapseDirection.COLLAPSE_DOWN)) { arrowUp(g, half_width, center_x); } } /** * @param g * @param half_length * @param center_y */ private void arrowLeft(Graphics g, int half_length, int center_y) { int[] x = new int[]{inset, getSize().width - inset, getSize().width - inset}; int[] y = new int[]{center_y, center_y-half_length, center_y + half_length}; g.setColor(Color.DARK_GRAY); g.fillPolygon(x, y, 3); g.setColor(Color.DARK_GRAY); g.drawLine(inset, center_y, getSize().width - inset, center_y - half_length); g.setColor(Color.GRAY); g.drawLine( getSize().width - inset, center_y + half_length, inset, center_y); g.setColor(Color.GRAY); g.drawLine( getSize().width - inset, center_y - half_length, getSize().width - inset, center_y + half_length); } /** * @param g * @param half_length * @param center_y */ private void arrowRight(Graphics g, int half_length, int center_y) { int[] x = new int[]{inset, inset, getSize().width - inset}; int[] y = new int[]{center_y+half_length, center_y-half_length, center_y}; g.setColor( Color.DARK_GRAY); g.fillPolygon(x,y,3); g.setColor( Color.DARK_GRAY); g.drawLine( inset, center_y + half_length, inset, center_y - half_length); g.setColor(Color.GRAY); g.drawLine( inset, center_y - half_length, getSize().width - inset, center_y); g.setColor( Color.LIGHT_GRAY); g.drawLine( getSize().width - inset, center_y, inset, center_y + half_length); } private void arrowUp(Graphics g, int half_length, int center_x) { int[] y = new int[]{inset, getSize().height - inset, getSize().height - inset}; int[] x = new int[]{center_x, center_x-half_length, center_x + half_length}; g.setColor(Color.DARK_GRAY); g.fillPolygon(x, y, 3); g.setColor(Color.GRAY); g.drawLine(center_x + half_length, getSize().height - inset, center_x, inset); g.setColor(Color.DARK_GRAY); g.drawLine(center_x, inset, center_x - half_length, getSize().height - inset); g.setColor(Color.LIGHT_GRAY); g.drawLine(center_x - half_length, getSize().height - inset, center_x + half_length, getSize().height - inset); } private void arrowDown(Graphics g, int half_length, int center_x) { int[] y = new int[]{inset, inset, getSize().height - inset}; int[] x = new int[]{center_x+half_length, center_x-half_length, center_x}; g.setColor( Color.DARK_GRAY); g.fillPolygon(x,y,3); g.setColor(Color.GRAY); g.drawLine( center_x - half_length, inset, center_x, getSize().height- inset); g.setColor( Color.DARK_GRAY); g.drawLine( center_x + half_length, inset, center_x - half_length, inset); g.setColor( Color.LIGHT_GRAY); g.drawLine(center_x, getSize().height - inset, center_x + half_length, inset); } private int getIndex() { final Container parent = getParent(); for(int i = 0; i < parent.getComponentCount(); i++ ){ if(OneTouchCollapseResizer.this.equals(parent.getComponent(i))){ if(direction.equals(Direction.RIGHT)){ return i + 1; } else if(direction.equals(Direction.LEFT)){ return i - 1; } else if(direction.equals(Direction.UP)){ return i - 1; } else if(direction.equals(Direction.DOWN)){ return i + 1; } } } return -1; } public void addCollapseListener(ComponentCollapseListener listener) { if(listener == null) return; synchronized (collapseListener) { collapseListener.add(listener); } } public void removeCollapseListener(ComponentCollapseListener listener) { if(listener == null) return; synchronized (collapseListener) { collapseListener.remove(listener); } } protected void fireCollapseStateChanged(Component resizedComponent, boolean expanded) { ResizeEvent event = new ResizeEvent(this, resizedComponent); synchronized (this.collapseListener) { for(ComponentCollapseListener listener : collapseListener) { try { if(expanded) { listener.componentExpanded(event); } else { listener.componentCollapsed(event); } } catch (Exception e) { LogUtils.severe(e); } } } } public static OneTouchCollapseResizer findResizerFor(Component component) { if(component != null) { Component parent = component.getParent(); if(parent != null) { if(parent instanceof Container) { Component[] children = ((Container) parent).getComponents(); for (Component child : children) { if(child instanceof OneTouchCollapseResizer) { return (OneTouchCollapseResizer) child; } } } return findResizerFor(parent); } } return null; } public interface ComponentCollapseListener { public void componentCollapsed(ResizeEvent event); public void componentExpanded(ResizeEvent event); } public void recalibrate() { if(getClientProperty(ALREADY_IN_PAINT) == null) { final JComponent parent = (JComponent) getParent(); if(parent != null) { parent.revalidate(); parent.repaint(); } } } }