package org.vorthmann.zome.ui;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Hashtable;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SpringLayout;
import javax.swing.SwingConstants;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.vorthmann.ui.CardPanel;
import org.vorthmann.ui.Controller;
public class NewLengthPanel extends JPanel implements PropertyChangeListener, ActionListener
{
private static final Logger logger = Logger .getLogger( "org.vorthmann.zome.ui" );
private final JButton scaleUp, scaleDown, superShortScale, shortScale, mediumScale, longScale;
private final JCheckBox halfCheckbox;
private final CardPanel halfCardPanel;
private final JLabel unitText, lengthText, sliderLabel;
private final JSlider scaleSlider;
private final JPanel coloredBackground, sliderPanel;
private final JComponent upDownButtons, scaleButtons;
private final JPanel lengthDisplay;
private boolean emitSliderEvents = true;
/**
* properties: unit, multiplier
*
* graphics: selectedOrbit
*/
protected Controller controller;
private LengthDialog lengthDialog;
private final JFrame frame;
@Override
public void actionPerformed( ActionEvent e )
{
controller .actionPerformed( e );
renderLength();
}
public void setController( Controller controller )
{
if ( this .controller != null )
{
this .controller .getMouseTool() .detach( this );
this .controller .removePropertyListener( this );
}
this .controller = controller;
if ( this .controller != null )
{
this .controller .addPropertyListener( this );
this .controller .getMouseTool() .attach( this );
}
lengthDialog = new LengthDialog( frame, controller .getSubController( "unit" ) );
boolean value = "true" .equals( controller .getProperty( "showStrutScales" ) );
lengthDisplay .setVisible( value );
switchOrbit();
}
public NewLengthPanel( JFrame frame )
{
this .frame = frame;
this .setLayout( new BorderLayout() );
{
coloredBackground = new JPanel( new BorderLayout() );
{
// left side: scale up/down buttons
upDownButtons = new JPanel();
upDownButtons .setLayout( new GridLayout( 2, 1 ) );
{
scaleUp = createButton( "scaleUp", new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
int value = scaleSlider .getValue();
if ( value < scaleSlider .getMaximum() )
scaleSlider .setValue( value + 1 );
}
} );
upDownButtons .add( scaleUp );
scaleDown = createButton( "scaleDown", new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
int value = scaleSlider .getValue();
if ( value > scaleSlider .getMinimum() )
scaleSlider .setValue( value - 1 );
}
} );
upDownButtons .add( scaleDown );
}
coloredBackground .add( upDownButtons, BorderLayout .WEST );
}
{
// center: scale slider
sliderPanel = new JPanel( new BorderLayout() );
sliderPanel .setBorder( BorderFactory .createEmptyBorder( 15, 15, 15, 15 ) );
// scale slider
scaleSlider = new JSlider( JSlider .VERTICAL, -6, 6, 0 );
scaleSlider .setMajorTickSpacing( 1 );
scaleSlider .setPaintTicks( true );
sliderLabel = new JLabel( "unit" );
Hashtable<Integer, JComponent > labelTable = new Hashtable<>();
labelTable .put( 0, sliderLabel );
scaleSlider .setLabelTable( labelTable );
scaleSlider .setPaintLabels( true );
scaleSlider .setToolTipText( "adjust scale for new struts" );
scaleSlider .setPreferredSize( new Dimension( 20, 100 ) );
scaleSlider .setSnapToTicks( true );
scaleSlider .addChangeListener( new ChangeListener()
{
@Override
public void stateChanged( ChangeEvent e )
{
// it is essential to report state changes only when they are initiated here,
// rather than in response to incoming property changes
if ( emitSliderEvents )
controller .setProperty( "scale", Integer .toString( scaleSlider .getValue() ) );
}
} );
sliderPanel .add( scaleSlider, BorderLayout .CENTER );
coloredBackground .add( sliderPanel, BorderLayout .CENTER );
}
{
// right side: fixed scale buttons
scaleButtons = new JPanel();
scaleButtons .setLayout( new SpringLayout() );
{
halfCardPanel = new CardPanel();
{
final JPanel halfPlaceholder = new JPanel();
halfPlaceholder .setPreferredSize( new Dimension( 25, 25 ) );
halfCardPanel .add( "full", halfPlaceholder );
halfCheckbox = new JCheckBox( "half " );
halfCheckbox .addActionListener( this );
halfCheckbox .setActionCommand( "toggleHalf" );
halfCardPanel .add( "half", halfCheckbox );
}
scaleButtons .add( halfCardPanel );
superShortScale = createButton( "supershort", "b0", this );
shortScale = createButton( "short", "b1", this );
mediumScale = createButton( "medium", "b2", this );
longScale = createButton( "long", "b3", this );
// set preferred width wide enough that the buttons don't resize
// when the button text changes (e.g. from "b0" to "shorter")
Dimension preferredSize = superShortScale.getPreferredSize();
preferredSize.width = 80;
superShortScale.setPreferredSize(preferredSize);
shortScale.setPreferredSize(preferredSize);
mediumScale.setPreferredSize(preferredSize);
longScale.setPreferredSize(preferredSize);
scaleButtons .add( longScale );
scaleButtons .add( mediumScale );
scaleButtons .add( shortScale );
scaleButtons .add( superShortScale );
}
SpringUtilities .makeCompactGrid( scaleButtons,
5, 1, //rows, cols
5, 5, //initX, initY
6, 6); //xPad, yPad
coloredBackground .add( scaleButtons, BorderLayout .EAST );
}
this .add( coloredBackground, BorderLayout .CENTER );
lengthDisplay = new JPanel( new BorderLayout() );
lengthDisplay .setBorder( BorderFactory .createEmptyBorder( 4, 3, 4, 0 ) );
Font biggerFont;
{
JPanel valuesColumn = new JPanel( new GridLayout( 2, 1 ) );
{
unitText = new JLabel();
biggerFont = unitText .getFont() .deriveFont( 14f );
unitText .setFont( biggerFont );
unitText .setHorizontalAlignment( SwingConstants .TRAILING );
valuesColumn .add( unitText );
}
{
lengthText = new JLabel();
lengthText .setFont( biggerFont );
lengthText .setHorizontalAlignment( SwingConstants .TRAILING );
valuesColumn .add( lengthText );
}
lengthDisplay .add( valuesColumn, BorderLayout .CENTER );
}
{
JPanel buttonsColumn = new JPanel( new GridLayout( 2, 1 ) );
{
JButton editButton = new JButton( "custom" );
// editButton .setEnabled( false );
editButton .setToolTipText( "customize the unit length" );
editButton .addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
// Tell the LengthController to push the custom unit to the NumberController
controller .actionPerformed( new ActionEvent( e .getSource(), e .getID(), "setCustomUnit" ) );
// ... then update the dialog
lengthDialog .syncFromModel();
lengthDialog .setVisible( true );
}} );
buttonsColumn .add( editButton );
}
{
JButton rezeroButton = createButton( "newZeroScale", "rezero", this );
rezeroButton .setToolTipText( "set this length as the unit length" );
buttonsColumn .add( rezeroButton );
}
lengthDisplay .add( buttonsColumn, BorderLayout .EAST );
}
this .add( lengthDisplay, BorderLayout .SOUTH );
lengthDisplay .setVisible( false );
}
}
protected static JButton createButton( String actionCommand, ActionListener listener )
{
String iconPath = "/org/vorthmann/zome/ui/" + actionCommand + ".gif";
JButton button = new JButton();
java.net.URL imgURL = LessonPanel.class .getResource( iconPath );
if ( imgURL != null )
button .setIcon( new ImageIcon( imgURL ) );
else
logger .log( Level.WARNING, "Couldn't find resource: " + iconPath );
button .addActionListener( listener );
button .setActionCommand( actionCommand );
return button;
}
protected static JButton createButton( String actionCommand, String label, ActionListener listener )
{
JButton button = new JButton( label );
button .addActionListener( listener );
button .setActionCommand( actionCommand );
return button;
}
private void switchOrbit()
{
if ( "true" .equals( controller .getProperty( "halfSizes" ) ) )
halfCardPanel .showCard( "half" );
else
halfCardPanel .showCard( "full" );
longScale .setText( controller .getProperty( "scaleName.long" ) );
mediumScale .setText( controller .getProperty( "scaleName.medium" ) );
shortScale .setText( controller .getProperty( "scaleName.short" ) );
superShortScale .setText( controller .getProperty( "scaleName.superShort" ) );
renderLength();
String colorHex = controller .getProperty( "color" );
if ( colorHex != null )
{
// this can happen when deselecting the circled orbit in non-single mode
Color color = Color .decode( colorHex );
upDownButtons .setBackground( color );
sliderPanel .setBorder( BorderFactory .createMatteBorder( 15, 15, 15, 15, color ) );
}
repaint();
}
private void renderLength()
{
halfCheckbox .setSelected( "true" .equals( controller .getProperty( "half" ) ) );
// it is essential to report state changes only when they are initiated here,
// rather than in response to incoming property changes
emitSliderEvents = false;
scaleSlider .setValue( Integer .parseInt( controller .getProperty( "scale" ) ) );
emitSliderEvents = true;
unitText .setText( "<html>unit = <b>" + controller .getProperty( "unitText" ) + "</b></html>" );
lengthText .setText( "<html>" + controller .getProperty( "scaleFactorHtml" ) + " \u2715 unit = <b>" + controller .getProperty( "lengthText" ) + "</b></html>" );
boolean custom = Boolean .parseBoolean( controller .getProperty( "unitIsCustom" ) );
if ( custom && ! lengthDisplay .isVisible() )
{
controller .setProperty( "showStrutScales", "true" );
}
}
@Override
public void propertyChange( PropertyChangeEvent e )
{
switch (e.getPropertyName()) {
case "length":
renderLength();
break;
case "selectedOrbit":
// this first one should fall "up" to the symmetry controller
Controller newController = controller .getSubController( "buildOrbits" );
newController = controller .getSubController( "currentLength" );
// newController will be null if all directions are disabled.
// in that case, just retain the controller from the previously selected orbit.
if( newController != null ) {
// we didn't set this.controller directly, since we need to disconnect from it first
setController( newController );
}
break;
case "showStrutScales":
lengthDisplay .setVisible( (Boolean) e.getNewValue() );
break;
}
}
}