/********************************************************
* Copyright (C) 2008 Course Scheduler Team
*
* 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.
*
* 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, write to:
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA
********************************************************/
/*********************************************************
* Course Scheduler
* File: OptionsFrame.java
*
* Contains class:
*
* OptionsFrame:
*
* Purpose: To provide an interface for specifying the
* desired application options
*
* @author Mike Reinhold
********************************************************/
package Scheduler; //define as member of scheduler package
/*********************************************************
* The following are the necessary imports for correct function
********************************************************/
import io.devyse.scheduler.analytics.keen.KeenEngine;
import java.awt.BorderLayout; //for main frame
import java.awt.Dimension; //for frame min dimensions
import java.awt.FlowLayout; //for flow layout creation
import java.awt.event.ActionListener; //for writing action listeners
import java.awt.event.ActionEvent; //for use in the action listener
import java.awt.event.WindowEvent; //for the window events
import java.awt.event.FocusListener; //for listening for focus events
import java.awt.event.FocusEvent; //for detecting focus changes
import javax.swing.BorderFactory; //for creating frame and panel borders
import javax.swing.JFormattedTextField;
import javax.swing.JFrame; //extended by this class
import javax.swing.JPanel; //used to separate gui components
import javax.swing.JLabel; //used to label other components
import javax.swing.JCheckBox; //used extensively
import javax.swing.JTabbedPane; //for separating options logically
import javax.swing.JTextField; //for obtaining user input
import javax.swing.GroupLayout; //used to arrange the layout
import javax.swing.ButtonGroup; //used to define the radio buttons group
import javax.swing.JRadioButton; //used for specific options
import javax.swing.JButton; //used for the apply and cancel buttons
import javax.swing.JOptionPane; //used for pop-up error messages
import javax.swing.text.NumberFormatter; //used for number filtering
import java.text.DecimalFormat; //used for number filtering
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner; //used to parse strings
import java.io.File; //used for file manipulation
import java.io.FilenameFilter; //used for finding the files
/*********************************************************
* Class OptionsFrame
*
* @purpose Provides a GUI for setting the application
* preferences
*
* @see JFrame
********************************************************/
public class OptionsFrame extends JFrame {
/********************************************************
* UPDATE SERIAL VERSION IN VERSION WHEN THIS FILE CHANGES
********************************************************/
protected static final long versionID = 2013010900031L; //object version
protected static final long serialVersionUID = 1L +
Version.options.id; //serial version
/*********************************************************
* The following are protected fields for the frame
********************************************************/
protected Dimension dim = new Dimension(475,368);//the frame dimensions
protected enableListener chkBox = new enableListener();//listener for checkbox enables
protected buttonListener button = new buttonListener();//listener for button clicks
protected textFocusListener focus = new textFocusListener();//listener for focus events on text fields
protected JTabbedPane mainTabs; //tab control for the different option tabs
/*********************************************************
* The following are protected fields for the main portion of the frame
********************************************************/
protected JPanel settingsMain; //main settings Panel
protected JPanel settingsRatings; //ratings settings Panel
protected GroupLayout generalTabLayout; //main group layout
protected GroupLayout ratingsTabLayout; //group layout for ratings
protected JCheckBox enableRatings; //to enable ratings
protected JCheckBox enableUGrad; //to enable undergraduate courses
protected JCheckBox overrideURL; //to enable URL override
protected JCheckBox overrideSID; //to enable SID ovverride
protected JTextField newURL; //enter the new url
protected JTextField newSID; //enter the new SID
protected JCheckBox enableCampusGrad; //to enable on campus grad courses
protected JCheckBox enableDistGrad; //to enable distance grad courses
protected JCheckBox enableOverrideGrad; //to enable URL override
protected JCheckBox enableOverrideDist; //to enable URL override
protected JTextField newGradURL; //to input new URL
protected JTextField newDistURL; //to input new URL
protected JTextField newCampus; //to input new on campus course download url
protected JTextField newDist; //to input new distance course download url
protected JCheckBox analyticsOptOut; //to option out of the analytics reporting
protected JLabel connectionTimeoutLabel;//label for the connection timeout text field
protected JTextField connectionTimeout; //for configuration of the socket connection timeout
/*********************************************************
* The following are the protected fields for the button portion
********************************************************/
protected JButton apply; //to apply the settings
protected JButton cancel; //to cancel the settings
protected JButton clear; //to clear the downloaded data
protected JPanel buttonPanel; //panel for the buttons
/*********************************************************
* The following are the protected fields for the ratings panel
********************************************************/
protected JPanel ratings; //for the ratings
protected JCheckBox enableRMPRatings; //for rate my professor ratings
protected JFormattedTextField maxGap; //for the max gap between classes
protected JLabel maxGapLbl; //to label to the max gap
protected JFormattedTextField minGap; //for the min gap between classes
protected JLabel minGapLbl; //to label the min gap
protected JCheckBox dayOff; //to enable a day off
protected JLabel startTime; //labels the start time field
protected JFormattedTextField start; //the start time field
protected JLabel endTime; //labels the end time field
protected JFormattedTextField end; //the end time field
protected GroupLayout ratingsLayout; //the layout for the ratings panel
/*********************************************************
* The following are the protected fields for the days panel
********************************************************/
protected JPanel daysOff; //to frame to hold the days off
protected JRadioButton monday; //to specify the day off
protected JRadioButton tuesday; //to specify the day off
protected JRadioButton wednesday; //to specify the day off
protected JRadioButton thursday; //to specify the day off
protected JRadioButton friday; //to specify the day off
protected ButtonGroup daysGroup; //to force single day off
protected GroupLayout daysLayout; //the layout for the days off panel
/*********************************************************
* THe following are protected fields within the option pane
********************************************************/
protected boolean isHidden; //a boolean for deciding when to update the components
/*********************************************************
* The following are private constants for the layout
********************************************************/
private static int marginSpace = 20; //the margin space between the edge of the frame and a control
private static int horizSpace = 5; //the horizontal space between controls
/*********************************************************
* (Constructor)
*
* @purpose Creates and sets up a new options frame
********************************************************/
public OptionsFrame(){
super("Settings"); //create a frame with the title Settings
super.setIconImage(Main.icon.getImage());//set the icon
setLayout(new BorderLayout()); //set the main frame layout
setMinimumSize(dim); //set the minimum size
setLocationRelativeTo(Main.master); //set to display in the middle of the master
setResizable(false); //do not allow resizing of the fram
settingsMain = new JPanel(); //create new panel for whole frame
generalTabLayout = new GroupLayout(settingsMain);//create grouplayout to manage the frame
settingsMain.setLayout(generalTabLayout);//set the layout manager
settingsRatings = new JPanel(); //create the panel
ratingsTabLayout = new GroupLayout(settingsRatings);//create the layout
settingsRatings.setLayout(ratingsTabLayout);//set the layout
//no longer relevant
enableUGrad = new JCheckBox("Enable Undergraduate Courses");//create the checkbox
enableUGrad.addActionListener(chkBox); //add the listener
enableUGrad.setMnemonic('U'); //set the mnemonic
enableUGrad.setToolTipText("Enable downloading of undergraduate courses.");
enableUGrad.setVisible(false);
enableRatings = new JCheckBox("Enable Ratings");//instantiate the checkbox
enableRatings.addActionListener(chkBox);//add this frame's check box listener
enableRatings.setMnemonic('E'); //set the mnemonic
enableRatings.setToolTipText("<html>Set if the courses " +
"and schedules are rated by the below settings.<br>" +
"These settings only alter the order in which schedules" +
" are dislayed.</html>"); //set the tool tip text
enableCampusGrad = new JCheckBox("Enable On-Campus Grad Courses");//create the checkbox
enableCampusGrad.addActionListener(chkBox);//add listener
enableCampusGrad.setMnemonic('m'); //set mnemonic
enableCampusGrad.setToolTipText("Enable downloading of on-campus graduate courses.");
enableCampusGrad.setVisible(false);
enableOverrideGrad = new JCheckBox("Enable On-Campus Download URL Override");//create checkbox
enableOverrideGrad.addActionListener(chkBox);//add listener
enableOverrideGrad.setMnemonic('n'); //set mnemonic
enableOverrideGrad.setToolTipText("Override the default download URL for on-campus courses.");
enableOverrideGrad.setVisible(false);
enableDistGrad = new JCheckBox("Enable Distance Grad Courses");//create the checkbox
enableDistGrad.addActionListener(chkBox);//add listener
enableDistGrad.setMnemonic('D'); //set mnemonic
enableDistGrad.setToolTipText("Enable downloading of distance learning graduate courses.");
enableDistGrad.setVisible(false);
enableOverrideDist = new JCheckBox("Enable Distance Download URL Override");//create checkbox
enableOverrideDist.addActionListener(chkBox);//add listener
enableOverrideDist.setMnemonic('i'); //set mnemonic
enableOverrideDist.setToolTipText("Override the default download URL for distance learning courses.");
enableOverrideDist.setVisible(false);
overrideURL = new JCheckBox("Enable Course Download URL Override");//create new checkbox
overrideURL.addActionListener(chkBox); //add this frames checkbox listener
overrideURL.setMnemonic('o'); //set the mnemonic for the checkbox
overrideURL.setToolTipText("Set if the default URL for downloading course " +
"information should be overridden with the specified URL.");
//set the tooltip
overrideSID = new JCheckBox("Enable Rate My Professer School ID Override");//instantiate
overrideSID.addActionListener(chkBox); //add this frame's checkbox listener
overrideSID.setMnemonic('R'); //set the mnemonic for the checkbox
overrideSID.setToolTipText("Set if the default School ID should be overridden" +
" with the specified School ID."); //set the tool tip
analyticsOptOut = new JCheckBox("Opt out of anonymous data collection");
analyticsOptOut.addActionListener(chkBox);
analyticsOptOut.setMnemonic('a');
analyticsOptOut.setToolTipText("Do not perform anonymous data collection");
connectionTimeout = new JFormattedTextField(NumberFormat.getIntegerInstance());
connectionTimeout.setToolTipText("The amount of time in milliseconds the network socket should be allowed to stay open while waiting for a connection or read response.");
connectionTimeout.setVisible(true);
connectionTimeoutLabel = new JLabel("Socket connection timeout (ms) for slow servers");
connectionTimeoutLabel.setDisplayedMnemonic('S');
connectionTimeoutLabel.setLabelFor(connectionTimeout);
newURL = new JTextField(20); //create the text field with min size
newSID = new JTextField(20); //create the text field with min size
newCampus = new JTextField(20); //create text field with min size
newDist = new JTextField(20); //create text field with min size
newURL.addFocusListener(focus); //add a focus listener for focus modifications
newSID.addFocusListener(focus); //add a focus listener for focus modifications
newCampus.addFocusListener(focus); //add focus listener
newDist.addFocusListener(focus); //add focus listener
newURL.setToolTipText("The URL to download course information from.");//add tool tips
newSID.setToolTipText("The School ID used by Rate My Professor.");//add tool tips
newCampus.setToolTipText("The URL to download on-campus graduate courses from.");
newDist.setToolTipText("The URL to download distance learning graduate courses from.");
buildRatingsPanel(); //build the ratings panel
apply = new JButton("Apply"); //make the apply button
apply.addActionListener(button); //add this frame's generic button listener
apply.setMnemonic('A'); //set the mnemonic
apply.setToolTipText("Apply the current settings and close the window.");//set tool tip
cancel = new JButton("Cancel"); //make the cancel button
cancel.addActionListener(button); //add this frame's generic button listener
cancel.setMnemonic('C'); //set the mnemonic
cancel.setToolTipText("Cancel the current changes to the settings and close the window.");//set tool tip
clear = new JButton("Clear Data"); //make the clear button
clear.addActionListener(button); //add this frame's generic button listener
clear.setMnemonic('l'); //set the mnemonic
clear.setToolTipText("Clear the application's data cache");//set tool tip
buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 10));//new panel with flow layout and spaces
buttonPanel.add(apply); //add the apply button first to the panel
buttonPanel.add(cancel); //add the cancel button second
buttonPanel.add(clear); //add the clear button third
generalTabLayout.setHorizontalGroup(generalTabLayout.createParallelGroup()//make the sequential groups defined below all horiz parallel
//.addGroup(generalTabLayout.createSequentialGroup()//create sequential group
// .addGap(2 * horizSpace) //add gap in front of component
// .addComponent(enableUGrad) //add the enable checkbox
//)
.addGroup(generalTabLayout.createSequentialGroup()//first sequential group parallel to the others
.addGap(2 * horizSpace) //add twice the horiz space
.addComponent(overrideURL) //add the override URL check box
.addGap(horizSpace) //add the horizontal space
.addComponent(newURL) //add the url input field
.addGap(2 * horizSpace) //add space to ensure a gap on the irhgt
)
.addGroup(generalTabLayout.createSequentialGroup()//second sequential group parallel to the others
.addGap(2 * horizSpace) //add twice the horiz space
.addComponent(overrideSID) //add the override sid check box
.addGap(horizSpace) //add the horiz space
.addComponent(newSID) //add the sid input field
.addGap(2 * horizSpace) //add space to ensure gap on the right
)
.addGroup(generalTabLayout.createSequentialGroup()
.addGap(2 * horizSpace)
.addComponent(connectionTimeoutLabel)
.addGap(horizSpace)
.addComponent(connectionTimeout)
.addGap(2 * horizSpace)
)
.addGroup(generalTabLayout.createSequentialGroup()
.addGap(2 * horizSpace)
.addComponent(analyticsOptOut)
.addGap(2*horizSpace)
)
//.addGroup(generalTabLayout.createSequentialGroup()//create sequential group
// .addGap(2 * horizSpace) //add gap in front of component
// .addComponent(enableCampusGrad) //add enable checkbox
//)
//.addGroup(generalTabLayout.createSequentialGroup()//create sequential group
// .addGap(2 * horizSpace) //add gap in front of component
// .addComponent(enableOverrideGrad)//add override checkbox
// .addGap(horizSpace) //add space between components
// .addComponent(newCampus) //add text field
// .addGap(2 * horizSpace) //add space
//)
//.addGroup(generalTabLayout.createSequentialGroup()//create sequential group
// .addGap(2 * horizSpace) //add space before component
// .addComponent(enableDistGrad) //add enable checkbox
//)
//.addGroup(generalTabLayout.createSequentialGroup()//create sequential group
// .addGap(2 * horizSpace) //add space before component
// .addComponent(enableOverrideDist)//add override checkbox
// .addGap(horizSpace) //add space between components
// .addComponent(newDist) //add text field
// .addGap(2 * horizSpace) //add space after
//)
);
generalTabLayout.setVerticalGroup(generalTabLayout.createSequentialGroup()//create vertical sequence of items
.addGap(2 * horizSpace) //add gap before new row
.addGroup(generalTabLayout.createParallelGroup()//create new row of items
.addComponent(overrideSID)//add the override sid check box
.addComponent(newSID)//add the sid input field
)
.addGap(2 * horizSpace) //add vertical space for margin
//.addComponent(enableUGrad) //add enable checkbox
.addGap(2 * horizSpace) //add space between components
.addGroup(generalTabLayout.createParallelGroup()//add a row of items
.addComponent(overrideURL)//add the override url check box
.addComponent(newURL)//add the url input field
)
.addGap(horizSpace)
.addGroup(generalTabLayout.createParallelGroup()
.addComponent(connectionTimeoutLabel)
.addComponent(connectionTimeout)
)
.addGap(2 * horizSpace)
.addComponent(analyticsOptOut)
//.addGap(2 * horizSpace) //add gap
.addGap(36 * horizSpace) //add space
//.addComponent(enableCampusGrad) //add enable checkbox
//.addGap(2 * horizSpace) //add gap
//.addGroup(generalTabLayout.createParallelGroup()//add parallel group
// .addComponent(enableOverrideGrad)//add override checkbox
// .addComponent(newCampus)//add text field
//)
//.addGap(2 * horizSpace) //add space
//.addComponent(enableDistGrad) //add enable checkbox
//.addGap(2 * horizSpace) //add space
//.addGroup(generalTabLayout.createParallelGroup()//add parallel group
// .addComponent(enableOverrideDist)//add override checkbox
// .addComponent(newDist)//add text field
//)
//.addGap((int)(1.5 * horizSpace)) //add a gap of floor(1.5 * horizSpace) before next row
);
ratingsTabLayout.setHorizontalGroup(ratingsTabLayout.createParallelGroup()
.addGroup(ratingsTabLayout.createSequentialGroup()//third sequential group parallel to the others
.addGap(2 * horizSpace) //add twice the horiz space
.addComponent(enableRatings) //add the ratings enable check box
)
.addGroup(ratingsTabLayout.createSequentialGroup()//fourth sequential group parallel to the others
.addComponent(ratings) //add the ratings panel
)
);
ratingsTabLayout.setVerticalGroup(ratingsTabLayout.createSequentialGroup()
.addGap(2 * horizSpace) //add gap before next sequential item
.addComponent(enableRatings) //add the enable ratings check box, own row
.addGap(2 * horizSpace) //add gap before ratings panel
.addComponent(ratings) //add the ratings panel, own row
.addGap((int)(1.5 * horizSpace)) //add a gap of floor(1.5 * horizSpace) before next row
);
mainTabs = new JTabbedPane(); //create tabbed pane
mainTabs.addTab("General", settingsMain);//add general tab
mainTabs.addTab("Ratings", settingsRatings);//add ratings tab
add(mainTabs, BorderLayout.CENTER); //put the settings main panel in the frame
add(buttonPanel, BorderLayout.SOUTH); //put the buttons in the pane
updateSettingsFrame(); //update the frame's fields
isHidden = true; //set that the frame is hidden
enableEvents(WindowEvent.WINDOW_ACTIVATED);//enable window activated event to manage directly
this.pack();
}
/*********************************************************
* @purpose To provide a way to update the frame when necessary
* by adding onto the super's processWindowEvent method by
* doing what we need to do and then calling the super's
* processWindowEvent method
*
* @param WindowEvent event: the triggered window event
********************************************************/
@Override
protected void processWindowEvent(WindowEvent event){
if(event.getID() == WindowEvent.WINDOW_ACTIVATED && isHidden){//check if the frame should update contents
updateSettingsFrame(); //only update when the frame was hidden and now is not
isHidden = false; //update frame then set to not hidden
}
super.processWindowEvent(event); //allow super to process event
}
/*********************************************************
* @purpose To update the frames component values based
* on Main.prefs
********************************************************/
public void updateSettingsFrame(){
Preferences prefs = Main.prefs; //get main's prefs
enableUGrad.setSelected(prefs.isDownloadUGrad());//set if under grad is enabled
overrideURL.setSelected(prefs.isOverRideURL());//set the checkbox value
newURL.setText(prefs.getURL()); //set url text
newURL.setEnabled(prefs.isOverRideURL());//set if the url field is enabled
overrideSID.setSelected(prefs.isOverRideSID());//set the checkbox value
newSID.setText(prefs.getSID()); //set the sid text
newSID.setEnabled(prefs.isOverRideSID());//set if the sid field is enabled
connectionTimeout.setText(NumberFormat.getIntegerInstance().format(prefs.getConnectionTimeout()));
enableCampusGrad.setSelected(prefs.isDownloadGrad());//set if on campus grad courses are downloaded
enableDistGrad.setSelected(prefs.isDownloadGradDist());//set if off campus grad courses are downloaded
enableOverrideDist.setSelected(prefs.isOverrideGradDist());//set if off campus override url should be used
enableOverrideGrad.setSelected(prefs.isOverrideGrad());//set if on campus override url should be used
newCampus.setText(prefs.getGradURL());//set the url text
newCampus.setEnabled(prefs.isOverrideGrad());//set the url enable
newDist.setText(prefs.getGradDistURL());//set the url text
newDist.setEnabled(prefs.isOverrideGradDist());//set the url enable
analyticsOptOut.setSelected(prefs.isAnalyticsOptOut());
enableRatings.setSelected(prefs.isRatingsEnabled());//set if ratings enabled
enableRatings(prefs.isRatingsEnabled());//set the ratings panel's enabled state
enableRMPRatings.setSelected(prefs.isRateMyProfessorEnabled());//set the rmp enable checkbox
boolean off = prefs.hasDayOff(); //make bool array for the selected day off
dayOff.setSelected(off); //set checkbox status
enableDays(off && prefs.isRatingsEnabled());//set the enabled state for the days panel
boolean monBool = prefs.getDaysOff()[Day.monday.value()];//get monday bool
boolean tueBool = prefs.getDaysOff()[Day.tuesday.value()];//get tuesday bool
boolean wedBool = prefs.getDaysOff()[Day.wednesday.value()];//get wednesday bool
boolean thuBool = prefs.getDaysOff()[Day.thursday.value()];//get thursday bool
boolean friBool = prefs.getDaysOff()[Day.friday.value()];//get friday bool
monday.setSelected(monBool); //set option button value
tuesday.setSelected(tueBool); //set option button value
wednesday.setSelected(wedBool); //set option button value
thursday.setSelected(thuBool); //set option button value
friday.setSelected(friBool); //set option button value
if (!(monBool || tueBool || wedBool || thuBool || friBool)){//check if none should be selected
daysGroup.clearSelection(); //set cleared if none should be selected
}
minGap.setText(Integer.toString((int)prefs.getShortestBreak()));//set min gap text
maxGap.setText(Integer.toString((int)prefs.getLongestBreak()));//set max gap text
minGap.setText((minGap.getText().compareTo("0.0") == Compare.equal.value()) ? "1.0" : minGap.getText());//fix the min gap text if necessary
maxGap.setText((maxGap.getText().compareTo("0.0") == Compare.equal.value()) ? "1.0" : maxGap.getText());//fix the max gap text if necessary
start.setText(prefs.getPreferred().getStartTime().toString());//set start time
end.setText(prefs.getPreferred().getEndTime().toString());//set end time
start.setText((start.getText().compareTo("0:00") == Compare.equal.value()) ? "8:00" : start.getText());//fix the start time text if necessary
end.setText((end.getText().compareTo("0:00") == Compare.equal.value()) ? "6:00" : end.getText());//fix the end time text if necessary
}
/*********************************************************
* @purpose Enable or disable the days frame as specified
*
* @param boolean val: if the controls should be enabled
********************************************************/
public void enableDays(boolean val){
monday.setEnabled(val); //set if the following controls are
tuesday.setEnabled(val); //enabled based on the parameter
wednesday.setEnabled(val);
thursday.setEnabled(val);
friday.setEnabled(val);
}
/*********************************************************
* @purpose Enable or disable the ratings panel as specified
*
* @param boolean val: if the controls should be enabled
********************************************************/
public void enableRatings(boolean val){ //set if the following controls are enabled
minGap.setEnabled(val); //based on the parameter
maxGap.setEnabled(val);
start.setEnabled(val);
end.setEnabled(val);
enableRMPRatings.setEnabled(val);
dayOff.setEnabled(val);
ratings.setEnabled(val);
maxGapLbl.setEnabled(val);
minGapLbl.setEnabled(val);
startTime.setEnabled(val);
endTime.setEnabled(val);
if(val && dayOff.isSelected()){ //special case for the days panel
enableDays(true); //which is inside the ratings panel
} //must also be enabled in the checkbox
else{
enableDays(false);
}
}
/*********************************************************
* @purpose Builds the ratings panel and laysout its components
********************************************************/
public void buildRatingsPanel(){
ratings = new JPanel(); //create the panel
ratings.setBorder(BorderFactory.createTitledBorder("Ratings"));//add the titled border
ratingsLayout = new GroupLayout(ratings);//create the layout manager
ratings.setLayout(ratingsLayout); //set the layout manager
minGapLbl = new JLabel("Minimum Gap: ");//create the text label
minGap = new JFormattedTextField(new NumberFormatter(new DecimalFormat("0"))); //create the text fields
minGap.setToolTipText("The minimum number of minutes preferred " +
"between classes."); //set the tool tip
minGapLbl.setToolTipText(minGap.getToolTipText());//set tool tip
//minGap.addFocusListener(focus); //add form's focus listener
maxGapLbl = new JLabel("Maximum Gap: ");//create the text label
maxGap = new JFormattedTextField(minGap.getFormatter());//create the text field
maxGap.setToolTipText("The maximum number of minutes preferred " +
"between classes."); //set tool tip text
maxGapLbl.setToolTipText(maxGap.getToolTipText());//set tool tip
maxGap.addFocusListener(focus); //add form's focus listener
startTime = new JLabel("Earliest Start Time: ");//create new text label
//try {
start = new JFormattedTextField();//new MaskFormatter("*#:##"));
//((MaskFormatter)start.getFormatter()).setInvalidCharacters("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
//} catch (ParseException e) {
// e.printStackTrace();
//}
start.setToolTipText("The preferred start time in the morning.");//set tool tip
startTime.setToolTipText(start.getToolTipText());//set tool tip
start.addFocusListener(focus); //add form's focus listener
endTime = new JLabel("Latest End Time: ");//create new text label
end = new JFormattedTextField(); //create text field
end.setToolTipText("The preferred end time in the afternoon.");//set tool tip
endTime.setToolTipText(end.getToolTipText());//set tool tip
end.addFocusListener(focus); //add form's focus listener
dayOff = new JCheckBox("Enable Preferred Day Off");//create new checkbox
dayOff.addActionListener(chkBox); //add form's action listener for chk boxes
dayOff.setMnemonic('P'); //set control's mnemonic
dayOff.setToolTipText("Set the preferred day for no classes " +
"if possible."); //set tool tip
daysOff = new JPanel(); //new panel for the days off
daysOff.setBorder(BorderFactory.createTitledBorder(""));//set the titled border
daysOff.setToolTipText(dayOff.getToolTipText());//set the tool tip text
daysLayout = new GroupLayout(daysOff); //create a group layout for the days panel
daysOff.setLayout(daysLayout); //set the layout
monday = new JRadioButton("Monday"); //create the days off radio buttons
tuesday = new JRadioButton("Tuesday"); //create the days off radio buttons
wednesday = new JRadioButton("Wednesday");//create the days off radio buttons
thursday = new JRadioButton("Thursday");//create the days off radio buttons
friday = new JRadioButton("Friday"); //create the days off radio buttons
monday.setToolTipText(dayOff.getToolTipText());//set the days tool tip text
tuesday.setToolTipText(dayOff.getToolTipText());//set the days tool tip text
wednesday.setToolTipText(dayOff.getToolTipText());//set the days tool tip text
thursday.setToolTipText(dayOff.getToolTipText());//set the days tool tip text
friday.setToolTipText(dayOff.getToolTipText());//set the days tool tip text
monday.setMnemonic('n'); //set the days mnemonic
tuesday.setMnemonic('T'); //set the days mnemonic
wednesday.setMnemonic('W'); //set the days mnemonic
thursday.setMnemonic('h'); //set the days mnemonic
friday.setMnemonic('F'); //set the days mnemonic
daysGroup = new ButtonGroup(); //create button group for the days
daysGroup.add(monday); //add the days to the button group
daysGroup.add(tuesday); //add the days to the button group
daysGroup.add(wednesday); //add the days to the button group
daysGroup.add(thursday); //add the days to the button group
daysGroup.add(friday); //add the days to the button group
enableRMPRatings = new JCheckBox("Enable Rate My Professor Ratings");//create new check box
enableRMPRatings.setMnemonic('M'); //set mnemonic
enableRMPRatings.setToolTipText("Set if professor ratings should" +
" be downloaded from ratemyprofessors.com");//set tool tip
//for the days panel
daysLayout.setHorizontalGroup(daysLayout.createSequentialGroup()//make a single horiz sequential group
.addComponent(monday) //add the days to the horizontal layout sequentially
.addComponent(tuesday) //add the days to the horizontal layout sequentially
.addComponent(wednesday) //add the days to the horizontal layout sequentially
.addComponent(thursday) //add the days to the horizontal layout sequentially
.addComponent(friday) //add the days to the horizontal layout sequentially
);
daysLayout.setVerticalGroup(daysLayout.createSequentialGroup()//make a single horiz group for the vertical layout
.addGroup(daysLayout.createParallelGroup()//create a parallel row for the vertical layout as theonly row
.addComponent(monday) //add the days to the vertical layout in parallel
.addComponent(tuesday) //add the days to the vertical layout in parallel
.addComponent(wednesday) //add the days to the vertical layout in parallel
.addComponent(thursday) //add the days to the vertical layout in parallel
.addComponent(friday) //add the days to the vertical layout in parallel
)
);
//for the ratings panel
ratingsLayout.setHorizontalGroup(ratingsLayout.createParallelGroup()//create a single parallel horiz column
.addGroup(ratingsLayout.createSequentialGroup()//make a sequential group of columns within that col
.addGroup(ratingsLayout.createParallelGroup()//make a parallel group within that group of columns
.addGroup(ratingsLayout.createSequentialGroup()//add these items to the first column
.addGap(marginSpace) //add item to the first column
.addComponent(minGapLbl)//add item to the first column
.addGap(horizSpace) //add item to the first column
.addComponent(minGap)//add item to the first column
)
.addGroup(ratingsLayout.createSequentialGroup()//create second group of sequential items for first column
.addGap(marginSpace) //add item to the first column
.addComponent(startTime)//add item to the first column
.addGap(horizSpace) //add item to the first column
.addComponent(start)//add item to the first column
)
)
.addGap(2 * horizSpace) //add space between columns
.addGroup(ratingsLayout.createParallelGroup()//create parallel group for second column
.addGroup(ratingsLayout.createSequentialGroup()//first group of items inside the column
.addGap(marginSpace) //add item to the second column
.addComponent(maxGapLbl)//add item to the second column
.addGap(horizSpace) //add item to the second column
.addComponent(maxGap)//add item to the second column
)
.addGroup(ratingsLayout.createSequentialGroup()//second group of items to add to the second column
.addGap(marginSpace) //add item to the second column
.addComponent(endTime) //add item to the second column
.addGap(horizSpace) //add item to the second column
.addComponent(end)//add item to the second column
)
)
)
.addGroup(ratingsLayout.createSequentialGroup()//add sequential group in the main column
.addGap(marginSpace) //add item to the column
.addComponent(dayOff) //add item to the column
)
.addGroup(ratingsLayout.createSequentialGroup()//create a new sequential group in the main column
.addGap(marginSpace - marginSpace/3)//add item to the column
.addComponent(daysOff) //add item to the column
.addGap(marginSpace) //add item to the column
)
.addGroup(ratingsLayout.createSequentialGroup()//create new sequential group in the main column
.addGap(marginSpace) //add item to the column
.addComponent(enableRMPRatings) //add item to the column
)
);
ratingsLayout.setVerticalGroup(ratingsLayout.createSequentialGroup()//create sequential vertical group for rows
.addGroup(ratingsLayout.createParallelGroup(GroupLayout.Alignment.BASELINE)//create first row
.addComponent(minGapLbl)//add item to row specifying size
.addComponent(minGap)//add item to row specifying size
.addComponent(maxGapLbl)//add item to row specifying size
.addComponent(maxGap)//add item to row specifying size
)
.addGap(2 * horizSpace) //add gap between rows
.addGroup(ratingsLayout.createParallelGroup(GroupLayout.Alignment.BASELINE)//create second row
.addComponent(startTime)//add item to row specifying size
.addComponent(start)//add item to row specifying size
.addComponent(endTime)//add item to row specifying size
.addComponent(end) //add item to row specifying size
)
.addGap(3 * horizSpace) //add gap between rows
.addComponent(dayOff) //add single item row
.addComponent(daysOff) //add single item row
.addGap(3 * horizSpace) //add gap between rows
.addComponent(enableRMPRatings) //add single item row
);
}
private void registerConfigEvent(){
Map<String, Object> configEvent = new HashMap<>();
configEvent.put("config.analytics.enabled", Main.prefs.isAnalyticsOptOut());
configEvent.put("config.term.current", Main.prefs.getCurrentTerm());
configEvent.put("config.course.graduate.distance.url", Main.prefs.getGradDistURL());
configEvent.put("config.course.graduate.campus.url", Main.prefs.getGradURL());
configEvent.put("config.course.undergraduate.url", Main.prefs.getURL());
configEvent.put("config.course.graduate.enabled", Main.prefs.isDownloadGrad());
configEvent.put("config.course.graduate.distance.enabled", Main.prefs.isDownloadGradDist());
configEvent.put("config.course.graduate.campus.enabled", Main.prefs.isDownloadGrad());
configEvent.put("config.course.undergraduate.enabled", Main.prefs.isDownloadUGrad());
configEvent.put("config.schedule.limit", Main.prefs.getGreyCodeLimit());
configEvent.put("config.course.graduate.campus.override", Main.prefs.isOverrideGrad());
configEvent.put("config.course.graduate.distance.override", Main.prefs.isOverrideGradDist());
configEvent.put("config.course.undergraduate.override", Main.prefs.isOverRideURL());
configEvent.put("config.rating.ratemyprofessor.override", Main.prefs.isOverRideSID());
configEvent.put("config.rating.ratemyprofessor.enabled", Main.prefs.isRateMyProfessorEnabled());
configEvent.put("config.rating.enabled", Main.prefs.isRatingsEnabled());
configEvent.put("config.rating.schedule.break.max", Main.prefs.getLongestBreak());
configEvent.put("config.rating.schedule.break.min", Main.prefs.getShortestBreak());
configEvent.put("config.rating.schedule.days.enabled", Main.prefs.hasDayOff());
configEvent.put("config.rating.ratemyprofessor.sid", Main.prefs.getSID());
configEvent.put("config.course.staleness.max", Main.prefs.getUpdateMin());
for(Day day : Day.values()){
configEvent.put("config.rating.schedule.days." + day.toString(), Main.prefs.getDaysOff()[day.ordinal()]);
}
KeenEngine.getDefaultKeenEngine().registerEvent(Main.KEEN_CONFIG, configEvent);
}
/*********************************************************
* @purpose To enable and disable items based on the status
* of the associated checkboxes
*
* @see ActionListener
********************************************************/
private class enableListener implements ActionListener{
/*********************************************************
* @pupose To enable or disable the appropriate sections
* based on the triggering checkbox
*
* @param ActionEvent: the triggering ActionEvent
*
* @see ActionListener
********************************************************/
public void actionPerformed(ActionEvent event){
Object source = event.getSource(); //get the sourcing object
if(source.equals(enableRatings)){ //check if is enableRatings checkbox
enableRatings(enableRatings.isSelected());//set the ratings group's enabled status
if (enableRatings.isSelected()){//if it is enabled
minGap.requestFocusInWindow();//set the focus
}
}
else if(source.equals(overrideURL)){//check if the overrideURL checkbox triggered the event
newURL.setEnabled(overrideURL.isSelected());//set the newURL text field's enable status
if (overrideURL.isSelected()){ //if it is enabled
newURL.requestFocusInWindow();//set the focus to the new URL text field
}
}
else if(source.equals(overrideSID)){//check if the overrideSID checkbox triggered the event
newSID.setEnabled(overrideSID.isSelected());//set the newSID text field's enable status
if (overrideSID.isSelected()){ //if it is enabled
newSID.requestFocusInWindow();//set the focus to the new SID text field
}
}
else if(source.equals(dayOff)){ //check if the dayOff checkbox triggered the event
enableDays(dayOff.isSelected());//set the enable for the group
}
else if(source.equals(enableOverrideGrad)){//check if on campus override triggered
newCampus.setEnabled(enableOverrideGrad.isSelected());//enable/disable the text field
if (enableOverrideGrad.isSelected()){//if became selected
newCampus.requestFocusInWindow();//focus in window
}
}
else if(source.equals(enableOverrideDist)){//check if off campus override triggered
newDist.setEnabled(enableOverrideDist.isSelected());//enable/disable the text field
if (enableOverrideDist.isSelected()){//if became selectged
newDist.requestFocusInWindow(); //request focus
}
}
}
}
/*********************************************************
* @purpose Provides a single interface that handles all the button events
*
* @see ActionListener
********************************************************/
private class buttonListener implements ActionListener{
/*********************************************************
* @purpose Provides a single method for handling button events
*
* @param ActionEvent event: the triggered event
*
* @see ActionListener
********************************************************/
public void actionPerformed(ActionEvent event){
Object source = event.getSource(); //get the source of teh event
boolean updateRMP = false; //boolean for downloading rmp
Number timeout;
if(source.equals(apply)){ //check if the source is the apply button
Preferences prefs = Main.prefs; //get the main form preferences
double min = 0; //create and initialize a double for the min gap
double max = 0; //create and initialize a double for the max gap
boolean cont = true; //create and initialise a boolean for error checking
Period preferred = new Period();//create a new preferred period
try {
timeout = NumberFormat.getIntegerInstance().parse(connectionTimeout.getText());
} catch (ParseException e) {
timeout = 45000;
}
if (enableRatings.isSelected()){
try{ //try to catch exceptions
min = Double.parseDouble(minGap.getText() + ".0");//get the mingap val as a double
if(min == 0){ //check for bounds errors with 0
min = 1; //fix bound error to 1
}
}
catch(Exception ex){ //if invalid input
JOptionPane.showMessageDialog(//show invalid input dialog
Main.master.mainMenu.optionsFrame,
"Invalid value for minimum preferred gap between classes.",
"Invalid Entry", JOptionPane.ERROR_MESSAGE);
cont = false; //set continuation to false
minGap.requestFocusInWindow();//request focus in mingap
}
try{ //try to catch exceptions
max = Double.parseDouble(maxGap.getText() + ".0");//get max gap value as a double
if(max == 0){ //check for bounds error when 0
max = 1; //correct bound error
}
}
catch(Exception ex){ //catch invalid input
JOptionPane.showMessageDialog(//show invalid input dialog
Main.master.mainMenu.optionsFrame,
"Invalid value for maximum preferred gap between classes.",
"Invalid Entry", JOptionPane.ERROR_MESSAGE);
if (cont){ //check if focus should vbe set
maxGap.requestFocusInWindow();//set the focus to maxGap
}
cont = false; //set to no continuation
}
try{ //try to catch errors
String period = start.getText() + " am - " + end.getText();//make new period string
Scanner parse = new Scanner(start.getText());//parse the end text for hour
parse.useDelimiter(":"); //using the ":" delimiter
int hour = parse.nextInt(); //get the hour
hour = (hour == 12) ? 0 : hour;//correct hour if necessary
if (hour > 12 || hour < 1){ //check for invalid 12 hour time hour
throw new Exception(); //throw exception if invalid
}
parse = new Scanner(end.getText());//parse the end text for hour
parse.useDelimiter(":"); //using the ":" delimiter
hour = parse.nextInt(); //get the hour
hour = (hour == 12) ? 0 : hour;//correct hour if necessary
if (hour > 12 || hour < 1){ //check for invalid 12 hour time hour
throw new Exception(); //throw exception if invalid
}
period += (hour <= 8) ? " pm" : " am";//append the am/pm portion
if (!preferred.setPeriod(period)){//check if setting the period fails
throw new Exception(); //if fails, throw exception
}
}
catch (Exception ex){ //if invalid input for period string
JOptionPane.showMessageDialog(//show invalid input dialog
Main.master.mainMenu.optionsFrame,
"Invalid value for preferred start or end time.",
"Invalid Entry", JOptionPane.ERROR_MESSAGE);
if (cont){ //check if focus should be set
start.requestFocusInWindow();//set focus to the start time
}
cont = false; //set continuation to false
}
if (daysGroup.getSelection() == null && dayOff.isSelected()){//check if no day off specified
JOptionPane.showMessageDialog(//show invalid day dialog
Main.master.mainMenu.optionsFrame,
"Please select a preferred day off.",
"Invalid Entry", JOptionPane.ERROR_MESSAGE);
if (cont){ //check if focus should be set
monday.requestFocusInWindow();//set focus to monday
}
cont = false; //set continuation to false
}
}
else{
min = 1; //set back to default
max = 480; //set back to default
}
if(!(enableUGrad.isSelected() || enableCampusGrad.isSelected() || enableDistGrad.isSelected())){
cont = false;
JOptionPane.showMessageDialog(Main.master.mainMenu.optionsFrame,
"Please select at least one type of courses to download (ie. Undergrad, On-Campus Graduate, Distance Learning Graduate).",
"Invalid Entry", JOptionPane.ERROR_MESSAGE);
}
if (cont){ //check if should continue, ie. no errors
prefs.setDayOff(dayOff.isSelected());//set the day off boolean
boolean[] days = new boolean[Day.values().length];//make bool array for daysOff
days[Day.monday.value()] = monday.isSelected();//get if day off
days[Day.tuesday.value()] = tuesday.isSelected();//get if day off
days[Day.wednesday.value()] = wednesday.isSelected();//get if day off
days[Day.thursday.value()] = thursday.isSelected();//get if day off
days[Day.friday.value()] = friday.isSelected();//get if day off
prefs.setDaysOff(days); //set the days off array
updateRMP = !prefs.isRateMyProfessorEnabled() && enableRMPRatings.isSelected();
prefs.setRateMyProfessorEnabled(enableRMPRatings.isSelected());//set if RMP is enabled
prefs.setRatingsEnabled(enableRatings.isSelected());//set if ratings are enabled
prefs.setDownloadUGrad(enableUGrad.isSelected());//set the download undergrad
prefs.setOverRideURL(overrideURL.isSelected());//set the url override
prefs.setURL(newURL.getText()); //set the url override value
prefs.setConnectionTimeout(timeout.intValue());
prefs.setOverRideSID(overrideSID.isSelected());//set the sid override
prefs.setSID(newSID.getText()); //set the sid override value
prefs.setDownloadGrad(enableCampusGrad.isSelected());//set the enable
prefs.setDownloadGradDist(enableDistGrad.isSelected());//set the enable
prefs.setOverrideGrad(enableOverrideGrad.isSelected());//set the override
prefs.setOverrideGradDist(enableOverrideDist.isSelected());//set the override
prefs.setGradURL(newCampus.getText());//set the url
prefs.setGradDistURL(newDist.getText());//set the url
prefs.setAnalyticsOptOut(analyticsOptOut.isSelected());
prefs.setShortestBreak(min); //set the shortest break
prefs.setLongestBreak(max); //set the longest break
prefs.setPreferred(preferred); //set the preferred period
updateSettingsFrame(); //update the settings frame
if(Main.prefs.save()){ //try saving and if successful
JOptionPane.showMessageDialog(Main.master.mainMenu.optionsFrame,
"Settings successfully applied!",//display success
"Success", JOptionPane.INFORMATION_MESSAGE);
Main.master.mainMenu.optionsFrame.setVisible(false);//make frame invisible
isHidden = true; //set hidden indicator to true
registerConfigEvent();
if (updateRMP){ //check if rmp values need to be updated
Main.master.setEnabled(false);//disable the main frame
Main.updateAllForRMP(); //update all open tabs and the main if necessary
}
Main.master.requestFocusInWindow();//request focus in the master
}
else{
JOptionPane.showMessageDialog(Main.master.mainMenu.optionsFrame,
"Preferences were applied, but were unable to be saved.",//display partial error
"Apply Success, Store Failure", JOptionPane.ERROR_MESSAGE);
}
Main.reRateAll();
}
}
else if(source.equals(cancel)){ //if the cancel
Main.master.mainMenu.optionsFrame.setVisible(false);
isHidden = true; //set the hidden field
Main.master.requestFocusInWindow();//request focus in the master
}
else if(source.equals(clear)){
int result = JOptionPane.showConfirmDialog(Main.master.mainMenu.optionsFrame,
"Are you sure you want to delete all downloaded course files" +
" and all generated schedules?");//get confirmation
if (result == JOptionPane.YES_OPTION){//if confirmed
boolean success = true;
try{
File dir = new File(Main.dataPath);//get the directory
FilenameFilter filter = new FilenameFilter() {//make filter so only the right
public boolean accept(File dir, String name) {//files are deleted
return (name.endsWith(Main.databaseExt) || //specifically, database files
name.endsWith(Main.scheduleExt)); //and schedule files
}
};
File[] children = dir.listFiles(filter);//array of matching items
//keep track of success for all or at least one failure
for(File item: children){ //for each file
success = item.delete() ? success : false;//delete it and keep track of sucess or at least single failure
}
}
catch(Exception ex){
success = false;
}
if (success){ //display complete success
JOptionPane.showMessageDialog(Main.master.mainMenu.optionsFrame,
"All cache items were successfully deleted.");
}
else{ //display at least single failure
JOptionPane.showMessageDialog(Main.master.mainMenu.optionsFrame,
"Some cache items were not deleted.");
}
}
}
}
}
/*********************************************************
* @purpose Selects all the text inside a text field when it
* gains focus
*
* @see FocusListener
********************************************************/
private class textFocusListener implements FocusListener{
/*********************************************************
* @purpose Selects all of the text inside the text field that
* triggered this event by gaining focus
*
* @param FocusEvent event: the triggering event
*
* @see FocusListener
********************************************************/
public void focusGained(FocusEvent event){
((JTextField)event.getSource()).selectAll();//select all textfield text
}
/*********************************************************
* @purpose Unused in this listener, place holder only for
* FocusListener
*
* @param FocusEvent event: the triggering event
*
* @see FocusListener
********************************************************/
public void focusLost(FocusEvent event){}
}
}