// This example is from _Java Examples in a Nutshell_. (http://www.oreilly.com)
// Copyright (c) 1997 by David Flanagan
// This example is provided WITHOUT ANY WARRANTY either expressed or implied.
// You may study, use, modify, and distribute it for non-commercial purposes.
// For any commercial use, see http://www.davidflanagan.com/javaexamples
import java.awt.*; // ScrollPane, PopupMenu, MenuShortcut, etc.
import java.awt.event.*; // New event model.
import java.io.*; // Object serialization streams.
import java.util.zip.*; // Data compression/decompression streams.
import java.util.Vector; // To store the scribble in.
/**
* This class demonstrates the use of object serialization to provide
* a file format for saving application state. It saves a user's scribbles
* as a compressed, serialized Vector of Line objects.
**/
public class SerializedScribble extends Frame {
/** A very simple main() method for our program. */
public static void main(String[] args) { new SerializedScribble(); }
/** Create a Frame, Menu, and Scribble component */
public SerializedScribble() {
super("SerialziedScribble"); // Create the window.
final Scribble scribble;
scribble = new Scribble(this, 300, 300); // Create a bigger scribble area.
this.add(scribble, "Center"); // Add it to the ScrollPane.
MenuBar menubar = new MenuBar(); // Create a menubar.
this.setMenuBar(menubar); // Add it to the frame.
Menu file = new Menu("File"); // Create a File menu.
menubar.add(file); // Add to menubar.
// Create three menu items, with menu shortcuts, and add to the menu.
MenuItem load, save, quit;
file.add(load = new MenuItem("Load"));
file.add(save = new MenuItem("Save"));
file.addSeparator(); // Put a separator in the menu
file.add(quit = new MenuItem("Quit"));
// Create and register action listener objects for the three menu items.
load.addActionListener(new ActionListener() { // Open a new window
public void actionPerformed(ActionEvent e) { scribble.load(); }
});
save.addActionListener(new ActionListener() { // Close this window.
public void actionPerformed(ActionEvent e) { scribble.save(); }
});
quit.addActionListener(new ActionListener() { // Quit the program.
public void actionPerformed(ActionEvent e) { System.exit(0); }
});
// Another event listener, this one to handle window close requests.
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) { System.exit(0); }
});
// Set the window size and pop it up.
this.pack();
this.show();
}
/**
* This class is a custom component that supports scribbling. Note that
* it extends Component rather than Canvas, making it "lightweight."
**/
static class Scribble extends Component {
protected short last_x, last_y; // Coordinates of last click.
protected Vector lines = new Vector(256,256); // Store the scribbles.
protected int width, height; // The preferred size.
protected Frame frame; // The frame we are within.
/** This constructor requires a Frame and a desired size */
public Scribble(Frame frame, int width, int height) {
this.frame = frame;
this.width = width;
this.height = height;
// We handle scribbling with low-level events, so we must specify
// which events we are interested in.
this.enableEvents(AWTEvent.MOUSE_EVENT_MASK);
this.enableEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK);
}
/**
* Specifies big the component would like to be. It always returns the
* preferred size passed to the Scribble() constructor
**/
public Dimension getPreferredSize() {return new Dimension(width, height);}
/** Draw all the saved lines of the scribble */
public void paint(Graphics g) {
for(int i = 0; i < lines.size(); i++) {
Line l = (Line)lines.elementAt(i);
g.drawLine(l.x1, l.y1, l.x2, l.y2);
}
}
/**
* This is the low-level event-handling method called on mouse events
* that do not involve mouse motion.
**/
public void processMouseEvent(MouseEvent e) {
if (e.getID() == MouseEvent.MOUSE_PRESSED) {
last_x = (short)e.getX(); last_y = (short)e.getY(); // Save position.
}
else super.processMouseEvent(e); // Pass other event types on.
}
/**
* This method is called for mouse motion events. It adds a line to the
* scribble, on screen, and in the saved representation
**/
public void processMouseMotionEvent(MouseEvent e) {
if (e.getID() == MouseEvent.MOUSE_DRAGGED) {
Graphics g = getGraphics(); // Object to draw with.
g.drawLine(last_x, last_y, e.getX(), e.getY()); // Draw this line
lines.addElement(new Line(last_x, last_y, // and save it, too.
(short) e.getX(), (short)e.getY()));
last_x = (short) e.getX(); // Remember current mouse coordinates.
last_y = (short) e.getY();
}
else super.processMouseMotionEvent(e); // Important!
}
/**
* Prompt the user for a filename, and save the scribble in that file.
* Serialize the vector of lines with an ObjectOutputStream.
* Compress the serialized objects with a GZIPOutputStream.
* Write the compressed, serialized data to a file with a FileOutputStream.
* Don't forget to flush and close the stream.
**/
public void save() {
// Create a file dialog to query the user for a filename.
FileDialog f = new FileDialog(frame, "Save Scribble", FileDialog.SAVE);
f.show(); // Display the dialog and block.
String filename = f.getFile(); // Get the user's response
if (filename != null) { // If user didn't click "Cancel".
try {
// Create the necessary output streams to save the scribble.
FileOutputStream fos = new FileOutputStream(filename);// Save to file
GZIPOutputStream gzos = new GZIPOutputStream(fos); // Compressed
ObjectOutputStream out = new ObjectOutputStream(gzos);// Save objects
out.writeObject(lines); // Write the entire Vector of scribbles
out.flush(); // Always flush the output.
out.close(); // And close the stream.
}
// Print out exceptions. We should really display them in a dialog...
catch (IOException e) { System.out.println(e); }
}
}
/**
* Prompt for a filename, and load a scribble from that file.
* Read compressed, serialized data with a FileInputStream.
* Uncompress that data with a GZIPInputStream.
* Deserialize the vector of lines with a ObjectInputStream.
* Replace current data with new data, and redraw everything.
**/
public void load() {
// Create a file dialog to query the user for a filename.
FileDialog f = new FileDialog(frame, "Load Scribble", FileDialog.LOAD);
f.show(); // Display the dialog and block.
String filename = f.getFile(); // Get the user's response
if (filename != null) { // If user didn't click "Cancel".
try {
// Create necessary input streams
FileInputStream fis = new FileInputStream(filename);// Read from file
GZIPInputStream gzis = new GZIPInputStream(fis); // Uncompress
ObjectInputStream in = new ObjectInputStream(gzis); // Read objects
// Read in an object. It should be a vector of scribbles
Vector newlines = (Vector)in.readObject();
in.close(); // Close the stream.
lines = newlines; // Set the Vector of lines.
repaint(); // And redisplay the scribble.
}
// Print out exceptions. We should really display them in a dialog...
catch (Exception e) { System.out.println(e); }
}
}
/**
* A class to store the coordinates of one scribbled line.
* The complete scribble is stored as a Vector of these objects
**/
static class Line implements Serializable {
public short x1, y1, x2, y2;
public Line(short x1, short y1, short x2, short y2) {
this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2;
}
}
}
}
|