/* * 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; }