/*
* [SandDepth.java]
*
* Summary: compute amount of sand needed for an aquarium.
*
* Copyright: (c) 2014-2017 Roedy Green, Canadian Mind Products, http://mindprod.com
*
* Licence: This software may be copied and used freely for any purpose but military.
* http://mindprod.com/contact/nonmil.html
*
* Requires: JDK 1.8+
*
* Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/
*
* Version History:
* 1.0 2014-08-11 initial version
* 1.1 2014-12-10 lint
*/
package com.mindprod.sanddepth;
import com.mindprod.common18.Build;
import com.mindprod.common18.CMPAboutJBox;
import com.mindprod.common18.Common18;
import com.mindprod.common18.FontFactory;
import com.mindprod.common18.HybridJ;
import com.mindprod.common18.Laf;
import com.mindprod.common18.Misc;
import com.mindprod.common18.ST;
import com.mindprod.common18.VersionCheck;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JSlider;
import javax.swing.JTextField;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.Color;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.net.URL;
import java.text.DecimalFormat;
/**
* possible tank shapes
*/
enum Shape
{
RECTANGULAR, CYLINDRICAL, MOON
}
/**
* compute amount of sand needed for an aquarium.
*
* Must be signed to allow Look and Feel to work.
*
* @author Roedy Green, Canadian Mind Products
* @version 1.1 2014-12-10 lint
* @since 2014-08-11
*/
public class SandDepth extends JApplet
{
// declarations
/**
* height of Applet box in pixels. Does not include surrounding frame.
* Includes menu bar and frame bar but not frame surrounding that. Includes bottom margin.
*/
private static final int APPLET_HEIGHT = 600;
/**
* Width of Applet box in pixels.
*/
private static final int APPLET_WIDTH = 840;
/**
* first year the program copyrighted
*/
private static final int FIRST_COPYRIGHT_YEAR = 2014;
/**
* inner margin. margin about a component not touching the edge of the frame
*/
private static final int INNER = 5;
/**
* outer margin, margin about a component touching the edge of the frame
*/
private static final int OUTER = 20;
private static final double DEFAULT_SG = 1.5d;
/**
* how many US gallons in a cubic inch
*/
private static final double US_GALS_PER_CU_INCH = 0.004329d;
/**
* how much a cc of water weighs in kilograms
*/
private static final double WEIGHT_OF_CC_WATER_IN_KG = 0.001d;
/**
* how much a cubic inch of water weighs in pounds
*/
private static final double WEIGHT_OF_CU_INCH_WATER_IN_POUNDS = 0.036_126_684_455d;
/**
* undisplayed copyright notice
*
* @noinspection UnusedDeclaration
*/
private static final String EMBEDDED_COPYRIGHT =
"Copyright: (c) 2014-2017 Roedy Green, Canadian Mind Products, http://mindprod.com";
/**
* when program released to the public
*/
private static final String RELEASE_DATE = "2014-12-10";
/**
* title for about box
*/
private static final String TITLE_STRING = "Aquarium Sand Depth Calculator";
/**
* program version for about box
*/
private static final String VERSION_STRING = "1.1";
/**
* background colour, light yellow
*/
private static final Color BACKGROUND_FOR_BODY = new Color( 0xf7fbd7 );
/**
* light green resultColor background
*/
private static final Color BACKGROUND_FOR_RESULT_LIGHT = new Color( 0xe3fdf7 );
/**
* medium green resultColor background
*/
private static final Color BACKGROUND_FOR_RESULT_MEDIUM = new Color( 0xafffe4 );
/**
* darker green resultColor background.
*/
private static final Color DARK_BACKGROUND_FOR_RESULT = new Color( 0x9fffd4 );
/**
* instructions colour
*/
private static final Color FOREGROUND_FOR_INSTRUCTIONS = new Color( 0x008000 );
/**
* label foreground
*/
private static final Color FOREGROUND_FOR_LABEL = new Color( 0x0000b0 );
/**
* foreground for titles
*/
private static final Color FOREGROUND_FOR_TITLE = new Color( 0xdc143c );
/**
* format for one decimal place
*/
private static final DecimalFormat DF1 = new DecimalFormat( "0.0" );
/**
* format for two decimal places
*/
private static final DecimalFormat DF2 = new DecimalFormat( "0.00" );
/**
* font for labels
*/
private static final Font FONT_FOR_LABEL = FontFactory.build( "Dialog", Font.PLAIN, 14 );
/**
* Font for for titles and About buttons
*/
private static final Font FONT_FOR_TITLE = FontFactory.build( "Dialog", Font.BOLD, 16 );
/**
* Font for for title second line
*/
private static final Font FONT_FOR_TITLE2 = FontFactory.build( "Dialog", Font.PLAIN, 14 );
/**
* used to stop cycle triggering of event when sliders set
*/
private boolean defibrillate = false;
/**
* true if we are in metric mode, false in imperial mode
*/
private boolean metric;
/**
* conversion factor when calculating sand depth, either imperial or metric
*/
private double depthConverter;
/**
* size of a slider tick for the length of tank
*/
private double lengthTickSize;
/**
* size of the a slider tick specific gravity
*/
private double specificGravityTickSize;
/**
* conversion factor for volume calc, either imperial or metric
*/
private double volumeConverter;
/**
* conversion factor for weight calc, either imperial or metric
*/
private double weightConverter;
/**
* size of the a slider tick for weight
*/
private double weightTickSize;
/**
* metric/imperial pair of toggle buttons
*/
private ButtonGroup modes;
/**
* toggle button for tank shape
*/
private ButtonGroup tankShape;
/**
* radio button to choose tank shape
*/
private JButton chooseCylindrical;
/**
* radio button to choose tank shape
*/
private JButton chooseMoon;
/**
* radio button to choose tank shape
*/
private JButton chooseRectangular;
/**
* label for the cylinder shape
*/
private JLabel cylindricalLabel;
/**
* label for the instructions
*/
private JLabel instructions;
/**
* label for the moon shape
*/
private JLabel moonLabel;
/**
* label for the rectangle shape
*/
private JLabel rectangularLabel;
/**
* label for sand depth
*/
private JLabel sandDepthLabel;
/**
* label for sand weight
*/
private JLabel sandWeightLabel;
/**
* label for specific gravity
*/
private JLabel specificGravityLabel;
/**
* label for tank area
*/
private JLabel tankAreaLabel;
/**
* label to tank diameter
*/
private JLabel tankDiameterLabel;
/**
* label to tank height
*/
private JLabel tankHeightLabel;
/**
* label to tank length
*/
private JLabel tankLengthLabel;
/**
* label for tank volume
*/
private JLabel tankVolumeLabel;
/**
* label to tank width
*/
private JLabel tankWidthLabel;
/**
* Applet Title
*/
private JLabel title;
/**
* title, second part
*/
private JLabel title2;
/**
* panel to held the three shape buttons
*/
private JPanel shapePanel;
/**
* radio button to choose imperial mode
*/
private JRadioButton chooseImperial;
/**
* radio button to choose metric mode
*/
private JRadioButton chooseMetric;
/**
* slider to control sand depth
*/
private JSlider sandDepthSlider;
/**
* slider to control sand weight
*/
private JSlider sandWeightSlider;
/**
* slider to control specific gravity
*/
private JSlider specificGravitySlider;
/**
* slider to control tank diameter
*/
private JSlider tankDiameterSlider;
/**
* slider to control tank height
*/
private JSlider tankHeightSlider;
/**
* slider to control tank length
*/
private JSlider tankLengthSlider;
/**
* slider to control tank width
*/
private JSlider tankWidthSlider;
/**
* sand depth display
*/
private JTextField sandDepth;
/**
* sand weight display
*/
private JTextField sandWeight;
/**
* specific gravity display
*/
private JTextField specificGravity;
/**
* tank area display
*/
private JTextField tankArea;
/**
* tank diameter display
*/
private JTextField tankDiameter;
/**
* tank height display
*/
private JTextField tankHeight;
/**
* tank length display
*/
private JTextField tankLength;
/**
* tank volume display
*/
private JTextField tankVolume;
/**
* tank width display
*/
private JTextField tankWidth;
private Shape shape = Shape.RECTANGULAR;
// /declarations
// methods
/**
* Simple way to create an ImageIcon
* Read a png, gif or jpg from the archive resource jar file.
*
* @param imageResourceName fully resource name of the image in the jar. if leave off off lead / will be relative to
* com.mindprod.example.
*
* @return ImageIcon corresponding to the png/gif/jpg.
*/
private static ImageIcon createImageIcon( String imageResourceName )
{
URL url = SandDepth.class.getResource( imageResourceName );
if ( url == null )
{
return null;
}
return new ImageIcon( url );
}
/**
* allocate the components
*/
private void buildComponents()
{
modes = new ButtonGroup();
tankShape = new ButtonGroup();
//
chooseMetric = new JRadioButton( "metric", false );
chooseMetric.setFont( FONT_FOR_LABEL );
chooseMetric.setForeground( FOREGROUND_FOR_LABEL );
//
chooseImperial = new JRadioButton( "imperial", true );
chooseImperial.setFont( FONT_FOR_LABEL );
chooseImperial.setForeground( FOREGROUND_FOR_LABEL );
//
modes.add( chooseMetric );
modes.add( chooseImperial );
//
shapePanel = new JPanel();
shapePanel.setBackground( BACKGROUND_FOR_BODY );
shapePanel.setLayout( new FlowLayout() );
//
chooseRectangular = new JButton();
decorateButton( chooseRectangular, "rect" );
//
chooseCylindrical = new JButton();
decorateButton( chooseCylindrical, "cyl" );
//
chooseMoon = new JButton();
decorateButton( chooseMoon, "moon" );
//
tankShape.add( chooseRectangular );
tankShape.add( chooseCylindrical );
tankShape.add( chooseMoon );
//
title = new JLabel( TITLE_STRING + " " + VERSION_STRING );
title.setFont( FONT_FOR_TITLE );
title.setForeground( FOREGROUND_FOR_TITLE );
//
title2 = new JLabel(
"released:" +
RELEASE_DATE +
" build:" +
Build.BUILD_NUMBER
);
title2.setFont( FONT_FOR_TITLE2 );
title2.setForeground( FOREGROUND_FOR_TITLE );
//
tankLengthLabel = new JLabel();
tankLengthLabel.setFont( FONT_FOR_LABEL );
tankLengthLabel.setForeground( FOREGROUND_FOR_LABEL );
//
tankWidthLabel = new JLabel();
tankWidthLabel.setFont( FONT_FOR_LABEL );
tankWidthLabel.setForeground( FOREGROUND_FOR_LABEL );
//
tankDiameterLabel = new JLabel();
tankDiameterLabel.setFont( FONT_FOR_LABEL );
tankDiameterLabel.setForeground( FOREGROUND_FOR_LABEL );
//
tankHeightLabel = new JLabel();
tankHeightLabel.setFont( FONT_FOR_LABEL );
tankHeightLabel.setForeground( FOREGROUND_FOR_LABEL );
//
sandDepthLabel = new JLabel();
sandDepthLabel.setFont( FONT_FOR_LABEL );
sandDepthLabel.setForeground( FOREGROUND_FOR_LABEL );
//
sandWeightLabel = new JLabel();
sandWeightLabel.setFont( FONT_FOR_LABEL );
sandWeightLabel.setForeground( FOREGROUND_FOR_LABEL );
sandWeightLabel.setBackground( DARK_BACKGROUND_FOR_RESULT );
//
tankVolumeLabel = new JLabel();
tankVolumeLabel.setFont( FONT_FOR_LABEL );
tankVolumeLabel.setForeground( FOREGROUND_FOR_LABEL );
//
tankAreaLabel = new JLabel();
tankAreaLabel.setFont( FONT_FOR_LABEL );
tankAreaLabel.setForeground( FOREGROUND_FOR_LABEL );
//
specificGravityLabel = new JLabel();
specificGravityLabel.setFont( FONT_FOR_LABEL );
specificGravityLabel.setForeground( FOREGROUND_FOR_LABEL );
//
rectangularLabel = new JLabel( "rectangular" );
rectangularLabel.setFont( FONT_FOR_LABEL );
rectangularLabel.setForeground( FOREGROUND_FOR_LABEL );
//
cylindricalLabel = new JLabel( "cylindrical" );
cylindricalLabel.setFont( FONT_FOR_LABEL );
cylindricalLabel.setForeground( FOREGROUND_FOR_LABEL );
//
moonLabel = new JLabel( "half-moon" );
moonLabel.setFont( FONT_FOR_LABEL );
moonLabel.setForeground( FOREGROUND_FOR_LABEL );
tankLengthSlider = new JSlider( JSlider.HORIZONTAL );
tankWidthSlider = new JSlider( JSlider.HORIZONTAL );
tankDiameterSlider = new JSlider( JSlider.HORIZONTAL );
tankHeightSlider = new JSlider( JSlider.HORIZONTAL );
sandDepthSlider = new JSlider( JSlider.HORIZONTAL );
sandWeightSlider = new JSlider( JSlider.HORIZONTAL );
specificGravitySlider = new JSlider( JSlider.HORIZONTAL );
//
tankLength = new JTextField();
tankLength.setHorizontalAlignment( JTextField.RIGHT );
tankLength.setEditable( false );
//
tankWidth = new JTextField();
tankWidth.setHorizontalAlignment( JTextField.RIGHT );
tankWidth.setEditable( false );
//
tankDiameter = new JTextField();
tankDiameter.setHorizontalAlignment( JTextField.RIGHT );
tankDiameter.setEditable( false );
//
tankHeight = new JTextField();
tankHeight.setHorizontalAlignment( JTextField.RIGHT );
tankHeight.setEditable( false );
//
sandDepth = new JTextField();
sandDepth.setHorizontalAlignment( JTextField.RIGHT );
sandDepth.setEditable( false );
sandDepth.setBackground( BACKGROUND_FOR_RESULT_MEDIUM );
//
sandWeight = new JTextField();
sandWeight.setHorizontalAlignment( JTextField.RIGHT );
sandWeight.setEditable( false );
sandWeight.setBackground( DARK_BACKGROUND_FOR_RESULT );
//
tankVolume = new JTextField();
tankVolume.setHorizontalAlignment( JTextField.RIGHT );
tankVolume.setEditable( false );
tankVolume.setBackground( BACKGROUND_FOR_RESULT_LIGHT );
//
tankArea = new JTextField();
tankArea.setHorizontalAlignment( JTextField.RIGHT );
tankArea.setEditable( false );
tankArea.setBackground( BACKGROUND_FOR_RESULT_LIGHT );
//
specificGravity = new JTextField();
specificGravity.setHorizontalAlignment( JTextField.RIGHT );
specificGravity.setEditable( false );
//
instructions = new JLabel( "" +
"- Select metric or imperial/US measure.
" +
"- Select your tank shape image: rectangular, cylindrical or half-moon. Selected shape will brighten.
" +
"- Use the sliders to select the size of your aquarium.
"
+ "- Use the slider to select the desired depth of sand to find out how much sand you need.
"
+ "- If you have a better value than " + DF2.format( DEFAULT_SG ) + " for the specific gravity of your sand, enter that too.
"
+ "- Alternatively, use the slider to enter the "
+ "weight of a quantity of sand to find out how deeply it will spread.
" +
"- You can also play with the look and feel menu.
" );
instructions.setFont( FontFactory.build( "Dialog", Font.PLAIN, 12 ) );
instructions.setForeground( FOREGROUND_FOR_INSTRUCTIONS );
instructions.setBackground( BACKGROUND_FOR_BODY );
chooseImperial();
}// /method
/**
* build a menu with Look & Feel and About across the top
*/
private void buildMenu()
{
// turn on anti-alias
System.setProperty( "swing.aatext", "true" );
final JMenuBar menubar = new JMenuBar();
setJMenuBar( menubar );
final JMenu lafMenu = Laf.buildLookAndFeelMenu();
if ( lafMenu != null )
{
menubar.add( lafMenu );
}
final JMenu menuHelp = new JMenu( "Help" );
menubar.add( menuHelp );
final JMenuItem aboutItem = new JMenuItem( "About" );
menuHelp.add( aboutItem );
aboutItem.addActionListener( new ActionListener()
{
public void actionPerformed( ActionEvent e )
{
// open about frame
new CMPAboutJBox( Misc.getParentFrame( SandDepth.this ),
TITLE_STRING,
VERSION_STRING,
"Calculate how much sand you need",
"for your aquarium.",
"freeware",
RELEASE_DATE,
FIRST_COPYRIGHT_YEAR,
"Roedy Green",
"SANDDEPTH",
"1.8" );
}
} );
}// /method
/**
* recalculate the sand depth
*/
private void calcSandDepth()
{
final String areaString = tankArea.getText();
if ( ST.isEmpty( areaString ) )
{
return;
}
final double area = Double.parseDouble( areaString );
final String weightString = sandWeight.getText();
if ( ST.isEmpty( weightString ) )
{
return;
}
final double weight = Double.parseDouble( weightString );
final String specificGravityString = specificGravity.getText();
if ( ST.isEmpty( specificGravityString ) )
{
return;
}
final double specificGravity = Double.parseDouble( specificGravityString );
double depth = weight / area * depthConverter / specificGravity;
sandDepth.setText( DF1.format( depth ) );
defibrillate = true;
sandDepthSlider.setValue( ( int ) ( depth / lengthTickSize ) );
}// /method
/**
* recalculate the sand Weight
*/
private void calcSandWeight()
{
final String areaString = tankArea.getText();
if ( ST.isEmpty( areaString ) )
{
return;
}
final double area = Double.parseDouble( areaString );
final String depthString = sandDepth.getText();
if ( ST.isEmpty( depthString ) )
{
return;
}
final double sandDepth = Double.parseDouble( depthString );
final String specificGravityString = specificGravity.getText();
if ( ST.isEmpty( specificGravityString ) )
{
return;
}
final double specificGravity = Double.parseDouble( specificGravityString );
// volume in cc * specific gravity gives weight in grams, convert to kg.
// or volume in cubic inches, * weight cubic inch of water in pounds, * specify gravity.
final double weight = area * sandDepth * weightConverter * specificGravity;
sandWeight.setText( DF1.format( weight ) );
defibrillate = true;
sandWeightSlider.setValue( ( int ) ( weight / weightTickSize ) );
}// /method
/**
* recalculate the tank area
*/
private void calcTankArea()
{
switch ( shape )
{
case RECTANGULAR:
final String lengthString = tankLength.getText();
// abort calc if all number snot ready.
if ( ST.isEmpty( lengthString ) )
{
return;
}
final double length = Double.parseDouble( lengthString );
//
final String widthString = tankWidth.getText();
// abort calc if all number snot ready.
if ( ST.isEmpty( widthString ) )
{
return;
}
final double width = Double.parseDouble( widthString );
//
// no unit conversions needed in metric or imperial
tankArea.setText( DF1.format( length * width ) );
break;
case CYLINDRICAL:
case MOON:
final String diameterString = tankDiameter.getText();
// abort calc if all number is not ready.
if ( ST.isEmpty( diameterString ) )
{
return;
}
final double diameter = Double.parseDouble( diameterString );
final double radius = diameter / 2;
double area = Math.PI * radius * radius;
if ( shape == Shape.MOON )
{
area /= 2;
}
tankArea.setText( DF1.format( area ) );
break;
default:
throw new IllegalArgumentException( "unexpected enum" );
}
}// /method
/**
* recalculate the tank volume
*/
private void calcTankVolume()
{
final String areaString = tankArea.getText();
if ( ST.isEmpty( areaString ) )
{
return;
}
final double area = Double.parseDouble( areaString );
final String heightString = tankHeight.getText();
// abort calc if all number snot ready.
if ( ST.isEmpty( heightString ) )
{
return;
}
final double height = Double.parseDouble( heightString );
// convert cc to liters or convert cu in to us gallons
final double capacity = area * height * volumeConverter;
tankVolume.setText( DF1.format( capacity ) );
}// /method
/**
* set up for calculating in imperial measure
*/
void chooseImperial()
{
lengthTickSize = 0.2d; // convert ints to two tenths of inches.
weightTickSize = 0.2d; // convert ints to 0.2 pounds.
specificGravityTickSize = 0.01d; // convert int to 0.01 sg
final int lengthSliderMultiplier = 5;
final int weightSliderMultiplier = 5;
volumeConverter = US_GALS_PER_CU_INCH;
weightConverter = WEIGHT_OF_CU_INCH_WATER_IN_POUNDS;
depthConverter = 1.0d / ( WEIGHT_OF_CU_INCH_WATER_IN_POUNDS );
tankAreaLabel.setText( "square in: area of the floor of the tank" );
sandDepthLabel.setText( "in: desired depth of sand" );
sandWeightLabel.setText( "pounds: weight of sand" );
tankDiameterLabel.setText( "in: diameter of the tank." );
tankHeightLabel.setText( "in: height of tank, " +
"(measured top to bottom,
including part above the water line). [optional]" );
tankLengthLabel.setText( "in: length of tank (measured left to right)" );
tankWidthLabel.setText( "in: width of tank (measured front to back)" );
tankVolumeLabel.setText( "US gallons: computed nominal capacity of the tank" );
specificGravityLabel.setText( "specific gravity: density of the sand" );
// in fifths of an inch
tankLengthSlider.setMinimum( 5 * lengthSliderMultiplier );
tankLengthSlider.setMaximum( 100 * lengthSliderMultiplier );
tankLengthSlider.setValue( 20 * lengthSliderMultiplier );
//
tankWidthSlider.setMinimum( 5 * lengthSliderMultiplier );
tankWidthSlider.setMaximum( 50 * lengthSliderMultiplier );
tankWidthSlider.setValue( 10 * lengthSliderMultiplier );
//
tankDiameterSlider.setMinimum( 5 * lengthSliderMultiplier );
tankDiameterSlider.setMaximum( 50 * lengthSliderMultiplier );
tankDiameterSlider.setValue( 12 * lengthSliderMultiplier );
//
tankHeightSlider.setMinimum( 5 * lengthSliderMultiplier );
tankHeightSlider.setMaximum( 50 * lengthSliderMultiplier );
tankHeightSlider.setValue( 12 * lengthSliderMultiplier );
//
sandDepthSlider.setMinimum( 0 * lengthSliderMultiplier + 1 );
sandDepthSlider.setMaximum( 12 * lengthSliderMultiplier );
sandDepthSlider.setValue( 1 * lengthSliderMultiplier );
// 1/5 of a pound
sandWeightSlider.setMinimum( 0 * weightSliderMultiplier + 1 );
sandWeightSlider.setMaximum( 500 * weightSliderMultiplier );
sandWeightSlider.setValue( 10 * weightSliderMultiplier );
//
specificGravitySlider.setMinimum( 80 ); // by 0.01
specificGravitySlider.setMaximum( 250 );
specificGravitySlider.setValue( 160 );
//
// mark all numerical choices as not yet selected, even though slider has a current position.
sandDepth.setText( null );
sandWeight.setText( null );
tankArea.setText( null );
tankDiameter.setText( null );
tankHeight.setText( null );
tankLength.setText( null );
tankVolume.setText( null );
tankWidth.setText( null );
specificGravity.setText( DF2.format( DEFAULT_SG ) );
}// /method
/**
* set up to calculate in metric measure
*/
void chooseMetric()
{
lengthTickSize = 0.5d; // convert ints to half cms .
weightTickSize = 0.1d; // convert ints to 0.1 kg .
specificGravityTickSize = 0.01d; // convert int to 0.01 sg
final int lengthSliderMultiplier = 2;
final int weightSliderMultiplier = 10;
volumeConverter = 1 / 1000d;
weightConverter = WEIGHT_OF_CC_WATER_IN_KG;
depthConverter = 1 / ( WEIGHT_OF_CC_WATER_IN_KG );
tankAreaLabel.setText( "square cm: area of the floor of the tank" );
sandDepthLabel.setText( "cm: desired depth of sand" );
sandWeightLabel.setText( "kg: weight of sand" );
tankDiameterLabel.setText( "cm: diameter of the tank" );
tankHeightLabel.setText( "cm: height of tank, " +
"(measured top to bottom,
including part above the water line). [optional]" );
tankLengthLabel.setText( "cm: length of tank (measured left to right)" );
tankWidthLabel.setText( "cm: width of tank (measured front to back)" );
tankVolumeLabel.setText( "liters: computed nominal capacity of the tank" );
specificGravityLabel.setText( "specific gravity: density of the sand" );
// in half cms.
tankLengthSlider.setMinimum( 10 * lengthSliderMultiplier );
tankLengthSlider.setMaximum( 275 * lengthSliderMultiplier );
tankLengthSlider.setValue( 51 * lengthSliderMultiplier );
//
tankWidthSlider.setMinimum( 10 * lengthSliderMultiplier );
tankWidthSlider.setMaximum( 115 * lengthSliderMultiplier );
tankWidthSlider.setValue( 25 * lengthSliderMultiplier );
//
tankHeightSlider.setMinimum( 10 * lengthSliderMultiplier );
tankHeightSlider.setMaximum( 115 * lengthSliderMultiplier );
tankHeightSlider.setValue( 31 * lengthSliderMultiplier );
//
sandDepthSlider.setMinimum( 0 * lengthSliderMultiplier + 1 );
sandDepthSlider.setMaximum( 32 * lengthSliderMultiplier );
sandDepthSlider.setValue( 3 * lengthSliderMultiplier );
//
// in 0.1 kg
sandWeightSlider.setMinimum( 0 * weightSliderMultiplier + 1 );
sandWeightSlider.setMaximum( 250 * weightSliderMultiplier );
sandWeightSlider.setValue( 5 * weightSliderMultiplier );
//
specificGravitySlider.setMinimum( 80 ); // by 0.01
specificGravitySlider.setMaximum( 250 );
specificGravitySlider.setValue( 160 );
//
sandDepth.setText( null );
sandWeight.setText( null );
tankArea.setText( null );
tankDiameter.setText( null );
tankHeight.setText( null );
tankLength.setText( null );
tankVolume.setText( null );
tankWidth.setText( null );
specificGravity.setText( DF2.format( DEFAULT_SG ) );
}// /method
private void decorateButton( JButton jbutton, String name )
{
jbutton.setName( name );
// suppress borders
jbutton.setBorderPainted( false );
// suppress press decoration
jbutton.setContentAreaFilled( false );
// suppress the ability of the jButton1 to be triggered by enter
jbutton.setDefaultCapable( false );
// hide focus rectangle
jbutton.setFocusPainted( false );
// set how wide the space must be around the text to the buttonBevelBorder. Also use ipadx/ipady
jbutton.setMargin( new Insets( 3, 3, 3, 3 ) );
// ignore clicks that come too fast
jbutton.setMultiClickThreshhold( 100/* millis */ );
// the images have transparent background
jbutton.setOpaque( false );
// hover help
jbutton.setToolTipText( "Select " + name + " for your aquarium shape." );
// Various icon images live in the jar as resources in directory
// What button looks like normally.
jbutton.setIcon( createImageIcon( "buttonimages/" + name + "normal.png" ) );
// What button looks like normally after setSelected( true ).
jbutton.setSelectedIcon( createImageIcon( "buttonimages/" + name + "selected.png" ) );
}
/**
* hook up button listeners
*/
private void hookListeners()
{
final ItemListener modeListener = new ItemListener()
{
public void itemStateChanged( ItemEvent e )
{
metric = chooseMetric.isSelected();
if ( metric )
{
chooseMetric();
}
else
{
chooseImperial();
}
}
};
chooseMetric.addItemListener( modeListener );
chooseImperial.addItemListener( modeListener );
//
tankDiameterSlider.addChangeListener( new ChangeListener()
{
@Override
public void stateChanged( final ChangeEvent e )
{
tankDiameter.setText( DF1.format( tankDiameterSlider.getValue() * lengthTickSize ) );
calcTankArea();
calcTankVolume();
calcSandWeight();
}
} );
//
tankLengthSlider.addChangeListener( new ChangeListener()
{
@Override
public void stateChanged( final ChangeEvent e )
{
tankLength.setText( DF1.format( tankLengthSlider.getValue() * lengthTickSize ) );
calcTankArea();
calcTankVolume();
calcSandWeight();
}
} );
tankWidthSlider.addChangeListener( new ChangeListener()
{
@Override
public void stateChanged( final ChangeEvent e )
{
tankWidth.setText( DF1.format( tankWidthSlider.getValue() * lengthTickSize ) );
calcTankArea();
calcTankVolume();
calcSandWeight();
}
} );
tankHeightSlider.addChangeListener( new ChangeListener()
{
@Override
public void stateChanged( final ChangeEvent e )
{
tankHeight.setText( DF1.format( tankHeightSlider.getValue() * lengthTickSize ) );
calcTankVolume();
calcSandWeight();
}
} );
sandDepthSlider.addChangeListener( new ChangeListener()
{
@Override
public void stateChanged( final ChangeEvent e )
{
if ( defibrillate )
{
defibrillate = false;
return;
}
sandDepth.setText( DF1.format( sandDepthSlider.getValue() * lengthTickSize ) );
calcSandWeight();
}
} );
sandWeightSlider.addChangeListener( new ChangeListener()
{
@Override
public void stateChanged( final ChangeEvent e )
{
if ( defibrillate )
{
defibrillate = false;
return;
}
sandWeight.setText( DF1.format( sandWeightSlider.getValue() * weightTickSize ) );
calcSandDepth();
}
} );
specificGravitySlider.addChangeListener( new ChangeListener()
{
@Override
public void stateChanged( final ChangeEvent e )
{
if ( defibrillate )
{
defibrillate = false;
return;
}
specificGravity.setText( DF2.format( specificGravitySlider.getValue() * specificGravityTickSize ) );
calcSandWeight();
}
} );
chooseRectangular.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( final ActionEvent e )
{
shape = Shape.RECTANGULAR;
pruneDisplay();
}
} );
chooseCylindrical.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( final ActionEvent e )
{
shape = Shape.CYLINDRICAL;
pruneDisplay();
}
} );
chooseMoon.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( final ActionEvent e )
{
shape = Shape.MOON;
pruneDisplay();
}
} );
}// /method
/**
* layout all the components in a GridBag
*
* @param contentPane where to place the components
*/
private void layoutComponents( Container contentPane )
{
// --0---0-----------1-------- 2------3---------
// 0 title -------------------- title2----------
// 2 chooseMetric checkImperial/ panel
// 3 slider value label diameter
// 3 slider value label length
// 4 slider value label width
// 5 --- value label area
// 6 slider value label height
// 7 --- value label volume
// 8 slider value label sand depth
// 9 slider value label sand weight
// 10 slider value label specific gravity
// 11 instructions ---------------------------------------
int row = 0;
// x y w h wtx wty anchor fill T L B R padx pady
contentPane.add( title,
new GridBagConstraints( 0,
row,
3,
1,
0.0,
0.0,
GridBagConstraints.WEST,
GridBagConstraints.NONE,
new Insets( OUTER, OUTER, INNER, INNER ),
0,
0 )
);
contentPane.add( title2,
new GridBagConstraints( 3,
row++,
1,
1,
0.0,
0.0,
GridBagConstraints.EAST,
GridBagConstraints.NONE,
new Insets( OUTER, INNER, INNER, OUTER ),
0,
0 )
);
contentPane.add( chooseMetric,
new GridBagConstraints( 0,
row,
1,
1,
0.0,
0.0,
GridBagConstraints.WEST,
GridBagConstraints.NONE,
new Insets( INNER, OUTER, INNER, INNER ),
0,
0 )
);
contentPane.add( chooseImperial,
new GridBagConstraints( 1,
row,
1,
1,
0.0,
0.0,
GridBagConstraints.WEST,
GridBagConstraints.NONE,
new Insets( INNER, INNER, INNER, INNER ),
0,
0 )
);
contentPane.add( shapePanel,
new GridBagConstraints( 2,
row++,
2,
1,
0.0,
0.0,
GridBagConstraints.EAST,
GridBagConstraints.HORIZONTAL,
new Insets( INNER, INNER, INNER, OUTER ),
0,
0 )
);
shapePanel.add( chooseRectangular );
shapePanel.add( rectangularLabel );
shapePanel.add( chooseCylindrical );
shapePanel.add( cylindricalLabel );
shapePanel.add( chooseMoon );
shapePanel.add( moonLabel );
contentPane.add( tankDiameterSlider,
new GridBagConstraints( 0,
row,
2,
1,
5.0,
0.0,
GridBagConstraints.EAST,
GridBagConstraints.HORIZONTAL,
new Insets( INNER, OUTER, 0, INNER ),
0,
0 )
);
contentPane.add( tankDiameter,
new GridBagConstraints( 2,
row,
1,
1,
0.0,
0.0,
GridBagConstraints.EAST,
GridBagConstraints.HORIZONTAL,
new Insets( INNER, INNER, 0, INNER ),
35,
0 )
);
contentPane.add( tankDiameterLabel,
new GridBagConstraints( 3,
row++,
1,
1,
2.0,
0.0,
GridBagConstraints.WEST,
GridBagConstraints.HORIZONTAL,
new Insets( INNER, INNER, 0, OUTER ),
0,
0 ) );
contentPane.add( tankLengthSlider,
new GridBagConstraints( 0,
row,
2,
1,
5.0,
0.0,
GridBagConstraints.EAST,
GridBagConstraints.HORIZONTAL,
new Insets( INNER, OUTER, 0, INNER ),
0,
0 )
);
contentPane.add( tankLength,
new GridBagConstraints( 2,
row,
1,
1,
0.0,
0.0,
GridBagConstraints.EAST,
GridBagConstraints.HORIZONTAL,
new Insets( INNER, INNER, 0, INNER ),
35,
0 )
);
contentPane.add( tankLengthLabel,
new GridBagConstraints( 3,
row++,
1,
1,
2.0,
0.0,
GridBagConstraints.WEST,
GridBagConstraints.HORIZONTAL,
new Insets( INNER, INNER, 0, OUTER ),
0,
0 ) );
contentPane.add( tankWidthSlider,
new GridBagConstraints( 0,
row,
2,
1,
0.0,
0.0,
GridBagConstraints.EAST,
GridBagConstraints.HORIZONTAL,
new Insets( INNER, OUTER, 0, INNER ),
0,
0 )
);
contentPane.add( tankWidth,
new GridBagConstraints( 2,
row,
1,
1,
0.0,
0.0,
GridBagConstraints.EAST,
GridBagConstraints.HORIZONTAL,
new Insets( INNER, INNER, 0, INNER ),
0,
0 )
);
contentPane.add( tankWidthLabel,
new GridBagConstraints( 3,
row++,
1,
1,
0.0,
0.0,
GridBagConstraints.WEST,
GridBagConstraints.HORIZONTAL,
new Insets( INNER, INNER, 0, OUTER ),
0,
0 ) );
contentPane.add( tankArea,
new GridBagConstraints( 2,
row,
1,
1,
0.0,
0.0,
GridBagConstraints.EAST,
GridBagConstraints.HORIZONTAL,
new Insets( INNER, INNER, 0, INNER ),
0,
0 )
);
contentPane.add( tankAreaLabel,
new GridBagConstraints( 3,
row++,
1,
1,
0.0,
0.0,
GridBagConstraints.WEST,
GridBagConstraints.HORIZONTAL,
new Insets( INNER, INNER, 0, OUTER ),
0,
0 ) );
contentPane.add( tankHeightSlider,
new GridBagConstraints( 0,
row,
2,
1,
0.0,
0.0,
GridBagConstraints.EAST,
GridBagConstraints.HORIZONTAL,
new Insets( INNER, OUTER, 0, INNER ),
0,
0 )
);
contentPane.add( tankHeight,
new GridBagConstraints( 2,
row,
1,
1,
0.0,
0.0,
GridBagConstraints.EAST,
GridBagConstraints.HORIZONTAL,
new Insets( INNER, INNER, 0, INNER ),
0,
0 )
);
contentPane.add( tankHeightLabel,
new GridBagConstraints( 3,
row++,
1,
1,
0.0,
0.0,
GridBagConstraints.WEST,
GridBagConstraints.HORIZONTAL,
new Insets( INNER, INNER, 0, OUTER ),
0,
0 ) );
contentPane.add( tankVolume,
new GridBagConstraints( 2,
row,
1,
1,
0.0,
0.0,
GridBagConstraints.EAST,
GridBagConstraints.HORIZONTAL,
new Insets( INNER, INNER, 0, INNER ),
0,
0 )
);
contentPane.add( tankVolumeLabel,
new GridBagConstraints( 3,
row++,
1,
1,
0.0,
0.0,
GridBagConstraints.WEST,
GridBagConstraints.HORIZONTAL,
new Insets( INNER, INNER, 0, OUTER ),
0,
0 ) );
contentPane.add( sandDepthSlider,
new GridBagConstraints( 0,
row,
2,
1,
0.0,
0.0,
GridBagConstraints.EAST,
GridBagConstraints.HORIZONTAL,
new Insets( INNER, OUTER, 0, INNER ),
0,
0 )
);
contentPane.add( sandDepth,
new GridBagConstraints( 2,
row,
1,
1,
0.0,
0.0,
GridBagConstraints.EAST,
GridBagConstraints.HORIZONTAL,
new Insets( INNER, INNER, 0, INNER ),
0,
0 )
);
contentPane.add( sandDepthLabel,
new GridBagConstraints( 3,
row++,
1,
1,
0.0,
0.0,
GridBagConstraints.WEST,
GridBagConstraints.HORIZONTAL,
new Insets( INNER, INNER, 0, OUTER ),
0,
0 ) );
contentPane.add( sandWeightSlider,
new GridBagConstraints( 0,
row,
2,
1,
0.0,
0.0,
GridBagConstraints.EAST,
GridBagConstraints.HORIZONTAL,
new Insets( INNER, OUTER, 0, INNER ),
0,
0 )
);
contentPane.add( sandWeight,
new GridBagConstraints( 2,
row,
1,
1,
0.0,
0.0,
GridBagConstraints.EAST,
GridBagConstraints.HORIZONTAL,
new Insets( INNER, INNER, 0, INNER ),
0,
0 )
);
contentPane.add( sandWeightLabel,
new GridBagConstraints( 3,
row++,
1,
1,
0.0,
0.0,
GridBagConstraints.WEST,
GridBagConstraints.HORIZONTAL,
new Insets( INNER, INNER, 0, OUTER ),
0,
0 ) );
contentPane.add( specificGravitySlider,
new GridBagConstraints( 0,
row,
2,
1,
0.0,
0.0,
GridBagConstraints.EAST,
GridBagConstraints.HORIZONTAL,
new Insets( INNER, OUTER, 0, INNER ),
0,
0 )
);
contentPane.add( specificGravity,
new GridBagConstraints( 2,
row,
1,
1,
0.0,
0.0,
GridBagConstraints.EAST,
GridBagConstraints.HORIZONTAL,
new Insets( INNER, INNER, 0, INNER ),
0,
0 )
);
contentPane.add( specificGravityLabel,
new GridBagConstraints( 3,
row++,
1,
1,
0.0,
0.0,
GridBagConstraints.WEST,
GridBagConstraints.HORIZONTAL,
new Insets( INNER, INNER, 0, OUTER ),
0,
0 ) );
contentPane.add( instructions,
new GridBagConstraints( 0,
row,
4,
1,
0.0,
0.0,
GridBagConstraints.WEST,
GridBagConstraints.HORIZONTAL,
new Insets( INNER, OUTER, OUTER, OUTER ),
0,
0 )
);
}// /method
/**
* hide parts of display not relevant now
*/
void pruneDisplay()
{
switch ( shape )
{
case RECTANGULAR:
chooseRectangular.setSelected( true );
chooseCylindrical.setSelected( false );
chooseMoon.setSelected( false );
tankLengthSlider.setVisible( true );
tankLength.setVisible( true );
tankLengthLabel.setVisible( true );
tankWidthSlider.setVisible( true );
tankWidth.setVisible( true );
tankWidthLabel.setVisible( true );
tankDiameterSlider.setVisible( false );
tankDiameter.setVisible( false );
tankDiameterLabel.setVisible( false );
break;
case CYLINDRICAL:
chooseRectangular.setSelected( false );
chooseCylindrical.setSelected( true );
chooseMoon.setSelected( false );
tankLengthSlider.setVisible( false );
tankLength.setVisible( false );
tankLengthLabel.setVisible( false );
tankWidthSlider.setVisible( false );
tankWidth.setVisible( false );
tankWidthLabel.setVisible( false );
tankDiameterSlider.setVisible( true );
tankDiameter.setVisible( true );
tankDiameterLabel.setVisible( true );
break;
case MOON:
chooseRectangular.setSelected( false );
chooseCylindrical.setSelected( false );
chooseMoon.setSelected( true );
tankLengthSlider.setVisible( false );
tankLength.setVisible( false );
tankLengthLabel.setVisible( false );
tankWidthSlider.setVisible( false );
tankWidth.setVisible( false );
tankWidthLabel.setVisible( false );
tankDiameterSlider.setVisible( true );
tankDiameter.setVisible( true );
tankDiameterLabel.setVisible( true );
break;
default:
throw new IllegalArgumentException( "unexpected enum" );
}
// we want to clear fields and reset sliders. This is the easiest way to do it.
if ( metric )
{
chooseMetric();
}
else
{
chooseImperial();
}
}
/**
* Allow this Applet to run as as application as well.
*
* @param args command line arguments ignored.
*/
public static void main( String args[] )
{
HybridJ.fireup( new SandDepth(),
TITLE_STRING + " " + VERSION_STRING,
APPLET_WIDTH,
APPLET_HEIGHT );
}// /method
/**
* Called by the browser or Applet viewer to inform
* this Applet that it is being reclaimed and that it should destroy
* any resources that it has allocated.
*/
public void destroy()
{
title = null;
title2 = null;
instructions = null;
}// /method
/**
* Called by the browser or Applet viewer to inform
* this Applet that it has been loaded into the system.
*/
@Override
public void init()
{
if ( !VersionCheck.isJavaVersionOK( 1, 7, 0, this.getContentPane() ) )
{
// effectively abort
return;
}
Common18.setLaf();
Container contentPane = this.getContentPane();
contentPane.setLayout( new GridBagLayout() );
contentPane.setBackground( BACKGROUND_FOR_BODY );
buildMenu(); // also initial L&F
buildComponents();
hookListeners();
layoutComponents( contentPane );
pruneDisplay();
this.validate();
this.setVisible( true );
}// /method
// /methods
}