/*
* Get Organized - Organize your schedule, course assignments, and grades
* Copyright © 2012 Alex Laird
* getorganized@alexlaird.com
* alexlaird.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package adl.go.gui;
import adl.go.resource.LocalUtility;
import adl.go.types.Course;
import adl.go.types.Term;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.font.FontRenderContext;
import java.awt.font.LineMetrics;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.text.ParseException;
import java.util.ArrayList;
import javax.swing.JPanel;
/**
* The grades panel is shown in the dialog. It is dynamically generated with
* grade information for the currently selected term each time the UI is
* updated.
*
* @author Alex Laird
*/
public class GradesGraphPanel extends JPanel
{
/**
* The left pad.
*/
private final int LEFT_PAD = 40;
/**
* The right pad.
*/
private final int RIGHT_PAD = 20;
/**
* The upper pad.
*/
private final int UPPER_PAD = 10;
/**
* The lower pad.
*/
private final int LOWER_PAD = 20;
/**
* The average pad distance.
*/
private final int AVG_PAD = 20;
/**
* The currently selected term to display grades for.
*/
private Term currentTerm;
/**
* A reference to the utility.
*/
private LocalUtility utility;
/**
* The list of courses containing a list of each courses graded assignments.
*/
private ArrayList<Object[]> courses = new ArrayList<Object[]> ();
/**
* Retrieve the currently displayed term.
*
* @return The currently displayed term.
*/
public Term getTerm()
{
return currentTerm;
}
public void setUtility(LocalUtility utility)
{
this.utility = utility;
}
/**
* Set the currently selected term.
*
* @param currentTerm
*/
public void setTerm(Term currentTerm)
{
this.currentTerm = currentTerm;
}
/**
* Rebuild the panel with grade information pertaining to the currently
* selected term.
*
* @param g The graphics for the panel.
*/
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent (g);
if (currentTerm != null)
{
// fill the courses list with their respective values for plotting
courses.clear ();
for (int i = 0; i < currentTerm.getCourseCount (); ++i)
{
Course course = currentTerm.getCourse (i);
ArrayList<Double> plotPoints = new ArrayList<Double> ();
ArrayList<Long> dayNum = new ArrayList<Long> ();
for (int j = 0; j < course.getGradedAssignmentCount (); ++j)
{
int index = j;
try
{
while (index + 1 < course.getGradedAssignmentCount ()
&& course.getDayNumAtPoint (index) == course.getDayNumAtPoint (index + 1))
{
++index;
}
}
catch (ParseException ex)
{
Domain.LOGGER.add (ex);
}
plotPoints.add (course.calculateGradeAtPoint (index));
try
{
dayNum.add (course.getDayNumAtPoint (index));
}
catch (ParseException ex)
{
dayNum.add ((long) index);
Domain.LOGGER.add (ex);
}
}
courses.add (new Object[]
{
plotPoints, dayNum
});
}
}
// build graphics
Graphics2D g2 = (Graphics2D) g;
if (utility != null)
{
g2.setFont (utility.currentTheme.fontBold11);
g2.setRenderingHint (RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
}
// draw the vertical lines
g2.draw (new Line2D.Double (LEFT_PAD, UPPER_PAD, LEFT_PAD, getHeight () - LOWER_PAD));
g2.draw (new Line2D.Double (getWidth () - RIGHT_PAD, UPPER_PAD, getWidth () - RIGHT_PAD, getHeight () - LOWER_PAD));
// draw the horizontal lines
g2.draw (new Line2D.Double (LEFT_PAD, getHeight () - LOWER_PAD, getWidth () - RIGHT_PAD, getHeight () - LOWER_PAD));
g2.draw (new Line2D.Double (LEFT_PAD, UPPER_PAD, getWidth () - RIGHT_PAD, UPPER_PAD));
// draw labels
Font font = g2.getFont ();
FontRenderContext frc = g2.getFontRenderContext ();
LineMetrics lm = font.getLineMetrics ("0", frc);
// draw blank graph and grid lines
g2.drawString ("100%", 2, LOWER_PAD - 5);
g2.drawString ("90%", 10, LOWER_PAD - 5 + (getHeight () - UPPER_PAD - LOWER_PAD) / 10);
g2.setPaint (Color.LIGHT_GRAY);
g2.draw (new Line2D.Double (41, LOWER_PAD - 10 + ((getHeight () - UPPER_PAD - LOWER_PAD) / 10), getWidth () - RIGHT_PAD - 1, LOWER_PAD - 10 + ((getHeight () - UPPER_PAD - LOWER_PAD) / 10)));
g2.setPaint (Color.BLACK);
g2.drawString ("80%", 10, LOWER_PAD - 5 + ((getHeight () - UPPER_PAD - LOWER_PAD) / 10) * 2);
g2.setPaint (Color.LIGHT_GRAY);
g2.draw (new Line2D.Double (41, LOWER_PAD - 10 + ((getHeight () - UPPER_PAD - LOWER_PAD) / 10) * 2, getWidth () - RIGHT_PAD - 1, LOWER_PAD - 10 + ((getHeight () - UPPER_PAD - LOWER_PAD) / 10) * 2));
g2.setPaint (Color.BLACK);
g2.drawString ("70%", 10, LOWER_PAD - 5 + ((getHeight () - UPPER_PAD - LOWER_PAD) / 10) * 3);
g2.setPaint (Color.LIGHT_GRAY);
g2.draw (new Line2D.Double (41, LOWER_PAD - 10 + ((getHeight () - UPPER_PAD - LOWER_PAD) / 10) * 3, getWidth () - RIGHT_PAD - 1, LOWER_PAD - 10 + ((getHeight () - UPPER_PAD - LOWER_PAD) / 10) * 3));
g2.setPaint (Color.BLACK);
g2.drawString ("60%", 10, LOWER_PAD - 5 + ((getHeight () - UPPER_PAD - LOWER_PAD) / 10) * 4);
g2.setPaint (Color.LIGHT_GRAY);
g2.draw (new Line2D.Double (41, LOWER_PAD - 10 + ((getHeight () - UPPER_PAD - LOWER_PAD) / 10) * 4, getWidth () - RIGHT_PAD - 1, LOWER_PAD - 10 + ((getHeight () - UPPER_PAD - LOWER_PAD) / 10) * 4));
g2.setPaint (Color.BLACK);
g2.drawString ("50%", 10, LOWER_PAD - 5 + ((getHeight () - UPPER_PAD - LOWER_PAD) / 10) * 5);
g2.setPaint (Color.LIGHT_GRAY);
g2.draw (new Line2D.Double (41, LOWER_PAD - 10 + ((getHeight () - UPPER_PAD - LOWER_PAD) / 10) * 5, getWidth () - RIGHT_PAD - 1, LOWER_PAD - 10 + ((getHeight () - UPPER_PAD - LOWER_PAD) / 10) * 5));
g2.setPaint (Color.BLACK);
g2.drawString ("40%", 10, LOWER_PAD - 5 + ((getHeight () - UPPER_PAD - LOWER_PAD) / 10) * 6);
g2.setPaint (Color.LIGHT_GRAY);
g2.draw (new Line2D.Double (41, LOWER_PAD - 10 + ((getHeight () - UPPER_PAD - LOWER_PAD) / 10) * 6, getWidth () - RIGHT_PAD - 1, LOWER_PAD - 10 + ((getHeight () - UPPER_PAD - LOWER_PAD) / 10) * 6));
g2.setPaint (Color.BLACK);
g2.drawString ("30%", 10, LOWER_PAD - 5 + ((getHeight () - UPPER_PAD - LOWER_PAD) / 10) * 7);
g2.setPaint (Color.LIGHT_GRAY);
g2.draw (new Line2D.Double (41, LOWER_PAD - 10 + ((getHeight () - UPPER_PAD - LOWER_PAD) / 10) * 7, getWidth () - RIGHT_PAD - 1, LOWER_PAD - 10 + ((getHeight () - UPPER_PAD - LOWER_PAD) / 10) * 7));
g2.setPaint (Color.BLACK);
g2.drawString ("20%", 10, LOWER_PAD - 5 + ((getHeight () - UPPER_PAD - LOWER_PAD) / 10) * 8);
g2.setPaint (Color.LIGHT_GRAY);
g2.draw (new Line2D.Double (41, LOWER_PAD - 10 + ((getHeight () - UPPER_PAD - LOWER_PAD) / 10) * 8, getWidth () - RIGHT_PAD - 1, LOWER_PAD - 10 + ((getHeight () - UPPER_PAD - LOWER_PAD) / 10) * 8));
g2.setPaint (Color.BLACK);
g2.drawString ("10%", 10, LOWER_PAD - 5 + ((getHeight () - UPPER_PAD - LOWER_PAD) / 10) * 9);
g2.setPaint (Color.LIGHT_GRAY);
g2.draw (new Line2D.Double (41, LOWER_PAD - 10 + ((getHeight () - UPPER_PAD - LOWER_PAD) / 10) * 9, getWidth () - RIGHT_PAD - 1, LOWER_PAD - 10 + ((getHeight () - UPPER_PAD - LOWER_PAD) / 10) * 9));
g2.setPaint (Color.BLACK);
g2.drawString ("0%", 18, LOWER_PAD - 5 + ((getHeight () - UPPER_PAD - LOWER_PAD) / 10) * 10);
if (currentTerm != null)
{
float yBase = getHeight () - LOWER_PAD + (LOWER_PAD - lm.getAscent () + lm.getDescent ()) / 2 + lm.getAscent ();
g2.drawString (currentTerm.getStartDate (), 10, yBase);
g2.drawString (currentTerm.getEndDate (), getWidth () - (float) font.getStringBounds (currentTerm.getEndDate (), frc).getWidth () - 10, yBase);
// draw graph lines
for (int i = 0; i < courses.size (); ++i)
{
ArrayList<Double> plotPoints = (ArrayList<Double>) courses.get (i)[0];
ArrayList<Long> dayNum = (ArrayList<Long>) courses.get (i)[1];
double xInc;
try
{
xInc = (double) (getWidth () - 3 * RIGHT_PAD) / currentTerm.getDayCount ();
}
catch (ParseException ex)
{
xInc = (double) (getWidth () - 3 * RIGHT_PAD) / (plotPoints.size () - 1);
Domain.LOGGER.add (ex);
}
double scale = (double) (getHeight () - (UPPER_PAD + LOWER_PAD));
g2.setPaint (currentTerm.getCourse (i).getColor ());
// draw graph lines to each assignment point
for (int j = 0; j < plotPoints.size () - 1; ++j)
{
double x1 = LEFT_PAD + dayNum.get (j) * xInc;
double y1 = getHeight () - LOWER_PAD - scale * (plotPoints.get (j) / 100);
double x2 = LEFT_PAD + dayNum.get (j + 1) * xInc;
double y2 = getHeight () - LOWER_PAD - scale * (plotPoints.get (j + 1) / 100);
g2.draw (new Line2D.Double (x1, y1, x2, y2));
}
// mark individual assignment grade points
g2.setPaint (currentTerm.getCourse (i).getColor ());
for (int j = 0; j < plotPoints.size (); ++j)
{
double x = LEFT_PAD + dayNum.get (j) * xInc;
double y = getHeight () - LOWER_PAD - scale * (plotPoints.get (j) / 100);
g2.fill (new Ellipse2D.Double (x - 2, y - 2, 4, 4));
}
}
}
}
}