550 lines
26 KiB
Plaintext
550 lines
26 KiB
Plaintext
CS1322 -- Fall 2002 -- Program 5
|
|
--------------------------------
|
|
|
|
Title: Graphical Sort Demo
|
|
Assigned: October 30, 2002
|
|
Phase 1 Instant Feedback Due: November 6, 2002 11:59:59
|
|
Project Due: November 11, 2002 11:59:59
|
|
|
|
*** NOTE: THIS FILE IS BEST VIEWED WITH AN EDITOR THAT CONTAINS
|
|
80 CHARACTER COLUMNS PER ROW
|
|
|
|
Files Provided
|
|
========================================================================
|
|
|
|
o P5.nfo.txt - this file
|
|
o SortBarPanel.class - A pluggable JPanel widget that
|
|
displays an array of Integers
|
|
graphically
|
|
|
|
Introduction
|
|
========================================================================
|
|
|
|
Welcome to the fifth programming assignment. By the end of this assignment
|
|
you should feel comfortable with several concepts, including:
|
|
|
|
o Graphical User Interfaces
|
|
o Event Driven Programming
|
|
o Model-View-Controller
|
|
|
|
|
|
Project Overview
|
|
========================================================================
|
|
|
|
Welcome to the 5th Programming Assignment!
|
|
Your job for this program will be to create a Graphical User Interface
|
|
that performs a variety of sorting algorithms on an array of Integers and
|
|
displays the algorithms' progress using a series of bars of assorted length
|
|
and color. There will be one bar per array index. The length and color of
|
|
the bar will correspond to the value of the array cell. The means of drawing
|
|
the bars is provided to you through a pluggable class that extends JPanel.
|
|
Your GUI will need to demonstrate the following sorting algorithms:
|
|
Bubble Sort, Insertion Sort, Quick Sort, and Merge Sort.
|
|
|
|
A visual guideline of what you'll be making can be found at:
|
|
|
|
http://128.61.63.158/cs1322/P5
|
|
|
|
|
|
|
|
PHASE I: The ArrayWrapper class - Instant Feedback
|
|
========================================================================
|
|
|
|
If you liked wrapper classes like Integer, Double, etc., you'll love this
|
|
class. Just as Integer is a wrapper for int, providing useful utilities for
|
|
use with ints (such as converting a String to an int), the ArrayWrapper class
|
|
will wrap an array of Comparables and provide various operations for that
|
|
array. One of the key features your ArrayWrapper class will provide is the
|
|
ability to notify other classes if the array inside the ArrayWrapper has been
|
|
altered. This is our segway into Events and Event Driven Programming.
|
|
|
|
Events
|
|
-------------------------------
|
|
|
|
The Object Oriented features of Java provide a unique way for classes to
|
|
communicate with each other (besides calling methods on each other). Special
|
|
classes called "Events" can be sent between other classes. Java provides a
|
|
series of Event classes as part of its API that can transmit a variety of
|
|
different signals including "the mouse was clicked" (MouseEvent), "a key was
|
|
pressed" (KeyEvent), "something has changed" (ChangeEvent), as well as others.
|
|
You can also code your own Events which can signal whatever you wish depending
|
|
on what's appropriate to the problem you are working on. Associated with each
|
|
Event is a Listener interface. So MouseListener corresponds to MouseEvent.
|
|
Any class that wants to receive an Event must implement the corresponding
|
|
Listener interface. When a class implements MouseListener, it must contain
|
|
the following methods: mouseClicked(MouseEvent e), mouseEntered(MouseEvent e),
|
|
mouseExited(MouseEvent e), mousePressed(MouseEvent e), and
|
|
mouseReleased(MouseEvent e). This methods are called by the object where the
|
|
event originated, usually from a button or clickable component in the case
|
|
of MouseEvents. We say the event was "fired" which basically means the
|
|
firing component instantiated a new MouseEvent, iterated through all of the
|
|
listeners registered to the component and called the appropriate listener
|
|
method on each listener, passing it the instantiated MouseEvent. In the
|
|
case of MouseEvents being fired by an button, for example, you typically do
|
|
not code the firing mechanism. This is provided for you in the widget
|
|
(button) code that already exists. You merely handle the listening end.
|
|
There are cases (this assignment is one of them) where you will handle
|
|
the registering of listeners and firing of events yourself.
|
|
|
|
So what does it mean to register a listener? In the case of MouseListeners,
|
|
anything that fires MouseEvents has two methods:
|
|
addMouseListener(MouseListener ml), and removeMouseListener(MouseListener ml).
|
|
When you execute a line of code such as:
|
|
|
|
<Firing Object>.addMouseListener(<object implementing MouseListener>);
|
|
|
|
the objects are communitcating like so:
|
|
|
|
Calling Object: "Hey Firing Object, I want you to notify this here
|
|
MouseListener whenever you fire a MouseEvent."
|
|
Firing Object: "Okay, no problem. I'll add it to my Vector of objects
|
|
to notify."
|
|
|
|
One of the five appropriate MouseListener methods is then called on all
|
|
registered listeners whenever needed.
|
|
|
|
-------------------------------
|
|
|
|
|
|
Create a class called "ArrayWrapper". This class will contain an
|
|
array of Comparables and provide some utilities for manipulating this
|
|
array. Your class should have the following:
|
|
|
|
o a private instance variable named "compArray" of type
|
|
Comparable[]
|
|
o a private instance variable named "listeners" of type Vector.
|
|
This variable will hold all the listeners of this ArrayWrapper to
|
|
be notified if the array changes.
|
|
o a constructor that takes in an array of Comparables and sets
|
|
the compArray instance variable to this parameter
|
|
|
|
|
|
Your ArrayWrapper class will be firing a ChangeEvent every time the
|
|
compArray variable is altered. A ChangeEvent is an extremely simple
|
|
event. It connotates a very generic meaning: "Something has
|
|
changed."
|
|
|
|
o a public method called addChangeListener(ChangeListener cl) which
|
|
will register the passed in listener to be notified of
|
|
ChangeEvents. It should only register the listener if it has not
|
|
already been registered. (We don't want any listener being
|
|
notified twice at the same time.)
|
|
|
|
o a public method called removeChangeListener(ChangeListener cl)
|
|
which will unregister this listener from this ArrayWrapper. That
|
|
listener should no longer be notified when firing ChangeEvents.
|
|
|
|
o a private method called fireChangeEvent() which notify all
|
|
registered listeners by calling the appropriate method. Check
|
|
out the API to see what the method is.
|
|
|
|
|
|
Your class should provide several utilities for use with arrays.
|
|
|
|
o a public method called swap(int i, int j) which will swap
|
|
the contents of the array at index i and index j. If an
|
|
exception is thrown, this method should print an appropriate
|
|
error message and make no modifications to the array.
|
|
|
|
o a public method called get(int i) which returns a Comparable
|
|
that returns the contents of the array at index i. If an
|
|
exception is thrown this method should print an appropriate error
|
|
message and return null.
|
|
|
|
o a public method called set(int i, Comparable value) which
|
|
sets the contents of the array. If an exception is thrown, this
|
|
method should print an appropriate error message and make no
|
|
modification to the array.
|
|
|
|
o a public method called compare(int i, int j) which will return
|
|
the result of a compareTo operation on the contents at index i
|
|
and index j. If an exception is thrown, this method should print
|
|
an appropriate error message and return 0.
|
|
|
|
o a public static method called getRandomIntegerArray(int size)
|
|
and returns Integer[]. This method will create an array of the
|
|
size specified and assign Integers with random values between 0
|
|
and (size - 1) to the contents of the array. These values should
|
|
be exclusive so any one value should not appear twice in the
|
|
array. (This random array is what you'll be sorting)
|
|
|
|
|
|
Don't forget: Any time compArray is altered, you will need to notify
|
|
all listeners. Keep this in mind when coding the above methods.
|
|
|
|
|
|
PHASE II: The Sorting Algorithms - Instant Feedback
|
|
========================================================================
|
|
|
|
You will be creating four sorting algorithms that can sort the contents
|
|
of an ArrayWrapper: BubbleSort, InsertionSort, QuickSort, and MergeSort.
|
|
For each sort, you must adhere to the algorithm or you receive no credit.
|
|
|
|
All the sorting algorithms will be placed in their own class with the
|
|
appropriate name: BubbleSort, InsertionSort, QuickSort, and MergeSort.
|
|
Since they are all going to be sorting and they are all going to be sorting
|
|
an ArrayWrapper, this seems like an idea case for the use of an abstract
|
|
class.
|
|
|
|
Create an abstract class called AbstractSort. This class will be a
|
|
parent class for all your sorting classes. Include the following in
|
|
AbstractSort:
|
|
|
|
o a protected instance variable called arrayWrap of type
|
|
ArrayWrapper. This will be the ArrayWrapper instance containing
|
|
the array you are sorting.
|
|
|
|
o a constructor which takes in an ArrayWrapper parameter and sets
|
|
arrayWrap to this parameter.
|
|
o accessor and modifier for arrayWrap
|
|
o a public abstract method called doSort() which returns void.
|
|
This method will be specified in the subclasses and will actually
|
|
perform the respective sorting algorithm on the arrayWrap
|
|
instance variable.
|
|
|
|
|
|
Create a class called BubbleSort which extends AbstractSort. This
|
|
class will sort (in ascending order) the contents of an ArrayWrapper
|
|
instance using bubble sort. BubbleSort should include the following:
|
|
|
|
o a constructor which takes in an ArrayWrapper parameter and
|
|
chains to the respective parent constructor.
|
|
o a public method called doSort() which will sort the array of
|
|
Comparables inside arrayWrap using the bubble sort algorithm.
|
|
You may only manipulate the array of Comparables through the
|
|
methods provided by ArrayWrapper. We do this so that when the
|
|
array is changed, arrayWrap can fire events to notify its
|
|
listeners that it has changed. The algorithm should sort in
|
|
ascending order.
|
|
|
|
Create a class called InsertionSort which extends AbstractSort. This
|
|
class will sort (in ascending order) the contents of an ArrayWrapper
|
|
instance using insertion sort. InsertionSort should include the following:
|
|
|
|
o a constructor which takes in an ArrayWrapper parameter and
|
|
chains to the respective parent constructor.
|
|
o a public method called doSort() which will sort the array of
|
|
Comparables inside arrayWrap using the insertion sort algorithm.
|
|
You may only manipulate the array of Comparables through the
|
|
methods provided by ArrayWrapper. We do this so that when the
|
|
array is changed, arrayWrap can fire events to notify its
|
|
listeners that it has changed. The algorithm should sort in
|
|
ascending order.
|
|
|
|
Create a class called MergeSort which extends AbstractSort. This
|
|
class will sort (in ascending order) the contents of an ArrayWrapper
|
|
instance using merge sort. MergeSort should include the following:
|
|
|
|
o a constructor which takes in an ArrayWrapper parameter and
|
|
chains to the respective parent constructor.
|
|
o a public method called doSort() which will sort the array of
|
|
Comparables inside arrayWrap using the merge sort algorithm.
|
|
You may only manipulate the array of Comparables through the
|
|
methods provided by ArrayWrapper. We do this so that when the
|
|
array is changed, arrayWrap can fire events to notify its
|
|
listeners that it has changed. The algorithm should sort in
|
|
ascending order.
|
|
|
|
Create a class called QuickSort which extends AbstractSort. This
|
|
class will sort (in ascending order) the contents of an ArrayWrapper
|
|
instance using quick sort. QuickSort should include the following:
|
|
|
|
o a constructor which takes in an ArrayWrapper parameter and
|
|
chains to the respective parent constructor.
|
|
o a public method called doSort() which will sort the array of
|
|
Comparables inside arrayWrap using the quick sort algorithm.
|
|
You may only manipulate the array of Comparables through the
|
|
methods provided by ArrayWrapper. We do this so that when the
|
|
array is changed, arrayWrap can fire events to notify its
|
|
listeners that it has changed. The algorithm should sort in
|
|
ascending order.
|
|
|
|
|
|
|
|
PHASE III - The GUI
|
|
========================================================================
|
|
|
|
You will need to create a Graphical User Interface (GUI) to interact
|
|
with this program. This is radically different from the typical way
|
|
we've interfaced with programs - text menus and prompts in a console.
|
|
A Graphical User Interface utilizes buttons, mouse clicks, drop down
|
|
menus, window widgets, etc to receive and display information to a
|
|
user. See the following for all your GUI learning needs:
|
|
|
|
http://www.cc.gatech.edu/classes/AY2003/cs1322_fall/
|
|
test_resources/current/study.html#GUIpieces
|
|
|
|
------------------------------------------------
|
|
|
|
Provided to you is a class called SortBarPanel which extends JPanel.
|
|
This widget can display an array of sixty Integers (the wrapper class Integer)
|
|
graphically as a horizontal bar graph. The length of each bar
|
|
corresponds to the value inside the array. When sorted ascendingly,
|
|
this bar graph will look like stair steps. This class has a default
|
|
constructor and a method:
|
|
|
|
public void display(Integer[] array)
|
|
|
|
which redraws the bar graph according to the array parameter.
|
|
|
|
------------------------------------------------
|
|
|
|
You will need a JFrame to display contain and display all the GUI
|
|
components. Create a class called "SortFrame" that extends JFrame.
|
|
This class will extend JFrame because it "is-a" JFrame. It will have
|
|
a "has-a" relationship to all the components to be contained within
|
|
the JFrame. This means that these components will be instance
|
|
variables of SortFrame. The components of SortFrame will incude a
|
|
JMenuBar containing a menu, a JMenu containing the four menu items for
|
|
the four sorts, four JMenuItems for the selection of each of the four
|
|
sorting algorithms, a SortBarPanel instance for displaying the status
|
|
of our sorting array graphically, and a "start" JButton to initiate
|
|
the sort of a random array. This class will be responsible
|
|
for redisplaying the sorting array by means of a SortBarPanel
|
|
instance. In order to do that, the SortFrame instance needs to be
|
|
notified everytime the array changes. This means SortFrame will have
|
|
to be a listener of ChangeEvents fired from the ArrayWrapper
|
|
containing the array being sorted. In order to be notified of these
|
|
changes, SortFrame must implement ChangeListener.
|
|
|
|
NOTE: Regarding JFrames: you cannot simply "add" components to the
|
|
JFrame container. there is actually another widget that accompanies
|
|
all JFrames called the content Pane. You must set the layout of this
|
|
component and add widgets to this component. The content pane of any
|
|
JFrame can be accessed via the method "getContentPane()". If you try
|
|
to add stuff to the JFrame directly, your code won't work.
|
|
|
|
NOTE: One thing to keep in mind when coding GUIs is that there are
|
|
lots of little yet simple methods to these GUI components. If you are
|
|
not used to scouring the API for solutions to your problem, now is the
|
|
time to start. In most cases, the means of accomplishing any basic
|
|
GUI task such as setting up your menus or setting up a layout is right
|
|
under your nose in the API. You just need to spend a little time
|
|
searching. If the solution to a task doesn't immidiately come to mind
|
|
as you read below, that's to be expected. But be sure you make an
|
|
honest effort to search for the solution on your own before you
|
|
contact the newsgroups or email your TA. It's the best way to learn.
|
|
|
|
Your SortFrame class should include the following:
|
|
|
|
o a private instance variable called "barPanel" of type
|
|
SortBarPanel. This will be the JPanel displaying the array.
|
|
o a private instance variable called "startButton" of type
|
|
JButton. This button will initiate the sort of a random array.
|
|
o a private instance variable called menuBar of type JMenuBar.
|
|
This variable will be the drop-down menu bar for the frame.
|
|
o a private instance variable called sortMenu of type JMenu.
|
|
This menu will be the only menu on the menu bar.
|
|
o four private instance variables named "bubbleItem",
|
|
"insertionItem", "quickItem", "mergeItem" of type JMenuItem.
|
|
|
|
o a public method called stateChanged which takes in a
|
|
ChangeEvent and returns void. This is the method you are
|
|
required to specify as a result of implementing ChangeListener.
|
|
This method should be called when something (an ArrayWrapper
|
|
perhaps?) fires a ChangeEvent. If this method is called by an
|
|
ArrayWrapper, that means the array inside the ArrayWrapper must
|
|
have changed somehow. You should redisplay this somehow altered
|
|
array using your SortBarPanel instance. The ChangeEvent
|
|
parameter isn't needed inside this method in this particular case.
|
|
|
|
o a default constructor that does the following:
|
|
- instantiates the super class and places the phrase "Sort
|
|
Demo" in the title bar of the frame.
|
|
- sets the layout of the content pane of the frame to be a
|
|
BorderLayout.
|
|
- creates all the menu Items and adds them to the sort menu.
|
|
Each JMenuItem's label should be "Bubble Sort", "Insertion
|
|
Sort", ... etc, respectively.
|
|
|
|
- adds the sort menu to the menu bar. The sort menu's label
|
|
should be "Sorts".
|
|
- adds the menu bar to the JFrame.
|
|
- creates the SortBarPanel instance and adds it to the CENTER
|
|
of the layout.
|
|
- creates the start button and adds it to the SOUTH of the
|
|
layout. The button label should be "Start".
|
|
- sets the size of the JFrame to be 600x500.
|
|
- makes it so the user cannot resize the frame.
|
|
- makes the default close operation for this JFrame to
|
|
exit on close which will cause the program to exit when we
|
|
close the frame using the mouse. If we don't do this, when
|
|
we click the X at the top right, the frame will disappear but
|
|
the JVM will still be running. If it isn't immidiately
|
|
apparent how to accomplish the above, read the API. This bullet
|
|
is teeming with hints.
|
|
- makes the frame visible.
|
|
|
|
NOTE: You are allowed and strongly encouraged to abstract the
|
|
above functionality to private helper methods.
|
|
|
|
Don't let this class wander off too far. We will be coming back to it
|
|
in the next phase. As it stands now, SortFrame should nicely display
|
|
a frame with a single menu that says "Sorts". When you click the
|
|
menu, the four Sorting Menu Items should be displayed. These menu
|
|
items won't do anything if you click them. In the center should be a
|
|
big white area that is the blank SortBarPanel. At the bottom should
|
|
be a button with the word "Start" in the middle. This button should
|
|
not yet do anything when clicked. The program should exit if you
|
|
click the X in the top right. Be sure to check out the website if you
|
|
can't quite visualize it.
|
|
|
|
|
|
|
|
PHASE IV - Event Handling
|
|
========================================================================
|
|
|
|
Thus far, we have a nice pretty GUI that doesn't do squat. The menus
|
|
don't work and neither do the buttons. We need an event handler to
|
|
receive these clicks and then perform an action. See the following
|
|
for all your event handling needs:
|
|
|
|
http://www.cc.gatech.edu/classes/AY2003/cs1322_fall/
|
|
test_resources/current/study.html#GUIEvents
|
|
|
|
We will encapsulate our event handling functionality in another
|
|
class. Create a class called "EventHandler". This class will be
|
|
responsible for listening for events fired from the JButton and
|
|
JMenuItems. (You don't have to worry about firing these events, only
|
|
receiving them) Since JButtons and JMenuItems fire ActionEvents, you
|
|
will need to have EventHandler implement ActionListener. EventHandler
|
|
should have the following:
|
|
|
|
o a private instance variable called "sort" that is of type
|
|
AbstractSort. This variable will be one of the four sorting
|
|
subclasses. It holds the currently loaded sort.
|
|
o a private instance variable called "chgListener" of type
|
|
ChangeListener. This will be the ChangeListener that needs to be
|
|
notified of any changes to the array being sorted.
|
|
|
|
o a public constructor that takes in a ChangeListener and assigns
|
|
this parameter to the instance variable chgListener.
|
|
o a public method called "actionPerformed" that takes in an
|
|
ActionEvent instance. This method is required when implementing
|
|
ActionListener. It is this method that will be called when the
|
|
JMenuItems are selected or the start button clicked. When the
|
|
JMenuItems are selected we want to display a random array of
|
|
sixty integers in the SortBarPanel. When the start button is
|
|
clicked, we want to start sorting the array. But actionPerformed
|
|
is called for both the JMenuItems and the JButton. So how do we
|
|
know which was the one that actually was clicked by the user?
|
|
This is where the ActionEvent parameter comes in handy. This
|
|
parameter contains all kinds of good information. Check out the
|
|
method "getSource()" in the class EventObject (a parent of
|
|
ActionEvent). This method returns the firing Object. So in this
|
|
case, getSource() will always return startButton or one of the
|
|
four JMenuItems. (How we get startButton and the sort JMenuItems
|
|
to direct their firings to EventHandler will be discussed in a
|
|
minute. Just go with it for now.) We can then use instanceof to
|
|
figure out whether or not the source is one of the four
|
|
JMenuItems or the startButton.
|
|
|
|
------
|
|
|
|
If the source was one of the sort JMenuItems, we want to create a
|
|
new random array, put it inside an ArrayWrapper to be sorted by
|
|
the sort chosen and display this array in our GUI.
|
|
|
|
- First create a new ArrayWrapper from a random array of
|
|
Integers. You coded a method that gave you a random array of a
|
|
given size and a constructor for ArrayWrapper that took in any
|
|
array to be wrapped. Bingo. Just make sure the size of the
|
|
random array you create is size 60.
|
|
|
|
- Now we want to be sure that whenever a change is made to the
|
|
array in our ArrayWrapper, the appropriate ChangeListener knows
|
|
about it. Our EventHandler constructor took in a ChangeListener
|
|
(which will be the SortFrame). We now want to
|
|
ask the ArrayWrapper to notify chgListener whenever the array is
|
|
changed. We can do this by telling the ArrayWrapper to add
|
|
chgListener to it's list of ChangeListeners to fire events to.
|
|
|
|
- But we now have another problem. How do we figure out which of
|
|
the four sorts was chosen. Look at the method
|
|
"getActionCommand()" in ActionEvent. This method returns the
|
|
label on the JMenuItem that fired the event. ("Bubble Sort",
|
|
"Insertion Sort", etc.) We can use this information to determine
|
|
which of the four sorts was chosen. Once we figure this out, we
|
|
create a new instance of the appropriate Sort class, passing in
|
|
the ArrayWrapper to sort, and set this instance to the "sort"
|
|
instance variable.
|
|
- We now need to have our SortFrame update the blank SortBarPanel
|
|
to display this random array contained in ArrayWrapper.
|
|
|
|
-----
|
|
|
|
If the startButton was the source of the event, first make sure
|
|
that "sort" is not null. If the user clicks the start button
|
|
before choosing a sorting algorithm, "sort" will be null. we
|
|
don't want to cause a NullPointerException. If it's not null,
|
|
we know the user just clicked a JMenuItem and "sort" has a random
|
|
array in it ready to go. We now call the Sorting class's
|
|
"doSort()" method.
|
|
|
|
|
|
------------------------------------------------
|
|
|
|
Back to SortFrame:
|
|
|
|
Now, in EventHandler, we have something that can process the menu
|
|
selections and the button clicks from the user. But we need to
|
|
connect the wires, so to speak, between the button/menu items to the
|
|
EventHandler. In ArrayWrapper, you coded a method called
|
|
addChangeListener which took in a ChangeListener to add to the list of
|
|
Listeners to notify. JButton and JMenuItem have a similar method
|
|
called "addActionListener". This method takes in an ActionListener.
|
|
Any time the button is clicked or the JMenuItem is selected, the
|
|
component fires an ActionEvent to all its registered listeners. Lo
|
|
and behold, EventHandler implements ActionListener! Add the following
|
|
to your SortFrame class:
|
|
|
|
o a private instance variable called "eh" of type EventHandler.
|
|
o Just after instantiating your JButton/JMenuItems, add "eh" to
|
|
these components' list of ActionListeners to notify. Now when a
|
|
user clicks these components, an ActionEvent will be fired to
|
|
"eh" where the actionPerformed method will determine what
|
|
component fired the event and take appropriate action.
|
|
|
|
|
|
|
|
PHASE V - Testing and Turnin
|
|
========================================================================
|
|
|
|
Create a class called P5 with a main method which instantiates SortFrame.
|
|
|
|
Test your code thoroughly. Some things:
|
|
|
|
- Swing GUI components can sometimes throw exceptions without the
|
|
program crashing. i.e. you may see a stack trace on the
|
|
console but amazingly the program still appears to be
|
|
functioning. We consider this no different than if the program
|
|
crashed. You should not have any stack traces from exceptions
|
|
of any kind. You should catch and handle all exceptions.
|
|
|
|
- You may notice that the start button seems to jam while the
|
|
sorting is going on. Also you may notice you can't seem to
|
|
close the JFrame or use the Sorts Menu. Don't worry about
|
|
this. While this would not be ideal for real software, the
|
|
means around this inconvinience is not within the realm of this
|
|
course.
|
|
|
|
- Alas, we can't allow you to change the speed of the sort. This
|
|
has been fixed in SortBarPanel.
|
|
|
|
Turn in all your java source files. You do not need to turn in the
|
|
nfo file, SortBarPanel.class or SortBarPanel.html
|
|
|
|
Remove all debug prints before submitting. Have fun. Good Luck.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|