/********************************************************
* 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: HelpAboutFrame.java
*
* Contains class:
*
* HelpAboutFrame:
*
* Purpose: To display the about information for the
* application
*
* @author Mike Reinhold
*********************************************************/
package Scheduler; //define as member of shceduler package
/*********************************************************
* Import the classes necessary to build and use the GUI
* too many to list their purposes
*********************************************************/
import io.devyse.scheduler.swing.handlers.DefaultBrowserHyperlinkListener;
import javax.swing.JEditorPane;
import javax.swing.JFrame; //this extends JFrame
import javax.swing.JPanel; //Uses numerous panels
import javax.swing.JLabel; //uses numerous labels
import javax.swing.JTabbedPane; //uses a tab pane
import javax.swing.JScrollPane; //uses a scroll pane
import javax.swing.GroupLayout; //uses a couple group layouts
import javax.swing.JButton; //uses a button
import javax.swing.UIDefaults;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import org.slf4j.ext.XLogger;
import org.slf4j.ext.XLoggerFactory;
import java.io.IOException;
import java.net.URL;
import java.util.Calendar; //for finding the year
import java.awt.BorderLayout; //border layout is used
import java.awt.Color;
import java.awt.Desktop;
import java.awt.Dimension; //for defining the frame min size
import java.util.Scanner; //for parsing the version
import java.awt.event.ActionListener; //listens for actions
import java.awt.event.ActionEvent; //the action event
import java.awt.event.KeyListener; //listens for keyboard events
import java.awt.event.KeyEvent; //the keyboard event
import java.awt.Font; //font definitions
/*********************************************************
* Class HelpAboutFrame
*
* @purpose Displays the Help -> About information
*
* @see JFrame
*********************************************************/
public class HelpAboutFrame extends JFrame {
/**
* Static logger
*/
private static XLogger logger = XLoggerFactory.getXLogger(HelpAboutFrame.class);
/********************************************************
* UPDATE SERIAL VERSION IN VERSION WHEN THIS FILE CHANGES
********************************************************/
protected final static long versionID = 2009021100070L;//file version
protected final static long serialVersionUID = 1L +
Version.helpAbout.id; //serial version
/********************************************************
* The following are the protected visual components of the frame
********************************************************/
protected JTabbedPane helpTabMain; //the tab controller
protected JScrollPane aboutScroller; //scroll manager for the file list
protected JPanel mainPanel; //the main panel for the center
protected JPanel filePanel; //the panel for the file tab
protected JPanel helpPanel; //the panel for the frame
protected GroupLayout group; //the layout for the file list
protected JLabel[] fileLabels; //array of (filname) labels for the file list
protected JLabel[] modLabels; //array of (modified date) labels for the file list
protected JLabel[] buildLabels; //array of (build number) labels for the file list
protected GroupLayout.SequentialGroup verSeq1;//file list vertical sequence 1
protected GroupLayout.SequentialGroup horizSeq1;//file list horizontal sequence 1
protected GroupLayout.ParallelGroup horizPar1;//file list horizontal parallel 1
protected GroupLayout.ParallelGroup horizPar2;//file list horizontal parallel 2
protected GroupLayout.ParallelGroup horizPar3;//file list horizontal parallel 3
protected GroupLayout.ParallelGroup[] verParGroups;//array of parallel groups for files
protected JButton ok; //button for closing the frame
protected JPanel okPanel; //panel for the ok button
protected JLabel title; //label for the title
protected JLabel version; //label for the version
protected JLabel build; //label for the build number
protected JLabel date; //label for the last modified date
protected JLabel author; //label for the author name
protected JLabel maintain; //label for the mainatiner's name
protected JLabel email; //label for maintainer's email
protected JLabel credits; //pane for the names of contributors
protected JLabel copyright; //label for copyright info
protected JEditorPane twitter; //label for the twitter account
protected JEditorPane mailingList; //label for the user group mailing list
protected JPanel generalPanel; //panel for the general info
protected JLabel ratingNotes; //pane for the rating disclaimer
protected GroupLayout generalLayout; //grouplayout for the general tab
protected JPanel titlePanel; //the panel to hold the title
protected int position; //the scroll position
protected int vertInc = Version.values().length/24 * 10;//the vertical increment
protected Dimension dim = new Dimension(550,450);//the frame dimensions
protected JLabel jvm; //label to display the jvm version
protected JEditorPane policyPanel;
/********************************************************
* @purpose create the Help -> About windows and prepare
* the data that it displays
********************************************************/
public HelpAboutFrame(){
super("About Course Scheduler"); //create frame with title
super.setIconImage(Main.icon.getImage());//set the icon
this.setResizable(false); //set not resizeable
this.setMinimumSize(dim); //set minimum size
this.setLocationRelativeTo(Main.master);//set to center of mainFrame
filePanel = new JPanel(); //make new panel for the file details
group = new GroupLayout(filePanel); //new group layout for the file details tab
filePanel.setLayout(group); //set the layout manager
horizSeq1 = group.createSequentialGroup();//create horizontal sequential group
horizPar1 = group.createParallelGroup();//create horizontal parallel group
horizPar2 = group.createParallelGroup();//create horizontal parallel group
horizPar3 = group.createParallelGroup();//create horizontal parallel group
int size = Version.values().length; //get number of classes for array size
fileLabels = new JLabel[size]; //make array for file names
modLabels = new JLabel[size]; //make array for modified dates
buildLabels = new JLabel[size]; //make array for build numbers
for(Version item: Version.values()){//for each class
int ordinal = item.ordinal(); //get the ordinal value
Scanner builder = new Scanner(item.toString());//create scanner on the class as a string
fileLabels[ordinal] = new JLabel(" File: " + builder.next() + " ");//build file name info
horizPar1.addComponent(fileLabels[ordinal]);//add componenet
modLabels[ordinal] = new JLabel("Modified: " + builder.next() + " ");//build modified date info
horizPar2.addComponent(modLabels[ordinal]);//add component
buildLabels[ordinal] = new JLabel("Build: " + builder.next() + " ");//build build number info
horizPar3.addComponent(buildLabels[ordinal]);//add component
if(ordinal % 2 == 1){
fileLabels[ordinal].setBackground(Color.white);
modLabels[ordinal].setBackground(Color.white);
buildLabels[ordinal].setBackground(Color.white);
fileLabels[ordinal].setOpaque(true);
modLabels[ordinal].setOpaque(true);
buildLabels[ordinal].setOpaque(true);
}
builder.close();
}
//horizSeq1.addGap(10); //pad 10 from the left edge
horizSeq1.addGroup(horizPar1); //add the file names
//horizSeq1.addGap(20); //pad 20 from the rightmost filename
horizSeq1.addGroup(horizPar2); //add the modified dates
//horizSeq1.addGap(20); //pad 20 from the rightmost modified date
horizSeq1.addGroup(horizPar3); //add the build numbers
group.setHorizontalGroup(horizSeq1);//set as horizontal group
verParGroups = new GroupLayout.ParallelGroup[size];//create the parallel groups
verSeq1 = group.createSequentialGroup();//create vertical sequential group
for(Version item: Version.values()){//for each class
int ordinal = item.ordinal(); //get the ordinal
verParGroups[ordinal] = group.createParallelGroup();//create a parallel group
verParGroups[ordinal].addComponent(fileLabels[ordinal]);//add the file label
verParGroups[ordinal].addComponent(modLabels[ordinal]);//add the modified label
verParGroups[ordinal].addComponent(buildLabels[ordinal]);//add the build label
verSeq1.addGap(2); //add a gap of 2
verSeq1.addGroup(verParGroups[ordinal]);//add the parallel group to the sequence
}
group.setVerticalGroup(verSeq1); //set as vertical group
group.linkSize(modLabels);
group.linkSize(buildLabels);
group.linkSize(fileLabels);
mainPanel = new JPanel(new BorderLayout());//create new panel with border layout
title = new JLabel(" Course Scheduler", Main.xlIcon, JLabel.CENTER);//set title text
title.setFont(new Font("Serif", Font.BOLD, 24));//set font
titlePanel = new JPanel(); //create new panel for title
titlePanel.add(title); //add title to the panel
mainPanel.add(titlePanel, BorderLayout.NORTH);//add panel to parent panel in NORTH sector
generalPanel = new JPanel(); //make new panel
generalLayout = new GroupLayout(generalPanel);//make grouplayout for panel
generalPanel.setLayout(generalLayout);//set layout manager
Font font = title.getFont();
StringBuffer style = new StringBuffer("font-family:" + font.getFamily() + ";");
style.append("font-weight:" + (font.isBold() ? "bold" : "normal") + ";");
style.append("font-size:" + font.getSize() + "pt;");
Color bgColor = title.getBackground();
UIDefaults defaults = new UIDefaults();
defaults.put("EditorPane[Enabled].backgroundPainter", bgColor);
twitter = new JEditorPane("text/html", "<html>Follow on Twitter: <a href=\"https://twitter.com/coursescheduler\">@coursescheduler</a></html>");
twitter.setEditable(false);
twitter.putClientProperty("Nimbus.Overrides", defaults);
twitter.putClientProperty("Nimbus.Overrides.InheritDefaults", true);
twitter.setBackground(bgColor);
twitter.addHyperlinkListener(new DefaultBrowserHyperlinkListener());
mailingList = new JEditorPane("text/html", "<html>Join the <a href=\"https://groups.google.com/d/forum/course-scheduler-user-group\">mailing list</a></html>");
mailingList.setEditable(false);
mailingList.putClientProperty("Nimbus.Overrides", defaults);
mailingList.putClientProperty("Nimbus.Overrides.InheritDefaults", true);
mailingList.setBackground(bgColor);
mailingList.addHyperlinkListener(new DefaultBrowserHyperlinkListener());
Scanner items = new Scanner(Version.version());//create scanner on the version
version = new JLabel(" Release Version: " + items.next());//get release version text
build = new JLabel(" Build Number: " + items.next());//get build number text
date = new JLabel(" Last Modified: " + Version.date());//get last modified date
author = new JLabel("Author: " + Main.author);//get author name
email = new JLabel("Email: " + Main.email);//get maintainer email
maintain = new JLabel("<html>Maintainer: " + Main.maintain + "</html>");//get maintainer name
ratingNotes = new JLabel(); //create pane for the rating comment
ratingNotes.setText("<html>Professor ratings are courtesy " +
"of ratemyprofessors.com. The ratings are " +
"not the view of the author or maintainers.</html>");//built rating comment
ratingNotes.setBackground(author.getBackground());//set background color
ratingNotes.setFont(author.getFont());//set font
items.close();
credits = new JLabel(); //make pane for credits
credits.setText("Contributers: " + Main.contributers);//set contributers text
credits.setBackground(mainPanel.getBackground());//set background color
credits.setFont(author.getFont()); //set font
Calendar calendar = Calendar.getInstance(); //make new date for today
copyright = new JLabel(" Copyright " +//build copyright string
"\u00a9" + " " + (calendar.get(Calendar.YEAR)));//with this year
jvm = new JLabel("Runtime: " + Main.jvm);
//horizontal layout definition
generalLayout.setHorizontalGroup(generalLayout.createParallelGroup()//set horizontal group to new parallel group
.addGroup(generalLayout.createSequentialGroup()//add a sequential group to the parallel group
.addGap(10) //add a gap of 10 to the sequence
.addGroup(generalLayout.createParallelGroup()//add a parallel group to the sequence
.addComponent(version) //add the version, build, and date to the parallel group
.addComponent(build)
.addComponent(date)
.addComponent(jvm)
)
.addGap(60) //add a gap of 60 to the sequence
.addGroup(generalLayout.createParallelGroup()//add a parallel group to the sequence
.addComponent(author) //add the author, email, and maintainers to the parallel group
.addComponent(email)
.addComponent(maintain)
.addComponent(twitter)
.addComponent(mailingList)
)
.addGap(60)
)
.addGroup(generalLayout.createSequentialGroup()//add another sequential group to the parallel group
.addGap(10) //add a gap of 10 to the sequence
.addGroup(generalLayout.createParallelGroup()//add a parallel group to the sequence
.addComponent(credits) //add credits, rating notes, and copyright info to the
.addComponent(ratingNotes)//parallel group
.addComponent(copyright)
)
)
);
//vertical layout definition
generalLayout.setVerticalGroup(generalLayout.createSequentialGroup()//set vertical group to new sequential group
.addGroup(generalLayout.createParallelGroup()//add a parallel group to the sequence
.addComponent(version) //add version and author to the parallel group
.addComponent(author)
)
.addGroup(generalLayout.createParallelGroup()//add another parallel group to the sequence
.addComponent(build) //add build and maintainers to the parallel group
.addComponent(maintain)
)
.addGroup(generalLayout.createParallelGroup()//add another parallel group to the sequence
.addComponent(date) //add date and email to the parallel group
.addComponent(email)
)
.addGroup(generalLayout.createSequentialGroup()
.addComponent(twitter)
.addComponent(mailingList)
)
.addComponent(jvm)
.addGap(30) //add a gap of 30 to the sequence
.addGroup(generalLayout.createSequentialGroup()//add a sequential group to the sequence
.addComponent(copyright) //add the copyright to the inner sequence
.addGap(5) //add a gap of 5 to the inner sequence
.addComponent(credits) //add the credits to the inner sequence
.addGap(5)
.addComponent(ratingNotes) //add the rating comments to the inner sequence
)
);
mainPanel.add(generalPanel, BorderLayout.CENTER);//add the general panel to the main panel center region
aboutScroller = new JScrollPane(filePanel);//add the filePanel to the scroll pane
aboutScroller.setAutoscrolls(true); //allow auto scrolling
aboutScroller.setWheelScrollingEnabled(true);//allow mouse wheel scrolling
aboutScroller.getVerticalScrollBar().setUnitIncrement(vertInc);//set the vertical increment
policyPanel = new JEditorPane();
policyPanel.setEditable(false);
URL policy = null;
try{
policy = Main.loader.getResource("Privacy.html");
}catch(Exception e){
logger.warn("Unable to load privacy policy", e);
}
try {
policyPanel.setContentType("text/html");
policyPanel.setPage(policy);
} catch (IOException e) {
logger.warn("Unable to display privacy policy", e);
}
policyPanel.addHyperlinkListener(new HyperlinkListener() {
@Override
public void hyperlinkUpdate(HyperlinkEvent hle) {
if (HyperlinkEvent.EventType.ACTIVATED.equals(hle.getEventType())) {
logger.debug("Hyperlink activated {}", hle.getURL());
Desktop desktop = Desktop.getDesktop();
try {
desktop.browse(hle.getURL().toURI());
} catch (Exception ex) {
logger.error("Unable to open hyperlink in external application", ex);
}
}
}
});
JScrollPane privacyPanel = new JScrollPane(policyPanel);
helpTabMain = new JTabbedPane(); //create the tab control structure
helpTabMain.addTab("General", mainPanel);//add the main panel as the General tab
helpTabMain.addTab("Classes", aboutScroller);//add the scroll pane (on the filePanel) as the Classes tab
helpTabMain.addTab("Privacy", privacyPanel);
helpTabMain.setMnemonicAt(0, KeyEvent.VK_G);//set the mnemonic for the general tab
helpTabMain.setMnemonicAt(1, KeyEvent.VK_C);//set the menemonic for the classes tab
helpTabMain.addKeyListener(new scrollListener());//add a key listener for the tab control to scroll the scroll pane
ok = new JButton("OK"); //create the ok button to close the form
ok.addActionListener(new okHelpListener());//add the listener
ok.setMaximumSize(new Dimension(50,20));//set max size of the button
ok.setMnemonic('O'); //set mnemonic for the button
ok.addKeyListener(new enterListener());//add listener for the enter key
okPanel = new JPanel(); //create panel for the ok button to preserve size and location
okPanel.add(ok); //add the button to the panel
helpPanel = new JPanel(); //make new panel for the whole frame
helpPanel.setLayout(new BorderLayout());//set layout to new border layout
helpPanel.add(helpTabMain, BorderLayout.CENTER);//add the tab control structure to the center region
helpPanel.add(okPanel, BorderLayout.SOUTH);//add the ok panel to the south region
this.add(helpPanel); //add the help panel to the frame
}
/********************************************************
* Class okHelpListener
*
* @purpose closes the frame when the action is seen
*
* @see ActionListener
********************************************************/
private class okHelpListener implements ActionListener{
/********************************************************
* @purpose closes the frame when the ok button sources an event
*
* @see ActionEvent, ActionListener
********************************************************/
public void actionPerformed(ActionEvent event){
Main.master.mainMenu.aboutHelpFrame.setVisible(false);//make frame invisible
HelpAboutFrame.this.helpTabMain.setSelectedIndex(0); //reset the tab index
}
}
/********************************************************
* Class enterListener
*
* @purpose listens for the enter key to be pressed and
* programmatically "clicks" the ok button to close the frame
*
* @see KeyListener
********************************************************/
private class enterListener implements KeyListener{
/********************************************************
* @purpose unused but required to implement KeyListener
*
* @see KeyListener
********************************************************/
public void keyTyped(KeyEvent event){} //not used
/********************************************************
* @purpose programatically "clicks" the ok button when
* the enter key is pressed, any other key does nothing
*
* @see KeyListener, KeyEvent
********************************************************/
public void keyPressed(KeyEvent event){
if (event.getKeyCode() == KeyEvent.VK_ENTER){//check if enter key
Main.master.mainMenu.aboutHelpFrame.ok.doClick();//do ok button click
}
}
/********************************************************
* @purpose unused but required to implement KeyListener
*
* @see KeyListener
********************************************************/
public void keyReleased(KeyEvent event){} //not used
}
/********************************************************
* Class scrollListener
*
* @purpose Allows the use of arrow keys to scroll the scroll pane
*
* @see KeyListener
********************************************************/
private class scrollListener implements KeyListener{
/********************************************************
* @purpose unused but required to implement KeyListener
*
* @see KeyListener
********************************************************/
public void keyTyped(KeyEvent event){} //not used
/********************************************************
* @purpose checks which key is pressed and appropriately
* scrolls the scroll pane
*
* @see KeyListener, KeyEvent
********************************************************/
public void keyPressed(KeyEvent event){
HelpAboutFrame current = Main.master.mainMenu.aboutHelpFrame;//get help frame
current.position = current.aboutScroller.getVerticalScrollBar().getValue();//get current position
if (event.getKeyCode() == KeyEvent.VK_UP){//check if up arrow key
current.position -= Math.max(current.vertInc, //calculate new position as the max of current - increment
current.aboutScroller.getVerticalScrollBar().getMinimum());//and the minimum value on the scroll bar
current.aboutScroller.getVerticalScrollBar().setValue(current.position);//set new position
}
else if(event.getKeyCode() == KeyEvent.VK_DOWN){//check if down arrow key
current.position += Math.min(current.vertInc, //calculate new position as the min of current + increment
current.aboutScroller.getVerticalScrollBar().getMaximum());//and the maximum valueon the scroll bar
current.aboutScroller.getVerticalScrollBar().setValue(current.position);//set the new position
}
}
/********************************************************
* @purpose unused but required to implement KeyListener
*
* @see KeyListener
********************************************************/
public void keyReleased(KeyEvent event){} //not used
}
}