001package edu.pdx.cs410J.grader.gradebook.ui;
002
003import edu.pdx.cs410J.ParserException;
004import edu.pdx.cs410J.grader.gradebook.*;
005
006import javax.swing.*;
007import javax.swing.border.Border;
008import javax.swing.event.ListSelectionEvent;
009import javax.swing.event.ListSelectionListener;
010import java.awt.*;
011import java.awt.event.ActionEvent;
012import java.awt.event.ActionListener;
013import java.awt.event.WindowAdapter;
014import java.awt.event.WindowEvent;
015import java.io.FileNotFoundException;
016import java.io.IOException;
017import java.util.Vector;
018
019/**
020 * This panel displays and edits information about a class stored in a
021 * <code>GradeBook</code>.
022 */
023@SuppressWarnings("serial")
024public class ClassPanel extends JPanel {
025  private GradeBook book;
026
027  private JFrame frame;
028
029  // GUI components we care about
030  private JLabel classNameLabel;
031  private JList<String> assignmentsList;
032  private AssignmentPanel assignmentPanel;
033
034  private JButton newAssignmentButton;
035  private JButton newStudentButton;
036  private LetterGradeRangesPanel undergradLetterGradeRangesPanel;
037  private LetterGradeRangesPanel gradLetterGradeRangesPanel;
038
039  /**
040   * Creats a <code>ClassPanel</code> and initializes its components.
041   */
042  public ClassPanel(JFrame frame) {
043    this.frame = frame;
044    this.setLayout(new BorderLayout());
045    this.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
046
047    addClassNamePanel();
048    addAssignmentsPanel();
049    addLetterGradeRangesPanel();
050    addNewStudentPanel();
051  }
052
053  private void addLetterGradeRangesPanel() {
054    JTabbedPane letterGradeRangesPanel = new JTabbedPane();
055    Border letterGradeRangesBorder = BorderFactory.createTitledBorder("Letter Grades");
056    letterGradeRangesPanel.setBorder(letterGradeRangesBorder);
057
058    this.undergradLetterGradeRangesPanel = new LetterGradeRangesPanel(Student.Section.UNDERGRADUATE);
059    letterGradeRangesPanel.add("Undergraduate", this.undergradLetterGradeRangesPanel);
060
061    this.gradLetterGradeRangesPanel = new LetterGradeRangesPanel(Student.Section.GRADUATE);
062    letterGradeRangesPanel.add("Graduate", this.gradLetterGradeRangesPanel);
063
064    this.add(letterGradeRangesPanel, BorderLayout.EAST);
065  }
066
067  private void addAssignmentsPanel() {
068    // Set up the assignments panel
069    JPanel assignmentsPanel = new JPanel();
070    Border assignmentBorder =
071      BorderFactory.createTitledBorder("Assignments");
072    assignmentsPanel.setBorder(assignmentBorder);
073    assignmentsPanel.setLayout(new BorderLayout());
074
075    JPanel listPanel = addAssignmentsListPanel();
076    addAddAssignmentButton(listPanel);
077    assignmentsPanel.add(listPanel, BorderLayout.WEST);
078    addInfoPanel(assignmentsPanel);
079    this.add(assignmentsPanel, BorderLayout.CENTER);
080  }
081
082  private JPanel addAssignmentsListPanel() {
083    JPanel listPanel = new JPanel();
084    listPanel.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
085    listPanel.setLayout(new BorderLayout());
086
087    this.assignmentsList = new JList<>();
088    assignmentsList.addListSelectionListener(new
089      ListSelectionListener() {
090        @Override
091        public void valueChanged(ListSelectionEvent e) {
092          String name = assignmentsList.getSelectedValue();
093          if (name != null) {
094            Assignment assign = book.getAssignment(name);
095            if (assign == null) {
096              String s = "No assignment named: " + name;
097              throw new IllegalArgumentException(s);
098            }
099            displayAssignment(assign);
100          }
101        }
102      });
103    listPanel.add(new JScrollPane(this.assignmentsList),
104                  BorderLayout.CENTER);
105    return listPanel;
106  }
107
108  private void addNewStudentPanel() {
109    // Add a panel for adding a new student
110    JPanel newStudentPanel = new JPanel();
111    newStudentPanel.setLayout(new FlowLayout());
112    newStudentPanel.add(new JLabel("New Student with id:"));
113    final JTextField newStudentField = new JTextField(12);
114    newStudentPanel.add(newStudentField);
115    this.newStudentButton = new JButton("Add");
116    this.newStudentButton.setEnabled(false);
117    this.newStudentButton.addActionListener(e -> {
118      String id = newStudentField.getText();
119      if (id == null || id.equals("")) {
120        showErrorMessageDialog("No student id specified");
121        return;
122      }
123
124      if (ClassPanel.this.book == null) {
125        showErrorMessageDialog("No grade book opened");
126        return;
127      }
128
129      Student student = new Student(id);
130      ClassPanel.this.book.addStudent(student);
131      newStudentField.setText("");
132    });
133    newStudentPanel.add(this.newStudentButton);
134    this.add(newStudentPanel, BorderLayout.SOUTH);
135  }
136
137  private void addAddAssignmentButton(JPanel listPanel) {
138    this.newAssignmentButton = new JButton("Add Assignment");
139    this.newAssignmentButton.setEnabled(false);
140    this.newAssignmentButton.addActionListener(e -> {
141      createAssignment();
142    });
143    listPanel.add(this.newAssignmentButton, BorderLayout.SOUTH);
144  }
145
146  private void addInfoPanel(JPanel assignmentsPanel) {
147    JPanel infoPanel = new JPanel();
148    infoPanel.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
149    infoPanel.setLayout(new BorderLayout());
150    this.assignmentPanel = new AssignmentPanel(false);
151    infoPanel.add(assignmentPanel, BorderLayout.CENTER);
152
153    addUpdateButton(infoPanel);
154
155    assignmentsPanel.add(infoPanel, BorderLayout.CENTER);
156  }
157
158  private void addUpdateButton(JPanel infoPanel) {
159    JPanel p = new JPanel();
160    p.setLayout(new FlowLayout());
161    JButton updateButton = new JButton("Update");
162    updateButton.addActionListener(new ActionListener() {
163        @Override
164        public void actionPerformed(ActionEvent e) {
165          // Get the selected assignment and update it
166          String name = assignmentsList.getSelectedValue();
167          Assignment assign = book.getAssignment(name);
168
169          // Update currently selected assignment
170          updateAssignment(assign);
171        }
172      });
173    p.add(updateButton);
174    infoPanel.add(p, BorderLayout.SOUTH);
175  }
176
177  private void addClassNamePanel() {
178    JPanel classNamePanel = new JPanel();
179    classNamePanel.setLayout(new BoxLayout(classNamePanel,
180                                           BoxLayout.X_AXIS));
181    classNamePanel.add(Box.createHorizontalGlue());
182    this.classNameLabel = new JLabel();
183    classNamePanel.add(this.classNameLabel);
184    classNamePanel.add(Box.createHorizontalGlue());
185    this.add(classNamePanel, BorderLayout.NORTH);
186  }
187
188  private void showErrorMessageDialog(String message) {
189    JOptionPane.showMessageDialog(ClassPanel.this,
190      new String[]{message},
191      "Error",
192      JOptionPane.ERROR_MESSAGE);
193  }
194
195  /**
196   * Creates a new assignment based on the contents of the "name" and
197   * "max points" fields.  While we're at it, we add it to the
198   * assignment book and display it in the assignment list.
199   */
200  private Assignment createAssignment() {
201    NewAssignmentDialog dialog = new NewAssignmentDialog(this.frame);
202    dialog.pack();
203    dialog.setLocationRelativeTo(frame);
204    dialog.setVisible(true);
205
206    Assignment newAssign = dialog.getAssignment();
207
208    if (newAssign != null) {
209      book.addAssignment(newAssign);
210      this.displayAssignments(book);
211      this.assignmentsList.setSelectedValue(newAssign.getName(),
212                                            true);
213    }
214    return newAssign;
215  }
216
217  /**
218   * Fills in the contents of an <code>Assignment</code> based on the
219   * contents of the GUI.
220   */
221  private void updateAssignment(Assignment assign) {
222    this.assignmentPanel.updateAssignment(assign);
223  }
224
225  /**
226   * Sets the <code>GradeBook</code> that is displayed and edited in
227   * this <code>ClassPanel</code>.
228   */
229  void displayGradeBook(GradeBook book) {
230    this.book = book;
231    this.newAssignmentButton.setEnabled(true);
232    this.newStudentButton.setEnabled(true);
233
234    // Display name of the class
235    double points = 0.0d;
236    for (String name : book.getAssignmentNames()) {
237      Assignment assign = book.getAssignment( name );
238      points += assign.getPoints();
239    }
240
241    String className =
242      String.format( "%s (%d students, %.2f points)", book.getClassName(), book.getStudentIds().size(), points );
243    this.classNameLabel.setText(className);
244
245    this.displayAssignments(book);
246    this.assignmentsList.clearSelection();  // No initial selection
247
248    displayLetterGradeRanges(book);
249  }
250
251  private void displayLetterGradeRanges(GradeBook book) {
252    this.undergradLetterGradeRangesPanel.displayLetterGradeRanges(book.getLetterGradeRanges(Student.Section.UNDERGRADUATE));
253    this.gradLetterGradeRangesPanel.displayLetterGradeRanges(book.getLetterGradeRanges(Student.Section.GRADUATE));
254  }
255
256  /**
257   * Displays the names of all of the assignments in a given grade
258   * book in the assignmentsList.
259   */
260  void displayAssignments(GradeBook book) {
261    // Display all of the assignments
262    Vector<String> assignmentNames = new Vector<>();
263    assignmentNames.addAll(book.getAssignmentNames());
264    this.assignmentsList.setListData(assignmentNames);
265  }
266  
267  /**
268   * Displays an assignment in the appropriate fields
269   */
270  private void displayAssignment(Assignment assign) {
271    this.assignmentPanel.displayAssignment(assign);
272  }
273
274  /**
275   * Test program that displays and edits the information in a given
276   * grade book stored in an XML file.
277   */
278  public static void main(String[] args) {
279    if (args.length < 1) {
280      System.err.println("** usage: java ClassPanel xmlFile");
281      System.exit(1);
282    }
283
284    final String fileName = args[0];
285
286    GradeBook book = null;
287    try {
288      XmlGradeBookParser parser = new XmlGradeBookParser(fileName);
289      book = parser.parse();
290
291    } catch (FileNotFoundException ex) {
292      System.err.println("** Could not find file: " + ex.getMessage());
293      System.exit(1);
294      
295    } catch (IOException ex) {
296      System.err.println("** IOException during parsing: " + ex.getMessage());
297      System.exit(1);
298
299    } catch (ParserException ex) {
300      System.err.println("** Error during parsing: " + ex);
301      System.exit(1);
302    }
303
304    JFrame frame = new JFrame("ClassPanel test");
305    ClassPanel classPanel = new ClassPanel(frame);
306    classPanel.displayGradeBook(book);
307
308    final GradeBook theBook = book;
309    frame.addWindowListener(new WindowAdapter() {
310        @Override
311        public void windowClosing(WindowEvent e) {
312          // Write changes to grade book back to file
313          try {
314            XmlDumper dumper = new XmlDumper(fileName);
315            dumper.dump(theBook);
316
317          } catch (IOException ex) {
318            System.err.println("** Error while writing XML file: " + ex);
319          }
320
321          System.exit(1);
322        }
323      });
324
325    frame.getContentPane().add(classPanel);
326    
327    frame.pack();
328    frame.setVisible(true);
329  }
330
331}