/*
* TideHeight.java
* Contains the code for the creation of the user interface of Earthguide's
* Java based Tide Calendar.
*/
//package tideCalendar;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.font.*;
import java.io.*;
import java.util.GregorianCalendar;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.Timer;
import java.util.TimerTask;
/**
* TideHeight is a class whose purpose is to create the user interface for
* the scrolling view of the tide. For the implementation of the calendar
* view, see CalendarFrame.java
*
* @author Tim Bollman
* @author Earthguide
* @version 2.5 06-20-05
*/
public class TideHeight extends JPanel
{
/* Global variables*/
//The Image at the top of the Window
private ImageIcon titleImage=Shared.loadImage("new_banner.jpg", 9800, this);
// Stores the data for the phases of the moon
private Moon myMoon = new Moon();
//What time scale is currently active in the tide view window. The
//three possible values are WEEK, MONTH, and YEAR (0,1,2)
private byte currentMode = Shared.WEEK;
// Stores the data that is given to the user about the height of the line
// at a given point on the screen. The default size is 600, but that size
// changes based on the currentMode
private float[] featuredLine = new float[600];
// Stores the height of the tide as used in the graphing function at a given
// point on the screen. The default size is 600, but that size changes
// based on the currentMode
private LinkedList ylocation[] = {new LinkedList(),new LinkedList()};
// Stores the time of the tide as used in the graphing function at a given
// point on the screen. The default size is 600, but that size changes
// based on the currentMode
private LinkedList xlocation[] = {new LinkedList(),new LinkedList()};
// A variable used to ensure that the tide-line will be redrawn in
// certain useful circumstances.
private boolean rebuild[] = {false,false};
// A Queue that is used to to store the ImageIcons of the moons. The
// ImageIcon's description is the relative percentage of fullness of the
// moon
private MoonQueue moonList = new MoonQueue(9, myMoon);
// A PopupFactory to create the popups used in this file
private PopupFactory factory = PopupFactory.getSharedInstance();
// The actual panel that contains the Tide wave.
private JPanel wave;
// The labels that report to the user what lines are currently being drawn
// on the screen
private JLabel lineLabel[];
// The popup that is used to make all of the popups used in this file
private Popup popup;
// Tells the program if it is currently running in simulation mode
private byte simulating = OFF;
// Tells the program to pause the simulation for a second while data is changed.
private boolean simPaused = false;
// the generic boolean and button for whether or not the first option
// in a time mode is selected
private boolean option1 = false;
private OptionButton opButton1;
// the generic boolean and button for whether or not the second option
// in a time mode is selected
private boolean option2 = false;
private OptionButton opButton2;
// the generic boolean and button for whether or not the second option
// in a time mode is selected
private boolean option3 = false;
private OptionButton opButton3;
private Rectangle simulationRect = new Rectangle(X_OFFSET,Y_OFFSET+HEADER_HEIGHT,WAVE_WIDTH,WAVE_HEIGHT-HEADER_HEIGHT);
// The Global Timer which is used to schedule the simulation TimerTask.
private Timer timer;
//Stores the last day that was accessed for the given tideLines
private int[] lastDay = {-100,-100};
/**
* Initializes the Continuous view for Earthguide's Tide Calendar.
*
*/
public TideHeight()
{
super(new BorderLayout());
//createMarks();
add(createTitle(), BorderLayout.NORTH);
addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
popupCheck();
}
});
// create the moon line
myMoon.moonReader("moonCharts/moonChart"+Shared.year);
wave = new JPanel()
{
public void paint(Graphics g)
{
//Calls parent to clear window
super.paint(g);
simPaused = true;
// Create the "window" that the Tide will be drawn in using a
// Graphics2D object.
Graphics2D G = (Graphics2D)g;
G.translate(X_OFFSET,Y_OFFSET);
/* The "Back" buttons*/
G.setFont(Format.MODE_SELECTED_FONT);
G.drawString("Continuous",0,383);
G.setColor(Format.CAL_LINECOLOR);
G.setFont(Format.MODE_UNSELECTED_FONT);
G.drawString("Calendar",WAVE_WIDTH-55,383);
//Main view screen
G.setColor(Format.VIEWSCREEN);
G.fillRoundRect(0,0,WAVE_WIDTH,WAVE_HEIGHT,15,15);
//Header
G.setColor(Format.VIEWSCREENHEAD);
G.fill(new RoundRectangle2D.Double(0,0,WAVE_WIDTH,
HEADER_HEIGHT,10, 10));
// Bottom of Header is flush with rest of viewscreen, not rounded.
G.fill(new Rectangle(0,2*HEADER_HEIGHT/3,
WAVE_WIDTH,HEADER_HEIGHT/3));
// Outline the view screen in gray
G.setColor(Format.SEPERATOR);
G.drawRoundRect(0,0,WAVE_WIDTH,WAVE_HEIGHT,15,15);
// differentiate between the Title and the view screen with a
// gray line
G.drawLine(0,HEADER_HEIGHT,WAVE_WIDTH,HEADER_HEIGHT);
//Create the LLMT line in black
drawTickMarks(G);
// Tell the program where it is allowed to draw (the main view
// screen) through the use of a clip
G.clip(new Rectangle2D.Double(0,0,WAVE_WIDTH,WAVE_HEIGHT));
switch(currentMode)
{
case Shared.WEEK:
drawWeek(G);
G.setStroke(Format.THICKSTROKE);
break;
case Shared.MONTH:
drawMonth(G);
G.setStroke(Format.THICKSTROKE);
break;
case Shared.YEAR:
drawYear(G);
break;
}
//initialize the generalPath that stores the path of the tideLine
GeneralPath polyline;
if (Shared.parameterArray[1] != null)
{
G.setColor(Format.LINE2);
polyline = graphTide(1);
G.draw(polyline);
}
if (Shared.parameterArray[0] != null)
{
G.setColor(Format.LINE1);
polyline = graphTide(0);
G.draw(polyline);
}
/*
G.setStroke(new BasicStroke());
for (int i = 0; i < tideLocations.length;i++)
{
int x = (int)Math.round(tideLocations[i].x - Shared.offset*Shared.PIXELCONVERT);
int y = (int)Math.round(tideLocations[i].y);
if (x < 0)
continue;
if (x > 600)
break;
//System.out.println(tideLocations[i]);
G.setColor(Color.magenta);
G.drawLine(x-3,y,x+3,y);
G.drawLine(x,y-3,x,y+3);
}
*/
G.setColor(Color.black);
simPaused = false;
}
};
wave.setBackground(Format.MAIN);
// Creates a Listener to monitor the movement of the mouse over the wave window.
// This Listener will then add the height and location of the wave at that
// point to the bottom of the screen
wave.addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
popupCheck();
if (currentMode != Shared.YEAR)
{//they could be attempting to click on a month selector
Point p = e.getPoint();
Rectangle inner;
int i=0;
Rectangle outer = new Rectangle(X_OFFSET,Y_OFFSET,WAVE_WIDTH,
HEADER_HEIGHT);
if (outer.contains(p)) //clicked outside the header
{
simPaused = true;
// point is within the header, so determine the month.
// Only need to check the first 11 months because if it is not
// in the first 11 months and it is within the header, it has
// to be in the last month.
Shared.offset = 0;
for (; i < 11; i++)
{
inner = new Rectangle(X_OFFSET+i*50,Y_OFFSET,50,30);
if (inner.contains(p))
break;
Shared.offset += Shared.monthEnd[i]*24;
}
Shared.currentMonth = i;
Shared.currentDay = 1;
rebuild[0] = true;
rebuild[1] = true;
wave.paintImmediately(wave.getVisibleRect());
simPaused = false;
return;
}
outer = new Rectangle(X_OFFSET+WAVE_WIDTH-60,
Y_OFFSET+WAVE_HEIGHT,67,17);
if (outer.contains(p))
{
if (currentMode != Shared.WEEK)
modeChanger(Shared.WEEK);
else
{// in week mode already, so end simulation and set
// the moons to refresh when we return from Calendar
// View by clearing the queue.
if (option1)
opButton1.doClick();
}
if (opButton1.isDeactivated())
opButton1.activate();
if (opButton2.isDeactivated())
opButton2.activate();
opButton1.setText("Auto-Scroll: Forward");
opButton2.setText("Auto-Scroll: Back");
opButton3.setText("Lunar Cycle");
Tides.changeToCalendar();
}
}
}
});
wave.addMouseMotionListener(new MouseMotionListener()
{
private final Rectangle waveBounds =
new Rectangle(X_OFFSET,Y_OFFSET+HEADER_HEIGHT,
WAVE_WIDTH,WAVE_HEIGHT-HEADER_HEIGHT);
private final Rectangle headerBounds =
new Rectangle(X_OFFSET,Y_OFFSET,WAVE_WIDTH,HEADER_HEIGHT);
private final Rectangle calBounds =
new Rectangle(X_OFFSET+WAVE_WIDTH-60,
Y_OFFSET+WAVE_HEIGHT,67,17);
private final Rectangle contBounds =
new Rectangle (X_OFFSET-5,Y_OFFSET+WAVE_HEIGHT,90,17);
public void mouseMoved(MouseEvent e)
{
Point p = e.getPoint();
if (simulating == OFF)
{
if (waveBounds.contains(p))
{
float offset = (p.x - X_OFFSET)/Shared.PIXELCONVERT + Shared.offset;
wave.setToolTipText(Shared.parameterArray[0].getExtremas(offset));
}
else if (headerBounds.contains(p))
{
int x = (p.x - X_OFFSET)/50;
String month = Shared.monthList[x];
wave.setToolTipText("Jump to " + month);
}
else if (calBounds.contains(p))
{
wave.setToolTipText("Change to Calendar View");
}
else if (contBounds.contains(p))
{
wave.setToolTipText("Current View");
}
else
{
wave.setToolTipText(null);
}
}
}
public void mouseDragged(MouseEvent e)
{return;}
});
//Begin creation of the labels at the bottom the the screen
add(wave, BorderLayout.CENTER);
add(createBottom(), BorderLayout.SOUTH);
}
/**
* Creates the Title Panel for the Tide Screen and returns it.
*
* @return the Title JPanel
*/
private JPanel createTitle()
{
/* The actual Panel that stores the title*/
JPanel titlePanel = new JPanel()
{
public void paintComponent(Graphics g)
{
super.paintComponent(g);
titleImage.paintIcon(this, g, 0,0);
}
};
titlePanel.setPreferredSize(new Dimension(Shared.FRAME_WIDTH,
Shared.FRAME_HEIGHT/8));
return titlePanel;
}
/**
* Creates the options panel that appears on the bottom of the screen.
*
* @return the Panel that will appear on the bottom of the screen
*/
private JPanel createBottom()
{
/* The panel that will contain the options. It will be split up
* into three options categories:
* Left) Choosing which lines are being graphed
* Center) Choosing the time mode that is drawn
* Right) myriad options that the user can choose based on the time mode
*
* There will be a logical split of the Panels into 2 sections: Title and
* buttons, which will create uniformity of space usage.
*/
final JPanel bottomPanel = new JPanel(new GridBagLayout());
// Constraints for the bottom panel that tell it how to place objects
GridBagConstraints c = new GridBagConstraints();
// Intermediary container
JPanel container1;
// Intermediary container
JPanel container2;
// Intermediary container
JPanel container3;
// Strings that describe formatting
final String STDINTRO="";
final String SIZEINTRO="";
final String TITLEINTRO="";
final String END = "";
// Used for titles of the 3 sections
JLabel myLabel;
// Creates the two labels that store the lines the user can see
lineLabel = new JLabel[2];
// Sets the initial values of the two labels
lineLabel[0] = new JLabel(SIZEINTRO+"None Selected"+END,
SwingConstants.LEFT);
lineLabel[1] = new JLabel(SIZEINTRO+"None Selected"+END,
SwingConstants.LEFT);
// The labels that will store the options availible to the user
final JLabel optionLabel1 = new JLabel();
final JLabel optionLabel2 = new JLabel();
// The Buttons that are used in the center panel
final JButton mode1 = new JButton();
final JButton mode2 = new JButton();
final JButton mode3 = new JButton();
// Reusable button
JButton myButton;
// Start function body
bottomPanel.setPreferredSize(new Dimension(660,100));
bottomPanel.setBackground(Format.BOTTOM);
lineLabel[0].setFont(Format.BASICFONT);
lineLabel[1].setFont(Format.BASICFONT);
optionLabel1.setFont(Format.BASICFONT);
optionLabel2.setFont(Format.BASICFONT);
//Title Panel
container1 = new JPanel(new FlowLayout(0,0,0));
container1.setBackground(Format.BOTTOM);
// Title 1
myLabel = new JLabel(TITLEINTRO+"Location"+END, SwingConstants.LEFT);
myLabel.setFont(Format.TITLEFONT);
myLabel.setPreferredSize(new Dimension(210,30));
myLabel.setMinimumSize(new Dimension(210,30));
container1.add(myLabel);
// Title 2
myLabel = new JLabel(TITLEINTRO+"Time Window"+END, SwingConstants.LEFT);
myLabel.setFont(Format.TITLEFONT);
myLabel.setPreferredSize(new Dimension(210,30));
myLabel.setMinimumSize(new Dimension(210,30));
container1.add(myLabel);
// Title 3
myLabel = new JLabel(TITLEINTRO+"Options"+END, SwingConstants.LEFT);
myLabel.setFont(Format.TITLEFONT);
myLabel.setPreferredSize(new Dimension(150,30));
myLabel.setMinimumSize(new Dimension(150,30));
container1.add(myLabel);
c.weighty = 1.0;
c.gridwidth = GridBagConstraints.REMAINDER;
c.ipadx = 0;
c.ipady = 0;
c.insets = new Insets(0,35,3,15);
bottomPanel.add(container1, c);
// Panel 1
// Used as outermost container. It contains the 3 panels
container1 = new JPanel(new GridLayout(1,3));
container1.setBackground(Format.BOTTOM);
container1.setPreferredSize(new Dimension(660,70));
container1.setMinimumSize(new Dimension(660,70));
// Used as the middle container. It is the 3 panels
container2 = new JPanel(new GridLayout(2,1));
container2.setBackground(Format.BOTTOM);
// Used to contain portions of the Panels
container3 = new JPanel(new FlowLayout(0,5,0));
container3.setBackground(Format.BOTTOM);
// Line 1
if (Shared.parameterArray[0] != null)
{
lineLabel[0].setText("" +
Shared.parameterArray[0].getLine() +
"");
}
lineLabel[0].setPreferredSize(new Dimension(165,30));
// button that creates the popup to change the 1st line
myButton = new JButton(TITLEINTRO+"1"+END);
myButton.setFont(Format.TITLEFONT);
myButton.setBackground(Format.BOTTOM);
myButton.setBorder(Format.LOCBORDER);
myButton.setPreferredSize(new Dimension(30,30));
myButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
final Component caller = (Component)e.getSource();
Point p = bottomPanel.getLocationOnScreen();
popupCheck();
caller.setBackground(Format.MODEBUTTON);
popup = new Popup(bottomPanel,createLinePane(0),
p.x+230,p.y+5)
{
public void hide()
{
caller.setBackground(Format.BOTTOM);
super.hide();
}
};
popup.show();
}
});
container3.add(myButton);
container3.add(lineLabel[0]);
container2.add(container3);
// Line 2
container3 = new JPanel(new FlowLayout(0,5,0));
container3.setBackground(Format.BOTTOM);
if (Shared.parameterArray[1] != null)
{
lineLabel[1].setText("" +
Shared.parameterArray[1].getLine() +
"");
}
lineLabel[1].setPreferredSize(new Dimension(165,30));
// button that creates the popup to change the second line
myButton = new JButton(TITLEINTRO+"2"+END);
myButton.setFont(Format.TITLEFONT);
myButton.setBackground(Format.BOTTOM);
myButton.setBorder(Format.LOCBORDER);
myButton.setPreferredSize(new Dimension(30,30));
myButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
final Component caller = (Component)e.getSource();
Point p = bottomPanel.getLocationOnScreen();
popupCheck();
caller.setBackground(Format.MODEBUTTON);
popup = new Popup(bottomPanel,createLinePane(1),
p.x+230,p.y+5)
{
public void hide()
{
caller.setBackground(Format.BOTTOM);
super.hide();
}
};
popup.show();
}
});
container3.add(myButton);
container3.add(lineLabel[1]);
container2.add(container3);
container1.add(container2);
//Panel 2 (Center panel) 3 sections: Title, button panel, and empty space
// Container1 stores the panel as a whole and container2 stores
// the 3 buttons and the filler panel at the bottom
container2 = new JPanel(new GridLayout(3,1));
container2.setBackground(Format.BOTTOM);
//Week button
container3 = new JPanel(new FlowLayout(0,5,0));
container3.setBackground(Format.BOTTOM);
mode1.setBackground(Format.MODEBUTTON);
mode1.setPreferredSize(new Dimension(15, 15));
mode1.setBorder(Format.MODEBORDER);
myLabel = new JLabel(STDINTRO+"One Week"+END);
myLabel.setFont(Format.BASICFONT);
myLabel.addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
mode1.doClick();
}
});
mode1.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
mode1.setBackground(Format.MODEBUTTON);
mode2.setBackground(Format.BOTTOM);
mode3.setBackground(Format.BOTTOM);
modeChanger(Shared.WEEK);
opButton1.setText("Auto-Scroll: Forward");
opButton2.setText("Auto-Scroll: Back");
opButton3.setText("Lunar Cycle");
if (opButton1.isDeactivated())
opButton1.activate();
if (opButton2.isDeactivated())
opButton2.activate();
if (opButton3.isDeactivated())
opButton3.activate();
}
});
container3.add(mode1);
container3.add(myLabel);
container2.add(container3);
//Month button
container3 = new JPanel(new FlowLayout(0,5,0));
container3.setBackground(Format.BOTTOM);
mode2.setBackground(Format.BOTTOM);
mode2.setPreferredSize(new Dimension(15, 15));
mode2.setBorder(Format.MODEBORDER);
myLabel = new JLabel(STDINTRO+"One Month"+END);
myLabel.setFont(Format.BASICFONT);
myLabel.addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
mode2.doClick();
}
});
mode2.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
mode1.setBackground(Format.BOTTOM);
mode2.setBackground(Format.MODEBUTTON);
mode3.setBackground(Format.BOTTOM);
modeChanger(Shared.MONTH);
opButton1.setText("Auto-Scroll: Forward");
opButton2.setText("Auto-Scroll: Back");
opButton3.setText("Lunar Cycle");
if (opButton1.isDeactivated())
opButton1.activate();
if (opButton2.isDeactivated())
opButton2.activate();
if (opButton3.isDeactivated())
opButton3.activate();
}
});
container3.add(mode2);
container3.add(myLabel);
container2.add(container3);
//Year button
container3 = new JPanel(new FlowLayout(0,5,0));
container3.setBackground(Format.BOTTOM);
mode3.setBackground(Format.BOTTOM);
mode3.setPreferredSize(new Dimension(15, 15));
mode3.setBorder(Format.MODEBORDER);
myLabel = new JLabel(STDINTRO+"One Year"+END);
myLabel.setFont(Format.BASICFONT);
myLabel.addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
mode3.doClick();
}
});
mode3.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
Shared.offset = 0;
mode1.setBackground(Format.BOTTOM);
mode2.setBackground(Format.BOTTOM);
mode3.setBackground(Format.MODEBUTTON);
modeChanger(Shared.YEAR);
if (!opButton1.isDeactivated())
opButton1.deactivate();
if (!opButton2.isDeactivated())
opButton2.deactivate();
if (!opButton3.isDeactivated())
opButton3.deactivate();
}
});
container3.add(mode3);
container3.add(myLabel);
container2.add(container3);
container1.add(container2);
//<panel 3 (Right panel) 3 sections?: title and 2 options
// Container 1 stores the panel as a whole and container 2 stores the
// option panels
container2 = new JPanel(new GridLayout(3,1));
container3 = new JPanel(new FlowLayout(0,5,0));
container2.setBackground(Format.BOTTOM);
container3.setBackground(Format.BOTTOM);
//option 1
//Preset to off.
myLabel = new JLabel();
opButton1 = new OptionButton();
opButton1.setOnBackground(Format.OPTION_ON);
opButton1.setOffBackground(Format.OPTION_OFF);
opButton1.setBorder(Format.OPTIONBORDER);
opButton1.setLabel(myLabel);
opButton1.setTextColor(Format.BOTTOMTEXT);
opButton1.setFont(Format.BASICFONT);
opButton1.setText("Auto-Scroll: Forward");
opButton1.setBackground(Format.OPTION_OFF);
opButton1.setPreferredSize(new Dimension(15, 15));
opButton1.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
// Week or Month: Simulation
// Year: <>
simPaused = true;
switch(currentMode)
{
case Shared.WEEK:
case Shared.MONTH:
if (option2)
opButton2.doClick();
if (!option1) //simulate
doSimulation();
else //stop simulation
{
endSimulation();
while(simulating != OFF);
}
break;
case Shared.YEAR:
break;
}
option1 = !option1;
opButton1.changeState();
simPaused = false;
}
});
container3.add(opButton1);
container3.add(myLabel);
container2.add(container3);
//option 2
//preset to off
container3 = new JPanel(new FlowLayout(0,5,0));
container3.setBackground(Format.BOTTOM);
myLabel = new JLabel();
opButton2 = new OptionButton();
opButton2.setLabel(myLabel);
opButton2.setOnBackground(Format.OPTION_ON);
opButton2.setOffBackground(Format.OPTION_OFF);
opButton2.setBorder(Format.OPTIONBORDER);
opButton2.setTextColor(Format.BOTTOMTEXT);
opButton2.setFont(Format.BASICFONT);
opButton2.setText("Auto-Scroll: Back");
opButton2.setBackground(Format.OPTION_OFF);
opButton2.setPreferredSize(new Dimension(15, 15));
opButton2.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
simPaused = true;
switch (currentMode)
{
case Shared.WEEK:
case Shared.MONTH:
if (option1)
opButton1.doClick();
if (!option2) //simulate
doBackwardsSimulation();
else //stop simulation
{
endSimulation();
while(simulating != OFF);
}
case Shared.YEAR:
break;
}
option2 = !option2;
opButton2.changeState();
simPaused = false;
}
});
container3.add(opButton2);
container3.add(myLabel);
container2.add(container3);
// option 3
// preset to off
container3 = new JPanel(new FlowLayout(0,5,0));
container3.setBackground(Format.BOTTOM);
myLabel = new JLabel();
opButton3 = new OptionButton();
opButton3.setLabel(myLabel);
opButton3.setOnBackground(Format.OPTION_ON);
opButton3.setOffBackground(Format.OPTION_OFF);
opButton3.setBorder(Format.OPTIONBORDER);
opButton3.setTextColor(Format.BOTTOMTEXT);
opButton3.setFont(Format.BASICFONT);
opButton3.setText("Lunar Cycle");
opButton3.setBackground(Format.OPTION_OFF);
opButton3.setPreferredSize(new Dimension(15, 15));
opButton3.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
opButton3.changeState();
option3 = !option3;
switch (currentMode)
{
case Shared.WEEK:
case Shared.MONTH:
wave.paintImmediately(wave.getVisibleRect());
break;
case Shared.YEAR:
break;
}
}
});
container3.add(opButton3);
container3.add(myLabel);
container2.add(container3);
container1.add(container2);
c.weighty = 2.0;
c.ipadx = 0;
c.ipady = 0;
c.insets = new Insets(0,60,0,0);
bottomPanel.add(container1, c);
return bottomPanel;
}
/**
* creates the Scrollable pane that is used in the popups for the line
* selection buttons.
*
* @param num1 the line number that the button was pressed for.
* @return the scrollable panel that will be put in the popup.
*/
public JPanel createLinePane(final int num1)
{
/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
* Used variables:
* JPanel container1: the object which contains all of the line
* buttons. It is added to scroller to ensure that it does not
* exceed the space alloted for it on the screen.
* JScrollPane scroller: the Java object which adds scrollbars if needed
* based on the size of the object which contains it.
* JPanel Container2: the container which contains scroller to ensure
* that the popup will be of the correct size.
* JButton menuItem: the clickable lines that will be stored in the pane
* int size: how large to make the container so that it will be the
* the minimum possible size for how many items are stored.
* final byte LINEMOD: how many pixels to apply for each line of text
*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
// the line that the popup has not been called for
final int num2 = num1^1; //sets num2 to be 1 or 0, whatever num1 is not
// the strings that denote the RGB color that is used by this line
final String COLOR1 = "";
final String COLOR2 = "";
final String NONE = "";
final String END = "";
// Java object which adds scrollbars if needed (based on the size of the
// information stored versus the size of the viewing panel
JScrollPane scroller;
// Intermediary containers
JPanel container1, container2;
// Name of the lines that are already being drawn
String line1 = null;
String line2 = null;
// The clickable items that will be stored in the pane
JButton menuItem;
// How large to make the viewing pane vertically
int size = 0;
// How many pixels each line of text uses
final byte LINEMOD = 10;
container1 = new JPanel();
container1.setLayout(new BoxLayout(container1,BoxLayout.Y_AXIS));
container1.setBackground(Format.BOTTOM);
container2 = new JPanel(new FlowLayout(0,0,0));
container2.setPreferredSize(new Dimension(370,90));//185
container2.setBackground(Format.BOTTOM);
// if the two respective lines exist, set the line strings to store the
// location names of the lines
if (Shared.parameterArray[num1] != null)
line1 = (Shared.parameterArray[num1].getLine()).toString();
if (Shared.parameterArray[num2] != null)
line2 = (Shared.parameterArray[num2].getLine()).toString();
for (int i = 0; i < Shared.lines.length; i++)
{
// if this line is the one that is currently selected by the
// button that called this popup
if ((Shared.lines[i].toString()).equals(line1))
{
// set the button to be the color of the line
menuItem = new JButton(COLOR1+Shared.lines[i]+END);
// No action should be performed except to close popup
menuItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
popupCheck();
}
});
}
// if this line is the lines stored in the alternate button
else if ((Shared.lines[i].toString()).equals(line2))
{
// set the button to be the color of the line
menuItem = new JButton(COLOR2+Shared.lines[i]+END);
final int q = i;
// temporary storage for the names of the lines
final String lineA = line1;
final String lineB = line2;
// the action of this button is to swap the 2 lines
menuItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
Parameter temp = Shared.parameterArray[num1];
Shared.parameterArray[num1] = Shared.parameterArray[num2];
Shared.parameterArray[num2] = temp;
wave.paintImmediately(simulationRect);
lineLabel[num1].setText(COLOR1+lineB+END);
if (Shared.parameterArray[num2] == null)
lineLabel[num2].setText(NONE+"None Selected"+END);
else
lineLabel[num2].setText(COLOR2+lineA+END);
//close popup
popupCheck();
}
});
}
else //not a previously selected line
{
final int q = i;
menuItem = new JButton(NONE+Shared.lines[i]+END);
// action is to replace the selector line with the clicked line
menuItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent E)
{
// used in case the lines are shorter than the current lines
float dis1, dis2 = Float.MAX_VALUE;
// change lines
Shared.parameterArray[num1] =
Grapher.fileReader(Shared.lines[q]);
// Change text in linelabel
lineLabel[num1].setText(COLOR1+Shared.lines[q]+END);
// update view screen
wave.paintImmediately(simulationRect);
// remove popup from screen
popupCheck();
// check if we need to change the total distance
dis1 = Shared.parameterArray[num1].getTotalDistance();
if (Shared.parameterArray[num2] != null)
dis2 = Shared.parameterArray[num2].getTotalDistance();
Shared.totalDistance = (long)(Shared.PIXELCONVERT *
((dis1 < dis2)?dis1:dis2));
}
});
}
// removes border from button to make it look like a line of text
menuItem.setBackground(Format.BOTTOM);
menuItem.setBorderPainted(false);
menuItem.setMargin(new Insets(0,0,0,0));
menuItem.setPreferredSize(new Dimension(330,
((menuItem.getText()).length()/30 + 1)*12));
menuItem.addMouseListener(new MouseAdapter()
{
public void mouseEntered(MouseEvent e)
{
JButton Caller = (JButton)e.getComponent();
Caller.setBackground(Format.CAL_MONTH1);//<>
}
public void mouseExited(MouseEvent e)
{
JButton Caller = (JButton)e.getComponent();
Caller.setBackground(Format.BOTTOM);
}
});
size += ((menuItem.getText()).length()/30 + 1)*LINEMOD;
container1.add(menuItem);
}
// Cancel button
menuItem = new JButton("" +
"Cancel");
menuItem.setBackground(Format.BOTTOM);
menuItem.setBorderPainted(false);
menuItem.setMargin(new Insets(0,0,0,0));
// no action, just removes popup from screen
menuItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
popupCheck();
}
});
menuItem.addMouseListener(new MouseAdapter()
{
public void mouseEntered(MouseEvent e)
{
JButton Caller = (JButton)e.getComponent();
Caller.setBackground(Format.CAL_MONTH1);//<>
}
public void mouseExited(MouseEvent e)
{
JButton Caller = (JButton)e.getComponent();
Caller.setBackground(Format.BOTTOM);
}
});
size += ((menuItem.getText()).length()/30 + 1)*LINEMOD;
container1.add(menuItem);
// Clear button
menuItem = new JButton("" +
"Clear");
menuItem.setBackground(Format.BOTTOM);
menuItem.setMargin(new Insets(0,0,0,0));
menuItem.setBorderPainted(false);
// Action depends on which button created popup. If it was
// the first line, clear both lines from the screen, if it was the second
// line, then just clear out the contents of the second line.
menuItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
if (num1 == 0) //clear both the lines
{
Shared.parameterArray[0] = null;
lineLabel[0].setText(NONE+"None Selected"+END);
}
//else just clear the second line
Shared.parameterArray[1] = null;
lineLabel[1].setText(NONE+"None Selected"+END);
// Clear popup
popupCheck();
Shared.totalDistance = Long.MAX_VALUE;
wave.paintImmediately(simulationRect);
}
});
menuItem.addMouseListener(new MouseAdapter()
{
public void mouseEntered(MouseEvent e)
{
JButton Caller = (JButton)e.getComponent();
Caller.setBackground(Format.CAL_MONTH1);//<>
}
public void mouseExited(MouseEvent e)
{
JButton Caller = (JButton)e.getComponent();
Caller.setBackground(Format.BOTTOM);
}
});
size += ((menuItem.getText()).length()/30 + 1)*LINEMOD;
container1.add(menuItem);
container1.setPreferredSize(new Dimension(330, size));
scroller = new JScrollPane(container1);
scroller.setPreferredSize(new Dimension(370,88));
scroller.setViewportBorder(null);
scroller.setBorder(null);
scroller.setBackground(Format.BOTTOM);
JPanel topRightContainer = new JPanel(new FlowLayout(0,0,0));
JPanel header = new JPanel(new FlowLayout(0,1,1))
{
public void paint(Graphics g)
{
g.setColor(Format.LINECOLOR);
g.drawLine(0,14,352,14);
g.drawLine(352,0,352,14);
g.setColor(Format.TITLE);
g.drawString("Tide Locations",150,11);
}
};
header.setPreferredSize(new Dimension(355,15));
header.setBackground(Format.BOTTOM);
JButton cancelButton = new JButton("X");
cancelButton.setPreferredSize(new Dimension(15,15));
cancelButton.setMargin(new Insets(0,0,0,0));
cancelButton.setBorder(null);
cancelButton.setBackground(Format.BOTTOM);
cancelButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
popupCheck();
}
});
cancelButton.addMouseListener(new MouseAdapter()
{
public void mouseEntered(MouseEvent e)
{
JButton Caller = (JButton)e.getComponent();
Caller.setText("X");
}
public void mouseExited(MouseEvent e)
{
JButton Caller = (JButton)e.getComponent();
Caller.setText("X");
}
});
topRightContainer.add(cancelButton);
scroller.setCorner(JScrollPane.UPPER_RIGHT_CORNER, topRightContainer);
scroller.setColumnHeaderView(header);
container2.add(scroller);
container2.setBorder(Format.STDBORDER);
return container2;
}
/**
* Changes the Time Scale being graphed to newMode
*
* @param newMode the Time mode that the viewing screen will be changed to.
*/
private void modeChanger(byte newMode)
{
float newFactor = Shared.HrsToPxls[newMode];
float oldFactor = Shared.PIXELCONVERT;
if (oldFactor == newFactor)//no action should be taken
return;
if (option1)
opButton1.doClick();
if (option2)
opButton2.doClick();
if (option3)
opButton3.doClick();
Shared.totalDistance =(long)((Shared.totalDistance/oldFactor)*newFactor);
if (moonList != null) moonList.clear();
Shared.PIXELCONVERT = newFactor;
currentMode = newMode;
lastDay[0] = -100; lastDay[1] = -100;
rebuild[0] = rebuild[1] = true;
wave.paintImmediately(wave.getVisibleRect());
}
private void drawTickMarks(Graphics2D G)
{
// Give some Tick marks for the height of the tide
// -Every 5 tick marks should be "bolder" and have a number
// -MLLW (ft) is the name of the median line.
G.setColor(Format.MEDIAN);
//Goes through the positive tick marks.
for (int i=2*WAVE_HEIGHT/3-WAVE_AMPLITUDE, j = 1;
i > HEADER_HEIGHT; i-= WAVE_AMPLITUDE,j++)
{
if (j % 5 != 0)
G.drawLine(0,i,5,i);
else
{
G.drawLine(0,i,8,i);
G.drawString(Integer.toString(j),9,i+5);
}
}
// Draw the Mean Low Low Water indicator and a black line across
// the "middle" of the viewscreen (actually about 2/3 way down)
G.drawString("MLLW (ft)",2,2*WAVE_HEIGHT/3-2);
G.drawLine(0, 2*WAVE_HEIGHT/3,
WAVE_WIDTH,2*WAVE_HEIGHT/3);
//Goes through the negative Tick marks
for (int i=2*WAVE_HEIGHT/3+WAVE_AMPLITUDE, j = 1;
i < WAVE_HEIGHT; i+= WAVE_AMPLITUDE,j++)
{
if (j % 5 != 0)
G.drawLine(0,i,5,i);
else
{
G.drawLine(0,i,8,i);
G.drawString(Integer.toString(j),9,i+5);
}
}
}
private void drawWeek(Graphics2D G)
{
// Draw the Months in the header of the viewscreen.
G.setFont(Format.MONTH_SELECTOR_FONT);
for (int i = 0; i < 12; i++)
{
if (i != Shared.currentMonth)
G.setColor(Format.MONTH_HEADER);
else
G.setColor(Format.MONTH_HEADER_SELECTED);
G.drawString(Shared.monthShort[i], 13+i*50,
2*HEADER_HEIGHT/3);
}
// get the number of days that have passed since the
// beginning of the new year
final int totalDays = (int)(Shared.offset/24);
final int DAY_DISTANCE = (int)(Shared.PIXELCONVERT*24);
int xLoc = (int)Math.round((totalDays*24-Shared.offset)*Shared.PIXELCONVERT);
//starting with the current day, draw in day seperators.
for (int i=totalDays;i<9+totalDays;i++)
{
if (Shared.beyondYear(i))
break;
ImageIcon moonPic;
//draw a day seperator
G.setColor(Format.SEPERATOR);
G.drawLine(xLoc,HEADER_HEIGHT,xLoc,WAVE_HEIGHT);
//tell the user what day it is
G.setColor(Format.DATETEXT);
FontRenderContext frc = G.getFontRenderContext();
TextLayout text = new TextLayout(Shared.toMonth(i),
Format.DATEFONT,frc);
Rectangle2D bounds = text.getBounds();
int buffer = (int)(DAY_DISTANCE - bounds.getWidth())/2;
text.draw(G, xLoc+buffer,WAVE_HEIGHT-70);
if (option3) //if the moon should be drawn
{
//get the pertinant moon
moonPic = (ImageIcon)moonList.getMoon(i);
//paint it on screen
moonPic.paintIcon(this, G, xLoc+22,HEADER_HEIGHT+40);
//if it is a full or new moon, tell the user.
String description = moonPic.getDescription();
if (description == "Full Moon" || description == "New Moon")
{
text = new TextLayout(description,Format.DATEFONT,frc);
bounds = text.getBounds();
buffer = (int)(DAY_DISTANCE - bounds.getWidth())/2;
text.draw(G, xLoc+buffer,HEADER_HEIGHT+35);
}
}
xLoc += DAY_DISTANCE;
}
}
private void drawMonth(Graphics2D G)
{
G.setFont(Format.MONTH_SELECTOR_FONT);
for (int i = 0; i < 12; i++)
{
if (i != Shared.currentMonth)
G.setColor(Format.MONTH_HEADER);
else
G.setColor(Format.MONTH_HEADER_SELECTED);
G.drawString(Shared.monthShort[i], 13+i*50,
2*HEADER_HEIGHT/3);
}
// get the number of days that have passed since the
// beginning of the new year
final int totalDays = (int)(Shared.offset/24/7)*7;
final int DAY_DISTANCE = (int)(Shared.PIXELCONVERT*24*7);
int xLoc = (int)Math.round((totalDays*24-Shared.offset)*Shared.PIXELCONVERT);
//starting with the current day, draw in day seperators.
for (int i=totalDays;i<42+totalDays;i+=7)
{
if (Shared.beyondYear(i))
break;
ImageIcon moonPic;
//draw a day seperator
G.setColor(Format.SEPERATOR);
G.drawLine(xLoc,HEADER_HEIGHT,xLoc,WAVE_HEIGHT);
//tell the user what day it is
G.setColor(Format.DATETEXT);
FontRenderContext frc = G.getFontRenderContext();
TextLayout text = new TextLayout(Shared.toMonth(i),
Format.DATEFONT,frc);
Rectangle2D bounds = text.getBounds();
int buffer = (int)(DAY_DISTANCE - bounds.getWidth())/2;
text.draw(G, xLoc+buffer,WAVE_HEIGHT-70);
if (option3) //if the moon should be drawn
{
//get the pertinant moon
moonPic = (ImageIcon)moonList.getMoon(i);
//paint it on screen
moonPic.paintIcon(this, G, xLoc+22,HEADER_HEIGHT+40);
//if it is a full or new moon, tell the user.
String description = moonPic.getDescription();
if (description == "Full Moon" || description == "New Moon")
G.drawString(description,xLoc+22,HEADER_HEIGHT+35);
}
xLoc += DAY_DISTANCE;
}
}
private void drawYear(Graphics2D G)
{
double m = 0;
G.setColor(Format.TITLE);
G.setFont(Format.BASICFONT);
G.drawString(Shared.year + " Full Year mode",10,2*HEADER_HEIGHT/3);
G.setFont(Format.DATEFONT);
int days = 0;
int month = 0;
while(month < 12)
{
int i = (int)(days*Shared.PIXELCONVERT*24);
G.setColor(Format.SEPERATOR);
G.drawLine(i, HEADER_HEIGHT,i,WAVE_HEIGHT);
G.setColor(Format.DATETEXT);
G.drawString(Shared.monthShort[month],i+5,4*WAVE_HEIGHT/5);
days += Shared.monthEnd[month++];
}
int i = (int)(days*Shared.PIXELCONVERT*24);
G.setColor(Format.SEPERATOR);
G.drawLine(i, HEADER_HEIGHT,i,WAVE_HEIGHT);
}
/**
* Graphs the tideLine
*/
synchronized private GeneralPath graphTide(int lineNum)
{
int day = (int)(Shared.offset/24);
GeneralPath polyline = null;
ListIterator xLI, yLI;
boolean first = true;
switch (simulating)
{
case OFF:
if (day != lastDay[lineNum])
{
Grapher.graph(Shared.offset, Shared.parameterArray[lineNum],ylocation[lineNum], xlocation[lineNum], WAVE_WIDTH, 2*WAVE_HEIGHT/3, 0, WAVE_AMPLITUDE, Shared.PIXELCONVERT);
lastDay[lineNum] = day;
}
polyline = new GeneralPath(GeneralPath.WIND_EVEN_ODD,WAVE_WIDTH);
polyline.moveTo(((Integer)xlocation[lineNum].get(0)).intValue(),((Integer)ylocation[lineNum].get(0)).intValue());
xLI = xlocation[lineNum].listIterator(1);
yLI = ylocation[lineNum].listIterator(1);
while (xLI.hasNext() && yLI.hasNext())
{
int x = ((Integer)xLI.next()).intValue();
int y = ((Integer)yLI.next()).intValue();
polyline.lineTo(x,y);
}
break;
case FORWARD:
int last=0;
if (!rebuild[lineNum])
last = ((Integer)xlocation[lineNum].getLast()).intValue();
else
{
rebuild[lineNum]=false;
xlocation[lineNum].clear();ylocation[lineNum].clear();
}
if (last < WAVE_WIDTH+10)
{
LinkedList xTemp = new LinkedList();
LinkedList yTemp = new LinkedList();
Grapher.graph(Shared.offset+(last-1)/Shared.PIXELCONVERT, Shared.parameterArray[lineNum],yTemp, xTemp, WAVE_WIDTH*2-last, 2*WAVE_HEIGHT/3, last, WAVE_AMPLITUDE, Shared.PIXELCONVERT);
xlocation[lineNum].addAll(xTemp);
ylocation[lineNum].addAll(yTemp);
}
polyline = new GeneralPath(GeneralPath.WIND_EVEN_ODD,WAVE_WIDTH);
xLI = xlocation[lineNum].listIterator(0);
yLI = ylocation[lineNum].listIterator(0);
while (xLI.hasNext() && yLI.hasNext())
{
//increment the x and y values that are
int x = ((Integer)xLI.next()).intValue() - 1;
int y = ((Integer)yLI.next()).intValue();
if (x < 0)
{
xLI.remove();
yLI.remove();
}
else
{
xLI.set(new Integer(x));
if (x < WAVE_WIDTH)
{
if (first)
{
polyline.moveTo(x,y);
first = false;
}
else
polyline.lineTo(x,y);
}
}
}
break;
case BACK:
int firstLoc = WAVE_WIDTH;
if (!rebuild[lineNum])
firstLoc = ((Integer)xlocation[lineNum].getFirst()).intValue();
else
{
rebuild[lineNum]=false;
xlocation[lineNum].clear();ylocation[lineNum].clear();
}
if (firstLoc > -10)
{
LinkedList xTemp = new LinkedList();
LinkedList yTemp = new LinkedList();
Grapher.graph(Shared.offset-(WAVE_WIDTH+firstLoc)/Shared.PIXELCONVERT, Shared.parameterArray[lineNum],yTemp, xTemp, WAVE_WIDTH+firstLoc, 2*WAVE_HEIGHT/3, -WAVE_WIDTH, WAVE_AMPLITUDE, Shared.PIXELCONVERT);
xlocation[lineNum].addAll(0,xTemp);
ylocation[lineNum].addAll(0,yTemp);
}
polyline = new GeneralPath(GeneralPath.WIND_EVEN_ODD,WAVE_WIDTH);
xLI = xlocation[lineNum].listIterator(0);
yLI = ylocation[lineNum].listIterator(0);
while (xLI.hasNext() && yLI.hasNext())
{
//increment the x and y values that are
int x = ((Integer)xLI.next()).intValue() +1;
int y = ((Integer)yLI.next()).intValue();
if (x > WAVE_WIDTH)
{
xLI.remove();
yLI.remove();
}
else
{
xLI.set(new Integer(x));
if (x >= 0)
{
if (first)
{
polyline.moveTo(x,y);
first = false;
}
else
polyline.lineTo(x,y);
}
}
}
break;
}
return polyline;
}
/**
* Checks if the popup exists. If it does, it removes the popup.
*/
private void popupCheck()
{
if (popup != null)
{
popup.hide();
popup = null;
}
}
/**
* Performs the simulation of the line progressing across the screen.
*/
private void doSimulation()
{
simulating = FORWARD;
timer = new Timer(true);
TimerTask simulator = new TimerTask()
{
public void run()
{
if (!simPaused)
{
int temp = (int)(Shared.offset/24);
Shared.offset += 1/Shared.PIXELCONVERT;
//if Offset > length, break;
if (temp < (int)(Shared.offset/24))
{
Shared.currentDay++;
if (Shared.currentDay > Shared.monthEnd[Shared.currentMonth])
{
Shared.currentDay=1;
Shared.currentMonth++;
wave.paintImmediately(X_OFFSET,Y_OFFSET,WAVE_WIDTH,HEADER_HEIGHT);
}
}
wave.paintImmediately(simulationRect);
}
}
};
timer.scheduleAtFixedRate(simulator, 0, 30);
}
private void doBackwardsSimulation()
{
simulating = BACK;
timer = new Timer(true);
TimerTask simulator = new TimerTask()
{
public void run()
{
if (!simPaused)
{
int temp = (int)(Shared.offset/24);
if (Shared.offset - 1/Shared.PIXELCONVERT < 0)
{
opButton2.doClick();
return;
}
Shared.offset -= 1/Shared.PIXELCONVERT;
if (temp > (int)(Shared.offset/24))
{
Shared.currentDay--;
if (Shared.currentDay <= 0)
{
Shared.currentMonth--;
wave.paintImmediately(X_OFFSET,Y_OFFSET,WAVE_WIDTH,HEADER_HEIGHT);
if (Shared.currentMonth < 0)
{
Shared.offset += 1/Shared.PIXELCONVERT;
Shared.currentDay++;
Shared.currentMonth++;
opButton2.doClick();
}
Shared.currentDay = Shared.monthEnd[Shared.currentMonth];
}
}
wave.paintImmediately(simulationRect);
}
}
};
timer.scheduleAtFixedRate(simulator, 0, 30);
}
/**
* Ends the simulation.
*/
private void endSimulation()
{
timer.cancel();
simulating = OFF;
}
private static final int WAVE_HEIGHT = 370;
private static final int WAVE_WIDTH = 600;
private static final int HEADER_HEIGHT = 30;
//X Offset for the Calendar Viewscreen
private static final int X_OFFSET = (Shared.FRAME_WIDTH - WAVE_WIDTH)/2;
//Y Offset for the Calendar Viewscreen
private static final int Y_OFFSET =(17*Shared.FRAME_HEIGHT/24-WAVE_HEIGHT)/2;
private static final int WAVE_AMPLITUDE = 16;
private static final byte OFF = 0x00;
private static final byte ON = 0x10;
private static final byte FORWARD = 0x11;
private static final byte BACK = 0x12;
}