first commit

This commit is contained in:
Jose Caban
2025-06-07 01:59:34 -04:00
commit 388ac241f0
3558 changed files with 9116289 additions and 0 deletions

BIN
CS2335/lab4/Images/Bold.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 B

BIN
CS2335/lab4/Images/grin.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

BIN
CS2335/lab4/Images/wink.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 B

View File

@@ -0,0 +1,167 @@
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Color;
/**
* CS2335 Lab 6 Example
*
* <PRE>
* BasicGrpahics.java shows the basics of how to draw simple objects
* <PRE>
*
* @author <A HREF="mailto:tedchoc@cc.gatech.edu">Ted Choc</A>
* @version Version 1.4, February 20, 2003
*/
public class BasicGraphics {
/**
* JFrame to which everything is rendered
*/
private JFrame guiFrame;
/**
* Content Pane of the JFrame
*/
private Container contentPane;
/**
* The panel to which all the graphics will be drawn
*/
private JPanel graphicsPanel;
/**
* A modifier for guiFrame
* @param guiFrame new guiFrame
*/
public void setGuiFrame(JFrame guiFrame) {
this.guiFrame = guiFrame;
}
/**
* A modifier for contentPane
* @param contentPane new contentPane
*/
public void setContentPane(Container contentPane) {
this.contentPane = contentPane;
}
/**
* A modifier for graphicsPanel
* @param graphicsPanel new graphicsPanel
*/
public void setGraphicsPanel(JPanel graphicsPanel) {
this.graphicsPanel = graphicsPanel;
}
/**
* An accessor for guiFrame
* @return JFrame guiFrame
*/
public JFrame getGuiFrame() {
return this.guiFrame;
}
/**
* An accessor for contentPane
* @return Container contentPane
*/
public Container getContentPane() {
return this.contentPane;
}
/**
* An accessor for graphicsPanel
* @return JPanel graphicsPanel
*/
public JPanel getGraphicsPanel() {
return this.graphicsPanel;
}
/**
* Create the window and sets it up for drawing
*/
public void makeWindow() {
guiFrame = new JFrame("Basic Graphics Objects");
/* Handle Closing the Window */
guiFrame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent we) {
System.exit(0);
}
});
contentPane = guiFrame.getContentPane();
graphicsPanel = new JPanel() {
/* Overwrite JPanels paintComponent Method */
public void paintComponent(Graphics g) {
super.paintComponent(g);
drawGraphics(g);
}
};
contentPane.add(graphicsPanel);
guiFrame.setSize(175, 240);
guiFrame.setVisible(true);
graphicsPanel.setBackground(Color.lightGray);
}
/**
* Method that draws the Graphics onto the graphicsPanel
* @param g graphics g object
*/
public void drawGraphics(Graphics g) {
/* Draws some Text */
g.setColor(Color.black);
g.drawString("Hey look, I'm some Text", 15, 15);
/* Draws a fancy 3-D Rectangle */
g.setColor(Color.red);
g.fill3DRect(50, 50, 50, 50, true);
g.setColor(Color.blue);
g.fillRect(60, 60, 30, 30);
/* Draws a line */
g.setColor(Color.yellow);
g.drawLine(50, 120, 130, 140);
/* Draws a circle */
g.setColor(Color.orange);
g.fillOval(140, 60, 20, 20);
/* Draws Complex Shape :) */
g.setColor(Color.red);
g.fillRoundRect(67, 145, 105, 30, 25, 25);
g.fillRoundRect(75, 170, 89, 33, 10, 10);
g.setColor(Color.white);
g.fillOval(72, 150, 18, 18);
g.fillOval(147, 150, 18, 18);
g.setColor(Color.yellow);
g.fillOval(84, 180, 6, 6);
g.fillOval(147, 180, 6, 6);
g.setColor(Color.black);
g.drawOval(72, 150, 18, 18);
g.drawOval(147, 150, 18, 18);
g.drawOval(84, 180, 6, 6);
g.drawOval(147, 180, 6, 6);
g.fillRoundRect(95, 150, 5, 50, 2, 2);
g.fillRoundRect(102, 150, 5, 50, 2, 2);
g.fillRoundRect(109, 150, 5, 50, 2, 2);
g.fillRoundRect(116, 150, 5, 50, 2, 2);
g.fillRoundRect(123, 150, 5, 50, 2, 2);
g.fillRoundRect(130, 150, 5, 50, 2, 2);
g.fillRoundRect(137, 150, 5, 50, 2, 2);
}
/**
* Main method that creates a new BasicGraphics
* @param args Command Line Arguments
*/
public static void main(String[] args) {
BasicGraphics test = new BasicGraphics();
test.makeWindow();
}
}

View File

@@ -0,0 +1,288 @@
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Polygon;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
/**
* CS2335 Lab 6 Example
*
* <PRE>
* SimpleAnimation.java shows the basics of how to create an animation
* <PRE>
*
* @author <A HREF="mailto:tedchoc@cc.gatech.edu">Ted Choc</A>
* @version Version 1.4, February 20, 2003
*/
public class SimpleAnimation {
/**
* JFrame to which everything is rendered
*/
private JFrame guiFrame;
/**
* Content Pane of the JFrame
*/
private Container contentPane;
/**
* The panel to which all the graphics will be drawn
*/
private JPanel graphicsPanel;
/**
* Determines the current rotation of the polygon
*/
private int rotationValue = 0;
/**
* Determines how the current polygon should be scaled
*/
private double scaleValue = 1.0;
/**
* Determines whether to shrink or grow the polygon
*/
private boolean increasingScale = true;
/**
* Determines what color the square should be
* 0 - Red, 1 - Green, 2 - Blue
*/
private int colorSelection = 0;
/**
* Determines the current color
*/
private Color currentColor = Color.red;
/**
* A modifier for guiFrame
* @param guiFrame new guiFrame
*/
public void setGuiFrame(JFrame guiFrame) {
this.guiFrame = guiFrame;
}
/**
* A modifier for contentPane
* @param contentPane new contentPane
*/
public void setContentPane(Container contentPane) {
this.contentPane = contentPane;
}
/**
* A modifier for graphicsPanel
* @param graphicsPanel new graphicsPanel
*/
public void setGraphicsPanel(JPanel graphicsPanel) {
this.graphicsPanel = graphicsPanel;
}
/**
* An accessor for guiFrame
* @return JFrame guiFrame
*/
public JFrame getGuiFrame() {
return this.guiFrame;
}
/**
* An accessor for contentPane
* @return Container contentPane
*/
public Container getContentPane() {
return this.contentPane;
}
/**
* An accessor for graphicsPanel
* @return JPanel graphicsPanel
*/
public JPanel getGraphicsPanel() {
return this.graphicsPanel;
}
/**
* Create the window and sets it up for drawing
*/
public void makeWindow() {
guiFrame = new JFrame("Simple Animation");
guiFrame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent we) {
System.exit(0);
}
});
contentPane = guiFrame.getContentPane();
graphicsPanel = new JPanel() {
public void paintComponent(Graphics g) {
super.paintComponent(g);
drawGraphics(g);
}
};
contentPane.add(graphicsPanel);
guiFrame.setSize(500, 500);
guiFrame.setVisible(true);
graphicsPanel.setBackground(Color.lightGray);
}
/**
* Draws the polygon on the Graphics
* @param g the graphics object to be drawn on
*/
public void drawGraphics(Graphics g) {
int[] xCoord = { 200, 300, 300, 200 };
int[] yCoord = { 200, 200, 300, 300 };
Polygon rectangle = new Polygon(xCoord, yCoord, 4);
//g.fillPolygon(rectangle);
g.setColor(currentColor);
g.fillPolygon(rotatePolygon(scalePolygon(rectangle, scaleValue),
rotationValue));
//g.setColor(Color.blue);
//g.fillPolygon(rotatePolygon(rectangle, 60));
}
/**
* Rotates the polygon by some angle - look into AffineTransform
* for a better way to handle this. This is also kind of a
* hack because it only works for rectangles, but it works for
* this example
* Rotation Matrix
* | cos(a) -sin(a) 0 |
* | sin(a) cos(a) 0 |
* | 0 0 1 |
* @param p polygon to be rotated
* @param theta the angle to be rotated by
* @return Polygon that has been rotated
*/
public Polygon rotatePolygon(Polygon p, int theta) {
int[] xCoord = p.xpoints;
int[] yCoord = p.ypoints;
int xMidpoint = ((xCoord[1] - xCoord[0]) / 2) + xCoord[0];
int yMidpoint = ((yCoord[2] - yCoord[1]) / 2) + yCoord[1];
p.translate(-xMidpoint, -yMidpoint);
xCoord = p.xpoints;
yCoord = p.ypoints;
int[] xRotCoord = new int[p.xpoints.length];
int[] yRotCoord = new int[p.ypoints.length];
int numPoints = p.npoints;
double thetaRad = Math.toRadians((double) theta);
for (int i = 0; i < numPoints; i++) {
xRotCoord[i] = (int) (xCoord[i] * Math.cos(thetaRad))
+ (int) (yCoord[i] * Math.sin(thetaRad));
yRotCoord[i] = (int) (-(xCoord[i]) * Math.sin(thetaRad))
+ (int) (yCoord[i] * Math.cos(thetaRad));
}
Polygon rotatedP = new Polygon(xRotCoord, yRotCoord, numPoints);
rotatedP.translate(xMidpoint, yMidpoint);
p.translate(xMidpoint, yMidpoint);
return rotatedP;
}
/**
* Scales the polygon by the specfied amount
* @param p polygon prior to being scaled
* @param scalar the amount to shrink or the grow the polygon by
* @return the polygon that has been scaled
*/
public Polygon scalePolygon(Polygon p, double scalar) {
int[] xCoord = p.xpoints;
int[] yCoord = p.ypoints;
int xMidpoint = ((xCoord[1] - xCoord[0]) / 2) + xCoord[0];
int yMidpoint = ((yCoord[2] - yCoord[1]) / 2) + yCoord[1];
p.translate(-xMidpoint, -yMidpoint);
xCoord = p.xpoints;
yCoord = p.ypoints;
int[] xScaleCoord = new int[p.xpoints.length];
int[] yScaleCoord = new int[p.ypoints.length];
int numPoints = p.npoints;
for (int i = 0; i < numPoints; i++) {
xScaleCoord[i] = (int) (scalar * xCoord[i]);
yScaleCoord[i] = (int) (scalar * yCoord[i]);
}
Polygon scaledP = new Polygon(xScaleCoord, yScaleCoord, numPoints);
scaledP.translate(xMidpoint, yMidpoint);
p.translate(xMidpoint, yMidpoint);
return scaledP;
}
/**
* Animates the square
*/
public void animateGraphics() {
ActionListener listener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
rotationValue = (rotationValue + 5) % 360;
if (increasingScale == true) {
if (scaleValue >= 2.5) {
increasingScale = false;
} else {
scaleValue = scaleValue + 0.05;
}
} else {
if (scaleValue <= 0.5) {
increasingScale = true;
} else {
scaleValue = scaleValue - 0.05;
}
}
if (colorSelection == 0) {
currentColor = new Color(currentColor.getRed() - 5,
currentColor.getGreen() + 5, 0);
if (currentColor.getRed() <= 0) {
colorSelection = 1;
}
} else if (colorSelection == 1) {
currentColor = new Color(0,
currentColor.getGreen() - 5,
currentColor.getBlue() + 5);
if (currentColor.getGreen() <= 0) {
colorSelection = 2;
}
} else {
currentColor = new Color(currentColor.getRed() + 5, 0,
currentColor.getBlue() - 5);
if (currentColor.getBlue() <= 0) {
colorSelection = 0;
}
}
graphicsPanel.repaint();
}
};
javax.swing.Timer animation = new javax.swing.Timer(50, listener);
animation.start();
}
/**
* Creates a new SimpleAnimation and begins the animation
* @param args Command Line Arguments
*/
public static void main(String[] args) {
SimpleAnimation test = new SimpleAnimation();
test.makeWindow();
test.animateGraphics();
}
}

View File

@@ -0,0 +1,28 @@
import java.io.Serializable;
public abstract class AbstractNetworkMessage implements Serializable {
//////////////////////////////// Data Members ///////////////////////////////
private String messageName;
//////////////////////////////// Constructors ///////////////////////////////
public AbstractNetworkMessage() {
messageName = "AbstractMessage";
}
public AbstractNetworkMessage(String name) {
messageName = name;
}
////////////////////////////////// Methods //////////////////////////////////
public String getMessageName() {
return messageName;
}
public String tokenize() {
return messageName;
}
}// end of class AbstractNetworkMessage

View File

@@ -0,0 +1,32 @@
import java.io.*;
import java.net.*;
class ClientExample {
public static final int PORT = 1234;
public static void main(String argv[]) {
try {
Socket s = new Socket("localhost", PORT);
BufferedReader br = new BufferedReader(new
InputStreamReader(s.getInputStream()));
PrintWriter pr = new PrintWriter(new
OutputStreamWriter(s.getOutputStream()));
ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream());
System.out.println(s.getLocalAddress().toString());
System.out.println(s.getLocalSocketAddress().toString());
String message = null;
BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));
while((message = stdIn.readLine()) != null) {
System.out.println("Sending message: " + message);
oos.writeObject(new TextMessage(message));
oos.flush();
}
} catch (IOException e) {
System.out.println("IO Exception: " + e.getMessage());
}
}
}

View File

@@ -0,0 +1,23 @@
public class NumberMessage extends AbstractNetworkMessage {
//////////////////////////////// Data Members ///////////////////////////////
private int myNumber;
//////////////////////////////// Constructors ///////////////////////////////
/**
* Creates a new <code>NumberMessage</code> instance.
*
*/
public NumberMessage(int number) {
super("NumberMessage");
myNumber = number;
}
////////////////////////////////// Methods //////////////////////////////////
public String tokenize() {
return super.tokenize() + ": " + Integer.toString(myNumber);
}
}// end of class NumberMessage

View File

@@ -0,0 +1,24 @@
import java.io.*;
import java.net.*;
class ClientExample {
public static final int PORT = 1234;
public static void main(String argv[]) {
try {
Socket s = new Socket("localhost", PORT);
BufferedReader br = new BufferedReader(new
InputStreamReader(s.getInputStream()));
PrintWriter pr = new PrintWriter(new
OutputStreamWriter(s.getOutputStream()));
System.out.println(s.getLocalAddress().toString());
System.out.println(s.getLocalSocketAddress().toString());
pr.println("Hello, world!");
pr.flush();
System.out.println(br.readLine());
} catch (IOException e) {
System.out.println("IO Exception: " + e.getMessage());
}
}
}

View File

@@ -0,0 +1,39 @@
import java.io.*;
import java.net.*;
class ServerExample extends Thread {
Socket socket;
public static final int PORT = 1234;
public ServerExample(Socket socket) {
this.socket = socket;
}
public void run() {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter pr = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
String s = br.readLine();
System.out.println("Received " + s);
pr.println("Thank you for your input");
pr.flush();
} catch (IOException ioe) {
System.out.println("Got IO Exception: " + ioe.getMessage());
}
}
public static void main(String argv[]) {
try {
ServerSocket ss = new ServerSocket(PORT);
while (true) {
Socket s = ss.accept();
new ServerExample(s).start();
}
} catch (IOException ioe) {
System.out.println("Got IO Exception: " + ioe.getMessage());
}
}
}

View File

@@ -0,0 +1,45 @@
import java.io.*;
import java.net.*;
class ServerExample extends Thread {
Socket socket;
public static final int PORT = 1234;
public ServerExample(Socket socket) {
this.socket = socket;
}
public void run() {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter pr = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
AbstractNetworkMessage anm;
while((anm = ((AbstractNetworkMessage)(ois.readObject()))) != null) {
String s = anm.tokenize();
System.out.println("Received " + s);
pr.println("Thank you for your input");
pr.flush();
}
} catch (IOException ioe) {
System.out.println("Got IO Exception: " + ioe.getMessage());
} catch(Exception e) {
System.out.println("Something aint workin': " + e.getMessage());
}
}
public static void main(String argv[]) {
try {
ServerSocket ss = new ServerSocket(PORT);
while (true) {
Socket s = ss.accept();
new ServerExample(s).start();
}
} catch (IOException ioe) {
System.out.println("Got IO Exception: " + ioe.getMessage());
}
}
}

View File

@@ -0,0 +1,19 @@
public class TextMessage extends AbstractNetworkMessage {
//////////////////////////////// Data Members ///////////////////////////////
private String myMessage;
//////////////////////////////// Constructors ///////////////////////////////
public TextMessage(String msg) {
super("TextMessage");
myMessage = msg;
}
////////////////////////////////// Methods //////////////////////////////////
public String tokenize() {
return super.tokenize() + ": " + myMessage;
}
}// end of class TextMessage

View File

@@ -0,0 +1,25 @@
import java.util.*;
class ThreadExample extends Thread {
String s;
Random r;
public ThreadExample(String s) {
this.s = s;
this.r = new Random();
}
public void run() {
for(int i=0; i<5; i++) {
System.out.println(s);
try {
this.sleep(r.nextInt(100));
} catch (InterruptedException e)
{}
}
}
public static void main(String args[]) {
new ThreadExample("String One").start();
new ThreadExample("String Two").start();
}
}

View File

@@ -0,0 +1,60 @@
import java.util.*;
import java.io.*;
public class SimpleThread implements Runnable {
private boolean terminate;
public SimpleThread() {
// super("Sleepy Thread");
terminate = true;
}
public boolean isAlive() {
return !terminate;
}
public void run() {
terminate = false;
while(!terminate) {
System.out.println("SimpleThread is alive!");
// try {
// this.sleep(2000);
// } catch(InterruptedException ie) {
// System.out.println("SimpleThread's sleep was interrupted!");
// break;
// }
for(int i=1; i<2000000; i+=2) {
i--;
}
}
System.out.println("\nSimpleThread is dead...");
}
public void kill() {
terminate = true;
}
public static void main(String args[]) {
SimpleThread st = new SimpleThread();
new Thread(st).start();
String input = null;
BufferedReader stdIn=new BufferedReader(new InputStreamReader(System.in));
try {
while((input = stdIn.readLine()) != null) {
if(input.equals("Kill")) {
st.kill();
break;
}
}
} catch(IOException ioe) {
System.err.println("Caught IO Exception: " + ioe.getMessage());
}
}
}// end of class SimpleThread

View File

@@ -0,0 +1,25 @@
import java.util.*;
class ThreadExample extends Thread {
String s;
Random r;
public ThreadExample(String s) {
this.s = s;
this.r = new Random();
}
public void run() {
for(int i=0; i<5; i++) {
System.out.println(s);
try {
this.sleep(r.nextInt(100));
} catch (InterruptedException e)
{}
}
}
public static void main(String args[]) {
new ThreadExample("String One").start();
new ThreadExample("String Two").start();
}
}

View File

@@ -0,0 +1,607 @@
<html><head><meta content="text/html; charset=ISO-8859-1" http-equiv="Content-Type"><title>Chapter<EFBFBD>4.<2E>
NetPaint -- A networked paint program.
</title><link rel="stylesheet" href="http://www.cc.gatech.edu/classes/AY2003/cs2335_spring/labs/style.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.59.1"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="chapter" lang="en"><div class="titlepage"><div><h2 class="title"><a name="id2783850"></a>Chapter<EFBFBD>4.<2E>
NetPaint -- A networked paint program.
</h2></div><div><div class="author"><h3 class="author">Joseph Czechowski</h3></div></div><div><div class="author"><h3 class="author">Ted Choc</h3></div></div><div><div class="author"><h3 class="author">Adrian Abraham</h3></div></div><div><div class="revhistory"><table border="1" width="100%" summary="Revision history"><tr><th align="left" valign="top" colspan="3"><b>Revision History</b></th></tr><tr><td align="left">Revision 1.0</td><td align="left">2004.01.16</td><td align="left">JC4</td></tr><tr><td align="left" colspan="3">Initial drafting of the lab, importing of parts of tools
section from previous semesters.</td></tr></table></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><a href="#id2785975">1. Overview of NetPaint</a></dt><dt><a href="#id2785992">2. Tools</a></dt><dd><dl><dt><a href="#id2786000">2.1. UI's In Java</a></dt><dt><a href="#id2786446">2.2. Threading In Java</a></dt><dt><a href="#id2786573">2.3. Introduction to Networking</a></dt></dl></dd><dt><a href="#id2786731">3. Project Requirements</a></dt><dd><dl><dt><a href="#id2786738">3.1. Overview</a></dt><dt><a href="#id2786755">3.2. Networking</a></dt><dt><a href="#id2786838">3.3. Draw Tools</a></dt><dt><a href="#id2787098">3.4. Other Required Features</a></dt></dl></dd><dt><a href="#id2787274">4. Suggested Design</a></dt><dd><dl><dt><a href="#id2787280"></a></dt></dl></dd><dt><a href="#id2787680">5. Deliverables</a></dt><dd><dl><dt><a href="#id2787698">5.1. Deliverables for Part 1</a></dt><dt><a href="#id2787792">5.2. Deliverables for Part 2</a></dt><dt><a href="#id2787868">5.3. Deliverables for Part 3</a></dt></dl></dd><dt><a href="#id2787914">6. Extra Credit</a></dt></dl></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="http://www.cc.gatech.edu/classes/AY2003/cs2335_spring/labs/images/important.png"></td><th align="left">Important</th></tr><tr><td colspan="2" align="left" valign="top"><p>
<span class="bold"><b>Part 1</b></span>
is due on WebCT before 2004.01.30 08:00:00 (A.M.)!
</p><p>
<span class="bold"><b>Part 2</b></span>
is due on WebCT before 2004.02.06 08:00:00 (A.M.)!
</p><p>
<span class="bold"><b>Part 3</b></span>
is due on WebCT before 2004.02.13 08:00:00 (A.M.)!
</p><p>
***DO NOT WAIT UNTIL THE LAST MINUTE TO SUBMIT YOUR ASSIGNMENT!!!***
</p></td></tr></table></div><div class="section" lang="en"><div class="titlepage"><div><h2 class="title" style="clear: both"><a name="id2785975"></a>1. Overview of NetPaint</h2></div></div><p>
Everyone is familiar with the basic paint programs that are typically
bundled with their favorite/least-favorite OS. NetPaint is similar to
those programs, but with the (obvious) added twist that it is networked.
</p></div><div class="section" lang="en"><div class="titlepage"><div><h2 class="title" style="clear: both"><a name="id2785992"></a>2. Tools</h2></div></div><div class="section" lang="en"><div class="titlepage"><div><h3 class="title"><a name="id2786000"></a>2.1. UI's In Java</h3></div></div><div class="section" lang="en"><div class="titlepage"><div><h4 class="title"><a name="id2786007"></a>2.1.1. Introduction</h4></div></div><p>
In the past, you have had experience using Swing components to
develop Graphical User Interfaces (GUIs). This lab will expand
these skills and give you the ability to create customized
graphics by using the Graphics[2D] class. By the end of the lab,
you will have created a simple paint program using these new GUI
creation skills.
</p></div><div class="section" lang="en"><div class="titlepage"><div><h4 class="title"><a name="id2785940"></a>2.1.2. Introduction to Graphics</h4></div></div><p>
In Java, manipulation of Graphics objects is not a particularly
difficult task. Since the Graphics class is abstract, you cannot
directly create one by calling its constructor. You must instead
obtain them from previously defined Graphics objects or by
calling the getGraphics() method on a
Component(java.awt.Component) or any of its subclasses. Before
you call the getGraphics() method on a component, it must be
displayable (i.e. the [J]Frame to which the component is added
must be visible - [J]Frame.setVisible(true)). Once you have a
Graphics object, you can begin to manipulate it. Graphics
objects have methods used to draw basic shapes, such as
characters, strings, polygons, but anything more complex will
require you to combine shapes to achieve the desired output.
Look at the Java API (java.awt.Graphics) for the exact commands
necessary to do this. Unfortunately, accessing graphics in this
manner causes some problems (mainly every time the window is
changed - minimized, resized, overlapped by another window,
etc... - the graphic you just made will be erased). To correct
this problem, you need to override the paintComponent() method
in the Component whose Graphic you wish to access. This method
is called every time the window needs to be redrawn, so by doing
all of your Graphics changes in this method you will fix the
aforementioned problem of losing your Graphic.
</p><p>
The Graphics2D class in Java allows for more flexibility when
rendering Graphical Objects (Graphics objects can be cast to
Graphics2D objects). Check out this
<a href="http://java.sun.com/docs/books/tutorial/2d/index.html" target="_top">
Graphics2D tutorial
</a>
for more information on this subject. Other sections of the Java
tutorial may be of additional assistance.
</p><p>
We've provided a simple Graphics example, BasicGraphics.java,
that shows you what can be done with basic calls to a Graphics
object.
</p></div><div class="section" lang="en"><div class="titlepage"><div><h4 class="title"><a name="id2786273"></a>2.1.3. Introduction to Animation</h4></div></div><p>
Although you will not be required to animate anything in this lab,
it might be helpful to look over this section for help in future
labs (wink, wink).
</p><p>
Making something animated is as simple as creating a Timer that
periodically repaints the components to be animated.Java
implements a Timer(javax.swing.Timer) to be used in animations.
A Timer requires an actionListener and a delay. After you call
start() on the Timer, the actionListener will be called every
&quot;delay&quot; milliseconds. The actionListener will make the changes
necessary to create the next frame and then call repaint on the
component, which actually renders it.
</p><p>
Drawing straight to the screen is a bad idea - it looks really
bad, since the individual objects will be drawn to the screen in
a delayed fashion, instead of all at once. To avoid this problem,
a technique called double buffering comes into play. Double
buffering is a method that renders the next image to be displayed
in an off-screen buffer and then swaps it with the current image
(do a search on Google for double buffering in Java for some good
examples). However, thanks to the helpful people at sun, if you
use Swing Components, double buffering is done for you.
</p><p>
We've also provided you with an Animation example,
<a href="SimpleAnimation.java" target="_top">SimpleAnimation.java</a>.
</p></div><div class="section" lang="en"><div class="titlepage"><div><h4 class="title"><a name="id2786332"></a>2.1.4. Graphics Optimizations</h4></div></div><p>
Since its nearly impossible to get the complete desired graphical
effects and animations by simply using Graphics2D, images often
need to come into play. While there are numerous image subclasses,
and practically infinite combination of ways to load them, for the
sake of simplicity and speed its recommended to use BufferedImages.
A BufferedImage can be drawn from a Graphics(2D) object's
drawImage(...) methods, the tricky part is efficiently loading an
image.
</p><p>
To load a BufferedImage from a file, the class you should be
interested in is javax.imageio.ImageIO and more specifically the
read(URL ...) method You'll notice the method is static, so there
is no need to init the class and can simply be used as such below:
</p><p>
<span class="bold"><b>BufferedImage</b></span> toLoad =
<span class="bold"><b>javax.imageio.ImageIO.read(
getClass().getResource(&quot;myImage.png&quot;));
</b></span>
</p><p>
The getClass().getResource(...) method calls are simply a way to
return a URL from path, referenced from the current classes location.
The read(...) method used as such above, well return a fully loaded
BufferedImage, meaning that you will NOT have worry about using
something such as the MediaTracker to ensure the image is ready to
be drawn.
</p><p>
If after using BufferedImages, you find your frame-rate is still being
hindered, you can attempt to pass off some of the work to the graphics
card by making the images hardware accelerated.
</p><pre class="screen">
import java.awt.*;
import java.awt.image.*;
public class ImageLoader
{
final GraphicsConfiguration gc;
public ImageLoader(GraphicsConfiguration gc)
{
if(gc==null)
{
gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
}
this.gc = gc;
}
public BufferedImage loadImage(String resource)
{
try
{
BufferedImage src = javax.imageio.ImageIO.read(getClass().getResource(resource));
BufferedImage dst = gc.createCompatibleImage(src.getWidth(),src.getHeight(),src.getColorModel().getTransparency());
Graphics2D g2d = dst.createGraphics();
g2d.setComposite(AlphaComposite.Src);
g2d.drawImage(src,0,0,null);
g2d.dispose();
return dst;
}
catch(java.io.IOException e)
{
return null;
}
}
}
</pre><p>
With a class similar to above, using the loadImage(...) method will
return a fully accelerated BufferedImage ready to be rendered. If
you look more carefully at the loadImage(...) method you should
already see concepts that are/will be familiar to you. A
BufferedImage has Graphics2D object that can be directly drawn upon
just like any other rendering surface. The loadImage(...) method
simply loads an image through the method explained above, then
creates another &quot;blank&quot; BufferedImage with acceleration, and then
proceeds to draw the first image onto the second. If desired, you
could add further code below the g2d.drawImage(...) to draw
lines/text/etc that would be permanently stored upon the image.
</p></div></div><div class="section" lang="en"><div class="titlepage"><div><h3 class="title"><a name="id2786446"></a>2.2. Threading In Java</h3></div></div><div class="section" lang="en"><div class="titlepage"><div><h4 class="title"><a name="id2786454"></a>2.2.1. Introduction</h4></div></div><p>
Threads allow multiple concurrent paths of execution. In Java,
there are different ways to create a new thread:
</p><div class="orderedlist"><ol type="A"><li>Implement the Runnable interface, and pass an instance
of the class to the Thread constructor, or</li><li>Extend Thread</li></ol></div><p>
</p><p>
When running on a machine with multiple processors, multiple
threads can actually run at the same time. Under normal
circumstances, on uniprocessor machines, the threads are time-shared,
rapidly switching back and forth, simulating concurrent execution.
</p><p>
A class which is going to run as its own thread needs to have a
method called run declared as public and void, with no
arguments. When the thread is started with start(), run will be
called and will run separately from the rest of the program.
</p><p>
We've provided an extremely simple threaded example called
ThreadExample.java.
</p><p>
If you try this out, you'll see that &quot;String One&quot; and &quot;String Two&quot;
will be printed on the console in some random order, since both
threads are running at once and sleeping random amounts between
messages.
</p><p>
Threads are important in a wide variety of applications. One of
their most important applications is networking. Traditional
networking makes heavy use of threads, because traditional
networking uses blocking I/O. In other words, when you call a
function, it doesn't return until it's finished. When you wait for a
client to connect, you can't continue until you receive a
connection. When you want to read from a connection, you can't
continue until you've filled your buffer. When you write to a
socket, you can't continue until all of the data has been
written. Without threads, it would be nearly impossible to do
anything useful since you would always be waiting on one client,
unable to handle all the others.
</p><p>
This is where threads come in. On the server end, each client will
get its own thread. That way, the blocking operations each block in
their own threads, without affecting any of the other threads. On
the client end, each client-server and client-client connection gets
its own thread. In addition, the user interface gets its own thread,
so that the user can interact with the program even when all of the
network connections are blocking.
</p></div></div><div class="section" lang="en"><div class="titlepage"><div><h3 class="title"><a name="id2786573"></a>2.3. Introduction to Networking</h3></div></div><p>
In Java, networking is fairly simple. On the server end, the server
creates a ServerSocket. This socket repeatedly accept()s, getting
a Socket back from each call and spawning a thread to handle each
new Socket.
</p><p>
On the client end, it's even easier. Just create a socket and
connect() it to a listening server.
</p><p>
The most useful Socket functions are getInputStream() and
getOutputStream(). By wrapping these streams in other IO classes
(such as PrintWriter and BufferedReader), you can do all of the
reading and writing you'll ever need.
</p><p>
We've provided you with a simple client and server pair, to see
what a threaded networking program looks like. These are the
ClientExample.java and ServerExample.java.
</p><p>
<span class="bold"><b>Server</b></span>
</p><div class="itemizedlist"><ul type="bullet"><li style="list-style-type: disc">Create a Server</li><li style="list-style-type: disc">Bind to a port</li><li style="list-style-type: disc">Wait for client connections (accept)</li><li style="list-style-type: disc">When a new connection comes in, get the socket and create
and start a new thread to communicate with the client. Be
sure to properly terminate the thread when you're done
with it!</li><li style="list-style-type: disc">Meanwhile, go back and wait for another connection
request.</li><li style="list-style-type: disc">When the server exits, close server socket and gracefully
shut down all threads.</li></ul></div><p>
</p><p>
<span class="bold"><b>Client</b></span>
</p><div class="itemizedlist"><ul type="bullet"><li style="list-style-type: disc">Create a networking thread and connect to server.
</li><li style="list-style-type: disc">Send/Recv Messages.</li><li style="list-style-type: disc">If you receive a client-to-client connection request,
spawn a new thread and create a new socket to handle
that.</li><li style="list-style-type: disc">Close Connection.</li></ul></div><p>
</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="http://www.cc.gatech.edu/classes/AY2003/cs2335_spring/labs/images/note.png"></td><th align="left">Note</th></tr><tr><td colspan="2" align="left" valign="top"><p>
Connection to the server must be threaded - otherwise,
interaction with the UI will not be possible.
</p></td></tr></table></div><p>
</p><p>
Here is a link to Java's Networking Tutorial:
<a href="http://java.sun.com/docs/books/tutorial/networking/index.html" target="_top">
http://java.sun.com/docs/books/tutorial/networking/index.html</a>
</p></div></div><div class="section" lang="en"><div class="titlepage"><div><h2 class="title" style="clear: both"><a name="id2786731"></a>3. Project Requirements</h2></div></div><div class="section" lang="en"><div class="titlepage"><div><h3 class="title"><a name="id2786738"></a>3.1. Overview</h3></div></div><p>
NetPaint is a networked paint program where two users may interact
with a single drawing canvas. Here both users can see the image and
both can interact with it (e.g. drawing new parts of the image).
</p></div><div class="section" lang="en"><div class="titlepage"><div><h3 class="title"><a name="id2786755"></a>3.2. Networking</h3></div></div><p>
Before programming you team should define a protocol that will be
used throughout the project. This will keep the confusion to a
minimum. There are many, many different ways to define a protocol,
but here is a simple one that should give you an idea of how to do
it. Also for the most part, your server will be broadcasting all of
the messages directly to all of the clients without any modification.
</p><pre class="screen">
-------------- Initial Sign-on ---------------
&gt; HELO: [username]
------------ Send a chat message ------------
&gt; CHAT: [username]:TEXT
--------------- Draw a line -----------------
&gt; LINE: [username]:StartX,StartY:EndX,EndY:RGB Color
------------- Draw a Rectangle --------------
&gt; RECT: [username]:StartX,StartY:Height:Width:RGB Color:Fill (Y/N)
-------------- Undo previous step -----------
&gt; UNDO: [username]
----------------- Sign-off ------------------
&gt; QUIT: [username]
Also note that by using the above protocol, you need to prevent users
from using a ':' in their username. This protocol was done as an example,
and does not represent the best solution or a complete one.
</pre><p>
</p><p>
Your server must support at least 5 simultaneous clients, which means that
the server must be multi-threaded. Also, the server must prevent two users
from specifying the same username. All of the networking in this lab must
be handled by using TCP Sockets, which are described very well in Java's networking
tutorial (linked in section 2.3).
</p><p>
<span class="bold"><b>Special Note:</b></span>
Your team must determine a method for handling the &quot;ordering of
events.&quot; This means that it is your choice as to how &quot;simultaneous
events&quot; (occurring on the two systems) are handled. You may select
an &quot;absolute ordering&quot; of events which prevents inconsistencies
between what each user sees **OR** you may select to use &quot;receive
ordering&quot; where each system processes the event in the order it
is received this is probably the easiest to implement but may
result in minor inconsistencies between what each of the users
sees.
</p></div><div class="section" lang="en"><div class="titlepage"><div><h3 class="title"><a name="id2786838"></a>3.3. Draw Tools</h3></div></div><p>
NetPaint should be able to do the following:
</p><div class="itemizedlist"><ul type="bullet"><li style="list-style-type: disc"><span class="bold"><b>Draw Shapes:</b></span>
The user should be able to draw:
<div class="itemizedlist"><ul type="bullet"><li style="list-style-type: disc">Rectangles
<span class="emphasis"><em>(See java.awt.Rectangle)</em></span></li><li style="list-style-type: disc">Polygons (made up of several points)
<span class="emphasis"><em>(See java.awt.Polygon)</em></span></li><li style="list-style-type: disc">Triangles (a polygon of three points)
<span class="emphasis"><em>(See java.awt.Polygon)</em></span></li><li style="list-style-type: disc">Lines
<span class="emphasis"><em>(See java.awt.geom.Line2D)</em></span></li><li style="list-style-type: disc">Quadratic Curves
<span class="emphasis"><em>(See java.awt.geom.QuadCurve2D)</em></span></li><li style="list-style-type: disc">Ellipses
<span class="emphasis"><em>(See java.awt.geom.Ellipse2D)</em></span></li></ul></div>
Except for the lines and curves all of the above should be able
to be inserted in &quot;filled&quot; or &quot;un-filled&quot; form (it is your team's
option whether or not the &quot;filled&quot; versions have a border or
not). Additionally the user should be able to choose the color
of the item to be inserted
<span class="emphasis"><em>(See javax.swing.JColorChooser,
javax.swing.colorchooser.AbstractColorChooserPanel)</em></span>
and pick the appropriate control points.
</li><li style="list-style-type: disc"><span class="bold"><b>Insert Images:</b></span>
Users should be able to select an image to load from their system
and insert into the picture. Additionally, the user should be able
to select the position of insertion and the insertion size.
(Your team may decide to allow only scaling of the image where
the X:Y ratio is maintained or your team may decide to permit
resizing where the image's ratio is not maintained.)
<span class="emphasis"><em>(See javax.swing.ImageIcon)</em></span><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="http://www.cc.gatech.edu/classes/AY2003/cs2335_spring/labs/images/note.png"></td><th align="left">Note</th></tr><tr><td colspan="2" align="left" valign="top"><p>
You are only required to support the use of images that are
available via the internet <span class="emphasis"><em>(See
javax.swing.ImageIcon(java.net.URL))</em></span> and will require you
to transfer only the text of the URL (e.g.
&quot;http://www.cc.gatech.edu/classes/AY2004/cs2335_spring/header.gif&quot;)
between the two systems and let both independently download
it from the internet.
</p><p>
<span class="bold"><b>EXTRA CREDIT OPTION:</b></span> For
extra credit your team may support the use of an image that
located on a local disk of one of the computers, here it is
YOUR responsibility for transferring the image to the other
computer. Do not assume that the other system has any other
way of accessing the image (e.g. it's not on the internet or
a networked drive).
</p></td></tr></table></div></li><li style="list-style-type: disc">Text
<span class="bold"><b>Insert Text:</b></span>
Users should be able to insert text onto the drawing space. When
inserting text, a user should have the ability to change the font
of the text, along with the size and color of the text. The user
will select a point on the screen which will be the starting point
for displaying the text. Then the user should be able to type such
that the text is constantly being updated on the graphics panel (i.e.
you can not just have them type into a pop-up window). There should
also be a button that allows the user to finalize their text and send
it to everyone else. NOTE: When inserting text, you can assume that
for each piece of text added there will be only one font type, size,
and color. But the user should be able to change the font, size, and
color anytime prior to pressing the &quot;finalize&quot; button.
</li></ul></div><p>
</p><p>
<span class="bold"><b>TIPS:</b></span> Your team may find it helpful
to visit the Graphics2D tutorial at
<a href="http://java.sun.com/docs/books/tutorial/2d/display/index.html" target="_top">http://java.sun.com/docs/books/tutorial/2d/display/index.html</a>
</p></div><div class="section" lang="en"><div class="titlepage"><div><h3 class="title"><a name="id2787098"></a>3.4. Other Required Features</h3></div></div><p>
</p><div class="itemizedlist"><ul type="bullet"><li style="list-style-type: disc"><span class="bold"><b>Save Session:</b></span>
At any point in the editing session any user should be able
save the current image to their local system. The format of
the save file is up to you, but it is suggested that it be
something simple (like a text file where the first thing on
a line is the name of the shape added and then each parameter
is listed after it).
</li><li style="list-style-type: disc"><span class="bold"><b>Load Session:</b></span>
At the start of an editing session the user should be prompted
as to whether they wish to start a new image or load an
existing one. If the user chooses to load an image it should
be loaded in and treated as if each of the loaded commands were
drawn at the start of the editing session by the person who
loaded the file. <span class="emphasis"><em>NOTE: Your program MUST be able to
load any of the images that were saved by your program (i.e.
you must support your own file format, but do not need to
support any other).</em></span></li><li style="list-style-type: disc"><span class="bold"><b>Undo:</b></span>
At any point in time a user should be able to &quot;undo&quot; their
previous command (if they just drew a line they would &quot;undo&quot;
the whole line, not just the last point). Users should not
be able to &quot;undo&quot; any command issued by another user (i.e.
you can't undo a line drawn by someone else).
</li><li style="list-style-type: disc"><span class="bold"><b>Super Undo (EXTRA CREDIT OPTION):</b></span>
For extra credit, you can support the ability to &quot;undo&quot; any
previous command that was done at any time (if the drawing was
started from a saved file, this would include any of the commands
used in creating that file). You will need to provide some method
for the user to view a listing of all of the previous commands
and easily selecting the one to undo. Unlike the normal &quot;undo&quot;
function (listed above) this one permits any user to &quot;undo&quot; any
command, even if they were not the one who originally issued
it.
</li><li style="list-style-type: disc"><span class="bold"><b>Chat:</b></span>
The program must have a chat feature that works similar to the
the basic chat programs we all know and love. :-) When displaying
the messages it should display the user's name of &quot;yourself&quot; in
one color and should display everyone else's user names in
another color (red and blue are typical for this, but you are
free to select any color that looks good).
<span class="emphasis"><em>(See javax.swing.JTextPane, it permits the programmer
to pass it HTML formatted text and then displays it as is
appropriate.)</em></span><p>
<span class="bold"><b>EXTRA CREDIT OPTION:</b></span> Provide
support for using the most common emoticons (e.g. the user's
typing :-P in the text input area will be translated into the
appropriate icon when displayed in the received test area.
</p></li><li style="list-style-type: disc"><span class="bold"><b>Help Screen:</b></span>
Describes how to use your program.
</li><li style="list-style-type: disc"><span class="bold"><b>About Screen:</b></span>
Displays the name of the program and the names and GTNum's
of the programmers.
</li></ul></div><p>
</p></div></div><div class="section" lang="en"><div class="titlepage"><div><h2 class="title" style="clear: both"><a name="id2787274"></a>4. Suggested Design</h2></div></div><div class="section" lang="en"><div class="titlepage"></div><p>
</p><div class="itemizedlist"><ul type="bullet"><li style="list-style-type: disc">
ServerMain -
<div class="itemizedlist"><ul type="opencircle"><li style="list-style-type: circle">
Listens for new client new connections, and spawns new
ServerThreads for each new incoming client connection.
</li><li style="list-style-type: circle">
Maintain a list of all currently connected clients
</li><li style="list-style-type: circle">
Must have to ability to broadcast messages to all connected
clients
</li><li style="list-style-type: circle">
Handle client disconnects
</li></ul></div></li><li style="list-style-type: disc">
ServerThread - Implement Runnable interface -OR- Extend Thread
<div class="itemizedlist"><ul type="opencircle"><li style="list-style-type: circle">
Pass messages from the client to the ServerMain to be broadcast
</li><li style="list-style-type: circle">
Inform ServerMain of client disconnects
</li></ul></div></li><li style="list-style-type: disc">
ClientNetworking - Implement Runnable interface =OR= Extend Thread
<div class="itemizedlist"><ul type="opencircle"><li style="list-style-type: circle">
Open a connection to the server
</li><li style="list-style-type: circle">
Receive messages from the server and pass them to ClientMain.
This will be the major/only functionality of the run method in
the thread spawned in the ClientNetworking class.
</li><li style="list-style-type: circle">
Send messages to the server
</li><li style="list-style-type: circle">
Inform client main of server failure
</li></ul></div></li><li style="list-style-type: disc">
ClientMain -
<div class="itemizedlist"><ul type="opencircle"><li style="list-style-type: circle">
Initializes and displays all graphics components, associating
all the components with the proper listeners.
<pre class="screen">
Here is an example of how your program could look:
+-------------------------------------------------------------------------------------+
| File | Options | Help |
+-------------------------------------------------------------------------------------+
| | |
| | Rectangle |
| | |
| |-------------|
| | |
| | Circle |
| | |
| |-------------|
| Graphics Display Panel | |
| | Text |
| | |
| |-------------|
| | |
| | Polygons |
| | |
| |-------------|
| | |
| | etc |
| | . |
|-----------------------------------------------------------------------| . |
| Option Bar - Change Color, Font Size, Font Type, etc... | . |
+-----------------------------------------------------------------------+-------------+
| |
| Received Text Area |
| |
+-------------------------------------------------------------------------------------+
| Text Input Area | Send Text Button |
+-------------------------------------------------------------------------------------+
</pre></li><li style="list-style-type: circle">
Creates the ClientNetworking class and then initializes it
</li><li style="list-style-type: circle">
Has a method that accepts a message from the ClientNetworking and
passes it to the proper component.
</li></ul></div></li><li style="list-style-type: disc">
GraphicsPanel - Extend JPanel (or if you are brave, Panel)
<div class="itemizedlist"><ul type="opencircle"><li style="list-style-type: circle">
Displays all of the drawn objects
</li></ul></div></li><li style="list-style-type: disc">
ClientEventListener - Implements MouseListener, ActionListener, KeyListener, MouseMotionListener
<div class="itemizedlist"><ul type="opencircle"><li style="list-style-type: circle">
Handle all keyboard, mouse, and button events.
</li></ul></div></li><li style="list-style-type: disc">
RenderableObject - &lt;&lt;Abstract&gt;&gt; representation of an object that can be drawn
and an association with its owner. Should declare an abstract method &quot;render&quot; which must
be overridden by all subclasses.
<div class="itemizedlist"><ul type="disc"><li>
RenderableImage - Contains reference to a picture (.jpeg, .gif, etc...) and have
the ability to render it to a graphics object
</li><li>
RenderableText - Contains a string as well as any modifiers to the text (Size, Font Type, etc...)
and have the ability to render it to a graphics object.
</li><li>
RenderableShape - Contains an object that implements the java.awt.Shape interface and have
the ability to render it to a graphics object.
</li></ul></div></li><li style="list-style-type: disc">
RenderableObjectList - Implements List Interface
<div class="itemizedlist"><ul type="opencircle"><li style="list-style-type: circle">
Maintains a list of RenderableObjects
</li><li style="list-style-type: circle">
Ability to return a java.util.ListIterator
</li><li style="list-style-type: circle">
Must be able to save and load the list to/from a file
</li></ul></div></li></ul></div><p>
</p><p>
Suggested Package Structure:
</p><pre class="screen">
- lab4
- server
* ServerMain (ex: lab4.server.ServerMain)
* ServerThread
- client
* ClientMain
- networking
* ClientNetworking
- model
* RenderableObjectList
* RenderableImage
* RenderableShape
* RenderableText
* RenderableObject
* ClientEventListener
- gui
* GraphicsPanel
Legend:
&quot;*&quot; - Denotes a class
&quot;-&quot; - Denotes a package
</pre><p>
</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="http://www.cc.gatech.edu/classes/AY2003/cs2335_spring/labs/images/note.png"></td><th align="left">Note</th></tr><tr><td colspan="2" align="left" valign="top"><p>
Your team is free to come up with an entirely new design, however
partial-credit opportunities will be limited if you use another design.
</p></td></tr></table></div></div></div><div class="section" lang="en"><div class="titlepage"><div><h2 class="title" style="clear: both"><a name="id2787680"></a>5. Deliverables</h2></div></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="http://www.cc.gatech.edu/classes/AY2003/cs2335_spring/labs/images/note.png"></td><th align="left">Note</th></tr><tr><td colspan="2" align="left" valign="top"><p>
We will not be checking PMD or Checkstyle for Part 1 or 2, but we
STRONGLY recommend you keep both tools in mind when programming.
</p></td></tr></table></div><div class="section" lang="en"><div class="titlepage"><div><h3 class="title"><a name="id2787698"></a>5.1. Deliverables for Part 1</h3></div></div><p>For the first part of the lab, you need to fully implement the server
component of your system. You should be able to ssh into your sever and
perform any commands, which should then be properly broadcast to all other
connected clients. Although your server must support all message types, you
may still change them for the further deliverables if necessary.
</p><p>
One of your group members must turn in the following:
</p><div class="itemizedlist"><ul type="bullet"><li style="list-style-type: disc">All source code needed to run your program.</li><li style="list-style-type: disc">The Ant build-file (build.xml), which needs to contain
the following targets (with the appropriate dependencies):
<div class="itemizedlist"><ul type="disc"><li>run - Runs your program and should be the default target</li><li>build - Compiles your program</li><li>checkstyle - Runs Checkstyle on your source code</li><li>pmd - Runs PMD on your source code</li><li>jar - Creates an executable Jar of your program</li><li>javadoc - Creates the JavaDoc for your program
(NOTE: JavaDoc must not produce any errors/warning to receive credit)</li><li>clean - Removes all files created from all other targets</li></ul></div></li><li style="list-style-type: disc">The completed Part 1 readme file (&quot;P1_README.txt&quot;).
</li></ul></div><p>
Please submit an archive of all these files in one of
the following formats: zip, tar, jar, or gzip.
</p></div><div class="section" lang="en"><div class="titlepage"><div><h3 class="title"><a name="id2787792"></a>5.2. Deliverables for Part 2</h3></div></div><p>
For the second part of the project, you must submit everything from
Part 1 and the basic client, which needs to support the following
features:
</p><div class="itemizedlist"><ul type="disc"><li>
Chat
</li><li>
Drawing of a simple black line
</li><li>
Undo
</li><li>
Must properly communicate with the Server and receive updates
from other clients
</li><li>
About screen
</li></ul></div><p>
</p><p>
One of your group members must turn in the following:
</p><div class="itemizedlist"><ul type="bullet"><li style="list-style-type: disc">All source code needed to run your program.</li><li style="list-style-type: disc">The Ant build-file (build.xml)</li><li style="list-style-type: disc">The completed Part 2 readme file (&quot;P2_README.txt&quot;).
</li></ul></div><p>
Please submit an archive of all these files in one of
the following formats: zip, tar, jar, or gzip.
</p></div><div class="section" lang="en"><div class="titlepage"><div><h3 class="title"><a name="id2787868"></a>5.3. Deliverables for Part 3</h3></div></div><p>
For the final part of the lab, you must submit everything from
Part 1 and Part 2, but now everything must be fully functional.
</p><p>
One of your group members must turn in the following:
</p><div class="itemizedlist"><ul type="bullet"><li style="list-style-type: disc">All source code needed to run your program.</li><li style="list-style-type: disc">The Ant build-file (build.xml)</li><li style="list-style-type: disc">The completed Part 3 readme file (&quot;P3_README.txt&quot;).
</li></ul></div><p>
Please submit an archive of all these files in one of
the following formats: zip, tar, jar, or gzip.
</p></div></div><div class="section" lang="en"><div class="titlepage"><div><h2 class="title" style="clear: both"><a name="id2787914"></a>6. Extra Credit</h2></div></div><p>
You may receive up to 20 points of extra credit for Part 3 of this lab.
You may attempt the following for extra credit (and their point values):
</p><div class="itemizedlist"><ul type="disc"><li>Amazing UI (to be determined by your TA) - 5pt</li><li>Image File Transfer (Non-URL) - 10pt</li><li>Super Undo - 5pt</li><li>Emoticons - 2pt</li><li>File Saving/Loading from (non-trivial) XML - 5pt</li></ul></div><p>
</p></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="http://www.cc.gatech.edu/classes/AY2003/cs2335_spring/labs/images/important.png"></td><th align="left">Important</th></tr><tr><td colspan="2" align="left" valign="top"><p>
Unless arranged in advance, your TA will be grading this assignment
on the RedHat systems available in the States Lab and will be using
the Java tools provided in the &quot;~cs2335/&quot; directory. It is YOUR
responsibility to verify that your program will work on these
systems prior to submitting it.
</p></td></tr></table></div></div></body></html>

120
CS2335/lab4/P1_README.txt Normal file
View File

@@ -0,0 +1,120 @@
CS2335 - Spring 2004 Lab 4 - NetPaint
=============================================================================
PART 1 -- README
=============================================================================
DIRECTIONS:
This form must be filled out and submitted with the rest of your files for
Part 1. For each item replace the underscore characters ('_') with the
proper information. If you need more lines than those shown, feel free to
add them.
=============================================================================
1. Team Members:
Name: Vladimir Urazov
GTNum: gtg308i
Name: Daniyar Zhanbekov
GTNum: gtg563g
2. Compilation Instructions:
The program can be easily compiled using the included ANT build
file build.xml. The default target there is run, which performs
all of the necessary compilation. But if one wanted to compile
the program without running, they could just use the "build"
target. That will simply build the code.
3. Instructions for Running the Program:
Once again, one could use the included ant build file. The "run"
target allows you to run the server. It also performs all of the
other necessary tasks for running the server, like compiling the
sources.
The server listens to connections from the clients and broadcasts
all the messages received to all the connected clients. It also
allows one to validate messages and if a badly formatted message
is received from the client, a bad format message is sent to the
sender.
The server understands three commands from command line. Type in
"messages" (without quotes) to see the messages that were
transmitted and saved (only chat messages and those related to
drawing are saved; maintenance messages, like join and quit
messages are not saved). Type in "clients" to see the list of
clients currently connected to the server. Type in "quit" to shut
down the server. When the server shuts down, it will wait for 60
seconds for the clients to send in a Quit message and then will
forcefully close the connections to those clients that did not
drop cleanly.
To test out the server, one could simply telnet into the host on
which it is running using the correct port. (The server prints
out the port being used on startup.) Once a client is connected,
the server expects a join message (in the format:
"HELO:username"). If any other message is received, the
connection is dropped.
When the user wants to quit, they need to send in a Quit message
(in the format: "QUIT:username"). The server will then close the
connection cleanly.
4. Known Bugs:
___None_____________________________________________________
____________________________________________________________
____________________________________________________________
____________________________________________________________
5. Detailed Description of Network Protocol:
SRVE - Server error. Rejection, drop, server quitting, etc. Format:
SRVE: Message string
SRVM - Server message. New user, lost user, etc. Format:
SRVM: Message string
HELO - User is joining the server. Format:
HELO: username
QUIT - User is quitting the server gracefully. Format:
QUIT: username
RECT - user is drawing a rectangle. Format:
RECT:msgid:username:startX:startY:height:width:color:fill(y|n)
TRIA - user is drawing a triangle. Format:
TRIA:msgid:username:x1:y1:x2:y2:x3:y3:color:fill(y|n)
LINE - user is drawing a line. Format:
LINE:msgid:username:startX:startY:rise:run:color
QUAD - User is drawing a quadratic curve. Format:
QUAD:msgid:username:x1:y1:x2:y2:ctrlx:ctrly:color
IMAG - User is inserting an image. Format:
IMAG:msgid:username:startX:startY:height:width:URL
Note here that URL can be either a global URL, in which case we would
download the image off the Internet, or just a filename, in which case
the users would connect to the client that chose the image p2p and
request a download.
UNDO - User is undoing an action. Format:
UNDO:msgid:username:undoid
undoid corresponds to the id of the message we want to undo.
CHAT - User is sending a chat message. Format:
CHAT:username:text
6. Testing/Development Environments (Include OS):
[REMINDER: This lab will be graded on the RedHat systems in
the States Lab.]
__Developed on Windows XP with Emacs. Tested also on RedHat_
machines in the state cluster as well as on helsinki._______
____________________________________________________________
____________________________________________________________
7. Comments:
____________________________________________________________
____________________________________________________________
____________________________________________________________
____________________________________________________________
____________________________________________________________

59
CS2335/lab4/P2_README.txt Normal file
View File

@@ -0,0 +1,59 @@
CS2335 - Spring 2004 Lab 4 - NetPaint
=============================================================================
PART 2 -- README
=============================================================================
DIRECTIONS:
This form must be filled out and submitted with the rest of your files for
Part 1. For each item replace the underscore characters ('_') with the
proper information. If you need more lines than those shown, feel free to
add them.
=============================================================================
1. Team Members:
Name: Daniyar Zhanbekov
GTNum: gtg563g
Name: Vladimir Urazov
GTNum: gtg308i
2. Compilation Instructions:
ant build___________________________________________________
____________________________________________________________
____________________________________________________________
____________________________________________________________
3. Instructions for Running the Program:
Server: ant run_server (Default working port is 4555)_______
Client: ant run_client______________________________________
____________________________________________________________
4. Known Bugs:
If the first or the last character in the chat message is
the ":", it will not be sent correctly.
The drawing canvas resizing option isn't working correctly,
so please use it without resizing it.
____________________________________________________________
5. Testing/Development Environments (Include OS):
[REMINDER: This lab will be graded on the RedHat systems in
the States Lab.]
Windows XP,Red Hat 9.0(States Lab)__________________________
____________________________________________________________
____________________________________________________________
____________________________________________________________
6. Comments:
When ant run_client is run, the dialog box with come up
asking you if you'd like to start a connection.If you chose
Cancel option you may reopen the connection dialog from the
File menu,by clicking File -> Connect. Once the connection
occurs, the Graphics Session and the Chat Sessions will
become active. The Undo menu option allow you to undo all of
the messages submitted by you, during your joint drawing
session. Disconnect Button allows you to end the connection
to the server, and the Quit button closes the NetPaint
application.
To draw a line, CLICK once for the initial point, and then
the line will be traced to the final destination where a
second CLICK will put the LINE on the canvas.

86
CS2335/lab4/P3_README.txt Normal file
View File

@@ -0,0 +1,86 @@
CS2335 - Spring 2004 Lab 4 - NetPaint
=============================================================================
PART 3 -- README
=============================================================================
DIRECTIONS:
This form must be filled out and submitted with the rest of your files for
Part 1. For each item replace the underscore characters ('_') with the
proper information. If you need more lines than those shown, feel free to
add them.
=============================================================================
1. Team Members:
Name: Vladimir Urazov
GTNum: gtg308i
Name: Daniyar Zhanbekov
GTNum: gtg563g
2. Put an 'X' Indicating All Extra Credit Options Completed:
[X] Amazing UI (All teams are are eligible for this one.)
[ ] Image File Transfer
[X] Super Undo
[X] Emoticons
[X] File Saving/Loading from XML
3. Compilation Instructions:
To compile the program, one can simply use the ant build
file. The command to build the sources into classes is "ant
build".
4. Instructions for Running the Program:
There are two parts to the program - the client and the
server. Both can be run using the ant build file, or one of the
jar files. In the buildfile, one can use the targets "run_server"
and "run_client" to run the server and the client
respectively. Also, there is a target named "jar", which allows
one to create two jar files - one for the server, and one for the
client. Each of those jar files can be executed to run the
respective part of the program.
5. Known Bugs:
The only thing worth mentioning here is that there are some fonts
under RedHat that don't seem to work. For instance, the font
named "Cursor" will not display anything if it is chosen with the
Text Insertion tool. Also, when working on the same session from
different operating systems (i.e. some cliens are connected from
a Linux box, and some others from a Windows box), the fonts will
sometimes not match if one of the users selects a font that is
not present on another machine. The text still displays, the
images on each machine aren't exactly the same.
Also, a minor annoyance is that the "maximize" button of the main
window does not seem to function.
6. Testing/Development Environments (Include OS):
[REMINDER: This lab will be graded on the RedHat systems in
the States Lab.]
Windows XP / EMACS and RedHat / EMACS
7. Short Description of File Format for Saved Files:
The session is saved in XML files. The schema is rather simple:
the root element is called "session", and within it are elements
which are named the same as the classes of different network
messages. Each such element contains different nested elements
which correspond to the properties of that tool. So, for
instance, line elements would have the coordinates of the line
ends, as well as the color in which the line should be drawn as
nested elements.
8. Comments:
See the help.html for program usage. This is also accessible
through the in-program help system.
We also implemented some features that weren't required, but
weren't suggested by the extra credit section of the requirements
document either. Such things include the ability to insert
differently styled text - in bold or italics, etc.

2
CS2335/lab4/TODO Normal file
View File

@@ -0,0 +1,2 @@
Modify Manifest.mf file to run our project.
Update build.xml

137
CS2335/lab4/build.xml Normal file
View File

@@ -0,0 +1,137 @@
<project name="NetPaint" default="run" basedir=".">
<description>
Performs different build tasks for the NetPain project.
</description>
<!-- Global Properties -->
<property name="user.name" location="Vladimir Urazov" />
<property name="src" location="src" />
<property name="docs" location="apidoc" />
<property name="doc_packages" value="netpaint.*" />
<property name="classes" location="build" />
<property name="libs" location="lib" />
<property name="jarname_server" value="NetPaintServer.jar" />
<property name="jarname_client" value="NetPaintClient.jar" />
<property name="runClass_server" value="netpaint.server.ServerRunner" />
<property name="runClass_client" value="netpaint.client.ClientRunner" />
<property name="checkstyle_logfile" value="checkstyle_log.txt" />
<property name="pmd_logfile" value="pmd_log.html" />
<property name="pmd_rules" value="rulesets/basic.xml,rulesets/unusedcode.xml,rulesets/design.xml,rulesets/imports.xml" />
<!-- Paths -->
<property name="pmd_path" location="/net/hu15/cs2335/java/pmd/lib" />
<property name="checkstyle_path" location="/net/hu15/cs2335/java/checkstyle" />
<path id="pmd.classpath">
<fileset dir="${pmd_path}">
<include name="*.jar"/>
</fileset>
</path>
<path id="checkstyle.classpath">
<fileset dir="${checkstyle_path}">
<include name="checkstyle-all-3.3.jar" />
</fileset>
</path>
<!-- Taskdefs -->
<taskdef resource="checkstyletask.properties" classpathref="checkstyle.classpath" />
<taskdef name="pmd" classname="net.sourceforge.pmd.ant.PMDTask" classpathref="pmd.classpath" />
<!-- Targets -->
<target name="init">
<!-- Create the time stamp -->
<tstamp />
</target>
<target name="build" depends="init" description="Compiles the program">
<!-- Make sure the necessary directory structure exists -->
<mkdir dir="${classes}" />
<!-- Compile -->
<javac srcdir="${src}" destdir="${classes}" />
<!-- Copy required resources -->
<copy todir="${classes}/netpaint/client/gui">
<fileset dir="Images"/>
</copy>
<copy file="help.html" todir="${classes}/netpaint/client/gui" />
</target>
<target name="javadoc" description="Creates JavaDoc for NetPaint">
<!-- Make sure the necessary directory structure exists -->
<mkdir dir="${docs}" />
<!-- Generate comments -->
<javadoc packagenames="${doc_packages}" sourcepath="${src}" destdir="${docs}" />
</target>
<target name="jar" depends="build" description="Package the compiled classes into a JAR file.">
<jar destfile="${jarname_server}" basedir="${classes}">
<manifest>
<attribute name="Built-By" value="${user.name}" />
<attribute name="Main-Class" value="${runClass_server}" />
</manifest>
</jar>
<jar destfile="${jarname_client}" basedir="${classes}">
<manifest>
<attribute name="Built-By" value="${user.name}" />
<attribute name="Main-Class" value="${runClass_client}" />
</manifest>
</jar>
</target>
<target name="run_server" depends="build" description="Start the server">
<java classname="${runClass_server}">
<classpath>
<pathelement path="${classes}" />
</classpath>
</java>
</target>
<target name="run_client" depends="build" description="Start the client">
<java classname="${runClass_client}" fork="true">
<classpath>
<pathelement path="${classes}" />
</classpath>
</java>
</target>
<target name="checkstyle" description="Checks all of the source code with Checkstyle.">
<checkstyle config="${checkstyle_path}/docs/sun_checks.xml" failOnViolation="false">
<fileset dir="${src}" includes="**/*.java" />
<formatter type="plain" toFile="${checkstyle_logfile}" />
</checkstyle>
</target>
<target name="pmd" description="Run PMD against all source code files.">
<pmd rulesetfiles="${pmd_rules}">
<fileset dir="${src}" includes="**/*.java" />
<formatter type="html" toFile="${pmd_logfile}" />
</pmd>
</target>
<target name="all">
<antcall target="checkstyle" />
<antcall target="pmd" />
<antcall target="build" />
<antcall target="javadoc" />
<!--<antcall target="jar" />-->
</target>
<target name="clean">
<delete dir="${classes}" />
<delete dir="${docs}" />
<delete file="${jarname_server}" />
<delete file="${jarname_client}" />
<delete file="${pmd_logfile}" />
<delete file="${checkstyle_logfile}" />
</target>
</project>

Binary file not shown.

View File

@@ -0,0 +1,55 @@
The clients need to be able to know of the following happenings:
1. Their connection has been rejected by the server
2. Another user has joined/dropped from the session
3. The server wants to tell them of an error
4. A user is sending a chat message
5. A user has drawn something
6. A user did an UNDO on something
SRVE - Server error. Rejection, drop, server quitting, etc. Format:
SRVE: Message string
SRVM - Server message. New user, lost user, etc. Format:
SRVM: Message string
HELO - User is joining the server. Format:
HELO: username
QUIT - User is quitting the server gracefully. Format:
QUIT: username
RECT - user is drawing a rectangle. Format:
RECT:msgid:username:startX:startY:height:width:color:fill(y|n)
POLY - user is drawing a polygon. Format:
RECT:msgid:username:color:fill(y|n):numpoints:x1:y1:x2:y2:...
TRIA - user is drawing a triangle. Format:
TRIA:msgid:username:x1:y1:x2:y2:x3:y3:color:fill(y|n)
LINE - user is drawing a line. Format:
LINE:msgid:username:startX:startY:rise:run:color
QUAD - User is drawing a quadratic curve. Format:
QUAD:msgid:username:x1:y1:x2:y2:ctrlx:ctrly:color
ELIP - User is drawing an ellipse. Format:
ELIP:msgid:username:startX:startY:height:width:color:fill(y|n)
TEXT - User is drawing text. Format:
TEXT:msgid:username:startX:startY:size:color:font:text
IMAG - User is inserting an image. Format:
IMAG:msgid:username:startX:startY:height:width:URL
Note here that URL can be either a global URL, in which case we would download the image off the Internet, or just a filename, in which case the users would connect to the client that chose the image p2p and request a download.
UNDO - User is undoing an action. Format:
UNDO:msgid:username:undoid
undoid corresponds to the id of the message we want to undo.
CHAT - User is sending a chat message. Format:
CHAT:username:text

View File

@@ -0,0 +1,50 @@
Bugs:
- Update the "Got client connection from" message to actually display a correct message.
+ Send all clients a message when someone joins the server.
+ Clean up messages!
+ Put all the "magic" strings into a central class. (HELO, QUIT, etc)
+ Take care of the magic number. (Same class as above.)
+ put a static gettype(string) function into abstractmessage
+ There is no way to get chat messages out of messagelist. Make one.
+ If there is a colon as the last character in the message, my parse method doesn't work (chat, say) Same for colon being the first character...
+ The content pane in chat panel doesn't scroll...
+ HTML don't work. Do a styled document, like it says in the tutorial!
+ Look into the problem of resizing the drawing canvas.
+ Look into the problem of giving the color indicator some size.
Look into the problem of loading resources.
+ When right-clicking, the tool doesn't exactly set off... Instead, it just resets the first position to -1, -1, and remains active...
+ Make it so that the textmessage stores the font parameters as actual bits of information, and only assembles the entire font when getFont is called. Otherwise, server gets fucked up.
+ Make it so updateFont of font selection panel makes the graphics panel rerender.
+ Test out this focus deal. Sometimes, the key presses may not register.
+ Implement different cases for key presses in key tool - caret, arrows, backspace, esc.
+ Pretty up text tool rendering - put a box around the text maybe?
+ Test out the finalize button it may get fucked up sometimes.
+ Make it so when one hits enter while entering text, not only is the message finalized, but also the font panel disables the finalize button.
+ Implement processXMLElement and propertiesToXML for graphics messages and the chat message.
+ There seems to be a bug in the isConnected method of Client.
Implemetation:
+ Draw rectangles
+ Draw polygons
+ Draw triangles
+ Draw lines
+ Draw Quadratic Curves
+ Draw ellipses
+ Insert Images
+ Specify source
+ Specify location
+ Specify size
+ Insert text
+ Specify font
+ Specify size
+ Specify decorations (B, I, U)
+ Save Session
+ Load Session
Super Undo
+ Chat Smilies
Help Screen
Write Tooltips

133
CS2335/lab4/help.html Normal file
View File

@@ -0,0 +1,133 @@
<html><head><title>HELP TUTORIAL</title>
</head><body bgcolor="#d0d0d0" >
<hr>
<b><center> TABLE OF CONTENTS </center></b>
<hr>
<hr>
<p>
<a href="#C1">
1) Connecting/Disconnecting from the Server.
</a>
</p>
<p>
<a href="#C2">
2) Load and Save Working Sessions.
</a>
</p>
<p>
<a href="#C3">
3) Undo and Super Undo Features.
</a>
</p>
<p>
<a href="#C4">
4) Use drawing Tools and Insert Text.
</a>
</p>
<p>
<a href="#C5">
5) Insert Images and Chat.
</a>
</p>
<hr>
<hr>
<p>
<a name="C1"><h2>Connecting and Disconnecting from the Server</h2>
<hr>
<p>When one first runs the Netpaint application, a dialog box comes to the foreground. It has the Server field, into which the ip address of the server is to be inserted.</p>
<p>A port field in the Dialog box requires the port on which the server is listening for clients. And the username field requires a username one wishes to use.</p>
<p>In case the dialog box is closed when the client first runs, a connection to the server may still be opened by clicking on the File/Connect menu, this will</p>
<p>bring up the Dialog box again</p>
<hr>
<a name="C2"><h2>Load and Save Working Sessions</h2>
<hr>
<p>If one wishes to Save a Session one can do so by clicking File/Save Session or File/Save Session As, a session will be saved in xml on clients,</p>
<p> harddrive regardless of whether client is connected to the server or not. If one wishes to Load a Session, one can do so by clicking File/Load Session.</p>
<p> If the client is connected to the server a client will be asked whether he/she wishes to override the current server session with the session he/she </p>
<p> is currently loading. If client chooses "yes" then the session that the client is loading will be uploaded to everyone currently connected to the server,</p>
<p> and their old session will be deleted. In case "no" is chosen, a client's session will be loaded solely to him alone, plus he will get everything that </p>
<p> is currently on the server. </p>
<hr>
<a name="C3"><h2>Undo and Super Undo Features</h2>
<hr>
<p> To use the Undo feature one needs to select Edit/Undo or Edit/Undo List options from the menu. If one selects the Undo option the last action that particular</p>
<p> client has performed will be undone. If one chooses the Edit/Undo List option (SuperUndo) a menu comes up that contains the list of all the actions performed </p>
<p> on the server, and the user will be able to select which actions he/she would like to have undone.</p>
<hr>
<a name="C4"><h2>Use drawing Tools and Insert Text</h2></a>
<hr>
<p>To use the drawing tools one needs to select a tool one whishes to use from the Tool panel, and then Click on the Graphics Session Panel to make it active:</p>
<p> You may also chose the color for the drawing from the Black Square color panel.Once the Session Panel is actie one can draw the following tools: </p>
<p><a href="#T1">
Line Tool:
</a></p>
<p><a href="#T2">
Triangle Tool:
</a></p>
<p><a href="#T3">
Ellipse Tool:
</a></p>
<p><a href="#T4">
Rectangle Tool:
</a></p>
<p><a href="#T5">
Polygon Tool:
</a></p>
<p><a href="#T6">
Quadratic Curve Tool:
</a></p>
<p><a href="#T7">
Text Tool:
</a></p>
<a name="T1"><h2>Line Tool:</h2>
<hr>
<p> Click on the Session Panel once to mark the begining of the line. click the second time to finish the line</p>
<a name="T2"><h2>Triangle Tool:</h2>
<hr>
<p> Click on the Session Panel once to mark the first point of the triangle. Notice on may also chose whether to draw the filled triangle by </p>
<p> clicking the fillshape button. Then click on the Session panel again mark the second point. Once you click on the Panel again, that fill mark </p>
<p> the final point and complete the triangle.</p>
<a name="T3"><h2>Ellipse Tool:</h2>
<hr>
<p> Click on the Session Panel to mark the lower left corner of the rectangle in which the Ellipse will be drawn.This shape may also be drawn filled. </p>
<p> Then click on the Panel again to select the upper right corner of the rectangle and therefore finalize the ellipse.</p>
<a name="T4"><h2>Rectangle Tool:</h2>
<hr>
<p> Click on the Session Panel to mark the lower corner of the rectangle, click again to mark the upper right corner and finalize it. This shape may</p>
<p> be also drawn filled </p>
<a name="T5"><h2>Polygon Tool:</h2>
<hr>
<p> Click on the Session Panel to mark the first point of the polygon, a small black square will be drawn at that location to mark the point in which to </p>
<p> end the polygon. Click again to mark additional points and then click into the black square to finalize the polygon. This shape may also be drawn filled.</p>
<a name="T6"><h2>Quadratic Curve Tool:</h2>
<hr>
<p> Drawn similar to a line by selecting two points. except then a third point may be seleceted to curve the existing line.</p>
<a name="T7"><h2>TextTool:</h2>
<hr>
<p>After selecting the letter A from the panel, and clicking to activate Session Panel.Fonts menu pops up, on which you may chose the font type,size, bold and </p>
<p> italic options. Do not click the checkmark button, until you have drawn some text on the Panel and wish to finalize it and send it to the server. If </p>
<p> the server recieves a bad message, it will drop the clients connection. </p>
<a name="C5"><h2>Insert Images and Chat</h2>
<hr>
<p>To insert and image click on the picture icon, this will bring up the load image menu.Insert the Url you wish to use to load the image into and press</p>
<p> the load menu button, this will upload the image. Then you may click on the panel to select where to insert the image. the image may also be scaled up or down</p>
<hr>
<hr>
</body></html>

2
CS2335/lab4/manifest.mf Normal file
View File

@@ -0,0 +1,2 @@
Manifest-Version: 1.0
Main-Class: lab4.server.ServerRunner

View File

@@ -0,0 +1,66 @@
package netpaint.client;
import netpaint.messaging.ChatMessage;
/** This class Iterates through the Chat messages.
* <PRE>
* ChatMessageListIterator.java
*
* Revisions: 1.0 Feb. 05, 2004
* Created the ChatMessageListIterator class
*
* </PRE>
*
* @author <A HREF="mailto:daniyarland@yahoo.com">Daniyar Zhanbekov</A>
* @version Version 1.0, Feb. 05, 2004
*/
public class ChatMessageListIterator {
/**
* The list of messages to iterate through.
*/
private MessageList list;
/**
* The index at which we are currently pointing.
*/
private int currentIndex;
/**
* Creates a new <code>ChatMessageListIterator</code> instance.
* @param messages a <code>MessageList</code> value
*/
protected ChatMessageListIterator(final MessageList messages) {
this.list = messages;
this.currentIndex = 0;
}
/**
* Returns true if there is another message to be iterated through
* still left, or false if we are at the end of the list.
*
* @return a <code>boolean</code> value
*/
public final boolean hasNext() {
return (currentIndex < list.numChatMessages());
}
/**
* Returns the next message and increments the iterator.
*
* @return a <code>ChatMessage</code> value
*/
public final ChatMessage next() {
ChatMessage result = list.chatElementAt(currentIndex);
currentIndex++;
return result;
}
}

View File

@@ -0,0 +1,502 @@
package netpaint.client;
import java.util.List;
import java.util.Vector;
import netpaint.messaging.MessageFactory;
import netpaint.messaging.MessageSettings;
import netpaint.messaging.AbstractMessage;
import netpaint.messaging.ChatMessage;
import netpaint.messaging.UndoMessage;
import netpaint.messaging.GraphicMessage;
import netpaint.messaging.ServerErrorMessage;
import netpaint.client.networking.ClientNetworking;
import netpaint.client.gui.ClientUI;
import netpaint.client.file.FileWriter;
import netpaint.client.file.FileReader;
/**
* Class Client: The client class will tie together all the other
* parts of the client program, like the networking and the UI.
*
* <PRE>
* Revision History:
* v1.0 (Jan. 31, 2004) - Created the Client class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Jan. 31, 2004
*/
public final class Client {
/**
* Implement as singleton.
*/
private static Client instance;
/**
* The object that encapsulates the networking functionality for the
* client.
*/
private ClientNetworking network;
/**
* This is the name under which the user is connected to the
* server. If no name was provided, a generic name will be used.
*/
private String userName;
/**
* Contains the list of saved messages: chat and graphics.
*/
private MessageList messages;
/**
* The writer that will be used for saving.
*/
private FileWriter fileWriter;
/**
* The reader that will be used for saving.
*/
private FileReader fileReader;
/**
* If the user has already saved the session once, this will contain
* the name of the save file where the session was saved to, so that
* when saving again, the same file would be overwritten.
*/
private String currentSaveName;
/**
* Creates a new <code>Client</code> instance. Since this is a
* singleton implementation, there is no need for the general public
* to instantiate the class.
*/
private Client() {
userName = null;
network = new ClientNetworking();
messages = new MessageList();
fileWriter = new FileWriter();
fileReader = new FileReader();
currentSaveName = null;
}
/**
* Returns the instance of the client.
*
* @return a <code>Client</code> value
*/
public static Client getInstance() {
if (instance == null) {
instance = new Client();
}
return instance;
}
/**
* Returns the name under which the user is connected to the server.
*
* @return a <code>String</code> value
*/
public String getUsername() {
return userName;
}
/**
* Gives the user a new name.
*
* @param name a <code>String</code> value
*/
protected void setUsername(final String name) {
this.userName = name;
}
/**
* Attempts to connect to the server specified in the parameters.
*
* @param host The hostname of the server to connect to.
* @param port The port at which we want to connect to the server.
* @param username The username that we want to use for this connection.
* @return True if the connection was successful, and false if it failed.
*/
public boolean connectToServer(final String host,
final int port,
final String username) {
boolean result = false;
List data = new Vector();
AbstractMessage message = null;
if (username != null) {
setUsername(username);
}
//Connect to the server
result = network.connect(host, port);
if (!result) {
//Could not connect to the server...
//Tell ClientUI to issue a warning saying the connection failed.
ClientUI.getInstance().showWarning("Connection Failed",
"Could not connect to the server.");
return false;
}
//Issue a helo message
data.add(getUsername());
message = MessageFactory.getInstance()
.makeMessage(MessageSettings.JOIN_TYPE, data);
result = sendMessage(message);
if (!result) {
//Failed to send the HELO message
if (ClientSettings.VERBOSE) {
System.out.println("Client: Could not send HELO message...");
}
//Tell ClientUI to issue a warning
ClientUI.getInstance().showWarning("Connection Failed",
"Could not send a join message to"
+ " the server.");
network.shutDown();
return false;
}
return true;
}
/**
* This function will issue a QUIT message to the server and then
* tell client networking to shut down.
*/
public void disconnectFromServer() {
List data = new Vector();
AbstractMessage message = null;
if (!network.isConnected()) {
return;
}
//Issue a quit message:
data.add(getUsername());
message = MessageFactory.getInstance()
.makeMessage(MessageSettings.QUIT_TYPE, data);
sendMessage(message);
//Shut down networking:
network.shutDown();
}
/**
* Returns true if the client is currrently connected to the server,
* and false if he is not.
*
* @return a <code>boolean</code> value
*/
public boolean isConnected() {
return network.isConnected();
}
/**
* Sends a message to the server. If the send succeeds, returns
* true. Otherwise returns false.
*
* @param message an <code>AbstractMessage</code> value
* @return a <code>boolean</code> value
*/
public boolean sendMessage(final AbstractMessage message) {
if (message == null) {
return false;
}
if (!network.isConnected()) {
return false;
}
network.sendMessage(message);
return true;
}
/**
* Returns an iterator for the messages in the list of graphics.
*
* @return a <code>MessageListIterator</code> value
*/
public GraphicMessageListIterator getGraphicsListIterator() {
return messages.getMessageIterator();
}
/**
* Returns the last received graphic message.
*
* @return a <code>GraphicMessage</code> value
*/
public GraphicMessage getLastGraphicMessage() {
return messages.getLastGraphicMessage();
}
/**
* Undoes the last graphic message by this client.
*/
public void undoLast() {
//Get the last message:
GraphicMessage last = messages.findLastMessage();
if (last == null) { return; }
undoMessage(last.getMessageId());
}
/**
* Undoes the message with the specified ID.
*
* @param messageId an <code>int</code> value
*/
public void undoMessage(final int messageId) {
List data = new Vector();
data.add(new Integer(-1));
data.add(getUsername());
data.add(new Integer(messageId));
//Issue undo message:
sendMessage(MessageFactory.getInstance()
.makeMessage(MessageSettings.DRAW_UNDO_TYPE, data));
}
/**
* Undoes all the messages.
*/
public void undoAll() {
while (messages.size() > 0) {
undoMessage(getLastGraphicMessage().getMessageId());
}
}
/**
* Returns an iterator for the messages in the list of chat messages.
*
* @return a <code>MessageListIterator</code> value
*/
public ChatMessageListIterator getChatListIterator() {
return messages.getChatIterator();
}
/**
* This function receives the message from the server. It needs to
* check the type of message first. Then, if the message is a
* graphics message, this should send it to the message list, and
* tell the graphics panel to rerender. If it is a chat message, it
* should be sent to the message list, and then to the chat
* panel. Etc.
*
* @param message an <code>AbstractMessage</code> value
*/
public void processMessage(final AbstractMessage message) {
if (message instanceof ServerErrorMessage) {
processServerError((ServerErrorMessage) message);
} else if (message instanceof UndoMessage) {
messages.removeMessage(((UndoMessage) message).getUndoId());
ClientUI.getInstance().getGraphicsPanel().markForRerender(true);
} else if (message instanceof GraphicMessage) {
messages.add((GraphicMessage) message);
ClientUI.getInstance().getGraphicsPanel().markForRerender(true);
} else if (message instanceof ChatMessage) {
messages.add((ChatMessage) message);
}
ClientUI.getInstance().getUndoPanel().updateList();
ClientUI.getInstance().sendMessageToChat(message);
}
/**
* This function will be called by the server listener when a server
* error message is received. It will check the message to see if it
* is fatal, and in case it is, it will shut down the
* connection. Otherwise, the connection will remain.
*
* @param message a <code>ServerErrorMessage</code> value
*/
public void processServerError(final ServerErrorMessage message) {
ClientUI.getInstance().showError("Server Error",
"Received a message from the server: \""
+ message.getServerMessage() + "\"");
disconnectFromServer();
}
/**
* Returns the current save file name.
*
* @return a <code>String</code> value
*/
public String getCurrentSaveName() {
return this.currentSaveName;
}
/**
* This function will save the current session to a file. It will
* first ask the client UI to issue a file save dialog and provide a
* filename to save the session to. It will then ask the message
* list to save its contents to the file specified by the user. It
* will also ask the client UI to issue warnings in case of an
* error.
*
* @param forceNew If this is true, then this will ask for the new
* save file name. Otherwise, will write to the previously saved to
* file if possible.
*/
public synchronized void saveSession(final boolean forceNew) {
String filename = getCurrentSaveName();
if (filename == null || forceNew) {
filename = ClientUI.getInstance().browseSaveFileName(filename);
}
if (filename == null || filename.equals("")) {
//Browsing failed somehow or was cancelled.
return;
}
if (!fileWriter.openFile(filename)) {
ClientUI.getInstance().showError("Could not Save",
"Could not open file " + filename
+ " to save the session. ");
return;
}
if (!fileWriter.writeMessages(messages)) {
ClientUI.getInstance().showError("Could not Save",
"Could not save the messages"
+ " to file " + filename);
}
fileWriter.closeFile();
currentSaveName = filename;
}
/**
* This function will load the current session from a file. It will
* first ask the client UI to issue a file load dialog to let the
* user specify the file to load the session from. It will then ask
* the message list class to load the messages from the file
* specified by the user.
*/
public synchronized void loadSession() {
String filename = ClientUI.getInstance()
.browseLoadFileName(getCurrentSaveName());
if (filename == null || filename.equals("")) {
return;
}
if (!fileReader.openFile(filename)) {
ClientUI.getInstance().showError("Could not Load",
"Could not open file " + filename
+ " to load the session. ");
return;
}
MessageList temp = fileReader.readFile();
if (temp == null) {
ClientUI.getInstance().showError("Could not Load",
"Could not load from file "
+ filename);
return;
}
//Ask if we want to overwrite all data on the server. If so,
//undo all current messages, and send out all those that were
//loaded. Otherwise, just overwrite what we have with the new
//messages.
if (isConnected()) {
boolean overwrite
= ClientUI.getInstance().showYesNoDialog("Send Messages?",
"Would you like to overwrite the messages on the server with the"
+ " ones that were just loaded?",
"Yes", "No");
if (overwrite) {
synchronizeServer(temp);
} else {
messages = temp;
ClientUI.getInstance().showGraphicsWindow();
}
} else {
messages = temp;
ClientUI.getInstance().showGraphicsWindow();
}
if (ClientSettings.VERBOSE) {
System.out.println("Client: Loaded " + messages.size()
+ " messages from " + filename + ".");
System.out.flush();
}
currentSaveName = filename;
ClientUI.getInstance().getGraphicsPanel().markForRerender(true);
}
/**
* Sends out all of the messages to the server.
* @param list The message list that we will use to synchronize the
* server with.
*/
public void synchronizeServer(final MessageList list) {
if (isConnected()) {
//Undo messages
undoAll();
//Send out messages
GraphicMessageListIterator gi = list.getMessageIterator();
while (gi.hasNext()) {
sendMessage(gi.next());
}
ChatMessageListIterator ci = list.getChatIterator();
while (ci.hasNext()) {
sendMessage(ci.next());
}
}
}
/**
* This function will be called by the server listener when the
* server drops the connection. It will take appropriate steps to
* shut down the connection and notify the user of the problem.
*/
public void serverQuit() {
ClientUI.getInstance().showWarning("Server Lost",
"The connection to the server has "
+ "been lost...");
network.shutDown();
}
/**
* This function will be called when the client wants to quit. It
* will close the connection to the server, and do all the other
* nice clean-up operations we need to do, after which it will
* terminate with a System.exit.
*/
public synchronized void clientQuit() {
disconnectFromServer();
System.exit(0);
}
}

View File

@@ -0,0 +1,66 @@
package netpaint.client;
import netpaint.client.gui.ClientUI;
/**
* Class ClientRunner: This class will run the client.
*
* <PRE>
* Revision History:
* v1.0 (Feb. 13, 2004) - Created the ClientRunner class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Feb. 13, 2004
*/
public final class ClientRunner {
/**
* Creates a new <code>ClientRunner</code> instance.
*/
private ClientRunner() {
}
/**
* Create the GUI and show it. For thread safety, this method
* should be invoked from the event-dispatching thread. This is
* purely for the chat testing main.
*/
private static void createAndShowGUI() {
//Make sure we have nice window decorations.
javax.swing.JFrame.setDefaultLookAndFeelDecorated(true);
//Start up the main window
ClientUI.getInstance().startMainWindow();
//Show the welcome dialog:
boolean choice = ClientUI.getInstance()
.showYesNoDialog("Welcome to NetPaint",
"You can either connect to a server, or load a pre-"
+ "saved session from an XML file.",
"Connect", "Load");
if (choice) {
ClientUI.getInstance().showConnectionDialog();
} else {
Client.getInstance().loadSession();
}
}
/**
* Main method for running the ClientUI class. This is basically a
* temporary main method that will allow us to test out the stuff in
* this class.
*
* @param args a String array of command line arguments.
*/
public static void main(final String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}

View File

@@ -0,0 +1,36 @@
package netpaint.client;
/**
* Class ClientSettings: Contains some settings shared among all
* client classes.
*
* <PRE>
* Revision History:
* v1.0 (Feb. 01, 2004) - Created the ClientSettings class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Feb. 01, 2004
*/
public final class ClientSettings {
/**
* This is the location where all the images used in the program
* will be located. Note that this is not to say: images downloaded
* during the sessions, but rather the images associated with the
* program - button icons, and such.
*/
public static final String IMAGE_LOCATION = "Images/";
/**
* Produce extra debug output?
*/
public static final boolean VERBOSE = false;
/**
* Creates a new <code>ClientSettings</code> instance.
*/
private ClientSettings() {
}
}

View File

@@ -0,0 +1,73 @@
package netpaint.client;
import netpaint.messaging.GraphicMessage;
/**
* Class GraphicMessageListIterator: Provides a convenient iterator for the
* list of graphics messages.
*
* <PRE>
* Revision History:
* v1.0 (Jan. 31, 2004) - Created the GraphicMessageListIterator class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Jan. 31, 2004
*/
public class GraphicMessageListIterator {
/**
* The list of messages to iterate through.
*/
private MessageList list;
/**
* The index at which we are currently pointing.
*/
private int currentIndex;
/**
* Creates a new <code>GraphicMessageListIterator</code> instance.
* @param messages a <code>MessageList</code> value
*/
protected GraphicMessageListIterator(final MessageList messages) {
if (ClientSettings.VERBOSE) {
System.out.println("GMLI: Making new iterator... Size: "
+ messages.size());
System.out.flush();
}
this.list = messages;
this.currentIndex = 0;
}
/**
* Returns true if there is another message to be iterated through
* still left, or false if we are at the end of the list.
*
* @return a <code>boolean</code> value
*/
public final boolean hasNext() {
return (currentIndex < list.size());
}
/**
* Returns the next message and increments the iterator.
*
* @return a <code>GraphicMessage</code> value
*/
public final GraphicMessage next() {
if (ClientSettings.VERBOSE) {
System.out.println("GMLI: Getting next graphic message...");
System.out.flush();
}
GraphicMessage result = list.graphicElementAt(currentIndex);
currentIndex++;
return result;
}
}

View File

@@ -0,0 +1,262 @@
package netpaint.client;
import java.util.List;
import java.util.Vector;
import netpaint.messaging.GraphicMessage;
import netpaint.messaging.ChatMessage;
/**
* Class MessageList: This is the list of messages received by the
* client. As they are received, they will be inserted sorted by their
* id number, so that when it is time to render the messages, they
* will all apear in the same order on all machines.
*
* <PRE>
* Revision History:
* v1.0 (Jan. 31, 2004) - Created the MessageList class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Jan. 31, 2004
*/
public class MessageList {
/**
* Contains the list of graphic messages received by the client so
* far.
*/
private List graphicMessages;
/**
* Contains the list of chat messages received by the client so far.
*/
private List chatMessages;
/**
* Creates a new <code>MessageList</code> instance.
*/
public MessageList() {
graphicMessages = new Vector();
chatMessages = new Vector();
}
/**
* Adds the message to the list of graphic messages.
*
* @param message a <code>GraphicMessage</code> value
*/
public final void add(final GraphicMessage message) {
int messageId = message.getMessageId();
if (this.graphicMessages.size() == 0) {
graphicMessages.add(message);
return;
}
for (int index = graphicMessages.size() - 1; index >= 0; index--) {
if (messageId > ((GraphicMessage) graphicMessages.get(index))
.getMessageId()) {
graphicMessages.add(index + 1, message);
break;
}
}
}
/**
* Adds the message to the list of chat messages.
*
* @param message a <code>GraphicMessage</code> value
*/
public final void add(final ChatMessage message) {
chatMessages.add(message);
}
/**
* Clears out the lists of messages received by the client.
*/
public final void clear() {
this.graphicMessages.clear();
this.chatMessages.clear();
}
/**
* Describe <code>get</code> method here.
*
* @param index an <code>int</code> value
* @return a <code>GraphicMessage</code> value
*/
protected final ChatMessage chatElementAt(final int index) {
return (ChatMessage) chatMessages.get(index);
}
/**
* Describe <code>get</code> method here.
*
* @param index an <code>int</code> value
* @return a <code>GraphicMessage</code> value
*/
protected final GraphicMessage graphicElementAt(final int index) {
return (GraphicMessage) graphicMessages.get(index);
}
/**
* Describe <code>getGraphicMessage</code> method here.
*
* @param messageId an <code>int</code> value
* @return a <code>GraphicMessage</code> value
*/
public final GraphicMessage getGraphicMessage(final int messageId) {
if (this.graphicMessages.size() == 0) {
return null;
}
for (int index = 0; index < graphicMessages.size(); index++) {
if (messageId == ((GraphicMessage) graphicMessages.get(index))
.getMessageId()) {
return (GraphicMessage) graphicMessages.get(index);
}
}
return null;
}
/**
* Returns the last received graphic message. The reason we need
* this is so that the graphicpanel could only redraw the last
* graphic message, as opposed to everything, thus improving
* perofrmance.
*
* @return a <code>GraphicMessage</code> value
*/
public final GraphicMessage getLastGraphicMessage() {
if (graphicMessages.size() == 0) {
return null;
}
return graphicElementAt(graphicMessages.size() - 1);
}
/**
* Describe <code>getIterator</code> method here.
*
* @return a <code>ChatMessageListIterator</code> value
*/
public final ChatMessageListIterator getChatIterator() {
return new ChatMessageListIterator(this);
}
/**
* Describe <code>getIterator</code> method here.
*
* @return a <code>GraphicMessageListIterator</code> value
*/
public final GraphicMessageListIterator getMessageIterator() {
return new GraphicMessageListIterator(this);
}
/**
* Tells whether both lists are empty.
*
* @return a <code>boolean</code> value
*/
public final boolean isEmpty() {
return (graphicMessages.isEmpty() && chatMessages.isEmpty());
}
/**
* Removes the message with the specified id from the list. If
* found, returns the removed message. Otherwise, returns null.
*
* @param messageId an <code>int</code> value
* @return a <code>GraphicMessage</code> value
*/
public final GraphicMessage removeMessage(final int messageId) {
if (this.graphicMessages.size() == 0) {
return null;
}
for (int index = 0; index < graphicMessages.size(); index++) {
if (messageId == ((GraphicMessage) graphicMessages.get(index))
.getMessageId()) {
return (GraphicMessage) graphicMessages.remove(index);
}
}
return null;
}
/**
* Returns the number of graphic messages received by the user.
*
* @return an <code>int</code> value
*/
public final int size() {
return graphicMessages.size();
}
/**
* Returns the number of messages in the chat message list.
*@return the size of the message
*/
protected final int numChatMessages() {
return chatMessages.size();
}
/**
* Returns the last message sent by a particular user, and
* returns it.
*
* @return GraphicMessage
*/
public final GraphicMessage findLastMessage() {
String userName = (Client.getInstance()).getUsername();
if (this.graphicMessages.size() == 0) {
return null;
}
for (int index = graphicMessages.size() - 1; 0 <= index; index--) {
GraphicMessage temp = (GraphicMessage) (graphicMessages.get(index));
if (userName.equals(temp.getAuthor())) {
return temp;
}
}
return null;
}
/**
* Describe <code>saveMessageList</code> method here.
*
* @param filename a <code>String</code> value
* @return a <code>boolean</code> value
*/
public final boolean saveMessageList(final String filename) {
return false;
}
/**
* Describe <code>loadMessageList</code> method here.
*
* @param filename a <code>String</code> value
* @return a <code>boolean</code> value
*/
public final boolean loadMessageList(final String filename) {
return false;
}
/**
* Describe <code>printMessages</code> method here.
*
*/
public final void printMessages() {
GraphicMessageListIterator i = getMessageIterator();
while (i.hasNext()) {
System.out.println("Message: " + i.next().tokenize());
}
}
}

View File

@@ -0,0 +1,323 @@
package netpaint.client.file;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.File;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.Attributes;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.parsers.SAXParser;
import org.xml.sax.SAXException;
import netpaint.client.MessageList;
import netpaint.messaging.GraphicMessage;
import netpaint.messaging.ChatMessage;
import netpaint.messaging.AbstractMessage;
import netpaint.messaging.MessageFactory;
/**
*
* Class FileReader: This is a filereader class.
*
* <PRE>
* Revision History:
* v1.0 (Jan. 31, 2004) - Created the FileReader class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Jan. 31, 2004
*/
public final class FileReader extends DefaultHandler {
/**
* This will display lots of output about the process of parsing.
*/
private static final boolean VERBOSE = false;
/**
* This is where the output will be written.
*/
private BufferedReader inStream;
/**
* Stores the name of the file that we are reading from.
*/
private String fileName;
/**
* This is the list that the file data will be loaded into.
*/
private MessageList myMessages;
/**
* The message that we are currently parsing.
*/
private AbstractMessage currentMessage;
/**
* The name of the current message.
*/
private String currentMessageName;
/**
* The name of the tag currently being parsed.
*/
private String currentName;
/**
* The value that has just been read from the file.
*/
private String currentValue;
/**
* Creates a new <code>FileReader</code> instance.
*/
public FileReader() {
inStream = null;
}
/**
* Tries to open the file with the name passed in for writing.
*
* @param filename a <code>String</code> value
* @return a <code>boolean</code> value
*/
public boolean openFile(final String filename) {
if (filename == null) {
return false;
}
try {
inStream = new BufferedReader(new java.io.FileReader(filename));
} catch (IOException ioe) {
System.out.println("FileReader: Got IOException");
System.out.println("\t" + ioe.getMessage());
System.out.flush();
return false;
}
fileName = filename;
return true;
}
/**
* If a file is currently open, closes it.
*/
public void closeFile() {
if (isFileOpen()) {
try {
inStream.close();
} catch (Exception e) {
System.out.println("FileReader: Could not close file.");
}
inStream = null;
fileName = null;
}
}
/**
* Returns true if the file is open and ready to be written to.
*
* @return a <code>boolean</code> value
*/
public boolean isFileOpen() {
return (inStream != null);
}
/**
* Reads the file if it is open, and if it is not, just returns false.
* @return a <code>MessageList</code> value
*/
public MessageList readFile() {
// Use the default (non-validating) parser
SAXParserFactory factory = SAXParserFactory.newInstance();
try {
// Parse the input
SAXParser saxParser = factory.newSAXParser();
saxParser.parse(new File(fileName), this);
} catch (Exception exc) {
System.out.println("FileReader: Got an error:");
System.out.println("\t" + exc.getClass().getName() + ": "
+ exc.getMessage());
return null;
}
if (VERBOSE) {
System.out.println("FileReader: Final Message Count - "
+ myMessages.size());
System.out.flush();
}
return myMessages;
}
/**
* Receive notification of the beginning of the document.
*/
public void startDocument() {
myMessages = new MessageList();
}
/**
* Processes the start of an XML element.
*
* @param uri a <code>String</code> value
* @param localName a <code>String</code> value
* @param qName a <code>String</code> value
* @param att an <code>Attributes</code> value
* @exception SAXException if an error occurs
*/
public void startElement(final String uri,
final String localName,
final String qName,
final Attributes att) throws SAXException {
if (VERBOSE) {
System.out.println("FileReader: Got element start - " + qName);
System.out.flush();
}
String name = localName;
if (name == null || name.equals("")) {
name = qName;
}
if (!qName.equals(XMLSettings.ROOT_ELEMENT_NAME)) {
if (currentMessage == null) {
String author = null;
String messageType = null;
String attributeName = null;
//Parse the author and type attributes and create a new
//network message
for (int i = 0; i < att.getLength(); i++) {
attributeName = att.getLocalName(i);
if (attributeName == null || attributeName.equals("")) {
attributeName = att.getQName(i);
}
if (VERBOSE) {
System.out.println("FileReader: For attribute - "
+ name);
}
if (attributeName.equals("author")) {
author = att.getValue(i);
} else if (attributeName.equals("type")) {
messageType = att.getValue(i);
} else {
System.out.println("FileReader: Unknown attribute: "
+ attributeName + "="
+ att.getValue(i));
}
}
if (author == null || messageType == null) {
System.out.println("FileReader: Could not read message.");
return;
}
currentMessage
= MessageFactory.getInstance().makeMessage(messageType,
author);
currentMessageName = name;
} else {
currentName = localName;
}
}
}
/**
* Processes string data within an XML element.
*
* @param ch a <code>char[]</code> value
* @param start an <code>int</code> value
* @param length an <code>int</code> value
* @exception SAXException if an error occurs
*/
public void characters(char[] ch,
int start,
int length) throws SAXException {
currentValue = new String(ch, start, length);
if (VERBOSE) {
System.out.println("FileReader: Got element text - "
+ currentValue);
System.out.flush();
}
}
/**
* Processes the end of an XML element.
*
* @param uri a <code>String</code> value
* @param localName a <code>String</code> value
* @param qName a <code>String</code> value
* @exception SAXException if an error occurs
*/
public void endElement(final String uri,
final String localName,
final String qName) throws SAXException {
String name = localName;
if (name == null || name.equals("")) {
name = qName;
}
if (VERBOSE) {
System.out.println("FileReader: Got end element - "
+ name);
System.out.flush();
}
if (currentMessage == null
&& !name.equals(XMLSettings.ROOT_ELEMENT_NAME)) {
System.out.println("FileReader: Unmatched closing tag... "
+ name);
return;
}
if (currentMessageName == name) {
//Finished message
if (VERBOSE) {
System.out.println("FileReader: Finished message - "
+ name);
System.out.println("\t" + currentMessage.tokenize());
System.out.flush();
}
if (currentMessage instanceof GraphicMessage) {
myMessages.add((GraphicMessage) currentMessage);
} else if (currentMessage instanceof ChatMessage) {
myMessages.add((ChatMessage) currentMessage);
} else {
System.out.println("FileReader: Got unknown message type.");
}
if (VERBOSE) {
System.out.println("FileReader: New Message Count - "
+ myMessages.size());
System.out.flush();
}
currentMessage = null;
} else if (!name.equals(XMLSettings.ROOT_ELEMENT_NAME)) {
currentMessage.processXMLElement(name, currentValue);
currentName = null;
currentValue = null;
}
}
/**
* Will be called when the end of the document is reached.
*/
public void endDocument() {
if (VERBOSE) {
System.out.println("FileReader: Finished reading the document.");
}
}
}

View File

@@ -0,0 +1,181 @@
package netpaint.client.file;
import java.io.PrintWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import netpaint.client.MessageList;
import netpaint.client.ChatMessageListIterator;
import netpaint.client.GraphicMessageListIterator;
import netpaint.messaging.GraphicMessage;
import netpaint.messaging.ChatMessage;
/**
*
* Class FileWriter: This is a filewriter class.
*
* <PRE>
* Revision History:
* v1.0 (Jan. 31, 2004) - Created the FileWriter class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Jan. 31, 2004
*/
public final class FileWriter {
/**
* This is where the output will be written.
*/
private PrintWriter outStream;
/**
* Creates a new <code>FileWriter</code> instance.
*/
public FileWriter() {
outStream = null;
}
/**
* Tries to open the file with the name passed in for writing.
*
* @param filename a <code>String</code> value
* @return a <code>boolean</code> value
*/
public boolean openFile(final String filename) {
if (filename == null) {
return false;
}
try {
outStream = new PrintWriter(new FileOutputStream(filename));
} catch (IOException ioe) {
System.out.println("FileWriter: Got IOException");
System.out.println("\t" + ioe.getMessage());
System.out.flush();
return false;
}
return true;
}
/**
* If a file is currently open, closes it.
*/
public void closeFile() {
if (isFileOpen()) {
outStream.close();
outStream = null;
}
}
/**
* Returns true if the file is open and ready to be written to.
*
* @return a <code>boolean</code> value
*/
public boolean isFileOpen() {
return (outStream != null);
}
/**
* Writes the messages to the file.
*
* @param messages a <code>MessageList</code> value
* @return true if everything was written successfully, and false if
* there was a problem.
*/
public boolean writeMessages(final MessageList messages) {
if (messages == null || !isFileOpen()) {
return false;
}
GraphicMessageListIterator gi = messages.getMessageIterator();
ChatMessageListIterator ci = messages.getChatIterator();
try {
//Print out the start of the document.
emit("<?xml version='1.0'?>");
nl();
nl();
//Print out the start of the root element.
emit("<" + XMLSettings.ROOT_ELEMENT_NAME + ">");
nl();
//Print out graphics messages
while (gi.hasNext()) {
GraphicMessage gm = gi.next();
String[] properties = gm.propertiesToXML();
emit(gm.beginElement());
nl();
if (properties != null) {
for (int i = 0; i < properties.length; i++) {
emit(properties[i]);
nl();
}
}
emit(gm.endElement());
nl();
}
nl();
nl();
//Print out chat messages
while (ci.hasNext()) {
ChatMessage cm = ci.next();
String[] properties = cm.propertiesToXML();
emit(cm.beginElement());
nl();
for (int i = 0; i < properties.length; i++) {
emit(properties[i]);
nl();
}
emit(cm.endElement());
nl();
}
nl();
//Print out the end of the root element.
emit("</" + XMLSettings.ROOT_ELEMENT_NAME + ">");
nl();
} catch (IOException ioe) {
System.out.println("FileWriter: Got IOException");
System.out.println("\t" + ioe.getMessage());
System.out.flush();
return false;
}
return true;
}
/**
* Writes to the output stream.
*
* @param s a <code>String</code> value
* @exception IOException if an error occurs
*/
private void emit(final String s) throws IOException {
outStream.print(s);
outStream.flush();
}
/**
* Prints out a new line.
*
* @exception IOException if an error occurs
*/
private void nl() throws IOException {
String lineEnd = System.getProperty("line.separator");
outStream.print(lineEnd);
}
}

View File

@@ -0,0 +1,28 @@
package netpaint.client.file;
/**
* Class XMLSettings: The XML Settings class contains some settings
* for the reading and writing of the XML save files.
*
* <PRE>
* Revision History:
* v1.0 (Feb. 11, 2004) - Created the XMLSettings class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Feb. 11, 2004
*/
public class XMLSettings {
/**
* The name of the root element of the XML.
*/
public static final String ROOT_ELEMENT_NAME = "session";
/**
* Creates a new <code>XMLSettings</code> instance.
*/
public XMLSettings() {
}
}

View File

@@ -0,0 +1,5 @@
<body>
Package <code>netpaint.client.file</code> contains the classes used
for file IO. Here, are contained classes that allow the saving and
loading of the client sessions for future reference.
</body>

View File

@@ -0,0 +1,472 @@
package netpaint.client.gui;
import java.util.List;
import java.util.Vector;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.JTextPane;
import javax.swing.JTextField;
import javax.swing.JScrollPane;
import javax.swing.JInternalFrame;
import javax.swing.ImageIcon;
import javax.swing.text.Style;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;
import javax.swing.text.StyleConstants;
import javax.swing.text.BadLocationException;
import netpaint.messaging.JoinMessage;
import netpaint.messaging.QuitMessage;
import netpaint.messaging.ChatMessage;
import netpaint.messaging.MessageFactory;
import netpaint.messaging.MessageSettings;
import netpaint.messaging.AbstractMessage;
import netpaint.messaging.AbstractServerMessage;
import netpaint.messaging.AdministrativeMessage;
import netpaint.client.Client;
/**
* Class ChatPanel: The chat panel allows the user to chat with the
* other users. It will receive chat messages and show them in one of
* the text fields.
*
* <PRE>
* Revision History:
* v1.0 (Jan. 31, 2004) - Created the ChatPanel class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Jan. 31, 2004
*/
public class ChatPanel extends JInternalFrame implements ActionListener {
/**
* This is where all of the chat messages received by the client are
* shown.
*/
private JTextPane contents;
/**
* This is where the user will type in the chat message.
*/
private JTextField inputField;
/**
* When this button is clicked, the message typed into the input
* field will be sent out.
*/
private JButton sendButton;
/**
* Creates a new <code>ChatPanel</code> instance.
*/
public ChatPanel() {
super(ClientUISettings.TITLE_CHAT, //Title
true, //Resizable
true, //Closable
true, //Maximizable
true); //Iconifiable
//super(new BorderLayout());
JPanel contentPane = new JPanel(new BorderLayout());
JPanel inputPanel = new JPanel(new BorderLayout());
JScrollPane contentScrollPane = null;
contents = createTextPane();
inputField = new JTextField();
sendButton = new JButton("Send");
contentScrollPane = new JScrollPane(contents);
contentScrollPane.setVerticalScrollBarPolicy(
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
contents.setEditable(false);
sendButton.setActionCommand("send");
sendButton.addActionListener(this);
inputField.addActionListener(this);
inputPanel.add(inputField, BorderLayout.CENTER);
inputPanel.add(sendButton, BorderLayout.EAST);
contentPane.add(contentScrollPane, BorderLayout.CENTER);
contentPane.add(inputPanel, BorderLayout.SOUTH);
this.setContentPane(contentPane);
//Don't close the chat window upon close attempt. Just hide it.
this.setDefaultCloseOperation(JInternalFrame.HIDE_ON_CLOSE);
}
/**
* Does all of the necessary initialization for the chat content text pane.
*
* @return a <code>JTextPane</code> value
*/
private JTextPane createTextPane() {
JTextPane result = new JTextPane();
StyledDocument sdoc = result.getStyledDocument();
addChatStyles(sdoc);
addImageStyles(sdoc);
return result;
}
/**
* Adds the chat text styles to the styled document passed in.
*
* @param document a <code>StyledDocument</code> value
*/
private void addChatStyles(final StyledDocument document) {
Style def = StyleContext.getDefaultStyleContext()
.getStyle(StyleContext.DEFAULT_STYLE);
Style regular = document.addStyle(ClientUISettings.CHAT_STYLE_TEXT, def);
StyleConstants.setFontFamily(regular, ClientUISettings.CHAT_FONT_FAMILY);
StyleConstants.setForeground(regular, ClientUISettings.CHAT_COLOR_TEXT);
Style name = document.addStyle(ClientUISettings.CHAT_STYLE_SELF, regular);
StyleConstants.setForeground(name, ClientUISettings.CHAT_COLOR_SELF);
name = document.addStyle(ClientUISettings.CHAT_STYLE_USER, regular);
StyleConstants.setForeground(name, ClientUISettings.CHAT_COLOR_USER);
name = document.addStyle(ClientUISettings.CHAT_STYLE_CONNECTED, regular);
StyleConstants.setForeground(name, ClientUISettings.CHAT_COLOR_CONNECTED);
StyleConstants.setBold(name, true);
name = document.addStyle(ClientUISettings.CHAT_STYLE_ADMIN, name);
StyleConstants.setForeground(name, ClientUISettings.CHAT_COLOR_ADMIN);
name = document.addStyle(ClientUISettings.CHAT_STYLE_SERV, name);
StyleConstants.setForeground(name, ClientUISettings.CHAT_COLOR_SERV);
}
/**
* Adds the smilies icons into the styled document.
*
* @param doc a <code>StyledDocument</code> value
*/
private void addImageStyles(final StyledDocument doc) {
Style def = StyleContext.getDefaultStyleContext()
.getStyle(ClientUISettings.CHAT_STYLE_TEXT);
Style s = null;
BufferedImage smilie = null;
//Smile
s = doc.addStyle(ClientUISettings.CHAT_SMILE, def);
StyleConstants.setAlignment(s, StyleConstants.ALIGN_CENTER);
smilie
= ImageLoader.getInstance().loadSystemImage(ClientUISettings.SMILE_ICON);
if (smilie != null) {
StyleConstants.setIcon(s, new ImageIcon(smilie));
}
//Wink
s = doc.addStyle(ClientUISettings.CHAT_WINK, def);
StyleConstants.setAlignment(s, StyleConstants.ALIGN_CENTER);
smilie
= ImageLoader.getInstance().loadSystemImage(ClientUISettings.WINK_ICON);
if (smilie != null) {
StyleConstants.setIcon(s, new ImageIcon(smilie));
}
//Frown
s = doc.addStyle(ClientUISettings.CHAT_FROWN, def);
StyleConstants.setAlignment(s, StyleConstants.ALIGN_CENTER);
smilie
= ImageLoader.getInstance().loadSystemImage(ClientUISettings.FROWN_ICON);
if (smilie != null) {
StyleConstants.setIcon(s, new ImageIcon(smilie));
}
//Tongue
s = doc.addStyle(ClientUISettings.CHAT_TONGUE, def);
StyleConstants.setAlignment(s, StyleConstants.ALIGN_CENTER);
smilie = ImageLoader.getInstance()
.loadSystemImage(ClientUISettings.TONGUE_ICON);
if (smilie != null) {
StyleConstants.setIcon(s, new ImageIcon(smilie));
}
//Grin
s = doc.addStyle(ClientUISettings.CHAT_GRIN, def);
StyleConstants.setAlignment(s, StyleConstants.ALIGN_CENTER);
smilie
= ImageLoader.getInstance().loadSystemImage(ClientUISettings.GRIN_ICON);
if (smilie != null) {
StyleConstants.setIcon(s, new ImageIcon(smilie));
}
}
/**
* Prints the message onto the contents text pane.
*
* @param message an <code>AbstractMessage</code> value
*/
public final synchronized void
receiveMessage(final AbstractMessage message) {
StyledDocument doc = contents.getStyledDocument();
try {
if (message instanceof AbstractServerMessage) {
//Insert server name
doc.insertString(doc.getLength(),
message.getAuthor() + ": ",
doc.getStyle(ClientUISettings.CHAT_STYLE_SERV));
//Insert message text
doc.insertString(doc.getLength(),
((AbstractServerMessage) message).getServerMessage()
+ "\n",
doc.getStyle(ClientUISettings.CHAT_STYLE_TEXT));
} else if (message instanceof ChatMessage) {
ChatMessage cm = (ChatMessage) message;
StyledText[] styled = parseMessage(cm.getChatMessage());
if (cm.getAuthor().equals(Client.getInstance().getUsername())) {
//My message
doc.insertString(doc.getLength(),
cm.getAuthor() + ": ",
doc.getStyle(ClientUISettings.CHAT_STYLE_SELF));
} else {
//Somebody else's message
doc.insertString(doc.getLength(),
cm.getAuthor() + ": ",
doc.getStyle(ClientUISettings.CHAT_STYLE_USER));
}
//Insert message text
for (int i = 0; i < styled.length; i++) {
doc.insertString(doc.getLength(),
styled[i].getText(),
doc.getStyle(styled[i].getStyle()));
}
doc.insertString(doc.getLength(),
"\n",
doc.getStyle(ClientUISettings.CHAT_STYLE_TEXT));
} else if (message instanceof AdministrativeMessage) {
Style s = null;
String action = null;
if (message.getAuthor().equals(Client.getInstance().getUsername())) {
//My message
s = doc.getStyle(ClientUISettings.CHAT_STYLE_CONNECTED);
} else {
//Somebody else's message
s = doc.getStyle(ClientUISettings.CHAT_STYLE_ADMIN);
}
if (message instanceof JoinMessage) {
action = "joined";
} else if (message instanceof QuitMessage) {
action = "quit";
}
doc.insertString(doc.getLength(),
message.getAuthor() + " has " + action
+ " the session.\n",
s);
} else {
return;
}
} catch (BadLocationException ble) {
System.err.println("ChatPanel: Could not insert text into the panel...");
}
//Move the caret position to the end.
contents.setCaretPosition(doc.getLength());
}
/**
* Parses the text and returns an array of styled bits.
*
* @param text a <code>String</code> value
* @return a <code>StyledText[]</code> value
*/
private StyledText[] parseMessage(final String text) {
List list = new Vector();
String currentString = "";
Object[] ores = null;
StyledText[] result = null;
for (int i = 0; i < text.length(); i++) {
char currentChar = text.charAt(i);
if (currentChar == ':') {
if (i == (text.length() - 1)) { break; }
if (text.charAt(i + 1) == ')') {
//Got Smile Smilie...
list.add(new StyledText(currentString,
ClientUISettings.CHAT_STYLE_TEXT));
list.add(new StyledText(":)",
ClientUISettings.CHAT_SMILE));
i++;
currentString = "";
} else if (text.charAt(i + 1) == '(') {
//Got Frown Smilie...
list.add(new StyledText(currentString,
ClientUISettings.CHAT_STYLE_TEXT));
list.add(new StyledText(":(",
ClientUISettings.CHAT_FROWN));
i++;
currentString = "";
} else if (text.charAt(i + 1) == 'D') {
//Got Grin Smilie...
list.add(new StyledText(currentString,
ClientUISettings.CHAT_STYLE_TEXT));
list.add(new StyledText(":D",
ClientUISettings.CHAT_GRIN));
i++;
currentString = "";
} else if (text.charAt(i + 1) == 'p') {
//Got tongue Smilie...
list.add(new StyledText(currentString,
ClientUISettings.CHAT_STYLE_TEXT));
list.add(new StyledText(":p",
ClientUISettings.CHAT_TONGUE));
i++;
currentString = "";
} else {
currentString += ":";
}
} else if (currentChar == ';') {
if (i == (text.length() - 1)) { break; }
if (text.charAt(i + 1) == ')') {
//Got Wink Smilie...
list.add(new StyledText(currentString,
ClientUISettings.CHAT_STYLE_TEXT));
list.add(new StyledText(";)",
ClientUISettings.CHAT_WINK));
i++;
currentString = "";
} else {
currentString += ";";
}
} else {
currentString += Character.toString(currentChar);
}
}
if (!currentString.equals("")) {
list.add(new StyledText(currentString,
ClientUISettings.CHAT_STYLE_TEXT));
}
ores = list.toArray();
result = new StyledText[ores.length];
for (int j = 0; j < ores.length; j++) {
result[j] = (StyledText) ores[j];
}
return result;
}
/**
* Checks for connection. If it is present, sends the contents of
* the input field, sends them out in a chat message, and clears the
* input field.
*/
private void sendMessage() {
String text = inputField.getText();
List data = new Vector();
AbstractMessage message = null;
if (text == null || text.equals("")) {
return;
}
data.add(Client.getInstance().getUsername());
data.add(text);
message = MessageFactory.getInstance()
.makeMessage(MessageSettings.CHAT_TYPE, data);
Client.getInstance().sendMessage(message);
inputField.setText("");
}
/**
* Invoked when an action occurs.
*
* @param e the event associated with the action.
*/
public final void actionPerformed(final ActionEvent e) {
sendMessage();
}
/**
* Clears out the contents of chat and hides the window.
*/
public final void clearAndHide() {
StyledDocument doc = contents.getStyledDocument();
try {
doc.remove(0, doc.getLength());
} catch (BadLocationException ble) {
System.out.println("Could not clear out the chat contents.");
}
this.setVisible(false);
}
/**
* This is a little utility class that allows one to easily keep
* track of differently styled bits of text.
*/
private final class StyledText {
/**
* The text bit.
*/
private String text;
/**
* The name of the style to apply to this piece of text
*/
private String styleName;
/**
* Creates a new <code>StyledText</code> instance.
*
* @param txt a <code>String</code> value
* @param style a <code>String</code> value
*/
public StyledText(final String txt, final String style) {
text = txt;
styleName = style;
}
/**
* Returns text.
*
* @return a <code>String</code> value
*/
public String getText() {
return text;
}
/**
* Returns the style name.
*
* @return a <code>String</code> value
*/
public String getStyle() {
return styleName;
}
}
}

View File

@@ -0,0 +1,624 @@
package netpaint.client.gui;
import javax.swing.JOptionPane;
import javax.swing.JInternalFrame;
import javax.swing.JScrollPane;
import javax.swing.JFileChooser;
import javax.swing.filechooser.FileFilter;
import java.io.File;
import netpaint.messaging.AbstractMessage;
/**
* Class ClientUI: The client UI class is basically a singleton facade
* for the whole gui package. It will provide the means for outside
* classes to communicate with the UI, as well as the channels for the
* UI to communicate to outer classes. Basically, it provides a set of
* methods to start up the front-end of the program, as well as
* communicate with the user through dialogs, etc., and manage the
* windows and such.
*
* <PRE>
* Revision History:
* v1.0 (Jan. 31, 2004) - Created the ClientUI class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Jan. 31, 2004
*/
public final class ClientUI {
/**
* Implement as singleton.
*/
private static ClientUI instance;
/**
* The main window instance.
*/
private MainWindow main;
/**
* The instance of the chat panel.
*/
private ChatPanel chatPanel;
/**
* The connection dialog.
*/
private ConnectionDialog dlgConnection;
/**
* The graphics panel in which the user can see what other users
* have drawn and also draw things himself.
*/
private GraphicsPanel graphicsPanel;
/**
* The internal frame that will contain the graphics panel.
*/
private JInternalFrame graphicsFrame;
/**
* This panel will contain the tool buttons and a color chooser.
*/
private ToolPanel toolPanel;
/**
* The internal frame that will contain the tool panel.
*/
private JInternalFrame toolFrame;
/**
* Contains the font selection panel for the text tool.
*/
private FontSelectionPanel fontPanel;
/**
* This internal frame will contain the options for the text insertion.
*/
private JInternalFrame fontFrame;
/**
* Contains the image selection panel.
*/
private ImageSelectionPanel imagePanel;
/**
* This internal frame will contain the options for the image insertion.
*/
private JInternalFrame imageFrame;
/**
* The undo frame.
*/
private SuperUndoPanel undoPanel;
/**
* The help panel.
*/
private HelpPanel helpPanel;
/**
* Creates a new <code>ClientUI</code> instance.
*/
private ClientUI() {
if (ClientUISettings.VERBOSE) {
System.out.println("Creating ClientUI");
System.out.flush();
}
main = new MainWindow();
dlgConnection = new ConnectionDialog(main);
chatPanel = new ChatPanel();
graphicsPanel = new GraphicsPanel();
toolPanel = new ToolPanel();
fontPanel = new FontSelectionPanel();
imagePanel = new ImageSelectionPanel();
undoPanel = new SuperUndoPanel();
helpPanel = new HelpPanel();
//Graphics
graphicsFrame = new JInternalFrame(ClientUISettings.TITLE_GRAPHICS, //Title
true, //Resizable
true, //Closable
true, //Maximizable
true); //Iconifiable
JScrollPane sp = new JScrollPane(graphicsPanel);
graphicsFrame.getContentPane().add(sp);
//Tools
toolFrame = new JInternalFrame(ClientUISettings.TITLE_TOOLS, //Title
false, //Resizable
false, //Closable
false, //Maximizable
false); //Iconifiable
toolFrame.getContentPane().add(toolPanel);
//Font
fontFrame = new JInternalFrame(ClientUISettings.TITLE_FONTS, //Title
false, //Resizable
false, //Closable
false, //Maximizable
false); //Iconifiable
fontFrame.getContentPane().add(fontPanel);
//Image
imageFrame = new JInternalFrame(ClientUISettings.TITLE_IMAGES, //Title
false, //Resizable
false, //Closable
false, //Maximizable
false); //Iconifiable
imageFrame.getContentPane().add(imagePanel);
//Sizes and locations
dlgConnection.pack();
chatPanel.setSize(ClientUISettings.CHAT_DEFAULT_SIZE);
chatPanel.setLocation(ClientUISettings.CHAT_DEFAULT_LOCATION);
graphicsFrame.setSize(ClientUISettings.GRAPHICS_DEFAULT_SIZE);
graphicsFrame.setLocation(ClientUISettings.GRAPHICS_DEFAULT_LOCATION);
toolFrame.setLocation(ClientUISettings.TOOLS_DEFAULT_LOCATION);
toolFrame.pack();
fontFrame.setLocation(ClientUISettings.FONTS_DEFAULT_LOCATION);
fontFrame.pack();
imageFrame.setLocation(ClientUISettings.FONTS_DEFAULT_LOCATION);
imageFrame.pack();
undoPanel.setLocation(ClientUISettings.UNDO_DEFAULT_LOCATION);
undoPanel.pack();
helpPanel.setSize(ClientUISettings.HELP_DEFAULT_SIZE);
helpPanel.setLocation(ClientUISettings.HELP_DEFAULT_LOCATION);
helpPanel.pack();
main.getDesktopPane().add(chatPanel);
main.getDesktopPane().add(graphicsFrame);
main.getDesktopPane().add(toolFrame);
main.getDesktopPane().add(fontFrame);
main.getDesktopPane().add(imageFrame);
main.getDesktopPane().add(undoPanel);
main.getDesktopPane().add(helpPanel);
main.setJMenuBar(new MainMenu());
}
/**
* Singleton implementation.
*
* @return a <code>ClientUI</code> value
*/
public static ClientUI getInstance() {
if (instance == null) {
instance = new ClientUI();
}
return instance;
}
/**
* This will show the main window of the front end - the one that
* will contain inside it the chat, the tools, the graphics and all
* that nice stuff.
*/
public void startMainWindow() {
if (!isMainRunning()) {
main.setVisible(true);
toolFrame.setVisible(true);
undoPanel.setVisible(true);
undoPanel.setVisible(false);
}
}
/**
* Shows the connection dialog.
*/
public void showConnectionDialog() {
if (isMainRunning()) {
dlgConnection.setLocationRelativeTo(main);
dlgConnection.setVisible(true);
}
}
/**
* If the graphics panel is invisible, shows it.
*/
public void showGraphicsWindow() {
if (isMainRunning()) {
if (!graphicsFrame.isVisible()) {
graphicsFrame.setVisible(true);
}
}
}
/**
* Deletes the contents of the graphics window and hides it.
*/
public void hideGraphicsWindow() {
if (isMainRunning()) {
if (graphicsFrame.isVisible()) {
graphicsFrame.setVisible(false);
}
}
}
/**
* Returns the graphics panel.
*
* @return a <code>GraphicsPanel</code> value
*/
public GraphicsPanel getGraphicsPanel() {
return graphicsPanel;
}
/**
* Displays the chat window if it needs showing.
*/
public void showChatWindow() {
if (isMainRunning()) {
if (!chatPanel.isVisible()) {
chatPanel.setVisible(true);
}
}
showGraphicsWindow();
}
/**
* Returns the active tool panel.
*
* @return a <code>ToolPanel</code> value
*/
public ToolPanel getToolPanel() {
return toolPanel;
}
/**
* Returns the font panel.
*
* @return a <code>FontSelectionPanel</code> value
*/
public FontSelectionPanel getFontPanel() {
return fontPanel;
}
/**
* Shows the font frame.
*/
public void showFontFrame() {
if (isMainRunning()) {
fontFrame.setVisible(true);
}
}
/**
* Hides the font frame.
*/
public void hideFontFrame() {
if (fontFrame.isVisible()) {
fontFrame.setVisible(false);
}
}
/**
* Returns the image panel.
*
* @return a <code>ImageSelectionPanel</code> value
*/
public ImageSelectionPanel getImagePanel() {
return imagePanel;
}
/**
* Shows the image frame.
*/
public void showImageFrame() {
if (isMainRunning()) {
imageFrame.setVisible(true);
}
}
/**
* Hides the image frame.
*/
public void hideImageFrame() {
if (imageFrame.isVisible()) {
imageFrame.setVisible(false);
}
}
/**
* Returns the undo panel.
*
* @return a <code>SuperUndoPanel</code> value
*/
public SuperUndoPanel getUndoPanel() {
return undoPanel;
}
/**
* Shows the undo frame.
*/
public void showUndoFrame() {
if (isMainRunning()) {
undoPanel.setVisible(true);
}
}
/**
* Hides the undo frame.
*/
public void hideUndoFrame() {
if (undoPanel.isVisible()) {
undoPanel.setVisible(false);
}
}
/**
* Shows the help frame.
*/
public void showHelpFrame() {
if (isMainRunning()) {
helpPanel.setVisible(true);
}
}
/**
* Hides the help frame.
*/
public void hideHelpFrame() {
if (helpPanel.isVisible()) {
helpPanel.setVisible(false);
}
}
/**
* Describe <code>sendMessageToChat</code> method here.
*
* @param message an <code>AbstractMessage</code> value
*/
public void sendMessageToChat(final AbstractMessage message) {
if (isMainRunning()) {
if (!chatPanel.isVisible()) { showChatWindow(); }
chatPanel.receiveMessage(message);
}
}
/**
* Deletes the contents of the chat window and hides it.
*/
public void hideChat() {
if (isMainRunning()) {
chatPanel.clearAndHide();
}
}
/**
* Returns true if the main window is currently running. No other
* windows will be allowed to show if the main window is not
* running.
*
* @return a <code>boolean</code> value
*/
public boolean isMainRunning() {
return main.isVisible();
}
////////////////////////// User message functions //////////////////////////
/**
* Displays a wanring dialog to the user.
*
* @param title a <code>String</code> value
* @param message a <code>String</code> value
*/
public void showWarning(final String title, final String message) {
if (isMainRunning()) {
showDialog(title, message, JOptionPane.WARNING_MESSAGE);
}
}
/**
* Shows an error message to the user.
*
* @param title a <code>String</code> value
* @param message a <code>String</code> value
*/
public void showError(final String title, final String message) {
if (isMainRunning()) {
showDialog(title, message, JOptionPane.ERROR_MESSAGE);
}
}
/**
* Displays an informative message to the user.
*
* @param title a <code>String</code> value
* @param message a <code>String</code> value
*/
public void showMessage(final String title, final String message) {
if (isMainRunning()) {
showDialog(title, message, JOptionPane.PLAIN_MESSAGE);
}
}
/**
* Displays a dialog with two buttons. The title and the contents of
* the dialog are passed in as the first two parameters. Tne next
* two parameters specify what labels to give to the two choice
* buttons. The function returns true if the first option was
* chosen, and false if the second option was chosen.
*
* @param title a <code>String</code> value
* @param message a <code>String</code> value
* @param yesOption a <code>String</code> value
* @param noOption a <code>String</code> value
* @return a <code>boolean</code> value
*/
public boolean showYesNoDialog(final String title,
final String message,
final String yesOption,
final String noOption) {
if (isMainRunning()) {
String[] options = {yesOption,
noOption };
int choice = JOptionPane.showOptionDialog(main,
message,
title,
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE,
null,
options,
yesOption);
if (choice == 0) {
return true;
}
return false;
}
return false;
}
/**
* A utility function for all the other showXXX functions to use.
*
* @param title a <code>String</code> value
* @param message a <code>String</code> value
* @param type an <code>int</code> value
*/
private void showDialog(final String title, final String message,
final int type) {
if (isMainRunning()) {
JOptionPane.showMessageDialog(main, message, title, type);
}
}
///////////////////////////// File Prompt Stuff /////////////////////////////
/**
* Gives the user a dialog for saving a file and allows him to
* browse the location and give the file a name. After the user
* selects the path and name of the file that he wants to save the
* session to, this value will be returned from this function.
*
* @param currentPath If this is not null, then the location that
* the browse dialog starts with will be this.
* @return a <code>String</code> value
*/
public String browseSaveFileName(final String currentPath) {
if (isMainRunning()) {
JFileChooser chooser = null;
String result = null;
int returnVal = -1;
if (currentPath == null) {
chooser = new JFileChooser();
} else {
chooser = new JFileChooser(currentPath);
}
chooser.setFileFilter(new SessionFileFilter());
returnVal = chooser.showSaveDialog(main);
if (returnVal == JFileChooser.APPROVE_OPTION) {
result = chooser.getSelectedFile().getPath();
if (result.lastIndexOf('.') < 0
|| !result.substring(result.lastIndexOf('.') + 1).equals("xml")) {
result += ".xml";
}
return result;
} else {
return null;
}
}
return null;
}
/**
* Shows the dialog for loading a file. Will return when the user
* selects a file the name of that file and path, or null if the
* user does not make a meaningful selection or presses cancel.
*
* @param currentPath If this is not null, then the location that
* the browse dialog starts with will be this.
* @return a <code>String</code> value
*/
public String browseLoadFileName(final String currentPath) {
if (isMainRunning()) {
JFileChooser chooser = null;
String result = null;
int returnVal = -1;
if (currentPath == null) {
chooser = new JFileChooser();
} else {
chooser = new JFileChooser(currentPath);
}
chooser.setFileFilter(new SessionFileFilter());
returnVal = chooser.showOpenDialog(main);
if (returnVal == JFileChooser.APPROVE_OPTION) {
result = chooser.getSelectedFile().getPath();
return result;
} else {
return null;
}
}
return null;
}
/**
* This class basically filters out everything that is not a
* directory or an xml file.
*/
private final class SessionFileFilter extends FileFilter {
/**
* Creates a new <code>SessionFileFilter</code> instance.
*/
public SessionFileFilter() {
}
/**
* Returns true if the file passed in should be accepted and shown
* in the open/save dialog, and false if it should not be
* displayed.
*
* @param f a <code>File</code> value
* @return a <code>boolean</code> value
*/
public boolean accept(final File f) {
String name = f.getName();
if (f.isDirectory()) {
return true;
}
if (name == null || name.lastIndexOf('.') < 0) {
return false;
}
if (name.substring(name.lastIndexOf('.') + 1).equals("xml")) {
return true;
}
return false;
}
/**
* Returns a description of this file filter.
*
* @return a <code>String</code> value
*/
public String getDescription() {
return "NetPaint Session Files";
}
}
}

View File

@@ -0,0 +1,625 @@
package netpaint.client.gui;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
/**
* Class ClientUISettings: This class will contain all of the settings
* for the looks of the client UI, all in one centralized place, so
* that when something needs to be changed, it will only need to be
* changed in one place.
*
* <PRE>
* Revision History:
* v1.0 (Feb. 01, 2004) - Created the ClientUISettings class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Feb. 01, 2004
*/
public class ClientUISettings {
/**
* The title that will be given to the super undo internal frame.
*/
public static final String UNDO_TITLE = "Super Undo";
/**
* The name of the undo button.
*/
public static final String UNDO_NAME = "Undo";
/**
* The name of the cancel button on the undo internal frame.
*/
public static final String UNDO_CANCEL = "Cancel";
/**
* The name of the style to use for chat text.
*/
public static final String CHAT_SMILE = "smile";
/**
* The name of the style to use for chat text.
*/
public static final String CHAT_WINK = "wink";
/**
* The name of the style to use for chat text.
*/
public static final String CHAT_FROWN = "frown";
/**
* The name of the style to use for chat text.
*/
public static final String CHAT_TONGUE = "tongue";
/**
* The name of the style to use for chat text.
*/
public static final String CHAT_GRIN = "grin";
/**
* The name of the file that contains the icon for the text tool.
*/
public static final String SMILE_ICON = "smile.gif";
/**
* The name of the file that contains the icon for the text tool.
*/
public static final String WINK_ICON = "wink.gif";
/**
* The name of the file that contains the icon for the text tool.
*/
public static final String FROWN_ICON = "frown.gif";
/**
* The name of the file that contains the icon for the text tool.
*/
public static final String TONGUE_ICON = "tongue.gif";
/**
* The name of the file that contains the icon for the text tool.
*/
public static final String GRIN_ICON = "grin.gif";
/**
* The name of the style to use for chat text.
*/
public static final String CHAT_STYLE_TEXT = "chatText";
/**
* The name of the style to use for the user's name.
*/
public static final String CHAT_STYLE_SELF = "selfName";
/**
* The name of the style to use for the names of other users.
*/
public static final String CHAT_STYLE_USER = "userName";
/**
* The name of the style to use for the server name.
*/
public static final String CHAT_STYLE_SERV = "servName";
/**
* The name of the style to use for the connected message.
*/
public static final String CHAT_STYLE_CONNECTED = "connected";
/**
* The name of the style to use for administrative messages.
*/
public static final String CHAT_STYLE_ADMIN = "administrative";
/**
* The color to use for chat text.
*/
public static final Color CHAT_COLOR_TEXT = Color.BLACK;
/**
* The color to use for the user's name.
*/
public static final Color CHAT_COLOR_SELF = Color.RED;
/**
* The color to use for the names of other users.
*/
public static final Color CHAT_COLOR_USER = Color.BLUE;
/**
* The color to use for the server name.
*/
public static final Color CHAT_COLOR_SERV = new Color(120, 0, 0);
/**
* The color in which the self join message will be shown
*/
public static final Color CHAT_COLOR_CONNECTED = new Color(20, 120, 20);
/**
* This is the color in which administrative messages will be shown.
*/
public static final Color CHAT_COLOR_ADMIN = new Color(110, 90, 20);
/**
* The font family to use for the chat text.
*/
public static final String CHAT_FONT_FAMILY = "SansSerif";
/**
* Display extra debug output?
*/
public static final boolean VERBOSE = false;
/**
* This is the title that will be given to the window that the
* chat resides in.
*/
public static final String TITLE_CHAT = "Chat";
/**
* This is the title that will be given to the window containing
* the actual program.
*/
public static final String TITLE_PROGRAM = "Java NetPaint by "
+ "Vladimir Urazov and Daniyar Zhanbekov";
/**
* This is the title that will be given to the window containing the
* graphics.
*/
public static final String TITLE_GRAPHICS = "Graphics Session";
/**
* This is the title that will be given to the server connection
* dialog.
*/
public static final String TITLE_CONNECT = "Connect to Server";
/**
* This is the title that will be given to the window containing the
* tool selection buttons.
*/
public static final String TITLE_TOOLS = "Tools";
/**
* The title that will be given to the font internal frame.
*/
public static final String TITLE_FONTS = "Fonts";
/**
* The title that will be given to the image internal frame.
*/
public static final String TITLE_IMAGES = "Load Image";
/**
* In the connect dialog, the name to give to the button that allows
* the user to connect to the server.
*/
public static final String CONNECT_CONNECT_NAME = "Connect";
/**
* In the connect dialog, the name to give to the button that allows
* the user to cancel the dialog without attempting a connect.
*/
public static final String CONNECT_CANCEL_NAME = "Cancel";
/**
* The number of rows in which to arrange the entries on the connect
* dialog.
*/
public static final int CONNECT_PANEL_ROWS = 3;
/**
* Offset from top-left corner for connect table.
*/
public static final int CONNECT_INIT_COORD = 3;
/**
* Number of pixels to pad between entries on connect dialog.
*/
public static final int CONNECT_PADDING = 3;
/**
* The number of columns to allocate for every field on the connect
* dialog.
*/
public static final int CONNECT_FIELD_LENGTH = 10;
/**
* The width we want the program window to initially be.
*/
public static final int PROGRAM_WINDOW_WIDTH = 800;
/**
* The height we want the program window to initially be.
*/
public static final int PROGRAM_WINDOW_HEIGHT = 600;
/**
* The default position of the chat panel.
*/
public static final Point CHAT_DEFAULT_LOCATION = new Point(80, 310);
/**
* The default size of the chat panel.
*/
public static final Dimension CHAT_DEFAULT_SIZE = new Dimension(700, 200);
/**
* The default position of the tools panel.
*/
public static final Point TOOLS_DEFAULT_LOCATION = new Point(0, 0);
/**
* The default location for the fonts internal frame.
*/
public static final Point FONTS_DEFAULT_LOCATION = new Point(130, 0);
/**
* The default position of the help panel.
*/
public static final Point HELP_DEFAULT_LOCATION = new Point(130, 0);
/**
* The default size of the help panel.
*/
public static final Dimension HELP_DEFAULT_SIZE = new Dimension(650, 250);
/**
* The default position of the graphics panel.
*/
public static final Point GRAPHICS_DEFAULT_LOCATION = new Point(250, 10);
/**
* The default position of the undo panel.
*/
public static final Point UNDO_DEFAULT_LOCATION = new Point(0, 253);
/**
* The default size of the graphics panel.
*/
public static final Dimension GRAPHICS_DEFAULT_SIZE
= new Dimension(500, 500);
/**
* The size of the graphics contents.
*/
public static final Dimension GRAPHICS_CONTENT_SIZE
= new Dimension(2000, 2000);
/**
* The default background color for the graphics panel.
*/
public static final Color GRAPHICS_BACKGROUND = Color.WHITE;
///////////////////////////////// Tool Stuff ////////////////////////////////
/**
* The default tool color.
*/
public static final Color TOOL_DEFAULT_COLOR = Color.BLACK;
/**
* The number of rows and columns that we want to arrange the tool
* selection buttons into.
*/
public static final Dimension TOOL_TABLE = new Dimension(2, 4);
/**
* The horizontal and vertical gaps between the buttons.
*/
public static final Dimension TOOL_TABLE_GAPS = new Dimension(5, 2);
/**
* The color indicator size.
*/
public static final Dimension TOOL_INDICATOR_SIZE = new Dimension(30, 30);
/**
* The name of the line tool.
*/
public static final String TOOL_LINE_NAME = "Line";
/**
* The tool tip to show for the line tool.
*/
public static final String TOOL_LINE_TTIP =
"Click once on the canvas for initial point. Click again"
+ "to finalize the line.";
/**
* The name of the file that contains the icon for the line tool.
*/
public static final String TOOL_LINE_ICON = "LineTool.gif";
/**
* The name of the rectangle tool.
*/
public static final String TOOL_RECTANGLE_NAME = "Rectangle";
/**
* The tool tip to show for the rectangle tool.
*/
public static final String TOOL_RECTANGLE_TTIP =
"Click once on the canvas to select the initial point of the "
+ "Rectangle.Click again to mark the point on the opposite side of the "
+ "diagonal.";
/**
* The name of the file that contains the icon for the rectangle tool.
*/
public static final String TOOL_RECTANGLE_ICON = "RectangleTool.gif";
/**
* The name of the triangle tool.
*/
public static final String TOOL_TRIANGLE_NAME = "Triangle";
/**
* The tool tip to show for the triangle tool.
*/
public static final String TOOL_TRIANGLE_TTIP =
"Click once on the canvas for initial point. Click again"
+ "to mark the second point,click a third time to finalize the triangle.";
/**
* The name of the file that contains the icon for the triangle tool.
*/
public static final String TOOL_TRIANGLE_ICON = "TriangleTool.gif";
/**
* The name of the polygon tool.
*/
public static final String TOOL_POLYGON_NAME = "Polygon";
/**
* The tool tip to show for the polygon tool.
*/
public static final String TOOL_POLYGON_TTIP =
"Click once for initial point. This will draw a small"
+ " black square. Select additional points of the polygon"
+ "when ready click inside"
+ "the black square to finalize the polygon";
/**
* The name of the file that contains the icon for the polygon tool.
*/
public static final String TOOL_POLYGON_ICON = "PolygonTool.gif";
/**
* The name of the ellipse tool.
*/
public static final String TOOL_ELLIPSE_NAME = "Ellipse";
/**
* The tool tip to show for the ellipse tool.
*/
public static final String TOOL_ELLIPSE_TTIP =
"Click once on the canvas to select the initial point of the "
+ "Rectangle in which the Ellipse will be drawn.Click again to mark"
+ "the point on the opposite side of the diagonal.";
/**
* The name of the file that contains the icon for the ellipse tool.
*/
public static final String TOOL_ELLIPSE_ICON = "EllipseTool.gif";
/**
* The name of the quadratic tool.
*/
public static final String TOOL_QUADRATIC_NAME = "Quadratic Curve";
/**
* The tool tip to show for the quadratic tool.
*/
public static final String TOOL_QUADRATIC_TTIP =
"Click once on the canvas for initial point. Click again"
+ "to finish the line. Then select a third point which will curve the line.";
/**
* The name of the file that contains the icon for the quadratic tool.
*/
public static final String TOOL_QUADRATIC_ICON = "QuadraticTool.gif";
/**
* The name of the image tool.
*/
public static final String TOOL_IMAGE_NAME = "Insert Image";
/**
* The tool tip to show for the image tool.
*/
public static final String TOOL_IMAGE_TTIP = "Place the URL into the Load "
+ " Image menu in the format: http://www... and then click on canvas to "
+ " place the image";
/**
* The name of the file that contains the icon for the image tool.
*/
public static final String TOOL_IMAGE_ICON = "ImageTool.gif";
/**
* The name of the text tool.
*/
public static final String TOOL_TEXT_NAME = "Text";
/**
* The tool tip to show for the text tool.
*/
public static final String TOOL_TEXT_TTIP =
"Type in text.Then you may alter text by selecting options in the fonts"
+ " menu. Press enter of click the checkmarked button to finalize text";
/**
* The name of the file that contains the icon for the text tool.
*/
public static final String TOOL_TEXT_ICON = "TextTool.gif";
/**
* The name of the outline fill switch.
*/
public static final String TOOL_OUTLINE_NAME = "Outline Shapes";
/**
* The tool tip to show for the outline fill switch.
*/
public static final String TOOL_OUTLINE_TTIP = "Select this tool if"
+ "you wish to draw the outline of the shape";
/**
* The name of the file that contains the icon for the outline fill switch.
*/
public static final String TOOL_OUTLINE_ICON = "Outline.gif";
/**
* The name of the filled fill switch.
*/
public static final String TOOL_FILL_NAME = "Fill Shapes";
/**
* The tool tip to show for the filled fill switch.
*/
public static final String TOOL_FILL_TTIP = "Select this tool"
+ "if you wish to draw filled shapes";
/**
* The name of the file that contains the icon for the filled fill switch.
*/
public static final String TOOL_FILL_ICON = "Filled.gif";
///////////////////////////// Font Panel Stuff //////////////////////////////
/**
* The name of the bold style button.
*/
public static final String FONT_BOLD_NAME = "Bold";
/**
* The tool tip to show for the bold style button.
*/
public static final String FONT_BOLD_TTIP = "Makes the text bold";
/**
* The name of the file that contains the icon for the bold style button.
*/
public static final String FONT_BOLD_ICON = "Bold.gif";
/**
* The name of the italic style button.
*/
public static final String FONT_ITALIC_NAME = "Italic";
/**
* The tool tip to show for the italic style button.
*/
public static final String FONT_ITALIC_TTIP = "Italisizes the text";
/**
* The name of the file that contains the icon for the italic style button.
*/
public static final String FONT_ITALIC_ICON = "Italic.gif";
/**
* The name of the underlined style button.
*/
public static final String FONT_UNDERLINED_NAME = "Underlined";
/**
* The tool tip to show for the underlined style button.
*/
public static final String FONT_UNDERLINED_TTIP = "Underlines the Text";
/**
* The name of the file that contains the icon for the underlined
* style button.
*/
public static final String FONT_UNDERLINED_ICON = "Underlined.gif";
/**
* The name of the finalize button.
*/
public static final String FONT_FINALIZE_NAME = "Finalize";
/**
* The tool tip to show for the finalize button.
*/
public static final String FONT_FINALIZE_TTIP = "Click this button"
+ "when you're ready to finalize your text and send it to the serer";
/**
* The name of the file that contains the icon for the finalize
* button.
*/
public static final String FONT_FINALIZE_ICON = "Finalize.gif";
////////////////////////////// Main Menu Stuff //////////////////////////////
/**
* The name that will be given to the "Connect" menu item.
*/
public static final String MENU_CONNECT_NAME = "Connect";
/**
* The name that will be given to the "Disconnect" menu item.
*/
public static final String MENU_DISCONNECT_NAME = "Disconnect";
/**
* The name that will be given to the "Load Session" menu item.
*/
public static final String MENU_LOAD_SESSION_NAME = "Load Session";
/**
* The name that will be given to the "Save Session"; menu item.
*/
public static final String MENU_SAVE_NAME = "Save Session";
/**
* The name that will be given to the "Save Session As..." menu item.
*/
public static final String MENU_SAVE_AS_NAME = "Save Session As...";
/**
* The name that will be given to the "Quit" menu item.
*/
public static final String MENU_QUIT_NAME = "Quit";
/**
* The name that will be given to the "Undo" menu item.
*/
public static final String MENU_UNDO_NAME = "Undo";
/**
* The name that will be given to the "Undo List" menu item.
*/
public static final String MENU_SUPER_UNDO_NAME = "Undo List";
/**
* The name that will be given to the "Contents" menu item.
*/
public static final String MENU_CONTENTS_NAME = "Contents";
/**
* The name that will be given to the "About" menu item.
*/
public static final String MENU_ABOUT_NAME = "About";
/**
* Since this is a utility class, there is no reason to instantiate it.
*/
private ClientUISettings() { }
}

View File

@@ -0,0 +1,311 @@
package netpaint.client.gui;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JTextField;
import javax.swing.SpringLayout;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import netpaint.client.Client;
/**
* Class ConnectionDialog: This is the dialog that will be shown when
* the user wiches to connect to a sever. It will allow the user to
* type in the host, port, and username to use when connecting to the
* server. It will then handle the connection to the server by calling
* the client. The dialog will look something like this:
*
* <PRE>
* +----------------------------------+
* | Connect to Server |
* +----------------------------------+
* | |
* | +--------------------+ |
* | Server: | | |
* | +--------------------+ |
* | +--------------------+ |
* | Port: | | |
* | +--------------------+ |
* | +--------------------+ |
* | Username: | | |
* | +--------------------+ |
* | |
* | +---------+ +--------+ |
* | | Connect | | Cancel | |
* | +---------+ +--------+ |
* | |
* +----------------------------------+
* </PRE>
*
* <PRE>
* Revision History:
* v1.0 (Feb. 02, 2004) - Created the ConnectionDialog class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Feb. 02, 2004
*/
public class ConnectionDialog extends JDialog
implements ActionListener {
/**
* The label for the server input field.
*/
private JLabel serverLabel;
/**
* The label for the port input field.
*/
private JLabel portLabel;
/**
* The label for the username input field.
*/
private JLabel usernameLabel;
/**
* The user will type the host name of the server into this input field.
*/
private JTextField serverInput;
/**
* The user will type in the port number to connect to in this
* field. It will have a default port number in it already, though.
*/
private JTextField portInput;
/**
* The user will type in the username to use when connecting to the
* server into this field. Note that colons (':') are not allowed in
* the username.
*/
private JTextField usernameInput;
/**
* Hit this button to connect to the server.
*/
private JButton connectButton;
/**
* Hit this button to cancel.
*/
private JButton cancelButton;
/**
* Creates a new <code>ConnectionDialog</code> instance.
* @param parent a <code>Frame</code> value
*/
public ConnectionDialog(final JFrame parent) {
super(parent, true);
setTitle(ClientUISettings.TITLE_CONNECT);
//Initialize all the components:
serverLabel = new JLabel("Server: ", JLabel.TRAILING);
portLabel = new JLabel("Port: ", JLabel.TRAILING);
usernameLabel = new JLabel("Username: ", JLabel.TRAILING);
serverInput = new JTextField("helsinki.cc.gatech.edu",
ClientUISettings.CONNECT_FIELD_LENGTH);
portInput = new JTextField("4555",
ClientUISettings.CONNECT_FIELD_LENGTH);
usernameInput = new JTextField(ClientUISettings.CONNECT_FIELD_LENGTH);
usernameInput.setActionCommand(ClientUISettings.CONNECT_CONNECT_NAME);
usernameInput.addActionListener(this);
connectButton = new JButton(ClientUISettings.CONNECT_CONNECT_NAME);
cancelButton = new JButton(ClientUISettings.CONNECT_CANCEL_NAME);
connectButton.setActionCommand(ClientUISettings.CONNECT_CONNECT_NAME);
cancelButton.setActionCommand(ClientUISettings.CONNECT_CANCEL_NAME);
connectButton.addActionListener(this);
cancelButton.addActionListener(this);
//Add the components to the dialog:
JPanel contents = new JPanel(new BorderLayout());
JPanel boxes = new JPanel(new SpringLayout());
JPanel buttons = new JPanel();
//Add boxes:
boxes.add(serverLabel);
serverLabel.setLabelFor(serverInput);
boxes.add(serverInput);
boxes.add(portLabel);
portLabel.setLabelFor(portInput);
boxes.add(portInput);
boxes.add(usernameLabel);
usernameLabel.setLabelFor(usernameInput);
boxes.add(usernameInput);
//Lay out the panel.
SpringUtilities.makeCompactGrid(boxes,
ClientUISettings.CONNECT_PANEL_ROWS, 2,
ClientUISettings.CONNECT_INIT_COORD,
ClientUISettings.CONNECT_INIT_COORD,
ClientUISettings.CONNECT_PADDING,
ClientUISettings.CONNECT_PADDING);
//Add buttons:
buttons.add(connectButton);
buttons.add(cancelButton);
//Add it all to the content pane.
contents.add(boxes, BorderLayout.CENTER);
contents.add(buttons, BorderLayout.PAGE_END);
this.setContentPane(contents);
this.setResizable(false);
}
/**
* Returns null if the server name is not valid, and the string with
* the server name if it is valid.
*
* @return a <code>String</code> value
*/
private String validateServer() {
String value = serverInput.getText();
if (value == null) {
return null;
}
return value;
}
/**
* Returns an integer with the port number if the thing entered was
* valid, and null if it wasn't.
*
* @return an <code>Integer</code> value
*/
private Integer validatePort() {
String value = portInput.getText();
int port = -1;
if (value == null) {
return null;
}
try {
port = Integer.parseInt(value);
} catch (NumberFormatException nfe) {
//Show error message about bad port format
ClientUI.getInstance().showError("Bad Port", "The value \"" + value
+ "\" is not acceptable for a connection "
+ "port. Please enter an integer number");
return null;
}
return new Integer(port);
}
/**
* Returns the username if it was valid, and null if it wasn't. It
* is not allowed to have colons in the username.
*
* @return a <code>String</code> value
*/
private String validateUsername() {
String value = usernameInput.getText();
int index = 0;
if (value == null) {
return null;
}
index = value.indexOf(':');
if (index < 0) {
return value;
} else {
//Show error message about how it's bad to put colons in username.
ClientUI.getInstance().showError("Bad username",
"The username cannot contain colons in"
+ " it. Please remove the colon(s) and "
+ "try again.");
}
return null;
}
/**
* Will be called when the connect button is clicked.
*/
private void connect() {
String host = validateServer();
Integer port = validatePort();
String username = validateUsername();
boolean result = false;
if (host == null
|| port == null
|| username == null) {
//Not enough data...
return;
}
result = Client.getInstance().connectToServer(host,
port.intValue(),
username);
if (!result) {
//Show error message about how we couldn't connect to the server.
ClientUI.getInstance().showError("Connection Failed",
"Could not connect to the server: "
+ username + "@" + host
+ ":" + port.toString());
} else {
//Connected successfully.
clearAndHide();
}
}
/**
* Will be called when the cancel button is clicked.
*/
private void cancel() {
clearAndHide();
}
/**
* Invoked when an action occurs.
*
* @param e the event associated with the action.
*/
public final void actionPerformed(final ActionEvent e) {
String actionCommand = e.getActionCommand();
if (actionCommand.equals(ClientUISettings.CONNECT_CONNECT_NAME)) {
connect();
} else if (actionCommand.equals(ClientUISettings.CONNECT_CANCEL_NAME)) {
cancel();
} else {
System.out.println("ConnectionDialog::actionPerformed - unknown "
+ "command - " + actionCommand);
}
}
/** This method clears the dialog and hides it. */
public final void clearAndHide() {
/*
serverInput.setText(null);
portInput.setText(null);
usernameInput.setText(null);
*/
setVisible(false);
}
}

View File

@@ -0,0 +1,427 @@
package netpaint.client.gui;
import java.util.Vector;
import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.awt.image.BufferedImage;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.ImageIcon;
import javax.swing.BoxLayout;
import javax.swing.JToggleButton;
/**
* Class FontSelectionPanel: This panel contains all of the controls
* necessary to allow the user to specify a font - the sizes, names,
* and styles. It also has a Font data field which can be accessed
* through the appropriate accessor and will always contain the
* currently selected font.
*
* <PRE>
* Revision History:
* v1.0 (Feb. 08, 2004) - Created the FontSelectionPanel class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Feb. 08, 2004
*/
public final class FontSelectionPanel extends JPanel
implements ActionListener {
/**
* This is the currently selected font.
*/
private Font currentFont;
/**
* Contains the name of the currently selected font.
*/
private String currentFontName;
/**
* The currently selected font size.
*/
private float currentFontSize;
/**
* Contains true if the bold toggle is currently turned on.
*/
private boolean boldSelected;
/**
* Contains true if the italic toggle button is currently turned on.
*/
private boolean italicSelected;
/**
* This is the button that will be clicked when the user wants to
* finalize the text input.
*/
private JButton finalizeButton;
/**
* Creates a new <code>FontSelectionPanel</code> instance.
*/
public FontSelectionPanel() {
super();
this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
this.add(makeFontNameBox());
this.add(makeFontSizeBox());
this.add(makeStyleSwitchPanel());
this.add(makeFinalizePanel());
updateCurrentFont();
}
/**
* Makes a combo box that allows you to select the font type.
*
* @return a <code>JComboBox</code> value
*/
private JComboBox makeFontNameBox() {
JComboBox result = null;
String[] allFamilies = GraphicsEnvironment
.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
result = new JComboBox(allFamilies);
result.setActionCommand("font_name_select");
result.addActionListener(this);
currentFontName = allFamilies[0];
return result;
}
/**
* Returns a combo box filled with some sizes for the fonts.
*
* @return a <code>JComboBox</code> value
*/
private JComboBox makeFontSizeBox() {
JComboBox result = null;
Vector sizes = new Vector();
sizes.add(new Integer(8));
sizes.add(new Integer(9));
sizes.add(new Integer(10));
sizes.add(new Integer(11));
sizes.add(new Integer(12));
sizes.add(new Integer(14));
sizes.add(new Integer(16));
sizes.add(new Integer(18));
sizes.add(new Integer(20));
sizes.add(new Integer(22));
sizes.add(new Integer(24));
sizes.add(new Integer(26));
sizes.add(new Integer(28));
sizes.add(new Integer(36));
sizes.add(new Integer(48));
sizes.add(new Integer(72));
result = new JComboBox(sizes);
result.setEditable(true);
result.setSelectedIndex(4);
result.setActionCommand("font_size_select");
result.addActionListener(this);
currentFontSize = 12;
return result;
}
/**
* Creates a new style button with the specified name, tool tip, and icon.
*
* @param name a <code>String</code> value
* @param toolTip a <code>String</code> value
* @param image a <code>String</code> value
* @return a <code>JToggleButton</code> value
*/
private JToggleButton makeStyleButton(final String name,
final String toolTip,
final String image) {
JToggleButton result = new JToggleButton();
result.setActionCommand(name);
result.addActionListener(this);
//Set up the image icon:
BufferedImage bim = ImageLoader.getInstance().loadSystemImage(image);
if (bim != null) {
//Successfully loaded the image
result.setIcon(new ImageIcon(bim));
} else {
//Could not load image...
result.setText(name);
result.setFont(result.getFont().deriveFont(Font.ITALIC));
result.setHorizontalAlignment(JButton.HORIZONTAL);
}
result.setToolTipText(name + ": " + toolTip);
result.setFocusPainted(false);
return result;
}
/**
* Makes the panel that contains toggle buttons to allow the user to
* select different font styles.
*
* @return a <code>JPanel</code> value
*/
private JPanel makeStyleSwitchPanel() {
JPanel result = new JPanel();
JToggleButton currentButton = null;
result.setLayout(new BoxLayout(result, BoxLayout.X_AXIS));
//Bold
currentButton = makeStyleButton(ClientUISettings.FONT_BOLD_NAME,
ClientUISettings.FONT_BOLD_TTIP,
ClientUISettings.FONT_BOLD_ICON);
result.add(currentButton);
boldSelected = false;
//Italic
currentButton = makeStyleButton(ClientUISettings.FONT_ITALIC_NAME,
ClientUISettings.FONT_ITALIC_TTIP,
ClientUISettings.FONT_ITALIC_ICON);
result.add(currentButton);
italicSelected = false;
/*
//Underlined
currentButton = makeStyleButton(ClientUISettings.FONT_UNDERLINED_NAME,
ClientUISettings.FONT_UNDERLINED_TTIP,
ClientUISettings.FONT_UNDERLINED_ICON);
result.add(currentButton);
*/
return result;
}
/**
* Makes a panel that contains the button to finalize the input of text.
*
* @return a <code>JPanel</code> value
*/
private JPanel makeFinalizePanel() {
JPanel result = new JPanel();
finalizeButton = new JButton();
finalizeButton.setActionCommand(ClientUISettings.FONT_FINALIZE_NAME);
finalizeButton.addActionListener(this);
//Set up the image icon:
BufferedImage bim = ImageLoader.getInstance()
.loadSystemImage(ClientUISettings.FONT_FINALIZE_ICON);
if (bim != null) {
//Successfully loaded the image
finalizeButton.setIcon(new ImageIcon(bim));
} else {
//Could not load image...
finalizeButton.setText(ClientUISettings.FONT_FINALIZE_NAME);
finalizeButton.setFont(finalizeButton.getFont().deriveFont(Font.ITALIC));
finalizeButton.setHorizontalAlignment(JButton.HORIZONTAL);
}
finalizeButton.setToolTipText(ClientUISettings.FONT_FINALIZE_NAME + ": "
+ ClientUISettings.FONT_FINALIZE_TTIP);
finalizeButton.setFocusPainted(false);
finalizeButton.setEnabled(false);
result.add(finalizeButton);
return result;
}
/**
* Returns the font currently slected in the combo box.
*
* @return a <code>Font</code> value
*/
public Font getSelectedFont() {
Font[] allFonts = null;
allFonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
//Find the font we want:
for (int i = 0; i < allFonts.length; i++) {
if (allFonts[i].getFamily().equals(currentFontName)) {
return allFonts[i];
}
}
//No font found...
return null;
}
/**
* Reads from the inputs and derives the new Font from the selected
* values.
*/
private synchronized void updateCurrentFont() {
Font baseFont = getSelectedFont();
Font oldFont = currentFont;
int style = 0;
boolean rerender = true;
if (baseFont == null) {
System.out.println("FontSelectionPanel: Could not get base font... "
+ "Old font preserved.");
return;
}
//Make sure we don't go into an infinite loop if this is the first
//time updating the font.
if (currentFont == null) {
rerender = false;
}
if (boldSelected) {
style |= Font.BOLD;
}
if (italicSelected) {
style |= Font.ITALIC;
}
currentFont = baseFont.deriveFont(style, currentFontSize);
if (currentFont == null) {
System.out.println("FontSelectionPanel: Could not update font...");
currentFont = oldFont;
}
if (ClientUISettings.VERBOSE) {
System.out.println("FontSelectionPanel: Current font updated:");
System.out.println("\t" + currentFont);
}
if (rerender) {
//Tell the graphics panel to update.
ClientUI.getInstance().getGraphicsPanel().markForRerender(false);
}
}
/**
* Returns the currently selected font.
*
* @return a <code>Font</code> value
*/
public Font getCurrentFont() {
return currentFont;
}
/**
* Enables the finalize button.
*/
public void enableFinalize() {
finalizeButton.setEnabled(true);
}
/**
* Disnables the finalize button.
*/
public void disableFinalize() {
finalizeButton.setEnabled(false);
}
/**
* Invoked when an action occurs.
*
* @param e the event associated with the action.
*/
public void actionPerformed(final ActionEvent e) {
String action = e.getActionCommand();
//The finalize button check will go here and will return
//before the next batch of if statements if it evaluates.
if (action.equals(ClientUISettings.FONT_FINALIZE_NAME)) {
disableFinalize();
ClientUI.getInstance().getToolPanel().getCurrentTool().finish();
return;
}
if (action.equals("font_name_select")) {
//Chose a different font name...
JComboBox src = (JComboBox) e.getSource();
currentFontName = (String) src.getSelectedItem();
if (ClientUISettings.VERBOSE) {
System.out.println("FontSelectionPanel: New font name - "
+ currentFontName);
}
} else if (action.equals("comboBoxEdited")) {
//Chose a different font size...
JComboBox src = (JComboBox) e.getSource();
String text = (String) src.getSelectedItem();
try {
currentFontSize = Float.parseFloat(text);
} catch (NumberFormatException nfe) {
System.out.println("FontSelectionPanel: Got bad size value - " + text);
src.setSelectedItem(new Integer((int) currentFontSize));
//Tell the client to only type in acceptable numbers.
ClientUI.getInstance()
.showWarning("Bad size input",
"Only decimal numbers are allowed as input into the fon"
+ "t size pane. Please input a valid decimal number.");
return;
}
} else if (action.equals("font_size_select")) {
//Chose a different font size...
JComboBox src = (JComboBox) e.getSource();
try {
currentFontSize = ((Integer) src.getSelectedItem()).intValue();
} catch (Exception exc) {
if (ClientUISettings.VERBOSE) {
System.out.println("FontSelectionPanel: Got an exception:");
System.out.println("\t" + exc.getMessage());
}
return;
}
if (ClientUISettings.VERBOSE) {
System.out.println("FontSelectionPanel: New font size - "
+ currentFontSize);
}
} else if (action.equals(ClientUISettings.FONT_BOLD_NAME)) {
boldSelected = !boldSelected;
if (ClientUISettings.VERBOSE) {
System.out.println("FontSelectionPanel: Bold selected - "
+ boldSelected);
}
} else if (action.equals(ClientUISettings.FONT_ITALIC_NAME)) {
italicSelected = !italicSelected;
if (ClientUISettings.VERBOSE) {
System.out.println("FontSelectionPanel: Italic selected - "
+ italicSelected);
}
} else {
System.out.println("FontSelectionPanel: Unknown action command - "
+ action);
}
updateCurrentFont();
}
}

View File

@@ -0,0 +1,337 @@
package netpaint.client.gui;
import java.awt.Point;
import java.awt.Graphics;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import javax.swing.JPanel;
import netpaint.client.Client;
import netpaint.client.GraphicMessageListIterator;
import netpaint.client.tools.AbstractTool;
import netpaint.client.tools.TextTool;
import netpaint.messaging.GraphicMessage;
/**
* Class GraphicsPanel: The graphics panel will display the graphics
* received by the user from the server, as well as allows the user to
* draw stuff himself.
*
* <PRE>
* Revision History:
* v1.0 (Feb. 05, 2004) - Created the GraphicsPanel class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Feb. 05, 2004
*/
public class GraphicsPanel extends JPanel implements MouseListener,
MouseMotionListener,
KeyListener {
/**
* Keeps track of the current mouse position.
*/
private Point mousePosition;
/**
* Will be true if the mouse pointer is currently within the bounds
* of the control, and false if it is outside the control.
*/
private boolean mouseActive;
/**
* Will be set to true if we want to repaint the whole component the
* next time around.
*/
private boolean fullRepaint;
/**
* The back buffer that will store all of the prerendered shapes.
*/
private BufferedImage backImage;
/**
* The graphics context of the backimage.
*/
private Graphics2D backBuffer;
/**
* Creates a new <code>GraphicsPanel</code> instance.
*/
public GraphicsPanel() {
super(true); //Want to make the panel double-buffered.
mousePosition = null;
mouseActive = false;
fullRepaint = false;
backImage = null;
backBuffer = null;
this.addMouseListener(this);
this.addMouseMotionListener(this);
this.addKeyListener(this);
}
/**
* Gets the mouse position if the mouse is currently
* active. Otherwise, return null.
*
* @return a <code>Point</code> value
*/
public final Point getMousePosition() {
return this.mousePosition;
}
/**
* Makes it so that panel is rerendered.
* @param all Send in true if the whole thing needs to be rerendered.
*/
public synchronized void markForRerender(final boolean all) {
if (ClientUISettings.VERBOSE) {
System.out.println("GraphicsPanel: Marked for rerender. All - "
+ all);
System.out.flush();
}
this.fullRepaint = all || this.fullRepaint;
this.repaint();
}
/**
* Invoked by Swing to draw components. Applications should not
* invoke paint directly, but should instead use the repaint method
* to schedule the component for redrawing.
*
* @param g the Graphics context in which to paint
*/
public final void paint(final Graphics g) {
Graphics2D graphics = (Graphics2D) g;
GraphicMessageListIterator i = null;
if (backBuffer == null) {
backImage = new BufferedImage(this.getWidth(),
this.getHeight(),
BufferedImage.TYPE_INT_RGB);
backBuffer = backImage.createGraphics();
backBuffer.setColor(ClientUISettings.GRAPHICS_BACKGROUND);
backBuffer.fillRect(0, 0, this.getWidth(), this.getHeight());
}
if (fullRepaint) {
if (ClientUISettings.VERBOSE) {
System.out.println("GraphicsPanel: Full Repaint!");
System.out.flush();
}
//Clear the component:
backBuffer.setColor(ClientUISettings.GRAPHICS_BACKGROUND);
backBuffer.fillRect(0, 0, this.getWidth(), this.getHeight());
//Render the currently finalized shapes:
i = Client.getInstance().getGraphicsListIterator();
while (i.hasNext()) {
GraphicMessage m = i.next();
if (m.isShown()) {
m.render(backBuffer);
}
}
//Client.getInstance().getLastGraphicMessage().render(backBuffer);
fullRepaint = false;
}
//Render graphics onto the panel:
g.drawImage(backImage, 0, 0, this);
//Render the currently drawn shape:
if (this.mouseActive
|| ClientUI.getInstance().getToolPanel().getCurrentTool()
instanceof TextTool) {
ClientUI.getInstance().getToolPanel().getCurrentTool().render(graphics);
}
}
/**
* Invoked when the mouse button has been clicked (pressed and
* released) on a component.
*
* @param e The mouse event associated with this event.
*/
public final void mouseClicked(final MouseEvent e) {
this.mousePosition = e.getPoint();
AbstractTool tool = ClientUI.getInstance().getToolPanel().getCurrentTool();
if (tool == null) {
//No tool selected. Nothing to do.
return;
}
if (e.getButton() == MouseEvent.BUTTON1) {
//Tell the current tool to process the click otherwise:
try {
tool.processClick(this.mousePosition);
} catch (Exception exc) {
System.out.println("GraphicsPanel: Got an error processing click:");
System.out.println("\t" + exc);
exc.printStackTrace();
}
} else {
//If it is a right-click, then cancel whatever there was in the tool:
tool.clear();
}
//Rerender:
this.markForRerender(false);
}
/**
* Invoked when the mouse enters a component.
*
* @param e The mouse event associated with this event.
*/
public final void mouseEntered(final MouseEvent e) {
//Can now render the current tool:
this.mouseActive = true;
this.mousePosition = e.getPoint();
this.requestFocus();
//Rerender:
this.markForRerender(false);
}
/**
* Invoked when the mouse exits a component.
*
* @param e The mouse event associated with this event.
*/
public final void mouseExited(final MouseEvent e) {
//Do not render current tool when the mouse is not in the control:
this.mouseActive = false;
this.mousePosition = null;
//Rerender:
this.markForRerender(false);
}
/**
* Invoked when the mouse cursor has been moved onto a component but
* no buttons have been pushed.
*
* @param e The mouse event associated with this event.
*/
public final void mouseMoved(final MouseEvent e) {
AbstractTool tool = ClientUI.getInstance().getToolPanel().getCurrentTool();
if (tool == null) {
//No tool selected. Nothing to do.
return;
}
if (tool.isActive()) {
//Update mouse position if the mouse is in the control:
this.mousePosition = e.getPoint();
//Rerender:
this.markForRerender(false);
}
}
/**
* Invoked when a mouse button is pressed on a component and then
* dragged.
*
* @param e The mouse event associated with this event.
*/
public final void mouseDragged(final MouseEvent e) {
AbstractTool tool = ClientUI.getInstance().getToolPanel().getCurrentTool();
if (tool == null) {
return;
}
if (tool.isActive()) {
tool.processDrag(new Point((int) (e.getPoint()
.getX() - mousePosition.getX()),
(int) (e.getPoint()
.getY() - mousePosition.getY())));
//Update mouse position if the mouse is in the control:
this.mousePosition = e.getPoint();
//Rerender:
this.markForRerender(false);
}
}
/**
* Invoked when a key has been typed. See the class description for
* KeyEvent for a definition of a key typed event.
*
* @param e a <code>KeyEvent</code> value
*/
public void keyTyped(KeyEvent e) {
AbstractTool tool = ClientUI.getInstance().getToolPanel().getCurrentTool();
if (tool == null) {
return;
}
if (tool.isActive()) {
tool.processKeypress(e.getKeyChar());
//Rerender:
this.markForRerender(false);
}
}
/**
* This will be used to tell the scroll pane how large we want to be.
*
* @return a <code>Dimension</code> value
*/
public Dimension getPreferredSize() {
return ClientUISettings.GRAPHICS_CONTENT_SIZE;
}
/**
* Invoked when a mouse button has been pressed on a component.
*
* @param e The mouse event associated with this event.
*/
public final void mousePressed(final MouseEvent e) { }
/**
* Invoked when a mouse button has been released on a component.
*
* @param e The mouse event associated with this event.
*/
public final void mouseReleased(final MouseEvent e) { }
/**
* Invoked when a key has been pressed. See the class description
* for KeyEvent for a definition of a key pressed event.
*
* @param e a <code>KeyEvent</code> value
*/
public void keyPressed(KeyEvent e) { }
/**
* Invoked when a key has been released. See the class description
* for KeyEvent for a definition of a key released event.
*
* @param e a <code>KeyEvent</code> value
*/
public void keyReleased(KeyEvent e) { }
}

View File

@@ -0,0 +1,115 @@
package netpaint.client.gui;
import javax.swing.event.InternalFrameEvent;
import javax.swing.event.InternalFrameAdapter;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JInternalFrame;
import javax.swing.JEditorPane;
import javax.swing.event.HyperlinkListener;
import javax.swing.event.HyperlinkEvent;
import javax.swing.text.html.HTMLFrameHyperlinkEvent;
import javax.swing.text.html.HTMLDocument;
/**
* Class HelpPanel: The help panel contains the help stuff.
*
* <PRE>
* Revision History:
* v1.0 (Feb. 12, 2004) - Created the HelpPanel class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Feb. 12, 2004
*/
public class HelpPanel extends JInternalFrame {
/**
* Creates a new <code>HelpPanel</code> instance.
*/
public HelpPanel() {
super("Help", //Title
true, //Resizable
true, //Closable
false, //Maximizable
true); //Iconifiable
JPanel contents = new JPanel();
JEditorPane editorPane = new JEditorPane();
editorPane.setEditable(false);
java.net.URL helpURL = this.getClass().getResource("help.html");
if (helpURL != null) {
try {
editorPane.setPage(helpURL);
} catch (java.io.IOException e) {
System.err.println("Attempted to read a bad URL: " + helpURL);
}
} else {
System.err.println("Couldn't find file: TextSampleDemoHelp.html");
}
editorPane.addHyperlinkListener(new Hyperactive());
/*
JEditorPane help = null;
try {
help = new JEditorPane(getClass().getResource("help.html"));
} catch (Exception e) {
help = new JEditorPane();
help.setText("Could not load help file!");
}
help.setEditable(false);
help.setPreferredSize(ClientUISettings.HELP_DEFAULT_SIZE);
*/
JScrollPane scroller = new JScrollPane(editorPane);
scroller.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
scroller.setPreferredSize(ClientUISettings.HELP_DEFAULT_SIZE);
contents.add(scroller);
this.setContentPane(contents);
//Define custom behaviour for closing:
this.setDefaultCloseOperation(JInternalFrame.DO_NOTHING_ON_CLOSE);
this.addInternalFrameListener(new InternalFrameAdapter() {
public void internalFrameClosing(InternalFrameEvent e) {
setVisible(false);
}
});
}
/**
* Will be used to jump hyperlinks.
*/
private class Hyperactive implements HyperlinkListener {
/**
* Called when a hypertext link is updated.
*
* @param e a <code>HyperlinkEvent</code> value
*/
public void hyperlinkUpdate(HyperlinkEvent e) {
if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
JEditorPane pane = (JEditorPane) e.getSource();
if (e instanceof HTMLFrameHyperlinkEvent) {
HTMLFrameHyperlinkEvent evt = (HTMLFrameHyperlinkEvent) e;
HTMLDocument doc = (HTMLDocument) pane.getDocument();
doc.processHTMLFrameHyperlinkEvent(evt);
} else {
try {
pane.setPage(e.getURL());
} catch (Throwable t) {
System.out.println("HelpPanel: Got bad hyperlink...");
}
}
}
}
}
}

View File

@@ -0,0 +1,155 @@
package netpaint.client.gui;
import java.net.URL;
import java.io.IOException;
import java.awt.Graphics2D;
import java.awt.AlphaComposite;
import java.awt.image.BufferedImage;
import java.awt.GraphicsEnvironment;
import java.awt.GraphicsConfiguration;
/**
* Class ImageLoader: The image loader class provides the
* functionality for loading images from the hard drive or the web
* into BufferedImages.
*
* <PRE>
* Revision History:
* v1.0 (Feb. 06, 2004) - Created the ImageLoader class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Feb. 06, 2004
*/
public final class ImageLoader {
/**
* Implement this as a singleton.
*/
private static ImageLoader instance;
/**
* The default graphics configuration for the current client.
*/
private GraphicsConfiguration config;
/**
* Creates a new <code>ImageLoader</code> instance.
*/
private ImageLoader() {
config = GraphicsEnvironment.getLocalGraphicsEnvironment()
.getDefaultScreenDevice().getDefaultConfiguration();
}
/**
* Singleton implementation.
*
* @return an <code>ImageLoader</code> value
*/
public static ImageLoader getInstance() {
if (instance == null) {
instance = new ImageLoader();
}
return instance;
}
/**
* Returns the graphics configuration used.
*
* @return a <code>GraphicsConfiguration</code> value
*/
public GraphicsConfiguration getImageConfiguration() {
return config;
}
/**
* Loads a local image for instance, for insertion into the graphics
* pane.
*
* @param imagePath a <code>String</code> value
* @return a <code>BufferedImage</code> value
*/
public BufferedImage loadLocalImage(final String imagePath) {
/*
if (ClientUISettings.VERBOSE) {
System.out.println("Loading image: " + imagePath);
System.out.println("Resource: "
+ this.getClass().getResource(imagePath));
System.out.flush();
}
*/
URL path = this.getClass().getResource(imagePath);
if (path == null) { return null; }
try {
BufferedImage result = javax.imageio.ImageIO.read(path);
return accelerateImage(result);
} catch (IOException ioe) {
System.out.println("Could not load image \"" + imagePath + "\"...");
System.out.println(ioe.getMessage());
return null;
}
}
/**
* Loads a system image from the standard location.
*
* @param imageName a <code>String</code> value
* @return a <code>BufferedImage</code> value
*/
public BufferedImage loadSystemImage(final String imageName) {
return loadLocalImage(netpaint.client.ClientSettings.IMAGE_LOCATION
+ imageName);
}
/**
* Loads the image from the internet.
*
* @param imageURL a <code>String</code> value
* @return a <code>BufferedImage</code> value
*/
public BufferedImage loadNetworkImage(final URL imageURL) {
BufferedImage result = null;
try {
result = javax.imageio.ImageIO.read(imageURL);
} catch (IOException ioe) {
System.err.println("ImageLoader: Got IOException:");
System.err.println("\t" + ioe.getMessage());
return null;
}
if (result == null) { return null; }
return accelerateImage(result);
}
/**
* Prepares a hardware-accelerated image if possible.
*
* @param src a <code>BufferedImage</code> value
* @return a <code>BufferedImage</code> value
*/
private BufferedImage accelerateImage(final BufferedImage src) {
BufferedImage result
= config.createCompatibleImage(src.getWidth(),
src.getHeight(),
src.getColorModel().getTransparency());
Graphics2D g2d = result.createGraphics();
g2d.setComposite(AlphaComposite.Src);
g2d.drawImage(src, 0, 0, null);
g2d.dispose();
return result;
}
}

View File

@@ -0,0 +1,126 @@
package netpaint.client.gui;
import java.net.URL;
import java.net.MalformedURLException;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.JTextField;
import javax.swing.BoxLayout;
/**
* This is the panel that will be displayed when the user selects the
* image tool. It will allow the user to load an image and then insert
* it onto the graphics panel.
*
* @author <a href="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</a>
* @version 1.0
*/
public class ImageSelectionPanel extends JPanel implements ActionListener {
/**
* The URL will be put here.
*/
private JTextField urlInputField;
/**
* When this button is clicked, the image will be loaded.
*/
private JButton loadButton;
/**
* The currently loaded image.
*/
private BufferedImage image;
/**
* The URL of the currently loaded image.
*/
private URL currentURL;
/**
* Creates a new <code>ImageSelectionPanel</code> instance.
*
*/
public ImageSelectionPanel() {
super();
this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
image = null;
urlInputField = new JTextField(20);
loadButton = new JButton("Load");
loadButton.addActionListener(this);
this.add(urlInputField);
this.add(loadButton);
}
/**
* Loads the image with the URL specified in the input field.
*/
private void loadImage() {
String text = null;
URL path = null;
text = urlInputField.getText();
if (text == null || text.equals("")) {
return;
}
try {
path = new URL(text);
} catch (MalformedURLException mue) {
//Tell the user he sucks.
ClientUI.getInstance().showError("Could not load image",
"The URL provided (" + text
+ ") is malformed.");
return;
}
image = ImageLoader.getInstance().loadNetworkImage(path);
if (image == null) {
//Tell the user he sucks.
ClientUI.getInstance().showError("Could not load image",
"Failed to load image from URL "
+ path);
return;
}
currentURL = path;
}
/**
* Returns the currently loaded image, or null if no image is loaded.
*
* @return a <code>BufferedImage</code> value
*/
public BufferedImage getImage() {
return image;
}
/**
* Returns the URL of the currently loaded image, or null if no
* image is loaded.
*
* @return an <code>URL</code> value
*/
public URL getImageURL() {
return currentURL;
}
/**
* Invoked when an action occurs.
*
* @param e the event associated with the action.
*/
public final void actionPerformed(final ActionEvent e) {
loadImage();
}
}

View File

@@ -0,0 +1,201 @@
package netpaint.client.gui;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JMenuBar;
import javax.swing.KeyStroke;
import netpaint.client.Client;
/**
* Class <code>MainMenu</code> contains the menu bar for the program.
*
* @author <a href="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</a>
* @version 1.0
*/
public class MainMenu extends JMenuBar implements ActionListener {
/**
* Creates a new <code>MainMenu</code> instance.
*/
public MainMenu() {
this.add(makeFileMenu());
this.add(makeEditMenu());
this.add(makeHelpMenu());
}
/**
* Makes a menu item with the specified parameters.
*
* @param name a <code>String</code> value
* @param mnemonic an <code>int</code> value
* @return a <code>JMenuItem</code> value
*/
private JMenuItem makeItem(final String name, final int mnemonic) {
JMenuItem result = new JMenuItem(name);
result.setActionCommand(name);
result.addActionListener(this);
if (mnemonic != 0) {
result.setAccelerator(KeyStroke.getKeyStroke(mnemonic,
ActionEvent.CTRL_MASK));
}
return result;
}
/**
* Makes the file menu.
*
* @return a <code>JMenu</code> value
*/
private JMenu makeFileMenu() {
JMenu result = new JMenu("File");
JMenuItem tempItem = null;
//Connect
tempItem = makeItem(ClientUISettings.MENU_CONNECT_NAME,
KeyEvent.VK_C);
result.add(tempItem);
//Disconnect
tempItem = makeItem(ClientUISettings.MENU_DISCONNECT_NAME,
KeyEvent.VK_D);
result.add(tempItem);
//Separator
result.addSeparator();
//Load Session
tempItem = makeItem(ClientUISettings.MENU_LOAD_SESSION_NAME,
KeyEvent.VK_O);
result.add(tempItem);
// tempItem.setEnabled(false);
//Save Session
tempItem = makeItem(ClientUISettings.MENU_SAVE_NAME, KeyEvent.VK_S);
result.add(tempItem);
// tempItem.setEnabled(false);
//Save Session As
tempItem = makeItem(ClientUISettings.MENU_SAVE_AS_NAME, 0);
result.add(tempItem);
// tempItem.setEnabled(false);
//Separator
result.addSeparator();
//Quit
tempItem = makeItem(ClientUISettings.MENU_QUIT_NAME, KeyEvent.VK_Q);
result.add(tempItem);
return result;
}
/**
* Makes the edit menu.
*
* @return a <code>JMenu</code> value
*/
private JMenu makeEditMenu() {
JMenu result = new JMenu("Edit");
JMenuItem tempItem = null;
//Undo
tempItem = makeItem(ClientUISettings.MENU_UNDO_NAME, KeyEvent.VK_Z);
result.add(tempItem);
//Undo List
tempItem = makeItem(ClientUISettings.MENU_SUPER_UNDO_NAME, KeyEvent.VK_U);
result.add(tempItem);
// tempItem.setEnabled(false);
return result;
}
/**
* Makes the help menu.
*
* @return a <code>JMenu</code> value
*/
private JMenu makeHelpMenu() {
JMenu result = new JMenu("Help");
JMenuItem tempItem = null;
//Contents
tempItem = makeItem(ClientUISettings.MENU_CONTENTS_NAME, 0);
result.add(tempItem);
// tempItem.setEnabled(false);
//About
tempItem = makeItem(ClientUISettings.MENU_ABOUT_NAME, KeyEvent.VK_A);
result.add(tempItem);
return result;
}
/**
* Invoked when an action occurs.
*
* @param e the event associated with the action.
*/
public final void actionPerformed(final ActionEvent e) {
String action = e.getActionCommand();
if (ClientUISettings.MENU_CONNECT_NAME.equals(action)) {
if (!Client.getInstance().isConnected()) {
ClientUI.getInstance().showConnectionDialog();
} else {
ClientUI.getInstance()
.showMessage("Already Connected",
"A connection to a server is already present. If you "
+ "wish to connect to another server, please disconne"
+ "ct from this one first.");
}
} else if (ClientUISettings.MENU_DISCONNECT_NAME.equals(action)) {
if (Client.getInstance().isConnected()) {
Client.getInstance().disconnectFromServer();
} else {
ClientUI.getInstance()
.showMessage("No Connection",
"No connection is currently present to a server.");
}
} else if (ClientUISettings.MENU_SAVE_NAME.equals(action)) {
Client.getInstance().saveSession(false);
} else if (ClientUISettings.MENU_SAVE_AS_NAME.equals(action)) {
Client.getInstance().saveSession(true);
} else if (ClientUISettings.MENU_LOAD_SESSION_NAME.equals(action)) {
Client.getInstance().loadSession();
} else if (ClientUISettings.MENU_QUIT_NAME.equals(action)) {
Client.getInstance().clientQuit();
} else if (ClientUISettings.MENU_UNDO_NAME.equals(action)) {
Client.getInstance().undoLast();
} else if (ClientUISettings.MENU_SUPER_UNDO_NAME.equals(action)) {
ClientUI.getInstance().showUndoFrame();
} else if (ClientUISettings.MENU_CONTENTS_NAME.equals(action)) {
ClientUI.getInstance().showHelpFrame();
} else if (ClientUISettings.MENU_ABOUT_NAME.equals(action)) {
ClientUI.getInstance()
.showMessage("About",
"NetPaint by Vladimir Urazov(gtg308i) and"
+ "Daniyar Zhanbekov(gtg563g).");
} else {
return;
}
}
}

View File

@@ -0,0 +1,76 @@
package netpaint.client.gui;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.WindowEvent;
import java.awt.event.WindowAdapter;
import javax.swing.JFrame;
import javax.swing.JDesktopPane;
import netpaint.client.Client;
/**
* Class MainWindow: The main window is just what the name implies. It
* is the container window for all of the other front end: graphics,
* tools, chat, etc.
*
* <PRE>
* Revision History:
* v1.0 (Feb. 02, 2004) - Created the MainWindow class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Feb. 02, 2004
*/
public class MainWindow extends JFrame {
/**
* This is the special desktop pane that will be the content pane
* for this window. It will allow us to minimize the internal
* frames, which will make it all pretty and amazing.
*/
private JDesktopPane desktop;
/**
* Creates a new <code>MainWindow</code> instance.
*/
public MainWindow() {
super(ClientUISettings.TITLE_PROGRAM);
//Put the window smack in the middle of the screen, and give it
//dimensions as defined by the settings:
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int xOffset = screenSize.width - ClientUISettings.PROGRAM_WINDOW_WIDTH;
int yOffset = screenSize.height - ClientUISettings.PROGRAM_WINDOW_HEIGHT;
this.setBounds(xOffset / 2, yOffset / 2,
screenSize.width - xOffset,
screenSize.height - yOffset);
//Create the desktop pane:
desktop = new JDesktopPane();
this.setContentPane(desktop);
//Make dragging smooth but ugly:
desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
//Define custom behaviour for closing:
this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
Client.getInstance().clientQuit();
}
});
}
/**
* Returns the desktop pane used in this frame.
*
* @return a <code>JDesktopPane</code> value
*/
public final JDesktopPane getDesktopPane() {
return desktop;
}
}

View File

@@ -0,0 +1,231 @@
package netpaint.client.gui;
import javax.swing.SpringLayout;
import java.awt.Component;
import java.awt.Container;
import javax.swing.Spring;
/**
* A 1.4 file that provides utility methods for creating form- or
* grid-style layouts with SpringLayout. These utilities were taken
* directly from the Java tutorials found here:
*
* "How to Use SpringLayout"
* http://java.sun.com/docs/books/tutorial/uiswing/layout/spring.html
*/
public class SpringUtilities {
/**
* Make PMD not worry about singleton stuff...
*
* @return a <code>boolean</code> value
*/
public final boolean test() { return true; }
/**
* A debugging utility that prints to stdout the component's
* minimum, preferred, and maximum sizes.
* @param c a <code>Component</code> value
*/
public static void printSizes(final Component c) {
System.out.println("minimumSize = " + c.getMinimumSize());
System.out.println("preferredSize = " + c.getPreferredSize());
System.out.println("maximumSize = " + c.getMaximumSize());
}
/**
* Aligns the first <code>rows</code> * <code>cols</code>
* components of <code>parent</code> in
* a grid. Each component is as big as the maximum
* preferred width and height of the components.
* The parent is made just big enough to fit them all.
*
* @param parent a <code>Container</code> value
* @param rows number of rows
* @param cols number of columns
* @param initialX x location to start the grid at
* @param initialY y location to start the grid at
* @param xPad x padding between cells
* @param yPad y padding between cells
*/
public static void makeGrid(final Container parent,
final int rows, final int cols,
final int initialX, final int initialY,
final int xPad, final int yPad) {
SpringLayout layout;
try {
layout = (SpringLayout) parent.getLayout();
} catch (ClassCastException exc) {
System.err.println("The first argument to makeGrid must "
+ "use SpringLayout.");
return;
}
Spring xPadSpring = Spring.constant(xPad);
Spring yPadSpring = Spring.constant(yPad);
Spring initialXSpring = Spring.constant(initialX);
Spring initialYSpring = Spring.constant(initialY);
int max = rows * cols;
//Calculate Springs that are the max of the width/height so that all
//cells have the same size.
Spring maxWidthSpring = layout
.getConstraints(parent.getComponent(0)).getWidth();
Spring maxHeightSpring = layout
.getConstraints(parent.getComponent(0)).getWidth();
for (int i = 1; i < max; i++) {
SpringLayout.Constraints cons = layout
.getConstraints(parent.getComponent(i));
maxWidthSpring = Spring.max(maxWidthSpring, cons.getWidth());
maxHeightSpring = Spring.max(maxHeightSpring, cons.getHeight());
}
//Apply the new width/height Spring. This forces all the
//components to have the same size.
for (int i = 0; i < max; i++) {
SpringLayout.Constraints cons = layout
.getConstraints(parent.getComponent(i));
cons.setWidth(maxWidthSpring);
cons.setHeight(maxHeightSpring);
}
//Then adjust the x/y constraints of all the cells so that they
//are aligned in a grid.
SpringLayout.Constraints lastCons = null;
SpringLayout.Constraints lastRowCons = null;
for (int i = 0; i < max; i++) {
SpringLayout.Constraints cons = layout
.getConstraints(parent.getComponent(i));
if (i % cols == 0) { //start of new row
lastRowCons = lastCons;
cons.setX(initialXSpring);
} else { //x position depends on previous component
cons.setX(Spring
.sum(lastCons.getConstraint(SpringLayout.EAST), xPadSpring));
}
if (i / cols == 0) { //first row
cons.setY(initialYSpring);
} else { //y position depends on previous row
cons.setY(Spring.sum(lastRowCons
.getConstraint(SpringLayout.SOUTH), yPadSpring));
}
lastCons = cons;
}
//Set the parent's size.
SpringLayout.Constraints pCons = layout.getConstraints(parent);
pCons.setConstraint(SpringLayout.SOUTH, Spring
.sum(Spring.constant(yPad),
lastCons.getConstraint(SpringLayout.SOUTH)));
pCons.setConstraint(SpringLayout.EAST, Spring
.sum(Spring.constant(xPad),
lastCons.getConstraint(SpringLayout.EAST)));
}
/**
* Used by makeCompactGrid.
* @param row an <code>int</code> value
* @param col an <code>int</code> value
* @param parent a <code>Container</code> value
* @param cols an <code>int</code> value
* @return a <code>SpringLayout.Constraints</code> value
*/
private static SpringLayout.Constraints getConstraintsForCell(final int row,
final int col,
final Container parent,
final int cols) {
SpringLayout layout = (SpringLayout) parent.getLayout();
Component c = parent.getComponent(row * cols + col);
return layout.getConstraints(c);
}
/**
* Aligns the first <code>rows</code> * <code>cols</code>
* components of <code>parent</code> in
* a grid. Each component in a column is as wide as the maximum
* preferred width of the components in that column;
* height is similarly determined for each row.
* The parent is made just big enough to fit them all.
*
* @param parent a <code>Container</code> value
* @param rows number of rows
* @param cols number of columns
* @param initialX x location to start the grid at
* @param initialY y location to start the grid at
* @param xPad x padding between cells
* @param yPad y padding between cells
*/
public static void makeCompactGrid(final Container parent,
final int rows, final int cols,
final int initialX, final int initialY,
final int xPad, final int yPad) {
SpringLayout layout;
try {
layout = (SpringLayout) parent.getLayout();
} catch (ClassCastException exc) {
System.err.println("The first argument to makeCompactGrid "
+ "must use SpringLayout.");
return;
}
//Align all cells in each column and make them the same width.
Spring x = Spring.constant(initialX);
for (int c = 0; c < cols; c++) {
Spring width = Spring.constant(0);
for (int r = 0; r < rows; r++) {
width = Spring.max(width,
getConstraintsForCell(r, c, parent, cols)
.getWidth());
}
for (int r = 0; r < rows; r++) {
SpringLayout.Constraints constraints =
getConstraintsForCell(r, c, parent, cols);
constraints.setX(x);
constraints.setWidth(width);
}
x = Spring.sum(x, Spring.sum(width, Spring.constant(xPad)));
}
//Align all cells in each row and make them the same height.
Spring y = Spring.constant(initialY);
for (int r = 0; r < rows; r++) {
Spring height = Spring.constant(0);
for (int c = 0; c < cols; c++) {
height = Spring.max(height,
getConstraintsForCell(r, c, parent, cols).
getHeight());
}
for (int c = 0; c < cols; c++) {
SpringLayout.Constraints constraints =
getConstraintsForCell(r, c, parent, cols);
constraints.setY(y);
constraints.setHeight(height);
}
y = Spring.sum(y, Spring.sum(height, Spring.constant(yPad)));
}
//Set the parent's size.
SpringLayout.Constraints pCons = layout.getConstraints(parent);
pCons.setConstraint(SpringLayout.SOUTH, y);
pCons.setConstraint(SpringLayout.EAST, x);
}
}

View File

@@ -0,0 +1,250 @@
package netpaint.client.gui;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.event.InternalFrameEvent;
import javax.swing.event.InternalFrameAdapter;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JInternalFrame;
import javax.swing.JList;
import javax.swing.JButton;
import javax.swing.DefaultListModel;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.BoxLayout;
import netpaint.messaging.GraphicMessage;
import netpaint.client.Client;
import netpaint.client.GraphicMessageListIterator;
/**
* Class SuperUndoPanel: The super undo panel displays the list of
* graphics messages and allows the user to undo them.
*
* <PRE>
* Revision History:
* v1.0 (Feb. 12, 2004) - Created the SuperUndoPanel class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Feb. 12, 2004
*/
public final class SuperUndoPanel extends JInternalFrame
implements ActionListener, ListSelectionListener {
/**
* The list model associated with the undo list.
*/
private DefaultListModel listModel;
/**
* The list.
*/
private JList list;
/**
* The undo button.
*/
private JButton undoButton;
/**
* Creates a new <code>SuperUndoPanel</code> instance.
*/
public SuperUndoPanel() {
super(ClientUISettings.UNDO_TITLE, //Title
false, //Resizable
false, //Closable
false, //Maximizable
true); //Iconifiable
JPanel contents = new JPanel(new BorderLayout());
contents.add(makeListPanel(), BorderLayout.CENTER);
contents.add(makeButtonPanel(), BorderLayout.SOUTH);
this.setContentPane(contents);
//Define custom behaviour for closing:
this.setDefaultCloseOperation(JInternalFrame.DO_NOTHING_ON_CLOSE);
this.addInternalFrameListener(new InternalFrameAdapter() {
public void internalFrameClosing(InternalFrameEvent e) {
setVisible(false);
}
});
}
/**
* Makes and returns a panel with the list in it.
*
* @return a <code>JPanel</code> value
*/
private JScrollPane makeListPanel() {
JScrollPane result = null;
listModel = new DefaultListModel();
listModel.addElement("Polygon stuff");
list = new JList(listModel);
list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
list.addListSelectionListener(this);
list.setVisibleRowCount(8);
result = new JScrollPane(list);
return result;
}
/**
* Makes the button panel.
*
* @return a <code>JPanel</code> value
*/
private JPanel makeButtonPanel() {
JPanel result = new JPanel();
JButton temp = null;
result.setLayout(new BoxLayout(result, BoxLayout.X_AXIS));
//Undo
undoButton = new JButton(ClientUISettings.UNDO_NAME);
undoButton.setActionCommand(ClientUISettings.UNDO_NAME);
undoButton.addActionListener(this);
undoButton.setEnabled(false);
result.add(undoButton);
//Cancel
temp = new JButton(ClientUISettings.UNDO_CANCEL);
temp.setActionCommand(ClientUISettings.UNDO_CANCEL);
temp.addActionListener(this);
result.add(temp);
return result;
}
/**
* Updates the list.
*/
public void updateList() {
GraphicMessageListIterator gmli
= Client.getInstance().getGraphicsListIterator();
clearList();
while (gmli.hasNext()) {
listModel.addElement(gmli.next());
}
list.ensureIndexIsVisible(listModel.getSize() - 1);
}
/**
* Clears out the list of messages.
*/
private void clearList() {
if (listModel != null) {
listModel.clear();
}
}
/**
* Enables all messages.
*/
private void enableAllMessages() {
for (int i = 0; i < listModel.getSize(); i++) {
GraphicMessage m = (GraphicMessage) listModel.elementAt(i);
m.setShowMessage(true);
}
}
/**
* Describe <code>disableMessage</code> method here.
*/
private void disableMessages() {
int[] indices = null;
//Enable all messages first
enableAllMessages();
//Now, disable the selected messages:
indices = list.getSelectedIndices();
for (int i = 0; i < indices.length; i++) {
((GraphicMessage) (listModel.elementAt(indices[i])))
.setShowMessage(false);
}
}
/**
* Shows or hides the panel.
*
* @param value a <code>boolean</code> value
*/
public void setVisible(final boolean value) {
if (isVisible() == value) {
return;
}
super.setVisible(value);
if (value) {
updateList();
} else {
clearList();
}
}
/**
* Invoked when an action occurs.
*
* @param e the event associated with the action.
*/
public void actionPerformed(final ActionEvent e) {
String action = e.getActionCommand();
if (ClientUISettings.UNDO_NAME.equals(action)) {
//Undo everything first.
int[] indices = list.getSelectedIndices();
Client c = Client.getInstance();
for (int i = 0; i < indices.length; i++) {
c.undoMessage(((GraphicMessage) listModel.elementAt(indices[i]))
.getMessageId());
}
//setVisible(false);
} else if (ClientUISettings.UNDO_CANCEL.equals(action)) {
setVisible(false);
} else {
System.out.println("SuperUndoPanel: Got an unknown action command - "
+ action);
}
}
/**This method is required by ListSelectionListener.
* Invoked when an action occurs.
*
* @param e the event associated with the action.
*/
public void valueChanged(ListSelectionEvent e) {
if (!e.getValueIsAdjusting()) {
if (list.getSelectedIndex() == -1) {
//No selection
enableAllMessages();
undoButton.setEnabled(false);
} else {
//Selection
disableMessages();
undoButton.setEnabled(true);
}
ClientUI.getInstance().getGraphicsPanel().markForRerender(true);
}
}
}

View File

@@ -0,0 +1,524 @@
package netpaint.client.gui;
import java.awt.Font;
import java.awt.Color;
import java.awt.Window;
import java.awt.GridLayout;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentAdapter;
import java.awt.image.BufferedImage;
import javax.swing.JPanel;
import javax.swing.JDialog;
import javax.swing.JButton;
import javax.swing.ImageIcon;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JToggleButton;
import javax.swing.BorderFactory;
import javax.swing.JColorChooser;
import netpaint.client.tools.AbstractTool;
import netpaint.client.tools.LineTool;
import netpaint.client.tools.TextTool;
import netpaint.client.tools.ImageTool;
import netpaint.client.tools.RectangleTool;
import netpaint.client.tools.EllipseTool;
import netpaint.client.tools.TriangleTool;
import netpaint.client.tools.QuadraticCurveTool;
import netpaint.client.tools.PolygonTool;
/**
* Class ToolPanel: The tool panel contains buttons for all of the
* tools available to the user and allows the selection of a tool, as
* well as color selection.
*
* <PRE>
* Revision History:
* v1.0 (Feb. 06, 2004) - Created the ToolPanel class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Feb. 06, 2004
*/
public class ToolPanel extends JPanel implements ActionListener {
/**
* This is a little panel that will indicate the currently chosen color.
*/
private JButton colorIndicator;
/**
* This panel will contain the switch between filled and outlined shapes.
*/
private JPanel fillSwitch;
/**
* Contains the currently selected color.
*/
private Color currentColor;
/**
* Contains a reference to the currently selected tool.
*/
private AbstractTool currentTool;
/**
* Will be set to true if we want to draw shapes filled in, and
* false if we want them to be just outlined.
*/
private boolean fillShapes;
/**
* Creates a new <code>ToolPanel</code> instance.
*/
public ToolPanel() {
super();
this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
if (ClientUISettings.VERBOSE) {
System.out.println("Creating ToolPanel");
}
fillShapes = false;
currentColor = ClientUISettings.TOOL_DEFAULT_COLOR;
this.add(makeButtonPanel()/*, BorderLayout.CENTER*/);
this.add(makeColorChooser()/*, BorderLayout.SOUTH*/);
this.add(makeFillSwitch());
//This refers to the fill switch panel. So keep it AFTER the
//creation of that panel.
setCurrentTool(new LineTool());
}
/**
* Creates a new tool icon with the specified name, tool tip, and icon.
*
* @param name a <code>String</code> value
* @param toolTip a <code>String</code> value
* @param image a <code>String</code> value
* @return a <code>JToggleButton</code> value
*/
private JToggleButton makeToolButton(final String name,
final String toolTip,
final String image) {
JToggleButton result = new JToggleButton();
result.setActionCommand(name);
result.addActionListener(this);
if (ClientUISettings.VERBOSE) {
System.out.println("ToolPanel: Making button - " + name + ": "
+ toolTip);
}
//Set up the image icon:
BufferedImage bim = ImageLoader.getInstance().loadSystemImage(image);
if (bim != null) {
//Successfully loaded the image
result.setIcon(new ImageIcon(bim));
} else {
//Could not load image...
result.setText(name);
result.setFont(result.getFont().deriveFont(Font.ITALIC));
result.setHorizontalAlignment(JButton.HORIZONTAL);
}
result.setToolTipText(name + ": " + toolTip);
result.setFocusPainted(false);
return result;
}
/**
* Creates and fills in the button panel with different buttons that
* would allow one to select a tool.
*
* @return a <code>JPanel</code> value
*/
private JPanel makeButtonPanel() {
JPanel result
= new JPanel(new GridLayout(ClientUISettings.TOOL_TABLE.height,
ClientUISettings.TOOL_TABLE.width,
ClientUISettings.TOOL_TABLE_GAPS.width,
ClientUISettings.TOOL_TABLE_GAPS.height));
ButtonGroup toolGroup = new ButtonGroup();
JToggleButton currentButton = null;
//Line
currentButton = makeToolButton(ClientUISettings.TOOL_LINE_NAME,
ClientUISettings.TOOL_LINE_TTIP,
ClientUISettings.TOOL_LINE_ICON);
toolGroup.add(currentButton);
toolGroup.setSelected(currentButton.getModel(), true);
result.add(currentButton);
//Rectangle
currentButton = makeToolButton(ClientUISettings.TOOL_RECTANGLE_NAME,
ClientUISettings.TOOL_RECTANGLE_TTIP,
ClientUISettings.TOOL_RECTANGLE_ICON);
toolGroup.add(currentButton);
result.add(currentButton);
//Triangle
currentButton = makeToolButton(ClientUISettings.TOOL_TRIANGLE_NAME,
ClientUISettings.TOOL_TRIANGLE_TTIP,
ClientUISettings.TOOL_TRIANGLE_ICON);
toolGroup.add(currentButton);
result.add(currentButton);
//Polygon
currentButton = makeToolButton(ClientUISettings.TOOL_POLYGON_NAME,
ClientUISettings.TOOL_POLYGON_TTIP,
ClientUISettings.TOOL_POLYGON_ICON);
toolGroup.add(currentButton);
result.add(currentButton);
//Ellipse
currentButton = makeToolButton(ClientUISettings.TOOL_ELLIPSE_NAME,
ClientUISettings.TOOL_ELLIPSE_TTIP,
ClientUISettings.TOOL_ELLIPSE_ICON);
toolGroup.add(currentButton);
result.add(currentButton);
//Quadratic
currentButton = makeToolButton(ClientUISettings.TOOL_QUADRATIC_NAME,
ClientUISettings.TOOL_QUADRATIC_TTIP,
ClientUISettings.TOOL_QUADRATIC_ICON);
toolGroup.add(currentButton);
result.add(currentButton);
//Image
currentButton = makeToolButton(ClientUISettings.TOOL_IMAGE_NAME,
ClientUISettings.TOOL_IMAGE_TTIP,
ClientUISettings.TOOL_IMAGE_ICON);
toolGroup.add(currentButton);
result.add(currentButton);
//Text
currentButton = makeToolButton(ClientUISettings.TOOL_TEXT_NAME,
ClientUISettings.TOOL_TEXT_TTIP,
ClientUISettings.TOOL_TEXT_ICON);
toolGroup.add(currentButton);
result.add(currentButton);
return result;
}
/**
* Returns a panel that contains a color chooser.
*
* @return a <code>JPanel</code> value
*/
private JPanel makeColorChooser() {
JPanel result = new JPanel();
JPanel container = new JPanel(new BorderLayout());
colorIndicator = new JButton(" ");
colorIndicator.setBackground(getCurrentColor());
colorIndicator.setActionCommand("color_chooser");
colorIndicator.addActionListener(this);
colorIndicator.setBorderPainted(false);
colorIndicator.setFocusPainted(false);
container.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createRaisedBevelBorder(),
BorderFactory.createLoweredBevelBorder()));
container.add(colorIndicator, BorderLayout.CENTER);
result.add(container);
return result;
}
/**
* Makes a JPanel with the fill switch.
*
* @return a <code>JPanel</code> value
*/
private JPanel makeFillSwitch() {
JPanel result = new JPanel(new GridLayout(1, 2, 5, 0));
ButtonGroup toolGroup = new ButtonGroup();
JToggleButton currentButton = null;
//Outline Shapes
currentButton = makeToolButton(ClientUISettings.TOOL_OUTLINE_NAME,
ClientUISettings.TOOL_OUTLINE_TTIP,
ClientUISettings.TOOL_OUTLINE_ICON);
toolGroup.add(currentButton);
toolGroup.setSelected(currentButton.getModel(), true);
result.add(currentButton);
//Fill Shapes
currentButton = makeToolButton(ClientUISettings.TOOL_FILL_NAME,
ClientUISettings.TOOL_FILL_TTIP,
ClientUISettings.TOOL_FILL_ICON);
toolGroup.add(currentButton);
result.add(currentButton);
fillSwitch = result;
return fillSwitch;
}
/**
* Returns true if the shapes drawn should be filled in, and false
* if they should be simply outlined.
*
* @return a <code>boolean</code> value
*/
public boolean getFillShape() {
return fillShapes;
}
/**
* Gives the new value to the fillShapes variable.
*
* @param newValue a <code>boolean</code> value
*/
protected void setFillShape(final boolean newValue) {
fillShapes = newValue;
}
/**
* Returns the currently selected color.
*
* @return a <code>Color</code> value
*/
public final Color getCurrentColor() {
return currentColor;
}
/**
* Gives the currently selected color a new value.
*
* @param newColor a <code>Color</code> value
*/
protected final void setCurrentColor(final Color newColor) {
//Update the color
this.currentColor = newColor;
//Update the current color on the indicator:
colorIndicator.setBackground(newColor);
}
/**
* Returns the currently active tool.
*
* @return an <code>AbstractTool</code> value
*/
public final AbstractTool getCurrentTool() {
return currentTool;
}
/**
* Sets the new tool to be currently selected.
*
* @param newTool an <code>AbstractTool</code> value
*/
protected final void setCurrentTool(final AbstractTool newTool) {
//Clear the old tool:
if (this.currentTool != null) {
this.currentTool.clear();
//Show/hide text panel if necessary:
if (this.currentTool instanceof TextTool) {
if (!(newTool instanceof TextTool)) {
//Switching from text tool to something else...
ClientUI.getInstance().hideFontFrame();
}
if (newTool instanceof ImageTool) {
//Switching from text to image tool...
ClientUI.getInstance().showImageFrame();
}
} else if (this.currentTool instanceof ImageTool) {
if (!(newTool instanceof ImageTool)) {
//Switching from image tool to something else...
ClientUI.getInstance().hideImageFrame();
}
if (newTool instanceof TextTool) {
//Switching from something to text tool...
ClientUI.getInstance().showFontFrame();
}
} else {
if (newTool instanceof ImageTool) {
//Switching from something to image tool...
ClientUI.getInstance().showImageFrame();
} else if (newTool instanceof TextTool) {
//Switching from something to text tool...
ClientUI.getInstance().showFontFrame();
}
}
}
//Set the new tool:
this.currentTool = newTool;
//Switch the fill/outline panel on/off:
boolean fillable = newTool.isFillableShape();
if (fillable != fillSwitch.isEnabled()) {
java.awt.Component[] children = fillSwitch.getComponents();
for (int i = 0; i < children.length; i++) {
children[i].setEnabled(fillable);
}
fillSwitch.setEnabled(fillable);
}
}
/**
* Shows the color chooser and returns the color that the user selected.
*
* @return a <code>Color</code> value
*/
protected Color showColorChooser() {
JColorChooser pane = new JColorChooser(getCurrentColor());
pane.setPreviewPanel(new JPanel());
ColorTracker ok = new ColorTracker(pane);
JDialog dialog = JColorChooser.createDialog(this,
"Choose shape color",
true,
pane,
ok,
null);
dialog.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
Window w = e.getWindow();
w.hide();
}
});
dialog.addComponentListener(new ComponentAdapter() {
public void componentHidden(ComponentEvent e) {
Window w = (Window) e.getComponent();
w.dispose();
}
});
dialog.show(); // blocks until user brings dialog down...
return ok.getColor();
}
/**
* Invoked when an action occurs.
*
* @param e the event associated with the action.
*/
public final void actionPerformed(final ActionEvent e) {
String action = e.getActionCommand();
if (ClientUISettings.VERBOSE) {
System.out.println(action + " button clicked.");
System.out.flush();
}
if (ClientUISettings.TOOL_LINE_NAME.equals(action)) {
setCurrentTool(new LineTool());
} else if (ClientUISettings.TOOL_RECTANGLE_NAME.equals(action)) {
setCurrentTool(new RectangleTool());
} else if (ClientUISettings.TOOL_TRIANGLE_NAME.equals(action)) {
setCurrentTool(new TriangleTool());
} else if (ClientUISettings.TOOL_POLYGON_NAME.equals(action)) {
setCurrentTool(new PolygonTool());
} else if (ClientUISettings.TOOL_ELLIPSE_NAME.equals(action)) {
setCurrentTool(new EllipseTool());
} else if (ClientUISettings.TOOL_QUADRATIC_NAME.equals(action)) {
setCurrentTool(new QuadraticCurveTool());
} else if (ClientUISettings.TOOL_IMAGE_NAME.equals(action)) {
setCurrentTool(new ImageTool());
} else if (ClientUISettings.TOOL_TEXT_NAME.equals(action)) {
setCurrentTool(new TextTool());
} else if (ClientUISettings.TOOL_OUTLINE_NAME.equals(action)) {
setFillShape(false);
} else if (ClientUISettings.TOOL_FILL_NAME.equals(action)) {
setFillShape(true);
} else if ("color_chooser".equals(action)) {
//Show the color chooser:
Color newColor = showColorChooser();
if (newColor != null) {
//The user has actually picked a color
setCurrentColor(newColor);
}
} else {
System.out.println("ToolPanel: Got an unkown action " + action);
System.out.flush();
}
}
/**
* I needed to show the color chooser as a dialog but Java doesn't
* allow to customize the color chooser in a dialog, as I needed
* to. So, I had to copy over the Java built-in showDialog method and
* add my own modifications to it. Now, that method requires the
* following class to operate, so I had to copy it over as well.
*
* @author <a href="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</a>
* @version 1.0
*/
class ColorTracker implements ActionListener {
/**
* The color chooser associated with this object.
*/
private JColorChooser chooser;
/**
* The color currently selected in the chooser.
*/
private Color color;
/**
* Creates a new <code>ColorTracker</code> instance.
*
* @param c a <code>JColorChooser</code> value
*/
public ColorTracker(final JColorChooser c) {
chooser = c;
}
/**
* Listens to actions performed in the color chooser.
*
* @param e an <code>ActionEvent</code> value
*/
public final void actionPerformed(final ActionEvent e) {
color = chooser.getColor();
}
/**
* Returns the color selected by the chooser.
*
* @return a <code>Color</code> value
*/
public final Color getColor() {
return color;
}
}
}

View File

@@ -0,0 +1,7 @@
<body>
Package <code>netpaint.client.gui</code> contains the classes for the
Client UI. Here are the main window, which contains all the other
components inside it, the chat panel which is an internal frame for
the main window and contains a simple chat client, as well as all
kinds of various dialogs.
</body>

View File

@@ -0,0 +1,29 @@
package netpaint.client.networking;
/**
* Class ClientNetworkSettings: Contains some settings for the client
* networking.
*
* <PRE>
* Revision History:
* v1.0 (Feb. 01, 2004) - Created the ClientNetworkSettings class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Feb. 01, 2004
*/
public final class ClientNetworkSettings {
/**
* Display verbose output?
*/
public static final boolean VERBOSE = false;
/**
* Creates a new <code>ClientNetworkSettings</code> instance.
*/
private ClientNetworkSettings() {
}
}

View File

@@ -0,0 +1,152 @@
package netpaint.client.networking;
import java.net.Socket;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.OutputStreamWriter;
import netpaint.messaging.AbstractMessage;
/**
* Class ClientNetworking: This class performs all of the necessary
* communications with the server. It will send and receive messages,
* as well as notify the server when we want to quit, etc.
*
* <PRE>
* Revision History:
* v1.0 (Jan. 31, 2004) - Created the ClientNetworking class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Jan. 31, 2004
*/
public class ClientNetworking {
/**
* The socket on which we want to connect to the server.
*/
private Socket socket;
/**
* Keeps track of whether the client is connected to a server or not.
*/
private boolean connected;
/**
* Contains the stream through which we can write to the socket.
*/
private PrintWriter socketWriter;
/**
* Creates a new <code>ClientNetworking</code> instance.
*/
public ClientNetworking() {
socket = null;
socketWriter = null;
connected = false;
}
/**
* Connects to the server at the specified location with the
* specified username. Returns true upon success, or false upon
* failure.
*
* @param host a <code>String</code> value
* @param port an <code>int</code> value
* @return a <code>boolean</code> value
*/
public final boolean connect(final String host,
final int port) {
socket = null;
connected = false;
//Try to open a connection:
try {
socket = new Socket(host, port);
} catch (IOException ioe) {
System.err.println("ClientNetworking: Could not connect to the server:");
System.err.println(ioe.getMessage());
socket = null;
connected = false;
return false;
}
//If we are still here, the connection was successfully opened.
//Get output stream:
try {
socketWriter =
new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
} catch (IOException ioe) {
System.err.println("ClientNetworking: Could not get output stream to "
+ "the server:\n" + ioe.getMessage());
try { socket.close(); } catch (IOException e) { socket = null; }
socket = null;
connected = false;
return false;
}
//Start the listener thread:
new Thread(new ServerListener(socket)).start();
if (ClientNetworkSettings.VERBOSE) {
System.out.println("ClientNetworking: Connected to server");
System.out.println("\t" + socket.getInetAddress().toString());
}
connected = true;
return true;
}
/**
* Returns true if the client is connected to a server, and false if
* it is not.
*
* @return a <code>boolean</code> value
*/
public final boolean isConnected() {
return connected;
}
/**
* Sends the abstract message over to the server.
*
* @param message an <code>AbstractMessage</code> value
*/
public final void sendMessage(final AbstractMessage message) {
if (ClientNetworkSettings.VERBOSE) {
System.out.println("ClientNetworking: Trying to send message");
System.out.println("\t" + message.tokenize());
System.out.println("Target: " + socket.getInetAddress().toString());
}
socketWriter.println(message.tokenize());
socketWriter.flush();
}
/**
* This function will shut down the connection to the server. It
* assumes that the QUIT message has already been issued, so it
* simply tells the server listener to stop listening, and closes
* the connection.
*/
public final void shutDown() {
if (ClientNetworkSettings.VERBOSE) {
System.out.println("ClientNetworking: Shutting down connection...");
}
try {
socketWriter.close();
socket.close();
} catch (IOException ioe) {
System.err.println("ClientNetworking: Couldn't even close the "
+ "connection cleanly...");
} finally {
socketWriter = null;
socket = null;
connected = false;
}
}
}

View File

@@ -0,0 +1,134 @@
package netpaint.client.networking;
import java.net.Socket;
import java.security.InvalidParameterException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import netpaint.messaging.AbstractMessage;
import netpaint.messaging.MessageFactory;
import netpaint.messaging.MessageSettings;
import netpaint.messaging.ServerErrorMessage;
import netpaint.client.Client;
/**
* Class ServerListener: The server listener class will run in its own
* thread and listen to the messages sent by the server. If a message
* is received, it will create something meaningful out of it using
* the message factory, and then pass the message on to the main
* client class.
*
* <PRE>
* Revision History:
* v1.0 (Jan. 31, 2004) - Created the ServerListener class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Jan. 31, 2004
*/
public class ServerListener implements Runnable {
/**
* Contains the socket on which we want to listen to the server.
*/
private Socket serverSocket;
/**
* A variable to keep track of when we should stop listening to the
* server.
*/
private boolean clientQuit;
/**
* Creates a new <code>ServerListener</code> instance.
* @param socket a <code>Socket</code> value
*/
public ServerListener(final Socket socket) {
this.serverSocket = socket;
if (!serverSocket.isBound()) {
throw new InvalidParameterException("ServerListener: "
+ "socket is not bound!");
}
clientQuit = true;
}
/**
* Stops the listener.
*/
public final void stop() {
this.clientQuit = true;
}
/**
* Listens to the messages from the server untill the clients says
* it's time to quit. Notifies the Client class when a message is
* received.
*/
public final void run() {
String message = null;
BufferedReader br = null;
AbstractMessage msg = null;
clientQuit = false;
//Open reader:
try {
br = new BufferedReader(new InputStreamReader(
serverSocket.getInputStream()));
} catch (IOException ioe) {
System.err.println("ServerListener: Could not open input "
+ "stream from the server.\n" + ioe.getMessage());
return;
}
while (!clientQuit) {
//Get next message:
try {
message = br.readLine();
if (ClientNetworkSettings.VERBOSE) {
System.out.println("ServerListerner: Got message from server");
System.out.println("\t" + message);
}
if (message == null) {
throw new IOException("Server has dropped the connection.");
}
} catch (IOException ioe) {
System.err.println("ServerListener: Could not read from server.\n"
+ ioe.getMessage());
//Notify the client that the server has quit
Client.getInstance().serverQuit();
return;
}
msg = MessageFactory.getInstance().makeMessage(message);
if (msg == null) {
//Got bad message format
//Client.getInstance().processBadMessage(message);
continue;
}
if (msg.getMessageType().equals(MessageSettings.SERVER_ERROR_TYPE)) {
//There's been an error on the server. Tell the client about it.
Client.getInstance().processServerError((ServerErrorMessage) msg);
}
//Break connection if the client couldn't recover after the error.
if (clientQuit) {
break;
}
//Send the message on to the client.
Client.getInstance().processMessage(msg);
}
}
}

View File

@@ -0,0 +1,6 @@
<body>
Package <code>netpaint.client.networking</code> contains classes
pertinent to the networking components of the client side of the
NetPaint application. Here is contained everything the client needs to
be able to connect to the server and exchange messages with it.
</body>

View File

@@ -0,0 +1,6 @@
<body>
Package <code>netpaint.client</code> contains generic client classes
that tie all of the other client-related components together, as well
as the subpackages that containt different types of functionality
pertinent to the client - networking, ui, file io, etc.
</body>

View File

@@ -0,0 +1,267 @@
package netpaint.client.tools;
import java.awt.Point;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Color;
import netpaint.client.Client;
import netpaint.client.gui.ClientUI;
import netpaint.messaging.MessageFactory;
import netpaint.messaging.GraphicMessage;
import netpaint.messaging.TextMessage;
import netpaint.messaging.FillableShape;
/**
* Class AbstractTool: This class will be the parent of all the
* different tools that the user can employ to draw in NetPaint.
*
* <PRE>
* Revision History:
* v1.0 (Jan. 31, 2004) - Created the AbstractTool class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Jan. 31, 2004
*/
public abstract class AbstractTool {
/**
* The number of times we have been clicked so far.
*/
private int clickCount;
/**
* The network message associated with this tool.
*/
private GraphicMessage message;
/**
* Creates a new <code>AbstractTool</code> instance.
*/
public AbstractTool() {
this(netpaint.messaging.MessageSettings.UNKNOWN_TYPE);
}
/**
* Creates a new <code>AbstractTool</code> instance associated with
* the specified message type.
*
* @param messageType a <code>String</code> value
*/
public AbstractTool(final String messageType) {
this.clickCount = 0;
this.message = (GraphicMessage) (MessageFactory.getInstance()
.makeMessage(messageType,
Client.getInstance()
.getUsername()));
}
/**
* Returns the number of clicks registered by the tool.
*
* @return an <code>int</code> value
*/
protected final int getClickCount() {
return this.clickCount;
}
/**
* Increments the value of click count and returns the new value.
*
* @return an <code>int</code> value
*/
protected final int incrementClickCount() {
return ++this.clickCount;
}
/**
* Resets the click count.
*/
protected final void resetClickCount() {
setClickCount(0);
}
/**
* Gives the click count the new value.
*
* @param newCount an <code>int</code> value
*/
protected final void setClickCount(final int newCount) {
if (newCount < 0) { return; }
this.clickCount = newCount;
}
/**
* Returns the color of the shape.
*
* @return a <code>Color</code> value
*/
public final Color getColor() {
return ClientUI.getInstance().getToolPanel().getCurrentColor();
}
/**
* This is the color in which the tool will be rendered. It will be
* the color of the target shape, plus some alpha.
*
* @return a <code>Color</code> value
*/
protected final Color getToolColor() {
Color original = getColor();
return new Color(original.getRed(),
original.getGreen(),
original.getBlue(),
ToolSettings.TOOL_ALPHA);
}
/**
* This is the fontin which the tool will be rendered. It will be
* the font of the target text.
*
* @return a <code>Font</code> value
*/
public final Font getFont() {
return ClientUI.getInstance().getFontPanel().getCurrentFont();
}
/**
* Returns true if the shape we are currently drawing is fillable
* and false if it is not.
*
* @return a <code>boolean</code> value
*/
public final boolean isFillableShape() {
boolean result = getMessage() instanceof FillableShape;
if (ToolSettings.VERBOSE) {
System.out.println("Tool: Fillable - " + (result ? "true" : "false"));
System.out.flush();
}
return result;
}
/**
* Returns true if the shape should be filled, and false if it
* should not be.
*
* @return a <code>boolean</code> value
*/
public final boolean getShapeFill() {
if (!isFillableShape()) {
return false;
} else {
return ClientUI.getInstance().getToolPanel().getFillShape();
}
}
/**
* Returns the graphic message associated with this tool.
*
* @return an <code>AbstractMessage</code> value
*/
public final GraphicMessage getMessage() {
return message;
}
/**
* Sets the message associated with this tool to the new value.
*
* @param newMessage a <code>GraphicMessage</code> value
*/
protected final void setMessage(final GraphicMessage newMessage) {
this.message = newMessage;
}
/**
* Returns true if the tool is currently active.
*
* @return a <code>boolean</code> value
*/
public final boolean isActive() {
return clickCount > 0;
}
/**
* This function will be called when the user clicks on the graphics
* panel.
*
* @param position a <code>Point</code> value
* @exception Exception if an unexpected number of clicks is registered.
*/
public abstract void processClick(final Point position) throws Exception;
/**
* Processes the key press. This function is only useful for the
* Text tool, and thus it is left empty here.
*
* @param key a <code>char</code> value
*/
public void processKeypress(final char key) { }
/**
* This function will be called when the mouse has been dragged on
* the graphics panel. Only the text tool will really register that,
* though.
*
* @param offset a <code>Point</code> value
*/
public void processDrag(final Point offset) { }
/**
* This will clear any progress the tool has made so far. If the
* user has already started drawing the shape, but has not yet
* finished, calling this method will discard all the progress made
* so far and start from the clean slate. It should be SYNCHRONIZED.
*/
public abstract void clear();
/**
* If the tool is currently being employed and the user has drawn
* some of the shape, but hasn't yet finished, then this function
* should render the intermediate view of the shape to the
* target. If the tool has already finished, or hasn't even started
* yet, then it should draw nothing. It should be SYNCHRONIZED.
*
* @param target a <code>Graphics2D</code> value
*/
public abstract void render(final Graphics2D target);
/**
* Should be called when the tool is done with one shape. It will
* clean up after the current execution of the tool, and allow for
* another shape to be registered. It should be SYNCHRONIZED.
*/
public final void finish() {
//Reset click count:
resetClickCount();
//First, dispatch the message:
sendMessage();
//Next, clean the data out:
clear();
}
/**
* This will send out a network message with the data contained in
* the current tool. It should be SYNCHRONIZED.
*/
protected final void sendMessage() {
message.setColor(getColor());
if (isFillableShape()) {
((FillableShape) message).setFilled(getShapeFill());
}
if (message instanceof TextMessage) {
((TextMessage) message).setFont(getFont());
}
Client.getInstance().sendMessage(message);
}
}

View File

@@ -0,0 +1,122 @@
package netpaint.client.tools;
import java.awt.Point;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import netpaint.util.GeometricUtilities;
import netpaint.client.Client;
import netpaint.messaging.EllipseMessage;
import netpaint.client.gui.ClientUI;
/**
* This tool allows drawing ellipses.
* <PRE>
* EllipseTool.java
*
* Revisions: 1.0 Feb. 09, 2004
* Created the EllipseTool class
*
* </PRE>
*
* @author <A HREF="mailto:daniyarland@yahoo.com">Daniyar Zhanbekov</A>
* @version Version 1.0, Feb. 09, 2004
*/
public class EllipseTool extends AbstractTool {
/**
* Creates a new <code>EllipseTool</code> instance.
*/
public EllipseTool() {
super(netpaint.messaging.MessageSettings.DRAW_ELLIPSE_TYPE);
}
/**
* This function will be called when the user clicks on the graphics
* panel.
*
* @param position a <code>Point</code> value
* @exception Exception if an error occurs
*/
public void processClick(final Point position) throws Exception {
EllipseMessage msg = null;
if (ToolSettings.VERBOSE) {
System.out.println("EllipseTool: Processing click #"
+ getClickCount());
}
if (getClickCount() == 0) {
//FIXME: Please
clear();
msg = (EllipseMessage) getMessage();
//This is the first click:
msg.setXCoord((float) position.getX());
msg.setYCoord((float) position.getY());
incrementClickCount();
} else if (getClickCount() == 1) {
//This is the second click:
msg = (EllipseMessage) getMessage();
msg.setWidth((float) (position.getX() - msg.getXCoord()));
msg.setHeight((float) (position.getY() - msg.getYCoord()));
//Done:
finish();
} else {
//Something went wrong:
throw new Exception("EllipseTool: Got an unexpected "
+ "number of clicks.");
}
}
/**
* This will clear any progress the tool has made so far. If the
* user has already started drawing the shape, but has not yet
* finished, calling this method will discard all the progress made
* so far and start from the clean slate.
*/
public synchronized void clear() {
resetClickCount();
setMessage(new EllipseMessage(Client.getInstance().getUsername()));
}
/**
* If the tool is currently being employed and the user has drawn
* some of the shape, but hasn't yet finished, then this function
* should render the intermediate view of the shape to the
* target. If the tool has already finished, or hasn't even started
* yet, then it should draw nothing.
*
* @param target a <code>Graphics2D</code> value
*/
public synchronized void render(final Graphics2D target) {
EllipseMessage msg = (EllipseMessage) getMessage();
Point mLocation
= ClientUI.getInstance().getGraphicsPanel().getMousePosition();
if (getClickCount() == 1) {
//The first point in the ellipse has been defined
float startX = msg.getXCoord();
float startY = msg.getYCoord();
float width = ((float) mLocation.getX()) - startX;
float height = ((float) mLocation.getY()) - startY;
Ellipse2D.Float ellipse = GeometricUtilities.getInstance()
.getEllipse(startX, startY, width, height);
target.setColor(getToolColor());
if (getShapeFill()) {
target.fill(ellipse);
} else {
target.draw(ellipse);
}
}
}
}

View File

@@ -0,0 +1,140 @@
package netpaint.client.tools;
import java.awt.Point;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import netpaint.messaging.ImageMessage;
import netpaint.client.gui.ClientUI;
import netpaint.client.Client;
/**
* Class ImageTool: The image tool allows you to insert an image into
* the graphics panel.
*
* <PRE>
* Revision History:
* v1.0 (Feb. 09, 2004) - Created the ImageTool class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Feb. 09, 2004
*/
public class ImageTool extends AbstractTool {
/**
* Creates a new <code>ImageTool</code> instance.
*/
public ImageTool() {
super(netpaint.messaging.MessageSettings.DRAW_IMAGE_TYPE);
incrementClickCount();
}
/**
* This function will be called when the user clicks on the graphics
* panel.
*
* @param position a <code>Point</code> value
* @exception Exception if an error occurs
*/
public void processClick(final Point position) throws Exception {
ImageMessage msg = null;
if (getClickCount() == 1) {
//FIXME: Please
clear();
BufferedImage image = ClientUI.getInstance().getImagePanel().getImage();
if (image == null) {
//Tell the user to load an image and THEN click!
ClientUI.getInstance().showWarning("Warning", "First you should paste the URL"
+ "into the URL text area, then click 'Load',"
+ " and then you may click on the"
+ "panel to select where to insert the image");
return;
}
msg = (ImageMessage) getMessage();
//This is the first click:
msg.setXCoord((float) position.getX());
msg.setYCoord((float) position.getY());
incrementClickCount();
} else if (getClickCount() == 2) {
msg = (ImageMessage) getMessage();
msg.setWidth((float) (position.getX() - msg.getXCoord()));
msg.setHeight((float) (position.getY() - msg.getYCoord()));
msg.setURL(ClientUI.getInstance().getImagePanel().getImageURL());
finish();
}
}
/**
* This will clear any progress the tool has made so far. If the
* user has already started drawing the shape, but has not yet
* finished, calling this method will discard all the progress made
* so far and start from the clean slate.
*/
public synchronized void clear() {
resetClickCount();
incrementClickCount();
setMessage(new ImageMessage(Client.getInstance().getUsername()));
}
/**
* If the tool is currently being employed and the user has drawn
* some of the shape, but hasn't yet finished, then this function
* should render the intermediate view of the shape to the
* target. If the tool has already finished, or hasn't even started
* yet, then it should draw nothing.
*
* @param target a <code>Graphics2D</code> value
*/
public synchronized void render(final Graphics2D target) {
ImageMessage msg = (ImageMessage) getMessage();
Point mLocation
= ClientUI.getInstance().getGraphicsPanel().getMousePosition();
if (getClickCount() == 1) {
float xCoord = (float) mLocation.getX();
float yCoord = (float) mLocation.getY();
BufferedImage image
= ClientUI.getInstance().getImagePanel().getImage();
if (image != null) {
target.drawImage(image, null, (int) xCoord, (int) yCoord);
}
} else if (getClickCount() == 2) {
float xCoord = msg.getXCoord();
float yCoord = msg.getYCoord();
float width = (float) (mLocation.getX() - xCoord);
float height = (float) (mLocation.getY() - yCoord);
BufferedImage image
= ClientUI.getInstance().getImagePanel().getImage();
if (image == null) { return; }
if (width < 1) {
width = 1.0f;
}
if (height < 1) {
height = 1.0f;
}
// target.drawImage(bim, null, (int) xCoord, (int) yCoord);
target.drawImage(image,
(int) xCoord,
(int) yCoord,
(int) width, (int) height, null);
}
}
}

View File

@@ -0,0 +1,112 @@
package netpaint.client.tools;
import java.awt.Point;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import java.awt.Color;
import netpaint.client.Client;
import netpaint.messaging.LineMessage;
import netpaint.client.gui.ClientUI;
/**
* Class LineTool: The line tool can be used to draw lines.
*
* <PRE>
* Revision History:
* v1.0 (Feb. 05, 2004) - Created the LineTool class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Feb. 05, 2004
*/
public class LineTool extends AbstractTool {
/**
* Creates a new <code>LineTool</code> instance.
*/
public LineTool() {
super(netpaint.messaging.MessageSettings.DRAW_LINE_TYPE);
}
/**
* This function will be called when the user clicks on the graphics
* panel.
*
* @param position a <code>Point</code> value
* @exception Exception if an error occurs
*/
public void processClick(final Point position) throws Exception {
LineMessage msg = null;
if (ToolSettings.VERBOSE) {
System.out.println("LineTool: Processing click #" + getClickCount());
}
if (getClickCount() == 0) {
//FIXME: Please
clear();
msg = (LineMessage) getMessage();
//This is the first click:
msg.setStartX((float) position.getX());
msg.setStartY((float) position.getY());
incrementClickCount();
} else if (getClickCount() == 1) {
//This is the second click:
msg = (LineMessage) getMessage();
msg.setRun((float) (position.getX() - msg.getStartX()));
msg.setRise((float) (position.getY() - msg.getStartY()));
//Done:
finish();
} else {
//Something went wrong:
throw new Exception("LineTool: Got an unexpected number of clicks.");
}
}
/**
* This will clear any progress the tool has made so far. If the
* user has already started drawing the shape, but has not yet
* finished, calling this method will discard all the progress made
* so far and start from the clean slate.
*/
public synchronized void clear() {
resetClickCount();
setMessage(new LineMessage(Client.getInstance().getUsername()));
}
/**
* If the tool is currently being employed and the user has drawn
* some of the shape, but hasn't yet finished, then this function
* should render the intermediate view of the shape to the
* target. If the tool has already finished, or hasn't even started
* yet, then it should draw nothing.
*
* @param target a <code>Graphics2D</code> value
*/
public synchronized void render(final Graphics2D target) {
LineMessage msg = (LineMessage) getMessage();
Point mLocation
= ClientUI.getInstance().getGraphicsPanel().getMousePosition();
if (getClickCount() == 1) {
//The first point in the line has been defined
Color originalColor = target.getColor();
float startX = msg.getStartX();
float startY = msg.getStartY();
float endX = (float) mLocation.getX();
float endY = (float) mLocation.getY();
target.setColor(getToolColor());
target.draw(new Line2D.Float(startX, startY, endX, endY));
target.setColor(originalColor);
} //Otherwise, the tool hasn't been used yet or the line is finished.
}
}

View File

@@ -0,0 +1,204 @@
package netpaint.client.tools;
import java.awt.Point;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.awt.Polygon;
import java.awt.Color;
import netpaint.client.Client;
import netpaint.messaging.PolygonMessage;
import netpaint.client.gui.ClientUI;
/** The polygon tool can be used to draw polygons.
* <PRE>
* PolygonTool.java
*
* Revisions: 1.0 Feb. 09, 2004
* Created the PolygonTool class
*
* </PRE>
*
* @author <A HREF="mailto:daniyarland@yahoo.com">Daniyar Zhanbekov</A>
* @version Version 1.0, Feb. 09, 2004
*/
public class PolygonTool extends AbstractTool {
/**Private object rectangle that is used to finish the polygon
*
*/
private Rectangle2D rect;
/**
* Get the rectangle.
* @return rectangle2D object.
*/
public final Rectangle2D getRect() {
return this.rect;
}
/**
* Set the rectangle
* @param v Value to assign to rectangle.
*/
public final void setRect(final Rectangle2D v) {
this.rect = v;
}
/**
* Creates a new <code>PolygonTool</code> instance.
*/
public PolygonTool() {
super(netpaint.messaging.MessageSettings.DRAW_POLYGON_TYPE);
}
/**
* This function will be called when the user clicks on the graphics
* panel.
*
* @param position a <code>Point</code> value
* @exception Exception if an error occurs
*/
public void processClick(final Point position) throws Exception {
PolygonMessage msg = null;
float x = -1;
float y = -1;
if (ToolSettings.VERBOSE) {
System.out.println("PolygonTool: Processing click #"
+ getClickCount());
}
if (getClickCount() == 0) { //First Click
//FIXME: Please
clear();
msg = (PolygonMessage) getMessage();
x = (float) position.getX();
y = (float) position.getY();
//Adding the clicks
msg.addPoint(x, y);
incrementClickCount();
//Creating the new rectangle
this.setRect(new Rectangle2D.Float(x - 3, y - 3, 6, 6));
} else if (getClickCount() > 0) {
//This is the second click or more
msg = (PolygonMessage) getMessage();
x = (float) position.getX();
y = (float) position.getY();
//Adding the clicks
msg.addPoint(x, y);
incrementClickCount();
//check if the point is within the rectangle?
if (getRect().contains((double) x, (double) y)) {
// msg.setNumPoints(getClickCount());
finish();
}
} else {
//Something went wrong:
throw new Exception("PolygonTool: Got an unexpected "
+ "number of clicks.");
}
}
/**
* This will clear any progress the tool has made so far. If the
* user has already started drawing the shape, but has not yet
* finished, calling this method will discard all the progress made
* so far and start from the clean slate.
*/
public synchronized void clear() {
resetClickCount();
setMessage(new PolygonMessage(Client.getInstance().getUsername()));
}
/**
* If the tool is currently being employed and the user has drawn
* some of the shape, but hasn't yet finished, then this function
* should render the intermediate view of the shape to the
* target. If the tool has already finished, or hasn't even started
* yet, then it should draw nothing.
*
* @param target a <code>Graphics2D</code> value
*/
public synchronized void render(final Graphics2D target) {
Rectangle2D.Float rect1;
PolygonMessage msg = (PolygonMessage) getMessage();
Point mLocation
= ClientUI.getInstance().getGraphicsPanel().getMousePosition();
if (getClickCount() > 0) {
//now draw the polygon
Object[] xPts;
Object[] yPts;
int[] xP;
int[] yP;
int curX = (int) ((float) mLocation.getX());
int curY = (int) ((float) mLocation.getY());
int numPoints = getClickCount();
xPts = msg.getXPoints().toArray();
yPts = msg.getYPoints().toArray();
xP = new int[xPts.length + 1];
yP = new int[yPts.length + 1];
//changing the array type to ints
for (int i = 0; i < xPts.length; i++) {
xP[i] = (int) (((Float) xPts[i]).floatValue());
yP[i] = (int) (((Float) yPts[i]).floatValue());
}
xP[xPts.length] = curX;
yP[yPts.length] = curY;
Polygon shape = new Polygon(xP, yP, numPoints + 1);
target.setColor(getToolColor());
if (getShapeFill()) {
target.fill(shape);
} else {
target.draw(shape);
}
//draw the square
rect1 = (Rectangle2D.Float) getRect();
target.setColor(Color.BLACK);
target.draw(rect1);
}
}
}

View File

@@ -0,0 +1,151 @@
package netpaint.client.tools;
import java.awt.Point;
import java.awt.Graphics2D;
import java.awt.geom.QuadCurve2D;
import java.awt.geom.Line2D;
import netpaint.client.Client;
import netpaint.messaging.QuadraticCurveMessage;
import netpaint.client.gui.ClientUI;
/**
* Class can be used to draw quadratic curves.
* <PRE>
* QuadraticCurveTool.java
*
* Revisions: 1.0 Feb. 09, 2004
* Created the QuadraticCurveTool class
*
* </PRE>
*
* @author <A HREF="mailto:daniyarland@yahoo.com">Daniyar Zhanbekov</A>
* @version Version 1.0, Feb. 09, 2004
*/
public class QuadraticCurveTool extends AbstractTool {
/**
* Creates a new <code>QuadraticCurveTool</code> instance.
*/
public QuadraticCurveTool() {
super(netpaint.messaging.MessageSettings.DRAW_QUADCURVE_TYPE);
}
/**
* This function will be called when the user clicks on the graphics
* panel.
*
* @param position a <code>Point</code> value
* @exception Exception if an error occurs
*/
public void processClick(final Point position) throws Exception {
QuadraticCurveMessage msg = null;
if (position == null) {
return;
}
if (ToolSettings.VERBOSE) {
System.out.println("QuadraticCurveTool: Processing click #"
+ getClickCount());
}
if (getClickCount() == 0) {
//FIXME: Please
clear();
msg = (QuadraticCurveMessage) getMessage();
//This is the first click:
msg.setX1((float) position.getX());
msg.setY1((float) position.getY());
incrementClickCount();
} else if (getClickCount() == 1) {
//This is the second click:
msg = (QuadraticCurveMessage) getMessage();
msg.setX2((float) position.getX());
msg.setY2((float) position.getY());
incrementClickCount();
} else if (getClickCount() == 2) {
//This is the third click:
msg = (QuadraticCurveMessage) getMessage();
msg.setCtrlX((float) (position.getX()));
msg.setCtrlY((float) (position.getY()));
//Done:
finish();
} else {
if (ToolSettings.VERBOSE) {
System.out.println("QuadraticCurveTool: Got an unexpected "
+ "number of clicks.");
}
//Something went wrong:
throw new Exception("QuadraticCurveTool: Got an unexpected "
+ "number of clicks.");
}
}
/**
* This will clear any progress the tool has made so far. If the
* user has already started drawing the shape, but has not yet
* finished, calling this method will discard all the progress made
* so far and start from the clean slate.
*/
public synchronized void clear() {
resetClickCount();
setMessage(new QuadraticCurveMessage(Client
.getInstance().getUsername()));
}
/**
* If the tool is currently being employed and the user has drawn
* some of the shape, but hasn't yet finished, then this function
* should render the intermediate view of the shape to the
* target. If the tool has already finished, or hasn't even started
* yet, then it should draw nothing.
*
* @param target a <code>Graphics2D</code> value
*/
public synchronized void render(final Graphics2D target) {
QuadraticCurveMessage msg = (QuadraticCurveMessage) getMessage();
Point mLocation
= ClientUI.getInstance().getGraphicsPanel().getMousePosition();
if (getClickCount() == 1) {
//The first point in the curve has been defined
float startX = msg.getX1();
float startY = msg.getY1();
float endX = (float) mLocation.getX();
float endY = (float) mLocation.getY();
target.setColor(getToolColor());
target.draw(new Line2D.Float(startX, startY, endX, endY));
} else if (getClickCount() == 2) {
//A line is drawn and the controls need to be set
float startX1 = msg.getX1();
float startY1 = msg.getY1();
float endX2 = msg.getX2();
float endY2 = msg.getY2();
float ctrlx = (float) mLocation.getX();
float ctrly = (float) mLocation.getY();
QuadCurve2D.Float curve = new QuadCurve2D.Float(startX1, startY1,
ctrlx, ctrly,
endX2, endY2);
target.setColor(getToolColor());
target.draw(curve);
} //Otherwise, the tool hasn't been used yet or the rectangle
//is finished.
}
}

View File

@@ -0,0 +1,118 @@
package netpaint.client.tools;
import java.awt.Point;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import netpaint.util.GeometricUtilities;
import netpaint.client.Client;
import netpaint.messaging.RectangleMessage;
import netpaint.client.gui.ClientUI;
/**
* Class RectangleTool: The rectangle tool can be used to draw rectangles.
*
* <PRE>
* Revision History:
* v1.0 (Feb. 07, 2004) - Created the RectangleTool class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Feb. 07, 2004
*/
public class RectangleTool extends AbstractTool {
/**
* Creates a new <code>RectangleTool</code> instance.
*/
public RectangleTool() {
super(netpaint.messaging.MessageSettings.DRAW_RECTANGLE_TYPE);
}
/**
* This function will be called when the user clicks on the graphics
* panel.
*
* @param position a <code>Point</code> value
* @exception Exception if an error occurs
*/
public void processClick(final Point position) throws Exception {
RectangleMessage msg = null;
if (ToolSettings.VERBOSE) {
System.out.println("RectangleTool: Processing click #"
+ getClickCount());
}
if (getClickCount() == 0) {
//FIXME: Please
clear();
msg = (RectangleMessage) getMessage();
//This is the first click:
msg.setXCoord((float) position.getX());
msg.setYCoord((float) position.getY());
incrementClickCount();
} else if (getClickCount() == 1) {
//This is the second click:
msg = (RectangleMessage) getMessage();
msg.setWidth((float) (position.getX() - msg.getXCoord()));
msg.setHeight((float) (position.getY() - msg.getYCoord()));
//Done:
finish();
} else {
//Something went wrong:
throw new Exception("RectangleTool: Got an unexpected "
+ "number of clicks.");
}
}
/**
* This will clear any progress the tool has made so far. If the
* user has already started drawing the shape, but has not yet
* finished, calling this method will discard all the progress made
* so far and start from the clean slate.
*/
public synchronized void clear() {
resetClickCount();
setMessage(new RectangleMessage(Client.getInstance().getUsername()));
}
/**
* If the tool is currently being employed and the user has drawn
* some of the shape, but hasn't yet finished, then this function
* should render the intermediate view of the shape to the
* target. If the tool has already finished, or hasn't even started
* yet, then it should draw nothing.
*
* @param target a <code>Graphics2D</code> value
*/
public synchronized void render(final Graphics2D target) {
RectangleMessage msg = (RectangleMessage) getMessage();
Point mLocation
= ClientUI.getInstance().getGraphicsPanel().getMousePosition();
if (getClickCount() == 1) {
//The first point in the rectangle has been defined
float startX = msg.getXCoord();
float startY = msg.getYCoord();
float width = ((float) mLocation.getX()) - startX;
float height = ((float) mLocation.getY()) - startY;
Rectangle2D.Float rect = GeometricUtilities.getInstance()
.getRectangle(startX, startY, width, height);
target.setColor(getToolColor());
if (getShapeFill()) {
target.fill(rect);
} else {
target.draw(rect);
}
} //Otherwise, the tool hasn't been used yet or the rectangle is finished.
}
}

View File

@@ -0,0 +1,312 @@
package netpaint.client.tools;
import java.awt.Point;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.awt.Font;
import java.awt.GraphicsEnvironment;
import netpaint.messaging.TextMessage;
import netpaint.client.gui.ClientUI;
import netpaint.client.gui.ImageLoader;
import netpaint.client.Client;
/**
* Class TextTool: The text tool allows you to type in text.
*
* <PRE>
* Revision History:
* v1.0 (Feb. 09, 2004) - Created the TextTool class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Feb. 09, 2004
*/
public class TextTool extends AbstractTool {
/**
* The caret to draw.
*/
private static BufferedImage caret;
/**
* The current position of the caret in the string.
*/
private int caretPosition;
/**
* Creates a new <code>TextTool</code> instance.
*/
public TextTool() {
super(netpaint.messaging.MessageSettings.DRAW_TEXT_TYPE);
caretPosition = 0;
}
/**
* Will load the caret.
* @param target a <code>Graphics2D</code> value
*/
private final void loadCaret(final Graphics2D target) {
caret = ImageLoader.getInstance()
.loadSystemImage(ToolSettings.TEXT_CARET_IMAGE);
if (caret == null) {
//Put in the text caret:
Font font = GraphicsEnvironment
.getLocalGraphicsEnvironment().getAllFonts()[0];
font = font.deriveFont(0, 12);
caret
= ImageLoader.getInstance().getImageConfiguration()
.createCompatibleImage((int) (font
.getStringBounds(ToolSettings.TEXT_CARET_ALT,
target.getFontRenderContext()).getWidth()),
(int) (font.getStringBounds(ToolSettings.TEXT_CARET_ALT,
target.getFontRenderContext())
.getHeight()), 1);
caret.getGraphics().setFont(font);
caret.getGraphics().drawString(ToolSettings.TEXT_CARET_ALT, 0, 0);
}
}
/**
* Sets the new caret position if possible.
*
* @param newPosition an <code>int</code> value
*/
public final void setCaretPosition(final int newPosition) {
TextMessage msg = (TextMessage) getMessage();
int length = msg.getText().length();
if (newPosition < 0) {
this.caretPosition = 0;
}
if (newPosition > length) {
this.caretPosition = length;
}
}
/**
* Returns the caret position.
*
* @return an <code>int</code> value
*/
public final int getCaretPosition() {
return caretPosition;
}
/**
* Moves the caret one to the left if possible.
*/
public final void moveCaretLeft() {
if (caretPosition > 0) {
caretPosition--;
}
}
/**
* Moves the caret one to the right is possible.
*/
public final void moveCaretRight() {
TextMessage msg = (TextMessage) getMessage();
int length = msg.getText().length();
if (caretPosition < length) {
caretPosition++;
}
}
/**
* Does a backspace.
*/
public final void backSpace() {
int oldPosition = getCaretPosition();
moveCaretLeft();
if (getCaretPosition() != oldPosition) {
delete();
}
}
/**
* Does a delete.
*/
public final void delete() {
TextMessage msg = (TextMessage) getMessage();
String text = msg.getText();
int length = text.length();
if (caretPosition < length) {
//Delete the char:
text = text.substring(0, caretPosition)
+ text.substring(caretPosition + 1);
msg.setText(text);
}
}
/**
* This function will be called when the user clicks on the graphics
* panel.
*
* @param position a <code>Point</code> value
* @exception Exception if an error occurs
*/
public void processClick(final Point position) throws Exception {
TextMessage msg = null;
if (getClickCount() == 0) {
//FIXME: Please
clear();
msg = (TextMessage) getMessage();
//This is the first click:
msg.setXCoord((float) position.getX());
msg.setYCoord((float) position.getY());
incrementClickCount();
}
}
/**
* This function will be called when the mouse has been dragged on
* the graphics panel. Only the text tool will really register that,
* though.
*
* @param offset a <code>Point</code> value
*/
public void processDrag(final Point offset) {
TextMessage msg = (TextMessage) getMessage();
if (getClickCount() > 0) {
//The text message has already been put down. Drag:
msg.setXCoord(msg.getXCoord() + (float) offset.getX());
msg.setYCoord(msg.getYCoord() + (float) offset.getY());
}
}
/**
* Processes the key press. This function is only useful for the
* Text tool, and thus it is left empty here.
*
* If the button pressed is ESC, the tool is cancelled
* If the button pressed is Shift, Ctrl, or Alt - ignore
* If the button pressed is an arrow, move the caret
* Otherwise, add to the string and move the caret
*
* @param key a <code>char</code> value
*/
public void processKeypress(final char key) {
TextMessage msg = (TextMessage) getMessage();
switch (key) {
case KeyEvent.VK_ESCAPE:
this.clear();
break;
case KeyEvent.VK_BACK_SPACE:
this.backSpace();
break;
case KeyEvent.VK_DELETE:
this.delete();
break;
case KeyEvent.VK_LEFT: case KeyEvent.VK_UP:
this.moveCaretLeft();
break;
case KeyEvent.VK_RIGHT: case KeyEvent.VK_DOWN:
this.moveCaretRight();
break;
case KeyEvent.VK_ENTER:
this.finish();
ClientUI.getInstance().getFontPanel().disableFinalize();
break;
default:
msg.setText(msg.getText() + Character.toString(key));
moveCaretRight();
if (msg.getText().length() < 2) {
ClientUI.getInstance().getFontPanel().enableFinalize();
}
break;
}
}
/**
* This will clear any progress the tool has made so far. If the
* user has already started drawing the shape, but has not yet
* finished, calling this method will discard all the progress made
* so far and start from the clean slate.
*/
public synchronized void clear() {
resetClickCount();
caretPosition = 0;
setMessage(new TextMessage(Client.getInstance().getUsername()));
}
/**
* If the tool is currently being employed and the user has drawn
* some of the shape, but hasn't yet finished, then this function
* should render the intermediate view of the shape to the
* target. If the tool has already finished, or hasn't even started
* yet, then it should draw nothing.
*
* @param target a <code>Graphics2D</code> value
*/
public synchronized void render(final Graphics2D target) {
TextMessage msg = (TextMessage) getMessage();
float offset = 0.0f;
msg.setFont(getFont());
if (caret == null) {
loadCaret(target);
}
if (getClickCount() > 0) {
//Set up the color:
target.setColor(getColor());
//Set up the font:
target.setFont(getFont());
offset = msg.getXCoord();
if ((msg.getText() != null) && (caretPosition > 0)) {
//Render text before the caret:
target.drawString(msg.getText().substring(0, caretPosition),
offset,
msg.getYCoord());
offset += msg.getFont()
.getStringBounds(msg.getText().substring(0, caretPosition),
target.getFontRenderContext()).getWidth();
}
//Render caret:
target.drawImage(caret, null, (int) offset,
(int) (msg.getYCoord() - caret.getHeight()));
offset += caret.getWidth();
if ((msg.getText() != null) && (caretPosition < msg.getText().length())) {
target.drawString(msg.getText().substring(caretPosition),
offset, msg.getYCoord());
}
}
}
}

View File

@@ -0,0 +1,45 @@
package netpaint.client.tools;
/**
* Class ToolSettings: This class contains some of the constants
* shared among all of the client tool classes.
*
* <PRE>
* Revision History:
* v1.0 (Feb. 05, 2004) - Created the ToolSettings class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Feb. 05, 2004
*/
public final class ToolSettings {
/**
* When we render an unfinished shape, it will be shown in the same
* colour as the target, but will also have a certain alpha value
* added. This is the alpha value to add.
*/
public static final int TOOL_ALPHA = 125;
/**
* The name of the image of the caret to be loaded.
*/
public static final String TEXT_CARET_IMAGE = "Caret.gif";
/**
* If the image for the caret cannot be loaded, this string will be
* displayed instead of it.
*/
public static final String TEXT_CARET_ALT = "I";
/**
* Verbose switch.
*/
public static final boolean VERBOSE = false;
/**
* Creates a new <code>ToolSettings</code> instance.
*/
private ToolSettings() { }
}

View File

@@ -0,0 +1,148 @@
package netpaint.client.tools;
import java.awt.Point;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.geom.Line2D;
import netpaint.client.Client;
import netpaint.messaging.TriangleMessage;
import netpaint.client.gui.ClientUI;
/** Class TriangleTool: The triangle tool can be used to draw
* triangles.
* <PRE>
* TriangleTool.java
*
* Revisions: 1.0 Feb. 09, 2004
* Created the TriangleTool class
*
* </PRE>
*
* @author <A HREF="mailto:daniyarland@yahoo.com">Daniyar Zhanbekov</A>
* @version Version 1.0, Feb. 09, 2004
*/
public class TriangleTool extends AbstractTool {
/**
* Creates a new <code>TriangleTool</code> instance.
*/
public TriangleTool() {
super(netpaint.messaging.MessageSettings.DRAW_TRIANGLE_TYPE);
}
/**
* This function will be called when the user clicks on the graphics
* panel.
*
* @param position a <code>Point</code> value
* @exception Exception if an error occurs
*/
public void processClick(final Point position) throws Exception {
TriangleMessage msg = null;
if (ToolSettings.VERBOSE) {
System.out.println("TriangleTool: Processing click #"
+ getClickCount());
}
if (getClickCount() == 0) {
//FIXME: Please
clear();
msg = (TriangleMessage) getMessage();
//This is the first click:
msg.setX1((float) position.getX());
msg.setY1((float) position.getY());
incrementClickCount();
} else if (getClickCount() == 1) {
//This is the second click:
msg = (TriangleMessage) getMessage();
msg.setX2((float) position.getX());
msg.setY2((float) position.getY());
incrementClickCount();
} else if (getClickCount() == 2) {
//This is the third click:
msg = (TriangleMessage) getMessage();
msg.setX3((float) position.getX());
msg.setY3((float) position.getY());
//Done:
finish();
} else {
//Something went wrong:
throw new Exception("TriangleTool: Got an unexpected "
+ "number of clicks.");
}
}
/**
* This will clear any progress the tool has made so far. If the
* user has already started drawing the shape, but has not yet
* finished, calling this method will discard all the progress made
* so far and start from the clean slate.
*/
public synchronized void clear() {
resetClickCount();
setMessage(new TriangleMessage(Client.getInstance().getUsername()));
}
/**
* If the tool is currently being employed and the user has drawn
* some of the shape, but hasn't yet finished, then this function
* should render the intermediate view of the shape to the
* target. If the tool has already finished, or hasn't even started
* yet, then it should draw nothing.
*
* @param target a <code>Graphics2D</code> value
*/
public synchronized void render(final Graphics2D target) {
TriangleMessage msg = (TriangleMessage) getMessage();
Point mLocation
= ClientUI.getInstance().getGraphicsPanel().getMousePosition();
if (getClickCount() == 1) {
//The first point in the triangle has been defined
float startX = msg.getX1();
float startY = msg.getY1();
float endX = (float) mLocation.getX();
float endY = (float) mLocation.getY();
target.setColor(getToolColor());
target.draw(new Line2D.Float(startX, startY, endX, endY));
} else if (getClickCount() == 2) {
//Two points have been defined and need to draw the triangle
float startX1 = msg.getX1();
float startY1 = msg.getY1();
float startX2 = msg.getX2();
float startY2 = msg.getY2();
float endX3 = (float) mLocation.getX();
float endY3 = (float) mLocation.getY();
//Creating a polygon object
int[] xPoints = {(int) startX1, (int) startX2, (int) endX3};
int[] yPoints = {(int) startY1, (int) startY2, (int) endY3};
Polygon poly = new Polygon(xPoints, yPoints, xPoints.length);
target.setColor(getToolColor());
if (getShapeFill()) {
target.fill(poly);
} else {
target.draw(poly);
}
} //Otherwise, the tool hasn't been used yet or
//the rectangle is finished.
}
} // end of class TriangleTool

View File

@@ -0,0 +1,9 @@
<body>
Package <code>netpaint.client.tools</code> contains a subset of UI
elements called tools. The client has access to different kinds of
tools when using netpaint, from simple lines to elaborate
polygons. This package contains implementations of each of those tool,
as well as how they look like when selected, and what they behave like
when the user interacts with the graphical component when the
particular tool is used.
</body>

View File

@@ -0,0 +1,189 @@
package netpaint.messaging;
import java.util.List;
/**
* Class AbstractMessage: This class is the base for all network
* messages that will be sent between the client and the server. All
* of the more specific message classes will be derived from it and
* will add more and more spcific functionality onto the basics
* provided in this class.
*
* <PRE>
* Revision History:
* v1.0 (Jan. 25, 2004) - Created the AbstractMessage class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Jan. 25, 2004
*/
public abstract class AbstractMessage {
/**
* This variable contains the four-character code for the message,
* like: HELO, or QUIT.
*/
private String messageType;
/**
* This string contains the name of the sender of this message,
* which will either be "Server" for server messages, or the name of
* the user for clients.
*/
private String messageAuthor;
/**
* Creates a new <code>AbstractMessage</code> instance.
*/
public AbstractMessage() {
this(MessageSettings.UNKNOWN_TYPE);
}
/**
* Creates a new <code>AbstractMessage</code> instance.
*
* @param type a <code>String</code> value
*/
public AbstractMessage(final String type) {
this(type, null);
}
/**
* Creates a new <code>AbstractMessage</code> instance.
*
* @param type a <code>String</code> value
* @param author a <code>String</code> value
*/
public AbstractMessage(final String type, final String author) {
messageType = type;
messageAuthor = author;
}
/**
* Returns the message type.
*
* @return a <code>String</code> value
*/
public final String getMessageType() {
return messageType;
}
/**
* Sets a new message type.
*
* @param newType a <code>String</code> value
*/
protected final void setMessageType(final String newType) {
this.messageType = newType;
}
/**
* Returns the message type of the message string passed in.
*
* @param message a <code>String</code> value
* @return a <code>String</code> value
*/
public static String getMessageType(final String message) {
if (message == null) {
return null;
}
if (message.length() < MessageSettings.TYPE_LENGTH) {
return null;
}
if (message.indexOf(':') != MessageSettings.TYPE_LENGTH) {
//The message type should be followed by a colon
return null;
}
return message.substring(0, MessageSettings.TYPE_LENGTH);
}
/**
* Returns the name of the user that sent out the message.
*
* @return a <code>String</code> value
*/
public final String getAuthor() {
return messageAuthor;
}
/**
* Gives message author a new value.
*
* @param author a <code>String</code> value
*/
protected final void setMessageAuthor(final String author) {
this.messageAuthor = author;
}
/**
* Returns a string representation of the message fit for plain-text
* transmission over the network.
*
* @return a <code>String</code> value
*/
public abstract String tokenize();
/**
* Returns the beginning of the element for the message.
*
* @return a <code>String</code> value
*/
public final String beginElement() {
return "<" + this.getClass().getName().toLowerCase() + " type=\""
+ getMessageType() + "\" author=\"" + getAuthor() + "\">";
}
/**
* Returns the end of the element.
*
* @return a <code>String</code> value
*/
public final String endElement() {
return "</" + this.getClass().getName().toLowerCase() + ">";
}
/**
* Returns an XMLized version of all of the messages
* properties. Each of the properties is contained in an element of
* the array, in the form: <name>value</name>.
*
* @return a <code>String[]</code> value
*/
public String[] propertiesToXML() { return null; }
/**
* Parses the string into the specific message.
*
* @param message a <code>String</code> value
* @return an <code>AbstractMessage</code> value
*/
protected static AbstractMessage parse(final String message) {
return null;
}
/**
* Builds a message from the data provided.
*
* @param data a <code>List</code> value
* @return an <code>AbstractMessage</code> value
*/
protected static AbstractMessage buildMessage(final List data) {
return null;
}
/**
* This will be called by the XML parser, when the data is not
* contained in an attribute, but rather in a nested element.
*
* @param name a <code>String</code> value
* @param value a <code>String</code> value
*/
public void processXMLElement(final String name,
final String value) {
}
}

View File

@@ -0,0 +1,106 @@
package netpaint.messaging;
/**
* Class AbstractServerMessage: This class extends Abstract Message
* and is the base for all server network messages.
* <PRE>
* Revision History:
* v1.0 (Jan. 29, 2004) - Created the AbstractServerMessage class
* </PRE>
*
* @author <A HREF="mailto:gtg563g@mail.gatech.edu">Daniyar Zhanbekov</A>
* @version Version 1.0, Jan. 29, 2004
*/
public abstract class AbstractServerMessage extends AbstractMessage {
/**
* This variable will conatin the String Message.
*/
private String serverMessage;
/**
* Creates a new <code>AbstractServerMessage</code> instance.
*
* @param type a <code>String</code> value
* @param incomingMessage a <code>String</code> value
*/
public AbstractServerMessage(final String type,
final String incomingMessage) {
super(type, MessageSettings.SERVER_USERNAME);
this.serverMessage = incomingMessage;
}
/**
* This is the accessor for serverMessage.
*
*@return server message
*/
public final String getServerMessage() {
return serverMessage;
}
/**
* This is a mutator for server message.
*
* @param newMessage a <code>String</code> value
*/
protected final void setServerMessage(final String newMessage) {
this.serverMessage = newMessage;
}
/**
* Returns a string representation of the message fit for plain-text
* transmission over the network.
*
* @return a <code>String</code> value
*/
public final String tokenize() {
return getMessageType() + ":" + getAuthor() + ":" + getServerMessage();
}
/**
* Parses the string into the specific message.
*
* @param message a <code>String</code> value
* @return an <code>AbstractMessage</code> value
*/
protected static AbstractMessage parse(final String message) {
String type = getMessageType(message);
String messageTail = null;
String theMessage = null;
int colonIndex = -1;
if (type == null) {
return null;
}
if (!(type.equals(MessageSettings.SERVER_MESSAGE_TYPE)
|| type.equals(MessageSettings.SERVER_ERROR_TYPE))) {
return null;
}
colonIndex = message.indexOf(':');
if (colonIndex < 0) { return null; }
messageTail = message.substring(colonIndex + 1);
//Get message
colonIndex = messageTail.indexOf(':');
if (colonIndex < 0) { return null; }
theMessage = messageTail.substring(colonIndex + 1);
if (type.equals(MessageSettings.SERVER_MESSAGE_TYPE)) {
return new GenericServerMessage(theMessage);
} else if (type.equals(MessageSettings.SERVER_ERROR_TYPE)) {
return new ServerErrorMessage(theMessage);
}
return null;
}
}

View File

@@ -0,0 +1,77 @@
package netpaint.messaging;
import java.util.StringTokenizer;
/**
* Class AdministrativeMessage: This class extends Client Message and
* is the base for all administrative network messages that will be
* sent by the client. All of the more specific client message classes
* will be derived from it and will add more and more spcific
* functionality onto the basics provided in this class.
*
* <PRE>
* Revision History:
* v1.0 (Jan. 29, 2004) - Created the AdministrativeMessage class
* </PRE>
*
* @author <A HREF="mailto:gtg563g@mail.gatech.edu">Daniyar Zhanbekov</A>
* @version Version 1.0, Jan. 29, 2004
*/
public abstract class AdministrativeMessage extends ClientMessage {
/**
* Creates a new <code>AdministrativeMessage</code> instance.
*
* @param type a <code>String</code> value
* @param author a <code>String</code> value
*/
public AdministrativeMessage(final String type, final String author) {
super(type, author);
}
/**
* Returns a string representation of the message fit for plain-text
* transmission over the network.
*
* @return a <code>String</code> value
*/
public final String tokenize() {
return getMessageType() + ":" + getAuthor();
}
/**
* Parses the string into the specific message.
*
* @param message a <code>String</code> value
* @return an <code>AbstractMessage</code> value
*/
protected static AbstractMessage parse(final String message) {
String type = getMessageType(message);
StringTokenizer st = null;
if (type == null) {
return null;
}
if (!(type.equals(MessageSettings.JOIN_TYPE)
|| type.equals(MessageSettings.QUIT_TYPE))) {
return null;
}
st = new StringTokenizer(message, ":");
//Message type:
if (!st.hasMoreTokens()) { return null; }
type = st.nextToken();
//Username:
if (!st.hasMoreTokens()) { return null; }
if (type.equals(MessageSettings.JOIN_TYPE)) {
return new JoinMessage(st.nextToken());
} else if (type.equals(MessageSettings.QUIT_TYPE)) {
return new QuitMessage(st.nextToken());
}
return null;
}
}

View File

@@ -0,0 +1,176 @@
package netpaint.messaging;
import java.util.List;
import java.util.StringTokenizer;
/**
* Class ChatMessage: This class extends Client Message
* and is the base for all chat network messages.
* <PRE>
* Revision History:
* v1.0 (Jan. 29, 2004) - Created the ChatMessage class
* </PRE>
*
* @author <A HREF="mailto:gtg563g@mail.gatech.edu">Daniyar Zhanbekov</A>
* @version Version 1.0, Jan. 29, 2004
*/
public class ChatMessage extends ClientMessage {
/**
* This is a variable that contains the chat message.
*/
private String message;
/**
* Creates a new <code>ChatMessage</code> instance.
* @param userName a <code>String</code> value
*/
public ChatMessage(final String userName) {
super(MessageSettings.CHAT_TYPE, userName);
message = null;
}
/**
* This is the accessor for chatMessage.
*
*@return chat message
*/
public final String getChatMessage() {
String prefix = "";
String suffix = "";
if (message == null || message.equals("")) {
return "";
}
if (message.charAt(0) == ':') {
prefix = " ";
}
if (message.charAt(message.length() - 1) == ':') {
suffix = " ";
}
return prefix + message + suffix;
}
/**
* This is a mutator for chat message.
*
* @param newMessage a <code>String</code> value
*/
protected final void setChatMessage(final String newMessage) {
this.message = newMessage;
}
/**
* Returns a string representation of the message fit for plain-text
* transmission over the network.
*
* @return a <code>String</code> value
*/
public final String tokenize() {
return getMessageType() + ":" + getAuthor() + ":" + getChatMessage();
}
/**
* Parses the string into the specific message.
*
* @param message a <code>String</code> value
* @return an <code>AbstractMessage</code> value
*/
protected static AbstractMessage parse(final String message) {
String type = getMessageType(message);
String usrName = null;
String chatMessage = null;
StringTokenizer st = null;
ChatMessage result = null;
if (type == null) {
return null;
}
if (!type.equals(MessageSettings.CHAT_TYPE)) {
return null;
}
st = new StringTokenizer(message, ":");
//Message Type
if (!st.hasMoreTokens()) { return null; }
type = st.nextToken();
//Username
if (!st.hasMoreTokens()) { return null; }
usrName = st.nextToken();
result = new ChatMessage(usrName);
//Chat message
if (!st.hasMoreTokens()) { return null; }
chatMessage = st.nextToken();
while (st.hasMoreTokens()) {
chatMessage += ":" + st.nextToken();
}
result.setChatMessage(chatMessage);
return result;
}
/**
* Builds a message from the data provided.
*
* @param data a <code>List</code> value
* @return an <code>AbstractMessage</code> value
*/
protected static AbstractMessage buildMessage(final List data) {
if (data == null || (data.size() != MessageSettings.CHAT_DATA_COUNT)) {
return null;
}
ChatMessage result = new ChatMessage((String) (data.get(0)));
result.setChatMessage((String) (data.get(1)));
return result;
}
/**
* Returns an XMLized version of all of the messages
* properties. Each of the properties is contained in an element of
* the array, in the form:
*
* CHAT:username:text
*
* @return a <code>String[]</code> value
*/
public String[] propertiesToXML() {
String[] result = new String[1];
result[0] = "<text>" + getChatMessage() + "</text>";
return result;
}
/** ELIP:msgid:username:xCoord:yCoord:width:height:color:fill
* This will be called by the XML parser, when the data is not
* contained in an attribute, but rather in a nested element.
*
* @param name a <code>String</code> value
* @param value a <code>String</code> value
*/
public void processXMLElement(final String name,
final String value) {
if (name.equals("text")) {
setChatMessage(value);
} else {
System.out.println("processXMLElement got invalid name\n" + name);
}
}
}

View File

@@ -0,0 +1,29 @@
package netpaint.messaging;
/**
* Class ClientMessage: This class extends Abstract Message and is the
* base for all network messages that will be sent by the clien. All
* of the more specific client message classes will be derived from it
* and will add more and more spcific functionality onto the basics
* provided in this class.
*
* <PRE>
* Revision History:
* v1.0 (Jan. 29, 2004) - Created the ClientMessage class
* </PRE>
*
* @author <A HREF="mailto:gtg563g@mail.gatech.edu">Daniyar Zhanbekov</A>
* @version Version 1.0, Jan. 29, 2004
*/
public abstract class ClientMessage extends AbstractMessage {
/**
* Creates a new <code>ClientMessage</code> instance.
*
* @param type a <code>String</code> value
* @param author a <code>String</code> value
*/
public ClientMessage(final String type, final String author) {
super(type, author);
}
}

View File

@@ -0,0 +1,385 @@
package netpaint.messaging;
import java.util.List;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import java.util.StringTokenizer;
import netpaint.util.GeometricUtilities;
/**
* Class EllipseMessage: Represents the ellipse graphic message. When the
* user draws a simple ellipse on the screen, this message is created and
* sent over to the server. The format of the network message is the
* following:
*
* ELIP:msgid:username:xCoord:yCoord:width:height:color:fill
*
* <PRE>
* Revision History:
* v1.0 (Jan. 29, 2004) - Created the EllipseMessage class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Jan. 29, 2004
*/
public class EllipseMessage extends GraphicMessage implements FillableShape {
/**
* The x coordinate of the start of the ellipse.
*/
private float xCoord;
/**
* The y coordinate of the start of the ellipse.
*/
private float yCoord;
/**
* The width of the ellipse.
*/
private float width;
/**
* The height of the ellipse.
*/
private float height;
/**
* True if the ellipse should be filled; false if only the border
* should be drawn.
*/
private boolean fill;
/**
* Creates a new <code>EllipseMessage</code> instance.
* @param author a <code>String</code> value
*/
public EllipseMessage(final String author) {
super(MessageSettings.DRAW_ELLIPSE_TYPE, author);
xCoord = -1;
yCoord = -1;
width = -1;
height = -1;
fill = false;
}
/**
* Get the value of xCoord.
* @return value of xCoord.
*/
public final float getXCoord() {
return xCoord;
}
/**
* Set the value of xCoord.
* @param v Value to assign to xCoord.
*/
public final void setXCoord(final float v) {
this.xCoord = v;
}
/**
* Get the value of yCoord.
* @return value of yCoord.
*/
public final float getYCoord() {
return yCoord;
}
/**
* Set the value of yCoord.
* @param v Value to assign to yCoord.
*/
public final void setYCoord(final float v) {
this.yCoord = v;
}
/**
* Get the value of width.
* @return value of width.
*/
public final float getWidth() {
return width;
}
/**
* Set the value of width.
* @param v Value to assign to width.
*/
public final void setWidth(final float v) {
this.width = v;
}
/**
* Get the value of height.
* @return value of height.
*/
public final float getHeight() {
return height;
}
/**
* Set the value of height.
* @param v Value to assign to height.
*/
public final void setHeight(final float v) {
this.height = v;
}
/**
* Get the value of fill.
* @return value of fill.
*/
public final boolean isFilled() {
return fill;
}
/**
* Set the value of fill.
* @param v Value to assign to fill.
*/
public final void setFilled(final boolean v) {
this.fill = v;
}
/**
* Returns a string representation of the message fit for plain-text
* transmission over the network. The format is as follows:
*
* <tt>ELIP:msgid:username:xCoord:yCoord:width:height:color:fill</tt>
*
* @return a <code>String</code> value
*/
public final String tokenize() {
String result = getMessageType() + ":";
result += Integer.toString(getMessageId()) + ":";
result += getAuthor() + ":";
result += Float.toString(getXCoord()) + ":";
result += Float.toString(getYCoord()) + ":";
result += Float.toString(getWidth()) + ":";
result += Float.toString(getHeight()) + ":";
result += Integer.toString(getColor().getRGB()) + ":";
if (isFilled()) { result += "1"; } else { result += "0"; }
return result;
}
/**
* Parses the string into the specific message.
*
* @param message a <code>String</code> value
* @return an <code>AbstractMessage</code> value
*/
protected static AbstractMessage parse(final String message) {
EllipseMessage result = null;
StringTokenizer st = null;
String type = getMessageType(message);
String temp = null;
int id = -1;
if (type == null) {
return null;
}
if (!type.equals(MessageSettings.DRAW_ELLIPSE_TYPE)) {
return null;
}
//Create StringTokenizer
st = new StringTokenizer(message, ":");
//The first token is the message type.
if (!st.hasMoreTokens()) {
return null;
}
type = st.nextToken();
//Next token is msgid:
if (!st.hasMoreTokens()) {
return null;
}
try {
id = Integer.parseInt(st.nextToken());
} catch (NumberFormatException nfe) {
return null;
}
//username
if (!st.hasMoreTokens()) {
return null;
}
result = new EllipseMessage(st.nextToken());
result.setMessageId(id);
//xCoord
if (!st.hasMoreTokens()) {
return null;
}
try {
result.setXCoord(Float.parseFloat(st.nextToken()));
} catch (NumberFormatException nfe) {
return null;
}
//yCoord
if (!st.hasMoreTokens()) {
return null;
}
try {
result.setYCoord(Float.parseFloat(st.nextToken()));
} catch (NumberFormatException nfe) {
return null;
}
//width
if (!st.hasMoreTokens()) {
return null;
}
try {
result.setWidth(Float.parseFloat(st.nextToken()));
} catch (NumberFormatException nfe) {
return null;
}
//height
if (!st.hasMoreTokens()) {
return null;
}
try {
result.setHeight(Float.parseFloat(st.nextToken()));
} catch (NumberFormatException nfe) {
return null;
}
//color
if (!st.hasMoreTokens()) {
return null;
}
try {
result.setColor(Integer.parseInt(st.nextToken()));
} catch (NumberFormatException nfe) {
return null;
}
//fill
if (!st.hasMoreTokens()) {
return null;
}
temp = st.nextToken();
if (temp.equals("1")) {
result.setFilled(true);
} else {
result.setFilled(false);
}
return result;
}
/**
* Builds a message from the data provided.
*
* @param data a <code>List</code> value
* @return an <code>AbstractMessage</code> value
*/
protected static AbstractMessage buildMessage(final List data) {
if (data == null
|| (data.size() != MessageSettings.ELLIPSE_DATA_COUNT)) {
return null;
}
int i = 1;
EllipseMessage result = new EllipseMessage((String) (data.get(i)));
result.setMessageId(((Integer) (data.get(0))).intValue());
result.setXCoord(((Float) (data.get(++i))).floatValue());
result.setYCoord(((Float) (data.get(++i))).floatValue());
result.setWidth(((Float) (data.get(++i))).floatValue());
result.setHeight(((Float) (data.get(++i))).floatValue());
result.setColor(((Integer) (data.get(++i))).intValue());
result.setFilled(((Boolean) (data.get(++i))).booleanValue());
return result;
}
/**
* Returns an XMLized version of all of the messages
* properties. Each of the properties is contained in an element of
* the array, in the form:
*
* ELIP:msgid:username:xCoord:yCoord:width:height:color:fill
*
* @return a <code>String[]</code> value
*/
public String[] propertiesToXML() {
String[] result = new String[7];
result[0] = "<msgid>" + Integer.toString(getMessageId()) + "</msgid>";
result[1] = "<xCoord>" + Float.toString(getXCoord()) + "</xCoord>";
result[2] = "<yCoord>" + Float.toString(getYCoord()) + "</yCoord>";
result[3] = "<width>" + Float.toString(getWidth()) + "</width>";
result[4] = "<height>" + Float.toString(getHeight()) + "</height>";
result[5] = "<color>" + Integer.toString(getColor().getRGB())
+ "</color>";
result[6] = "<fill>" + (isFilled() ? "1" : "0") + "</fill>";
return result;
}
/** ELIP:msgid:username:xCoord:yCoord:width:height:color:fill
* This will be called by the XML parser, when the data is not
* contained in an attribute, but rather in a nested element.
*
* @param name a <code>String</code> value
* @param value a <code>String</code> value
*/
public void processXMLElement(final String name,
final String value) {
if (name.equals("msgid")) {
setMessageId(Integer.parseInt(value));
} else if (name.equals("xCoord")) {
setXCoord(Float.parseFloat(value));
} else if (name.equals("yCoord")) {
setYCoord(Float.parseFloat(value));
} else if (name.equals("width")) {
setWidth(Float.parseFloat(value));
} else if (name.equals("height")) {
setHeight(Float.parseFloat(value));
} else if (name.equals("color")) {
setColor(Integer.parseInt(value));
} else if (name.equals("fill")) {
if (value.equals("1")) {
setFilled(true);
} else {
setFilled(false);
}
} else {
System.out.println("processXMLElement got invalid name\n" + name);
}
}
/**
* Renders the current graphic onto the graphic object passed in.
*
* @param target a <code>Graphics2D</code> value
*/
public final void render(final Graphics2D target) {
Ellipse2D shape = GeometricUtilities.getInstance()
.getEllipse(getXCoord(), getYCoord(), getWidth(), getHeight());
target.setColor(getColor());
if (isFilled()) {
target.fill(shape);
} else {
target.draw(shape);
}
}
}

View File

@@ -0,0 +1,24 @@
package netpaint.messaging;
/**
* Interface <code>FillableShape</code> should be implemented by all
* the messages that have a fill boolean value.
*
* @author <a href="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</a>
* @version 1.0
*/
public interface FillableShape {
/**
* Get the value of fill.
* @return value of fill.
*/
public boolean isFilled();
/**
* Set the value of fill.
* @param v Value to assign to fill.
*/
public void setFilled(final boolean v);
}

View File

@@ -0,0 +1,50 @@
package netpaint.messaging;
import java.util.List;
/**
* Class GenericServerMessage: This is a generic server message.
*
* <PRE>
* Revision History:
* v1.0 (Jan. 27, 2004) - Created the GenericServerMessage class
* </PRE>
*
* @author <A HREF="mailto:gtg563g@prism.gatech.edu">Daniyar Zhanbekov</A>
* @version Version 1.0, Jan. 29, 2004
*/
public class GenericServerMessage extends AbstractServerMessage {
/**
* Creates a new <code>GenericServerMessage</code> instance.
*
* @param message a <code>String</code> value
*/
public GenericServerMessage(final String message) {
super(MessageSettings.SERVER_MESSAGE_TYPE, message);
}
/**
* Builds a message from the data provided.
*
* @param data a <code>List</code> value
* @return an <code>AbstractMessage</code> value
*/
protected static AbstractMessage buildMessage(final List data) {
if (data == null || (data.size() != 1)) {
return null;
}
return new GenericServerMessage((String) (data.get(0)));
}
/**
* makes a message from the data provided.
*
*
*
*/
protected void makeMessage() {
}
}

View File

@@ -0,0 +1,143 @@
package netpaint.messaging;
import java.awt.Color;
import java.awt.Graphics2D;
/**
* Class GraphicMessage: This class represents all of the graphic
* messages that the user can send to the server. That is, whenever
* something gets drawn on the screen by the user, he sends one of
* these to the server for confirmation and to broadcast to all the
* others.
*
* <PRE>
* Revision History:
* v1.0 (Jan. 29, 2004) - Created the GraphicMessage class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Jan. 29, 2004
*/
public abstract class GraphicMessage extends ClientMessage {
/**
* The message ID of this graphic. This will be used to preserve
* order of drawings on screen. Before rendering, all graphic
* messages will be sorted by message ID, and that will allow to
* achieve the same view on all clients, as the ID's will be the
* same over all clients and on the server.
*/
private int messageId;
/**
* The color of this graphic.
*/
private Color graphicColor;
/**
* Will be true if the message should be shown.
*/
private boolean showMessage;
/**
* Creates a new <code>GraphicMessage</code> instance.
* @param type a <code>String</code> value
* @param author a <code>String</code> value
*/
public GraphicMessage(final String type, final String author) {
super(type, author);
messageId = -1;
graphicColor = new Color(MessageSettings.DEFAULT_GRAPHIC_COLOR);
showMessage = true;
}
/**
* Returns the message id of the current graphic.
*
* @return an <code>int</code> value
*/
public final int getMessageId() {
return messageId;
}
/**
* Gives the current message a new id.
*
* @param newId an <code>int</code> value
*/
public final void setMessageId(final int newId) {
if (newId < 0 && messageId >= 0) {
//We have a perfectly good id already and we want to reset
//it. This shouldn't happen.
return;
}
this.messageId = newId;
}
/**
* Returns the color object of this graphic.
*
* @return a <code>Color</code> value
*/
public final Color getColor() {
return graphicColor;
}
/**
* Sets a new color for this graphic.
*
* @param c a <code>Color</code> value
*/
public final void setColor(final Color c) {
this.graphicColor = c;
}
/**
* Shows the message.
* @param value a <code>boolean</code> value
*/
public final void setShowMessage(final boolean value) {
showMessage = value;
}
/**
* Returns true if the mesage is visible.
*
* @return a <code>boolean</code> value
*/
public final boolean isShown() {
return showMessage;
}
/**
* Sets a new color for this graphic.
*
* @param rgb an <code>int</code> value
*/
public final void setColor(final int rgb) {
if (MessageSettings.VERBOSE) {
System.out.println("GraphicMessage: Got color " + rgb);
}
this.graphicColor = new Color(rgb);
}
/**
* Renders the current graphic onto the graphic object passed in.
*
* @param target a <code>Graphics2D</code> value
*/
public abstract void render(final Graphics2D target);
/**
* Returns a string representation of the message.
*
* @return a <code>String</code> value
*/
public final String toString() {
return getMessageType();
}
}

View File

@@ -0,0 +1,434 @@
package netpaint.messaging;
import java.util.List;
import java.util.StringTokenizer;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.net.MalformedURLException;
import netpaint.client.gui.ImageLoader;
import netpaint.client.gui.ClientUI;
import netpaint.util.GeometricUtilities;
/**
* Class ImageMessage: Represents the image graphic message. When the
* user inserts an image from the internet to the screen, this message
* is created and sent over to the server. The format of the network
* message is the following:
*
* IMAG:msgid:username:startX:StartY:height:width:URL
*
* <PRE>
* Revision History:
* v1.0 (Jan. 29, 2004) - Created the ImageMessage class
* </PRE>
*
* @author <A HREF="mailto:gtg563g@mail.gatech.edu">Daniyar Zhanbekov</A>
* @version Version 1.0, Jan. 29, 2004
*/
public class ImageMessage extends GraphicMessage {
/**
* The x coordinate of the start of the image.
*/
private float xCoord;
/**
* The y coordinate of the start of the image.
*/
private float yCoord;
/**
* The width of the image.
*/
private float width;
/**
* The height of the image.
*/
private float height;
/**
* URL for the image.
*/
private URL newURL;
/**
* image.
*/
private BufferedImage image;
/**
* boolean for loading.
*/
private boolean loading;
/**
* boolean for errorloading.
*/
private boolean errorLoading;
/**
* Creates a new <code>ImageMessage</code> instance.
* @param author a <code>String</code> value
*/
public ImageMessage(final String author) {
super(MessageSettings.DRAW_IMAGE_TYPE, author);
image = null;
loading = false;
errorLoading = false;
xCoord = -1;
yCoord = -1;
width = -1;
height = -1;
newURL = null;
}
/**
* Get the value of the URL.
* @return an <code>URL</code> value
*/
public final URL getURL() {
return newURL;
}
/**
* Set the value of the URL.
* @param url an <code>URL</code> value
*/
public final void setURL(final URL url) {
this.newURL = url;
}
/**
* Get the value of xCoord.
* @return value of xCoord.
*/
public final float getXCoord() {
return xCoord;
}
/**
* Set the value of xCoord.
* @param v Value to assign to xCoord.
*/
public final void setXCoord(final float v) {
this.xCoord = v;
}
/**
* Get the value of yCoord.
* @return value of yCoord.
*/
public final float getYCoord() {
return yCoord;
}
/**
* Set the value of yCoord.
* @param v Value to assign to yCoord.
*/
public final void setYCoord(final float v) {
this.yCoord = v;
}
/**
* Get the value of width.
* @return value of width.
*/
public final float getWidth() {
return width;
}
/**
* Set the value of width.
* @param v Value to assign to width.
*/
public final void setWidth(final float v) {
this.width = v;
}
/**
* Get the value of height.
* @return value of height.
*/
public final float getHeight() {
return height;
}
/**
* Set the value of height.
* @param v Value to assign to height.
*/
public final void setHeight(final float v) {
this.height = v;
}
/**
* Returns a string representation of the message fit for plain-text
* transmission over the network. The format is as follows:
*
* <tt>IMAG:msgid:username:xCoord:yCoord:height:width:URL</tt>
*
* @return a <code>String</code> value
*/
public final String tokenize() {
String result = getMessageType() + ":";
result += Integer.toString(getMessageId()) + ":";
result += getAuthor() + ":";
result += Float.toString(getXCoord()) + ":";
result += Float.toString(getYCoord()) + ":";
result += Float.toString(getWidth()) + ":";
result += Float.toString(getHeight()) + ":";
result += getURL().toString();
return result;
}
/**
* Parses the string into the specific message.
*
* @param message a <code>String</code> value
* @return an <code>AbstractMessage</code> value
*/
protected static AbstractMessage parse(final String message) {
ImageMessage result = null;
StringTokenizer st = null;
String type = getMessageType(message);
String temp = null;
int id = -1;
if (type == null) {
return null;
}
if (!type.equals(MessageSettings.DRAW_IMAGE_TYPE)) {
return null;
}
//Create StringTokenizer
st = new StringTokenizer(message, ":");
//The first token is the message type.
if (!st.hasMoreTokens()) {
return null;
}
type = st.nextToken();
//Next token is msgid:
if (!st.hasMoreTokens()) {
return null;
}
try {
id = Integer.parseInt(st.nextToken());
} catch (NumberFormatException nfe) {
return null;
}
//username
if (!st.hasMoreTokens()) {
return null;
}
result = new ImageMessage(st.nextToken());
result.setMessageId(id);
//xCoord
if (!st.hasMoreTokens()) {
return null;
}
try {
result.setXCoord(Float.parseFloat(st.nextToken()));
} catch (NumberFormatException nfe) {
return null;
}
//yCoord
if (!st.hasMoreTokens()) {
return null;
}
try {
result.setYCoord(Float.parseFloat(st.nextToken()));
} catch (NumberFormatException nfe) {
return null;
}
//width
if (!st.hasMoreTokens()) {
return null;
}
try {
result.setWidth(Float.parseFloat(st.nextToken()));
} catch (NumberFormatException nfe) {
return null;
}
//height
if (!st.hasMoreTokens()) {
return null;
}
try {
result.setHeight(Float.parseFloat(st.nextToken()));
} catch (NumberFormatException nfe) {
return null;
}
//URL
if (!st.hasMoreTokens()) {
return null;
}
//The URL may have :'s in it...
temp = st.nextToken();
while (st.hasMoreTokens()) {
temp += ":" + st.nextToken();
}
try {
result.setURL(new URL(temp));
} catch (MalformedURLException mue) {
return null;
}
return result;
}
/**
* Builds a message from the data provided.
*
* @param data a <code>List</code> value
* @return an <code>AbstractMessage</code> value
*/
protected static AbstractMessage buildMessage(final List data) {
if (data == null
|| (data.size() != MessageSettings.IMAGE_DATA_COUNT)) {
return null;
}
int i = 1;
ImageMessage result = new ImageMessage((String) (data.get(i)));
result.setMessageId(((Integer) (data.get(0))).intValue());
result.setXCoord(((Float) (data.get(++i))).floatValue());
result.setYCoord(((Float) (data.get(++i))).floatValue());
result.setWidth(((Float) (data.get(++i))).floatValue());
result.setHeight(((Float) (data.get(++i))).floatValue());
result.setURL(((URL) (data.get(++i))));
return result;
}
/**
* Returns an XMLized version of all of the messages
* properties. Each of the properties is contained in an element of
* the array, in the form:
*
* IMAG:msgid:username:xCoord:yCoord:width:height:URL</tt>
*
* @return a <code>String[]</code> value
*/
public String[] propertiesToXML() {
String[] result = new String[6];
result[0] = "<msgid>" + Integer.toString(getMessageId()) + "</msgid>";
result[1] = "<xCoord>" + Float.toString(getXCoord()) + "</xCoord>";
result[2] = "<yCoord>" + Float.toString(getYCoord()) + "</yCoord>";
result[3] = "<width>" + Float.toString(getWidth()) + "</width>";
result[4] = "<height>" + Float.toString(getHeight()) + "</height>";
result[5] = "<url>" + getURL().toString() + "</url>";
return result;
}
/**
* This will be called by the XML parser, when the data is not
* contained in an attribute, but rather in a nested element.
* IMAG:msgid:username:xCoord:yCoord:height:width:URL</tt>
*
* @param name a <code>String</code> value
* @param value a <code>String</code> value
*/
public void processXMLElement(final String name,
final String value) {
if (name.equals("msgid")) {
setMessageId(Integer.parseInt(value));
} else if (name.equals("xCoord")) {
setXCoord(Float.parseFloat(value));
} else if (name.equals("yCoord")) {
setYCoord(Float.parseFloat(value));
} else if (name.equals("width")) {
setWidth(Float.parseFloat(value));
} else if (name.equals("height")) {
setHeight(Float.parseFloat(value));
} else if (name.equals("url")) {
try {
setURL(new URL(value));
} catch (MalformedURLException mue) {
System.out.println("MalformedURLEXception in ImageMessage\n");
}
} else {
System.out.println("processXMLElement got invalid name\n" + name);
}
}
/**
* Renders the current graphic onto the graphic object passed in.
*
* @param target a <code>Graphics2D</code> value
*/
public final synchronized void render(final Graphics2D target) {
if (image == null) {
if (!loading && !errorLoading) {
loading = true;
new Thread(new Runnable() {
public void run() {
image = ImageLoader.getInstance()
.loadNetworkImage(getURL());
if (image == null) {
errorLoading = true;
}
ClientUI.getInstance().getGraphicsPanel()
.markForRerender(true);
}
}).start();
}
//Draw a rectangle in place of the image:
target.setColor(Color.BLACK);
target.draw(GeometricUtilities.getInstance()
.getRectangle(getXCoord(), getYCoord(),
getWidth(), getHeight()));
if (loading) {
if (errorLoading) {
//Show error
target.drawString("Could not load image.",
getXCoord() + 10, getYCoord() + 10);
} else {
//Show loading
target.drawString("Loading image...",
getXCoord() + 10, getYCoord() + 10);
}
}
} else {
target.drawImage(image,
(int) getXCoord(), (int) getYCoord(),
(int) getWidth(), (int) getHeight(), null);
}
}
}

View File

@@ -0,0 +1,58 @@
package netpaint.messaging;
import java.util.List;
/**
* Class JoinMessage: Sent by the client to the server when it wants
* to join the server.
*
* <PRE>
* Revision History:
* v1.0 (Jan. 27, 2004) - Created the JoinMessage class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Jan. 27, 2004
*/
public class JoinMessage extends AdministrativeMessage {
/**
* Creates a new <code>JoinMessage</code> instance.
*
* @param userName a <code>String</code> value
*/
public JoinMessage(final String userName) {
super(MessageSettings.JOIN_TYPE, userName);
}
/**
* Builds a message from the data provided.
*
* @param data a <code>List</code> value
* @return an <code>AbstractMessage</code> value
*/
protected static AbstractMessage buildMessage(final List data) {
if (data == null || (data.size() != 1)) {
return null;
}
String name = (String) (data.get(0));
if (name.indexOf(':') >= 0) {
//Can't have colons in the name of the user:
return null;
}
return new JoinMessage(name);
}
/**
* makes a message from the data provided.
*
*
*/
protected void makeMessage() {
}
}

View File

@@ -0,0 +1,351 @@
package netpaint.messaging;
import java.util.List;
import java.util.StringTokenizer;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
/**
* Class LineMessage: Represents the line graphic message. When the
* user draws a simple line on the screen, this message is created and
* sent over to the server. The format of the network message is the
* following:
*
* LINE:msgid:username:startX:startY:run:rise:color
*
* <PRE>
* Revision History:
* v1.0 (Jan. 29, 2004) - Created the LineMessage class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Jan. 29, 2004
*/
public class LineMessage extends GraphicMessage {
/**
* The x coordinate of the start of the line.
*/
private float startX;
/**
* The y coordinate of the start of the line.
*/
private float startY;
/**
* The x component of the length of the line.
*/
private float run;
/**
* The y component of the lenght of the line.
*/
private float rise;
/**
* The shape to draw.
*/
private Line2D shape;
/**
* Creates a new <code>LineMessage</code> instance.
* @param author a <code>String</code> value
*/
public LineMessage(final String author) {
super(MessageSettings.DRAW_LINE_TYPE, author);
startX = -1;
startY = -1;
run = -1;
rise = -1;
shape = null;
}
/**
* Get the value of startX.
* @return value of startX.
*/
public final float getStartX() {
return startX;
}
/**
* Set the value of startX.
* @param v Value to assign to startX.
*/
public final void setStartX(final float v) {
this.startX = v;
}
/**
* Get the value of startY.
* @return value of startY.
*/
public final float getStartY() {
return startY;
}
/**
* Set the value of startY.
* @param v Value to assign to startY.
*/
public final void setStartY(final float v) {
this.startY = v;
}
/**
* Get the value of run.
* @return value of run.
*/
public final float getRun() {
return run;
}
/**
* Set the value of run.
* @param v Value to assign to run.
*/
public final void setRun(final float v) {
this.run = v;
}
/**
* Get the value of rise.
* @return value of rise.
*/
public final float getRise() {
return rise;
}
/**
* Set the value of rise.
* @param v Value to assign to rise.
*/
public final void setRise(final float v) {
this.rise = v;
}
/**
* Returns a string representation of the message fit for plain-text
* transmission over the network. The format is as follows:
*
* <tt>LINE:msgid:username:startX:startY:run:rise:color</tt>
*
* @return a <code>String</code> value
*/
public final String tokenize() {
String result = getMessageType() + ":";
result += Integer.toString(getMessageId()) + ":";
result += getAuthor() + ":";
result += Float.toString(getStartX()) + ":";
result += Float.toString(getStartY()) + ":";
result += Float.toString(getRun()) + ":";
result += Float.toString(getRise()) + ":";
result += Integer.toString(getColor().getRGB());
//result += getColor().toString();
return result;
}
/**
* Parses the string into the specific message.
*
* @param message a <code>String</code> value
* @return an <code>AbstractMessage</code> value
*/
protected static AbstractMessage parse(final String message) {
LineMessage result = null;
StringTokenizer st = null;
String type = getMessageType(message);
int id = -1;
if (type == null) {
return null;
}
if (!type.equals(MessageSettings.DRAW_LINE_TYPE)) {
return null;
}
//Create StringTokenizer
st = new StringTokenizer(message, ":");
//The first token is the message type.
if (!st.hasMoreTokens()) {
return null;
}
type = st.nextToken();
//Next token is msgid:
if (!st.hasMoreTokens()) {
return null;
}
try {
id = Integer.parseInt(st.nextToken());
} catch (NumberFormatException nfe) {
return null;
}
//username
if (!st.hasMoreTokens()) {
return null;
}
result = new LineMessage(st.nextToken());
result.setMessageId(id);
//startX
if (!st.hasMoreTokens()) {
return null;
}
try {
result.setStartX(Float.parseFloat(st.nextToken()));
} catch (NumberFormatException nfe) {
return null;
}
//startY
if (!st.hasMoreTokens()) {
return null;
}
try {
result.setStartY(Float.parseFloat(st.nextToken()));
} catch (NumberFormatException nfe) {
return null;
}
//run
if (!st.hasMoreTokens()) {
return null;
}
try {
result.setRun(Float.parseFloat(st.nextToken()));
} catch (NumberFormatException nfe) {
return null;
}
//rise
if (!st.hasMoreTokens()) {
return null;
}
try {
result.setRise(Float.parseFloat(st.nextToken()));
} catch (NumberFormatException nfe) {
return null;
}
//color
if (!st.hasMoreTokens()) {
return null;
}
try {
result.setColor(Integer.parseInt(st.nextToken()));
} catch (NumberFormatException nfe) {
return null;
}
return result;
}
/**
* Builds a message from the data provided.
*
* @param data a <code>List</code> value
* @return an <code>AbstractMessage</code> value
*/
protected static AbstractMessage buildMessage(final List data) {
if (data == null || (data.size() != MessageSettings.LINE_DATA_COUNT)) {
return null;
}
int i = 1;
LineMessage result = new LineMessage((String) (data.get(i)));
result.setMessageId(((Integer) (data.get(0))).intValue());
result.setStartX(((Float) (data.get(++i))).floatValue());
result.setStartY(((Float) (data.get(++i))).floatValue());
result.setRun(((Float) (data.get(++i))).floatValue());
result.setRise(((Float) (data.get(++i))).floatValue());
result.setColor(((Integer) (data.get(++i))).intValue());
return result;
}
/**
* Returns an XMLized version of all of the messages
* properties. Each of the properties is contained in an element of
* the array, in the form:
* <name>value</name>. LINE:msgid:username:startX:startY:run:rise:color
*
* @return a <code>String[]</code> value
*/
public String[] propertiesToXML() {
String[] result = new String[6];
result[0] = "<msgid>" + Integer.toString(getMessageId()) + "</msgid>";
result[1] = "<startX>" + Float.toString(getStartX()) + "</startX>";
result[2] = "<startY>" + Float.toString(getStartY()) + "</startY>";
result[3] = "<run>" + Float.toString(getRun()) + "</run>";
result[4] = "<rise>" + Float.toString(getRise()) + "</rise>";
result[5] = "<color>" + Integer.toString(getColor().getRGB())
+ "</color>";
return result;
}
/**
* This will be called by the XML parser, when the data is not
* contained in an attribute, but rather in a nested element.
*
* @param name a <code>String</code> value
* @param value a <code>String</code> value
*/
public void processXMLElement(final String name,
final String value) {
if (name.equals("msgid")) {
setMessageId(Integer.parseInt(value));
} else if (name.equals("startX")) {
setStartX(Float.parseFloat(value));
} else if (name.equals("startY")) {
setStartY(Float.parseFloat(value));
} else if (name.equals("run")) {
setRun(Float.parseFloat(value));
} else if (name.equals("rise")) {
setRise(Float.parseFloat(value));
} else if (name.equals("color")) {
setColor(Integer.parseInt(value));
} else {
System.out.println("processXMLElement got invalid name\n" + name);
}
}
/**
* Renders the current graphic onto the graphic object passed in.
*
* @param target a <code>Graphics2D</code> value
*/
public final void render(final Graphics2D target) {
if (MessageSettings.VERBOSE) {
System.out.println("Rendering a line (" + getStartX()
+ ", " + getStartY() + ")");
}
if (shape == null) {
//Create the shape to render
shape = new Line2D.Float(getStartX(),
getStartY(),
getRun() + getStartX(),
getRise() + getStartY());
}
//Set the new color for this shape:
target.setColor(this.getColor());
//Render shape:
target.draw(shape);
}
}

View File

@@ -0,0 +1,260 @@
package netpaint.messaging;
import java.util.List;
/**
* Class MessageFactory: Creates messages.
*
* <PRE>
* Revision History:
* v1.0 (Jan. 26, 2004) - Created the MessageFactory class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Jan. 26, 2004
*/
public final class MessageFactory {
/**
* Implement as a singleton.
*/
private static MessageFactory instance;
/**
* Creates a new <code>MessageFactory</code> instance.
*/
private MessageFactory() { }
/**
* Singleton implementation.
*
* @return a <code>MessageFactory</code> value
*/
public static MessageFactory getInstance() {
if (instance == null) {
instance = new MessageFactory();
}
return instance;
}
/**
* Parses the string passed in and returns the appropriate network message.
*
* @param message a <code>String</code> value
* @return an <code>AbstractMessage</code> value
*/
public AbstractMessage makeMessage(final String message) {
String type = AbstractMessage.getMessageType(message);
if (type == null) {
return null;
}
if (type.equals(MessageSettings.JOIN_TYPE)) {
return JoinMessage.parse(message);
}
if (type.equals(MessageSettings.QUIT_TYPE)) {
return QuitMessage.parse(message);
}
if (type.equals(MessageSettings.SERVER_ERROR_TYPE)) {
return ServerErrorMessage.parse(message);
}
if (type.equals(MessageSettings.SERVER_MESSAGE_TYPE)) {
return GenericServerMessage.parse(message);
}
if (type.equals(MessageSettings.DRAW_RECTANGLE_TYPE)) {
return RectangleMessage.parse(message);
}
if (type.equals(MessageSettings.DRAW_POLYGON_TYPE)) {
return PolygonMessage.parse(message);
}
if (type.equals(MessageSettings.DRAW_TRIANGLE_TYPE)) {
return TriangleMessage.parse(message);
}
if (type.equals(MessageSettings.DRAW_LINE_TYPE)) {
return LineMessage.parse(message);
}
if (type.equals(MessageSettings.DRAW_QUADCURVE_TYPE)) {
return QuadraticCurveMessage.parse(message);
}
if (type.equals(MessageSettings.DRAW_ELLIPSE_TYPE)) {
return EllipseMessage.parse(message);
}
if (type.equals(MessageSettings.DRAW_TEXT_TYPE)) {
return TextMessage.parse(message);
}
if (type.equals(MessageSettings.DRAW_IMAGE_TYPE)) {
return ImageMessage.parse(message);
}
if (type.equals(MessageSettings.DRAW_UNDO_TYPE)) {
return UndoMessage.parse(message);
}
if (type.equals(MessageSettings.CHAT_TYPE)) {
return ChatMessage.parse(message);
}
return null;
}
/**
* Builds a network message class from a list of data.
*
* @param messageType a <code>String</code> value
* @param data a <code>List</code> value
* @return an <code>AbstractMessage</code> value
*/
public AbstractMessage makeMessage(final String messageType,
final List data) {
if (messageType == null
|| (messageType.length() != MessageSettings.TYPE_LENGTH)) {
return null;
}
if (messageType.equals(MessageSettings.JOIN_TYPE)) {
return JoinMessage.buildMessage(data);
}
if (messageType.equals(MessageSettings.QUIT_TYPE)) {
return QuitMessage.buildMessage(data);
}
if (messageType.equals(MessageSettings.SERVER_ERROR_TYPE)) {
return ServerErrorMessage.buildMessage(data);
}
if (messageType.equals(MessageSettings.SERVER_MESSAGE_TYPE)) {
return GenericServerMessage.buildMessage(data);
}
if (messageType.equals(MessageSettings.DRAW_RECTANGLE_TYPE)) {
return RectangleMessage.buildMessage(data);
}
if (messageType.equals(MessageSettings.DRAW_POLYGON_TYPE)) {
return PolygonMessage.buildMessage(data);
}
if (messageType.equals(MessageSettings.DRAW_TRIANGLE_TYPE)) {
return TriangleMessage.buildMessage(data);
}
if (messageType.equals(MessageSettings.DRAW_LINE_TYPE)) {
return LineMessage.buildMessage(data);
}
if (messageType.equals(MessageSettings.DRAW_QUADCURVE_TYPE)) {
return QuadraticCurveMessage.buildMessage(data);
}
if (messageType.equals(MessageSettings.DRAW_ELLIPSE_TYPE)) {
return EllipseMessage.buildMessage(data);
}
if (messageType.equals(MessageSettings.DRAW_TEXT_TYPE)) {
return TextMessage.buildMessage(data);
}
if (messageType.equals(MessageSettings.DRAW_IMAGE_TYPE)) {
return ImageMessage.buildMessage(data);
}
if (messageType.equals(MessageSettings.DRAW_UNDO_TYPE)) {
return UndoMessage.buildMessage(data);
}
if (messageType.equals(MessageSettings.CHAT_TYPE)) {
return ChatMessage.buildMessage(data);
}
return null;
}
/**
* Creates a message of the specified type with the default
* parameters. The message returned is basically considered empty.
*
* @param messageType a <code>String</code> value
* @param authorName a <code>String</code> value
* @return an <code>AbstractMessage</code> value
*/
public AbstractMessage makeMessage(final String messageType,
final String authorName) {
if (messageType == null || messageType.equals("")) {
return null;
}
if (messageType.equals(MessageSettings.JOIN_TYPE)) {
return new JoinMessage(authorName);
}
if (messageType.equals(MessageSettings.QUIT_TYPE)) {
return new QuitMessage(authorName);
}
if (messageType.equals(MessageSettings.SERVER_ERROR_TYPE)) {
return new ServerErrorMessage(authorName);
}
if (messageType.equals(MessageSettings.SERVER_MESSAGE_TYPE)) {
return new GenericServerMessage(authorName);
}
if (messageType.equals(MessageSettings.DRAW_RECTANGLE_TYPE)) {
return new RectangleMessage(authorName);
}
if (messageType.equals(MessageSettings.DRAW_POLYGON_TYPE)) {
return new PolygonMessage(authorName);
}
if (messageType.equals(MessageSettings.DRAW_TRIANGLE_TYPE)) {
return new TriangleMessage(authorName);
}
if (messageType.equals(MessageSettings.DRAW_LINE_TYPE)) {
return new LineMessage(authorName);
}
if (messageType.equals(MessageSettings.DRAW_QUADCURVE_TYPE)) {
return new QuadraticCurveMessage(authorName);
}
if (messageType.equals(MessageSettings.DRAW_ELLIPSE_TYPE)) {
return new EllipseMessage(authorName);
}
if (messageType.equals(MessageSettings.DRAW_TEXT_TYPE)) {
return new TextMessage(authorName);
}
if (messageType.equals(MessageSettings.DRAW_IMAGE_TYPE)) {
return new ImageMessage(authorName);
}
if (messageType.equals(MessageSettings.DRAW_UNDO_TYPE)) {
return new UndoMessage(authorName);
}
if (messageType.equals(MessageSettings.CHAT_TYPE)) {
return new ChatMessage(authorName);
}
return null;
}
}

View File

@@ -0,0 +1,169 @@
package netpaint.messaging;
/**
* Class MessageSettings: This class contains the settings shared
* among all the different messages, as well as the MessageFactory.
*
* <PRE>
* Revision History:
* v1.0 (Jan. 29, 2004) - Created the MessageSettings class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Jan. 29, 2004
*/
public final class MessageSettings {
/**
* Message type associated with all unknown messages.
*/
public static final String UNKNOWN_TYPE = "ABST";
/**
* Message type associated with all server messages.
*/
public static final String SERVER_MESSAGE_TYPE = "SRVM";
/**
* Message type associated with all server errors.
*/
public static final String SERVER_ERROR_TYPE = "SRVE";
/**
* Message type associated with all join messages.
*/
public static final String JOIN_TYPE = "HELO";
/**
* Message type associated with all quit messages.
*/
public static final String QUIT_TYPE = "QUIT";
/**
* Message type associated with all rectangle messages.
*/
public static final String DRAW_RECTANGLE_TYPE = "RECT";
/**
* Message type associated with all polygon messages.
*/
public static final String DRAW_POLYGON_TYPE = "POLY";
/**
* Message type associated with all triangle messages.
*/
public static final String DRAW_TRIANGLE_TYPE = "TRIA";
/**
* Message type associated with all line messages.
*/
public static final String DRAW_LINE_TYPE = "LINE";
/**
* Message type associated with all quad curve messages.
*/
public static final String DRAW_QUADCURVE_TYPE = "QUAD";
/**
* Message type associated with all ellipse messages.
*/
public static final String DRAW_ELLIPSE_TYPE = "ELIP";
/**
* Message type associated with all draw text messages.
*/
public static final String DRAW_TEXT_TYPE = "TEXT";
/**
* Message type associated with all image messages.
*/
public static final String DRAW_IMAGE_TYPE = "IMAG";
/**
* Message type associated with all undo messages.
*/
public static final String DRAW_UNDO_TYPE = "UNDO";
/**
* Message type associated with all chat messages.
*/
public static final String CHAT_TYPE = "CHAT";
/**
* The length of the message type string.
*/
public static final int TYPE_LENGTH = 4;
/**
* The user name that the server will show up as when it sends messages.
*/
public static final String SERVER_USERNAME = "Server";
/**
* The default color in which all the graphics are drawn.
*/
public static final int DEFAULT_GRAPHIC_COLOR = 0;
/**
* Verbose switch.
*/
public static final boolean VERBOSE = false;
/**
* The number of data elements that must be present in the chat message.
*/
public static final int CHAT_DATA_COUNT = 2;
/**
* The number of data elements that must be present in the line message.
*/
public static final int LINE_DATA_COUNT = 7;
/**
* The number of data elements that must be present in the quadratic
* message.
*/
public static final int QUADRATIC_DATA_COUNT = 9;
/**
* The number of data elements that must be present in the rectangle
* message.
*/
public static final int RECTANGLE_DATA_COUNT = 8;
/**
* The number of data elements that must be present in the ellipse
* message.
*/
public static final int ELLIPSE_DATA_COUNT = 8;
/**
* The number of data elements that must be present in the triangle
* message.
*/
public static final int TRIANGLE_DATA_COUNT = 10;
/**
* The number of data elements that must be present in the Image
* message.
*/
public static final int IMAGE_DATA_COUNT = 7;
/**
* The number of data elements that must be present in the Text
* message.
*/
public static final int TEXT_DATA_COUNT = 9;
/**
* The number of data elements that must be present in the undo message.
*/
public static final int UNDO_DATA_COUNT = 3;
/**
* This is a utility class. No need to instantiate it.
*/
private MessageSettings() {
}
}

View File

@@ -0,0 +1,460 @@
package netpaint.messaging;
import java.util.List;
import java.util.Vector;
import java.util.StringTokenizer;
import java.awt.Graphics2D;
import java.awt.Polygon;
/**
* Class PolygonMessage: Represents the polygon graphic message. When the
* user draws a simple polygon on the screen, this message is created and
* sent over to the server. The format of the network message is the
* following:
*
* POLY:msgid:username:color:fill(y|n):numpoints:x1:y1:x2:y2...
*
* <PRE>
* Revision History:
* v1.0 (Jan. 29, 2004) - Created the PolygonMessage class
* </PRE>
*
* @author <A HREF="mailto:gtg563g@prism.gatech.edu">Daniyar Zhanbekov</A>
* @version Version 1.0, Jan. 29, 2004
*/
public class PolygonMessage extends GraphicMessage implements FillableShape {
/**
*The number of points in the polygon
*/
private int numPoints;
/**
* The vector of x coordinates of the polygon.
*/
private List xPoints;
/**
* The vector of y coordinates of the polygon.
*/
private List yPoints;
/**
* True if the shape should be filled; false if only the border
* should be drawn.
*
* @return a <code>boolean</code> value
*/
private boolean fill = false;
/**
* Creates a new <code>PolygonMessage</code> instance.
* @param author a <code>String</code> value
*/
public PolygonMessage(final String author) {
super(MessageSettings.DRAW_POLYGON_TYPE, author);
numPoints = 0;
xPoints = new Vector();
yPoints = new Vector();
fill = false;
}
/**
* Get the number of points
* @return number of points.
*/
public final int getNumPoints() {
return numPoints;
}
/**
* Set the number of points
* @param v Value to assign to numPoints.
*/
private final void setNumPoints(final int v) {
this.numPoints = v;
}
/**
*Add x and y coordinates to array of X and Y pts.
*@param x value to add
*@param y value to add
*/
public final void addPoint(final float x, final float y) {
xPoints.add(new Float(x));
yPoints.add(new Float(y));
numPoints++;
}
/**
* Get the array of xCoord.
* @return array of xCoord.
*/
public final List getXPoints() {
return xPoints;
}
/**
* Get the array of yCoord.
* @return array of yCoord.
*/
public final List getYPoints() {
return yPoints;
}
/**
* Get the value of fill.
* @return value of fill.
*/
public final boolean isFilled() {
return fill;
}
/**
* Set the value of fill.
* @param v Value to assign to fill.
*/
public final void setFilled(final boolean v) {
this.fill = v;
}
/**
* Returns a string representation of the message fit for plain-text
* transmission over the network. The format is as follows:
* POLY:msgid:username:color:fill(y|n):numpoints:x1:y1:x2:y2...
*
*
* @return a <code>String</code> value
*/
public final String tokenize() {
int numP = getNumPoints();
String result = getMessageType() + ":";
result += Integer.toString(getMessageId()) + ":";
result += getAuthor() + ":";
result += Integer.toString(getColor().getRGB()) + ":";
if (isFilled()) { result += "1:"; } else { result += "0:"; }
result += Integer.toString(numP) + ":";
for (int i = 0; i < numP; i++) {
List x = getXPoints();
List y = getYPoints();
result += Float.toString(((Float) x.get(i)).floatValue()) + ":";
if (i == numP - 1) {
result += Float.toString(((Float) y.get(i)).floatValue());
} else {
result += Float.toString(((Float) y.get(i)).floatValue()) + ":";
}
}
return result;
}
/**
* Parses the string into the specific message.
* POLY:msgid:username:color:fill(y|n):numpoints:x1:y1:x2:y2...
* @param message a <code>String</code> value
* @return an <code>AbstractMessage</code> value
*/
protected static AbstractMessage parse(final String message) {
PolygonMessage result = null;
StringTokenizer st = null;
String type = getMessageType(message);
String temp = null;
int id = -1;
int numPts = -1;
if (type == null) {
return null;
}
if (!type.equals(MessageSettings.DRAW_POLYGON_TYPE)) {
return null;
}
//Create StringTokenizer
st = new StringTokenizer(message, ":");
//The first token is the message type.
if (!st.hasMoreTokens()) {
return null;
}
type = st.nextToken();
//Next token is msgid:
if (!st.hasMoreTokens()) {
return null;
}
try {
id = Integer.parseInt(st.nextToken());
} catch (NumberFormatException nfe) {
return null;
}
//username
if (!st.hasMoreTokens()) {
return null;
}
result = new PolygonMessage(st.nextToken());
result.setMessageId(id);
//color
if (!st.hasMoreTokens()) {
return null;
}
try {
result.setColor(Integer.parseInt(st.nextToken()));
} catch (NumberFormatException nfe) {
return null;
}
//fill
if (!st.hasMoreTokens()) {
return null;
}
temp = st.nextToken();
if (temp.equals("1")) {
result.setFilled(true);
} else {
result.setFilled(false);
}
//numPoints
if (!st.hasMoreTokens()) {
return null;
}
try {
numPts = Integer.parseInt(st.nextToken());
} catch (NumberFormatException nfe) {
return null;
}
//Vector
for (int i = 0; i < numPts; i++) {
float x;
float y;
if (!st.hasMoreTokens()) {
return null;
}
try {
x = Float.parseFloat(st.nextToken());
} catch (NumberFormatException nfe) {
return null;
}
if (!st.hasMoreTokens()) {
return null;
}
try {
y = Float.parseFloat(st.nextToken());
} catch (NumberFormatException nfe) {
return null;
}
result.addPoint(x, y);
}
return result;
}
/**
* Builds a message from the data provided.
* POLY:msgid:username:color:fill(y|n):numpoints:x1:y1:x2:y2...
* @param data a <code>List</code> value
* @return an <code>AbstractMessage</code> value
*/
protected static AbstractMessage buildMessage(final List data) {
if (data == null) {
return null;
}
int numP;
int i = 1;
PolygonMessage result = new PolygonMessage((String) (data.get(i)));
result.setMessageId(((Integer) (data.get(0))).intValue());
result.setColor(((Integer) (data.get(++i))).intValue());
result.setFilled(((Boolean) (data.get(++i))).booleanValue());
result.setNumPoints(((Integer) (data.get(++i))).intValue());
numP = result.getNumPoints();
for (int j = 0; j < numP; j++) {
float x;
float y;
x = ((Float) (data.get(++i))).floatValue();
y = ((Float) (data.get(++i))).floatValue();
result.addPoint(x, y);
}
return result;
}
/**
* Returns an XMLized version of all of the messages
* properties. Each of the properties is contained in an element of
* the array, in the form:
* POLY:msgid:username:color:fill(y|n):numpoints:x1:y1:x2:y2...
*
*
* @return a <code>String[]</code> value
*/
public String[] propertiesToXML() {
int numP = getNumPoints();
int arrSize = numP + 4;
String[] arr = new String[arrSize];
Vector result = new Vector();
Object[] oArray;
List x = getXPoints();
List y = getYPoints();
result.add("<msgid>" + Integer.toString(getMessageId()) + "</msgid>");
result.add("<color>" + Integer.toString(getColor().getRGB())
+ "</color>");
result.add("<fill>" + (isFilled() ? "1" : "0") + "</fill>");
result.add("<numPoints>" + Integer.toString(numP) + "</numPoints>");
for (int i = 0; i < numP; i++) {
result.add("<point>" + Float.toString(((Float) x.get(i)).floatValue())
+ ":" + Float.toString(((Float) y.get(i)).floatValue())
+ "</point>");
}
oArray = result.toArray();
for (int j = 0; j < arrSize; j++) {
arr[j] = (String) (oArray[j]);
}
return arr;
}
/**
* This will be called by the XML parser, when the data is not
* contained in an attribute, but rather in a nested element.
*
* POLY:msgid:username:color:fill(y|n):numpoints:x1:y1:x2:y2...
* @param name a <code>String</code> value
* @param value a <code>String</code> value
*/
public void processXMLElement(final String name,
final String value) {
if (name.equals("msgid")) {
setMessageId(Integer.parseInt(value));
} else if (name.equals("color")) {
setColor(Integer.parseInt(value));
} else if (name.equals("fill")) {
if (value.equals("1")) {
setFilled(true);
} else {
setFilled(false);
}
} else if (name.equals("point")) {
float x;
float y;
String x1;
String y1;
//parse value ex 12:3
x1 = value.substring(0, value.indexOf(':'));
y1 = value.substring(value.indexOf(':') + 1);
x = Float.parseFloat(x1);
y = Float.parseFloat(y1);
if (MessageSettings.VERBOSE) {
System.out.println("PolygonMessage: Got values: " + x + ", "
+ y);
System.out.flush();
}
//add to vector
addPoint(x, y);
} else {
System.out.println("processXMLElement got invalid name\n" + name);
}
}
/**
* Renders the current graphic onto the graphic object passed in.
*
* @param target a <code>Graphics2D</code> value
*/
public final void render(final Graphics2D target) {
Object[] xPts;
Object[] yPts;
int[] xP;
int[] yP;
int numP;
xPts = getXPoints().toArray();
yPts = getYPoints().toArray();
//changing the Object array into int []
xP = new int[xPts.length];
yP = new int[yPts.length];
for (int i = 0; i < xPts.length; i++) {
xP[i] = (int) (((Float) xPts[i]).floatValue());
yP[i] = (int) (((Float) yPts[i]).floatValue());
}
numP = getNumPoints();
Polygon shape = new Polygon(xP, yP, numP);
target.setColor(getColor());
if (isFilled()) {
target.fill(shape);
} else {
target.draw(shape);
}
}
}

View File

@@ -0,0 +1,409 @@
package netpaint.messaging;
import java.awt.Graphics2D;
import java.awt.geom.QuadCurve2D;
import java.util.List;
import java.util.StringTokenizer;
/**
* Class QuadraticCurveMessage: Represents the quadratic curve graphic
* message. When the user draws a quadratic curve on the screen, this
* message is created and sent over to the server. The format of the
* network message is the following:<BR><BR>
*
* QUAD:msgid:username:x1:y1:x2:y2:ctrlx:ctrly:color
*
* <PRE>
* Revision History:
* v1.0 (Jan. 29, 2004) - Created the QuadraticCurveMessage class
* </PRE>
*
* @author <A HREF="mailto:gtg563g@prism.gatech.edu">Daniyar Zhanbekov</A>
* @version Version 1.0, Jan. 29, 2004
*/
public class QuadraticCurveMessage extends GraphicMessage {
/**
* The x coordinate of the start point of the quadratic curve segment.
*/
private float x1;
/**
* The y coordinate of the start point of the quadratic curve segment.
*/
private float y1;
/**
* The x coordinate of the end point of the quadratic curve segment.
*/
private float x2;
/**
* The y coordinate of the start point of the quadratic curve segment.
*/
private float y2;
/**
* The x coordinate of the control point of the quadratic curve segment.
*/
private float ctrlx;
/**
* The y coordinate of the control point of the quadratic curve segment.
*/
private float ctrly;
/**
* Creates a new <code>QuadraticCurveMessage</code> instance.
* @param author a <code>String</code> value
*/
public QuadraticCurveMessage(final String author) {
super(MessageSettings.DRAW_QUADCURVE_TYPE, author);
x1 = -1;
y1 = -1;
x2 = -1;
y2 = -1;
ctrlx = -1;
ctrly = -1;
}
/**
* Get the value of startX1.
* @return value of startX1.
*/
public final float getX1() {
return x1;
}
/**
* Set the value of startX1.
* @param v Value to assign to startX1.
*/
public final void setX1(final float v) {
this.x1 = v;
}
/**
* Get the value of startX2.
* @return value of startX2.
*/
public final float getX2() {
return x2;
}
/**
* Set the value of startX2.
* @param v Value to assign to startX2.
*/
public final void setX2(final float v) {
this.x2 = v;
}
/**
* Get the value of startY1.
* @return value of startY1.
*/
public final float getY1() {
return y1;
}
/**
* Set the value of startY1.
* @param v Value to assign to startY1.
*/
public final void setY1(final float v) {
this.y1 = v;
}
/**
* Get the value of startY2.
* @return value of startY2.
*/
public final float getY2() {
return y2;
}
/**
* Set the value of startX2.
* @param v Value to assign to startX2.
*/
public final void setY2(final float v) {
this.y2 = v;
}
/**
* Get the value of ctrlx.
* @return value of ctrlx.
*/
public final float getCtrlX() {
return ctrlx;
}
/**
* Set the value of ctrlx.
* @param v Value to assign to ctrlx.
*/
public final void setCtrlX(final float v) {
this.ctrlx = v;
}
/**
* Get the value of ctrly.
* @return value of ctrly
*/
public final float getCtrlY() {
return ctrly;
}
/**
* Set the value of ctrly.
* @param v Value to assign to ctrly.
*/
public final void setCtrlY(final float v) {
this.ctrly = v;
}
/**
* Returns a string representation of the message fit for plain-text
* transmission over the network. The format is as follows:
*
* <tt>QUAD:msgid:username:x1:y1:x2:y2:ctrlx:ctrly:color</tt>
*
* @return a <code>String</code> value
*/
public final String tokenize() {
String result = getMessageType() + ":";
result += Integer.toString(getMessageId()) + ":";
result += getAuthor() + ":";
result += Float.toString(getX1()) + ":";
result += Float.toString(getY1()) + ":";
result += Float.toString(getX2()) + ":";
result += Float.toString(getY2()) + ":";
result += Float.toString(getCtrlX()) + ":";
result += Float.toString(getCtrlY()) + ":";
result += Integer.toString(getColor().getRGB());
return result;
}
/**
* Parses the string into the specific message.
*
* @param message a <code>String</code> value
* @return an <code>AbstractMessage</code> value
*/
protected static AbstractMessage parse(final String message) {
QuadraticCurveMessage result = null;
StringTokenizer st = null;
String type = getMessageType(message);
int id = -1;
if (type == null) {
return null;
}
if (!type.equals(MessageSettings.DRAW_QUADCURVE_TYPE)) {
return null;
}
//Create StringTokenizer
st = new StringTokenizer(message, ":");
//The first token is the message type.
if (!st.hasMoreTokens()) {
return null;
}
type = st.nextToken();
//Next token is msgid:
if (!st.hasMoreTokens()) {
return null;
}
try {
id = Integer.parseInt(st.nextToken());
} catch (NumberFormatException nfe) {
return null;
}
//username
if (!st.hasMoreTokens()) {
return null;
}
result = new QuadraticCurveMessage(st.nextToken());
result.setMessageId(id);
//x1
if (!st.hasMoreTokens()) {
return null;
}
try {
result.setX1(Float.parseFloat(st.nextToken()));
} catch (NumberFormatException nfe) {
return null;
}
//y1
if (!st.hasMoreTokens()) {
return null;
}
try {
result.setY1(Float.parseFloat(st.nextToken()));
} catch (NumberFormatException nfe) {
return null;
}
//x2
if (!st.hasMoreTokens()) {
return null;
}
try {
result.setX2(Float.parseFloat(st.nextToken()));
} catch (NumberFormatException nfe) {
return null;
}
//y2
if (!st.hasMoreTokens()) {
return null;
}
try {
result.setY2(Float.parseFloat(st.nextToken()));
} catch (NumberFormatException nfe) {
return null;
}
//ctrlx
if (!st.hasMoreTokens()) {
return null;
}
try {
result.setCtrlX(Float.parseFloat(st.nextToken()));
} catch (NumberFormatException nfe) {
return null;
}
//ctrly
if (!st.hasMoreTokens()) {
return null;
}
try {
result.setCtrlY(Float.parseFloat(st.nextToken()));
} catch (NumberFormatException nfe) {
return null;
}
//color
if (!st.hasMoreTokens()) {
return null;
}
try {
result.setColor(Integer.parseInt(st.nextToken()));
} catch (NumberFormatException nfe) {
return null;
}
return result;
}
/**
* Builds a message from the data provided.
*
* @param data a <code>List</code> value
* @return an <code>AbstractMessage</code> value
*/
protected static AbstractMessage buildMessage(final List data) {
if (data == null
|| (data.size() != MessageSettings.QUADRATIC_DATA_COUNT)) {
return null;
}
int i = 1;
QuadraticCurveMessage result =
new QuadraticCurveMessage((String) (data.get(1)));
result.setMessageId(((Integer) (data.get(0))).intValue());
result.setX1(((Float) (data.get(++i))).floatValue());
result.setY1(((Float) (data.get(++i))).floatValue());
result.setX2(((Float) (data.get(++i))).floatValue());
result.setY2(((Float) (data.get(++i))).floatValue());
result.setCtrlX(((Float) (data.get(++i))).floatValue());
result.setCtrlY(((Float) (data.get(++i))).floatValue());
result.setColor(((Integer) (data.get(++i))).intValue());
return result;
}
/**
* Returns an XMLized version of all of the messages
* properties. Each of the properties is contained in an element of
* the array, in the form:
* QUAD:msgid:username:x1:y1:x2:y2:ctrlx:ctrly:color
*
*
* @return a <code>String[]</code> value
*/
public String[] propertiesToXML() {
String[] result = new String[8];
result[0] = "<msgid>" + Integer.toString(getMessageId()) + "</msgid>";
result[1] = "<x1>" + Float.toString(getX1()) + "</x1>";
result[2] = "<y1>" + Float.toString(getY1()) + "</y1>";
result[3] = "<x2>" + Float.toString(getX2()) + "</x2>";
result[4] = "<y2>" + Float.toString(getY2()) + "</y2>";
result[5] = "<ctrlx>" + Float.toString(getCtrlX()) + "</ctrlx>";
result[6] = "<ctrly>" + Float.toString(getCtrlY()) + "</ctrly>";
result[7] = "<color>" + Integer.toString(getColor().getRGB())
+ "</color>";
return result;
}
/** QUAD:msgid:username:x1:y1:x2:y2:ctrlx:ctrly:color
* This will be called by the XML parser, when the data is not
* contained in an attribute, but rather in a nested element.
*
* @param name a <code>String</code> value
* @param value a <code>String</code> value
*/
public void processXMLElement(final String name,
final String value) {
if (name.equals("msgid")) {
setMessageId(Integer.parseInt(value));
} else if (name.equals("x1")) {
setX1(Float.parseFloat(value));
} else if (name.equals("y1")) {
setY1(Float.parseFloat(value));
} else if (name.equals("x2")) {
setX2(Float.parseFloat(value));
} else if (name.equals("y2")) {
setY2(Float.parseFloat(value));
} else if (name.equals("ctrlx")) {
setCtrlX(Float.parseFloat(value));
} else if (name.equals("ctrly")) {
setCtrlY(Float.parseFloat(value));
} else if (name.equals("color")) {
setColor(Integer.parseInt(value));
} else {
System.out.println("processXMLElement got invalid name\n" + name);
}
}
/**
* Renders the current graphic onto the graphic object passed in.
*
* @param target a <code>Graphics2D</code> value
*/
public final void render(final Graphics2D target) {
QuadCurve2D.Float curve = new QuadCurve2D.Float(getX1(), getY1(),
getCtrlX(), getCtrlY(),
getX2(), getY2());
target.setColor(getColor());
target.draw(curve);
}
}

Some files were not shown because too many files have changed in this diff Show More