/** file: ObjectDraw.java * author: Robert M. Keller * copyright 1997 by Robert M. Keller, all rights reserved * purpose: Object-drawing applet (for pedagogical purposes) * * This is a simple drawing program. It illustrates several object-oriented * ideas, including inheritance. * * The user is able to draw various shapes with the mouse. The shapes * can be moved, grouped hierarchically, cut, and pasted, etc. * * The user can also have a textual description (S expression) of any * object appear in a separate window. * * Currently there are five choice menus: * * Stay/Revert: With Revert, the program always goes back to Move * mode after doing an operation. With Stay, it stays * in the same mode. * * Mode: Selects between Move, Draw, Cut, Paste, etc. * * Shape: Selects the shape to be drawn. * * Fill: Selects whether the shape is filled or outline. * * Color: Selects the color **/ /* $Revision: 1.5 $, $Date: 1997/10/16 00:37:15 $ */ /* Method: Each type of shape is a class, as defined by the following inheritance hierarchy: Rectangle | | Shape | | ---------------------------------------- | | | | | | | | Box Oval Line Group Rectangle is the one defined in package java.awt. It is not a graphic object as such, but simply defines the rectangular region which bounds the object. It is used to determine where the mouse is clicked (via the 'inside' method). Class Shape is a relatively abstract class. It carries values common to all shape classes, such as color and filled. Even though Line and Group ignore color and fill, it was decided for simplicity not to make a separate node in the hierarchy for unfillable objects. A Group contains a set of shapes, currently represented using the class java.util.Vector. Continuous curves are represented by (generally large) groups of small lines. Each class has its own draw method, which translate into appropriate calls for java.awt. In the case of Group, the draw method calls the draw methods for each shape in the group. When a shape is drawn, cut, etc. the entire image is redrawn. This eliminates the need to have an erase method. */ import java.applet.*; // applet classes import java.awt.*; // Abstract Window Toolkit classes import java.util.*; // utility classes /** * ObjectDraw is the applet **/ public class ObjectDraw extends Applet { Graphics graphics; // graphics buffer Image image; // Image for graphics Vector allShapes = new Vector(); // vector of all shapes in drawing Shape currentShape; // the currently selected shape Shape justCut; // the shape just cutd boolean continuous; // used for free-form drawing Vector curve; int xDown, yDown; // where the mouse was pressed int xCut, yCut; // where shape was cut int grid = 1; // grid size int maxGrid = 64; // maximum grid size DefTable descriptions; // table of descriptions DescriptionFrame descriptionFrame; // frame for descriptions // widgets static public Color backgroundColor = Color.white, groupingColor = Color.black; static public Font MainFont = new Font("Helvetica", Font.BOLD, 18); Slider gridSlider // slider for grid size = new Slider(this, "Grid", MainFont, 1, 1, maxGrid, 2); Choice shapeChoice = new Choice(); // choice menu for shape int chosenShape = BOX; // default for menu int drawShape = chosenShape; // drawShape is shaped to be DRAWN // (as opposed to for grouping) Choice modeChoice = new Choice(); // choice menu for mode int chosenMode = DRAW; // default mode Choice colorChoice = new Choice(); // choice menu for color Color chosenColor = Color.black; // default color Vector allColors = new Vector(); // vector of all colors in menu Choice fillChoice = new Choice(); // choice menu for fill boolean fillMode = false; Choice stayChoice = new Choice(); // choice menu for stay/revert boolean stayMode = true; // table of drawing modes and corresponding indices (for use in switches) static String mode[] = { "Move", "Draw", "Cut", "CutAll", "Paste", "Copy", "Group", "GroupAll", "Ungroup", "Lower", "Raise", "Describe" }; static final int MOVE = 0, DRAW = 1, CUT = 2, CUTALL = 3, PASTE = 4, COPY = 5, GROUP = 6, GROUPALL = 7, UNGROUP = 8, LOWER = 9, RAISE = 10, DESCRIBE = 11; // table of shapes and corresponding indices (for use in switches) static String shape[] = {"Box", "Line", "Curve", "Oval"}; static final int BOX = 0, LINE = 1, CURVE = 2, OVAL = 3; /** * Initialize the applet. **/ public void init() { setLayout(new FlowLayout(FlowLayout.LEFT)); setBackground(backgroundColor); // set the background color image = createImage(size().width, size().height); graphics = image.getGraphics(); // make a graphics buffer // set up widgets stayChoice.setFont(MainFont); // add Stay/Revert menu stayChoice.addItem("Stay"); stayChoice.addItem("Revert"); add(stayChoice); modeChoice.setFont(MainFont); // add mode choice menu for( int i = 0; i < mode.length; i++ ) // add mode choices modeChoice.addItem(mode[i]); setMode(chosenMode); add(modeChoice); // add choice menu to applet shapeChoice.setFont(MainFont); // add shape choice menu for( int i = 0; i < shape.length; i++ ) // add shape choices shapeChoice.addItem(shape[i]); add(shapeChoice); // add shape menu to applet fillChoice.setFont(MainFont); fillChoice.addItem("NoFill"); fillChoice.addItem("Fill"); add(fillChoice); // add fill menu to applet colorChoice.setFont(MainFont); // add color choice menu addColor("Black", Color.black); // add colors to menu addColor("Red", Color.red); // and to color String table addColor("Blue", Color.blue); addColor("Orange", Color.orange); addColor("Yellow", Color.yellow); addColor("Green", Color.green); addColor("Cyan", Color.cyan); addColor("Magenta", Color.magenta); addColor("Gray", Color.gray); addColor("DarkGray", Color.darkGray); addColor("LightGray", Color.lightGray); addColor("Pink", Color.pink); addColor("White", Color.white); add(colorChoice); } // // Clear graphics // void clearGraphics() { graphics.clearRect(0, 0, size().width, size().height); } // // Draw all items in the window. // void display() { for( Enumeration e = allShapes.elements(); e.hasMoreElements(); ) { ((Shape)e.nextElement()).draw(graphics); } graphics.setColor(Color.black); graphics.drawRect(0, 0, size().width-1, size().height-1); // border repaint(); } // // Redraw all items in the window. // void redisplay() { clearGraphics(); display(); } // // Start drawing a new shape. // void startShape(int x, int y, Color color, boolean fillMode) { switch( chosenShape ) { case BOX: currentShape = new Box(x, y, color, fillMode); break; case OVAL: currentShape = new Oval(x, y, color, fillMode); break; case LINE: currentShape = new Line(x, y, color); break; case CURVE: currentShape = new Line(x, y, color); break; } redisplay(); currentShape.draw(graphics); repaint(); } // // Grow the current shape as the mouse is dragged. // void growShape(int x, int y) { redisplay(); currentShape.width = x - currentShape.x; // adjust size currentShape.height = y - currentShape.y; currentShape.draw(graphics); repaint(); } // // Complete the current shape, adding it to allShapes. // void finishShape() { currentShape.complete(); allShapes.addElement(currentShape); } // // Move the shape to another location. // void moveShape(int x, int y) { if( currentShape == null ) return; currentShape.moveBy(x - xDown, y - yDown); xDown = x; yDown = y; } // // Set the mode as specified and set the modeChoice menu // void setMode(int i) { switch( i ) { case GROUPALL: all(); group(2); i = chosenMode; // don't change mode for GroupAll break; case CUTALL: all(); group(1); if( currentShape == null ) break; cut(currentShape.x, currentShape.y); i = chosenMode; // don't change mode for CutAll redisplay(); break; } chosenMode = i; modeChoice.select(mode[i]); } // // Look for shape at a given position, setting currentShape to one found. // Return true if found, otherwise false. // boolean findShape(int x, int y) { for (int i = allShapes.size()-1; i >= 0; i-- ) // start at end of Vector { Shape s = (Shape)allShapes.elementAt(i); if( s.inside(x, y) ) { currentShape = s; return true; } } currentShape = null; return false; } // // Move current shape so that it is displayed first, selected last. // void lower() { if( currentShape == null ) return; allShapes.removeElement(currentShape); allShapes.insertElementAt(currentShape, 0); } // // Move current shape so that it is displayed last, selected first. // void raise() { if( currentShape == null ) return; allShapes.removeElement(currentShape); allShapes.addElement(currentShape); } // // Cut the selected shape. // void cut(int x, int y) { if( currentShape == null ) return; xCut = x; yCut = y; allShapes.removeElement(currentShape); justCut = currentShape; } // // Paste pastes the most recently cut shape, if any. // with x and y specifying an offset from that position void paste(int x, int y) { if( justCut == null ) return; currentShape = justCut.copy(); allShapes.addElement(currentShape); currentShape.moveBy(x, y); } // // Group surrounded shapes into one. // void group(int minGroupSize) { Vector members = establishGroup(currentShape); if( members.size() < minGroupSize ) { // a little warning: don't make groups of size 0 or 1 System.out.println("group of size " + members.size() + " not created"); currentShape = null; return; } // remove members in group from allShapes for( Enumeration e = members.elements(); e.hasMoreElements(); ) allShapes.removeElement(e.nextElement()); // make the group the current shape currentShape = new Group(currentShape.x, currentShape.y, this, members); allShapes.addElement(currentShape); } // // Ungroup selected shape if it is a group. // void ungroup() { if( currentShape == null ) return; if( !(currentShape instanceof Group) ) return; allShapes.removeElement(currentShape); Group group = (Group)currentShape; if( group.shared ) { // clone Vector first so as not to mess up sharing group = group.deepCopy(); } // add component members to allShapes, setting their coordinates for( Enumeration e = group.members.elements(); e.hasMoreElements();) { Shape s = ((Shape)e.nextElement()); s.moveBy(currentShape.x, currentShape.y); allShapes.addElement(s); } // no shape is current after ungrouping currentShape = null; } // // Make a Vector of the shapes contained inside shape s. // Vector establishGroup(Shape s) { Vector v = new Vector(); for( Enumeration e = allShapes.elements(); e.hasMoreElements(); ) { Shape t = (Shape)e.nextElement(); // check whether t is inside boundary of s if( t.x >= s.x && t.y >= s.y && t.x + t.width <= s.x + s.width && t.y + t.height <= s.y + s.height ) { v.addElement(t); // put t on new Vector } } return v; } // All makes a shape which surrounds all shapes (e.g. for grouping) void all() { currentShape = new Box(0, 0, groupingColor, false); currentShape.width = size().width; currentShape.height = size().height; } // // revert sets the mode back to Move if stayMode is false // void revert() { if( !stayMode ) { setMode(MOVE); } } /** * mouseDown is called when the mousebutton is depressed. **/ public boolean mouseDown(Event e, int x, int y) { x = (x/grid)*grid; y = (y/grid)*grid; xDown = x; yDown = y; switch( chosenMode ) { case MOVE: findShape(x, y); break; case DRAW: continuous = false; chosenShape = drawShape; if( chosenShape == CURVE ) { continuous = true; curve = new Vector(); } startShape(x, y, chosenColor, fillMode); break; case GROUP: chosenShape = BOX; startShape(x, y, groupingColor, false); break; case DESCRIBE: findShape(x, y); if( currentShape == null ) break; if( descriptionFrame == null ) { descriptionFrame = new DescriptionFrame(this); } // create description table descriptions = new DefTable(); // clear text of frame, then add descriptions descriptionFrame.clearText(); currentShape.describe(descriptionFrame.shape, this); descriptions.describe(descriptionFrame.defs, this); descriptionFrame.update(); break; } return true; } /** * mouseDrag is called when the mouse is dragged. **/ public boolean mouseDrag(Event e, int x, int y) { x = (x/grid)*grid; y = (y/grid)*grid; switch( chosenMode ) { case MOVE: moveShape(x, y); redisplay(); break; case DRAW: if( continuous ) { // finish this shape and start a new one, adding to curve growShape(x, y); finishShape(); curve.addElement(currentShape); startShape(x, y, chosenColor, fillMode); } else { growShape(x, y); } break; case GROUP: growShape(x, y); break; } return true; } /** * mouseUp is called when the mousebutton is released. **/ public boolean mouseUp(Event v, int x, int y) { x = (x/grid)*grid; y = (y/grid)*grid; switch( chosenMode ) { case MOVE: currentShape = null; break; case DRAW: finishShape(); if( continuous ) { // finish the curve curve.addElement(currentShape); currentShape = new Group(currentShape.x, currentShape.y, this, curve); // remove members in group from allShapes for( Enumeration e = curve.elements(); e.hasMoreElements(); ) allShapes.removeElement(e.nextElement()); allShapes.addElement(currentShape); } break; case CUT: if( findShape(x, y) ) cut(x, y); break; case PASTE: paste(x-xCut, y-yCut); break; case COPY: if( findShape(x, y) ) { cut(x, y); paste(0, 0); paste(grid, -grid); } break; case GROUP: group(2); break; case UNGROUP: findShape(x, y); ungroup(); break; case LOWER: findShape(x, y); lower(); findShape(x, y); break; case RAISE: findShape(x, y); raise(); findShape(x, y); break; } revert(); redisplay(); return true; } /** * action handles events targeted for buttons, etc. * These events do not go to mouseUp etc. **/ public boolean action(Event event, Object arg) { if( event.target == stayChoice ) { stayMode = ((String)arg == "Stay"); } else if( event.target == modeChoice ) { setMode(findString((String)arg, mode)); } else if( event.target == shapeChoice ) { drawShape = chosenShape = findString((String)arg, shape); setMode(DRAW); // setting shape assumes user wants to draw } else if( event.target == fillChoice ) { fillMode = ((String)arg == "Fill" ); setMode(DRAW); // setting fill assumes user wants to draw } else if( event.target == colorChoice ) { chosenColor = colorFromString((String)arg); setMode(DRAW); // setting color assumes user wants to draw } return super.action(event, arg); // Delegate all other actions to super. } /** * Set mode or perform action when user moves a slider **/ public boolean handleEvent(Event event) { if( event.target == gridSlider.scroll ) { grid = (int)gridSlider.getValue(); // grid slider return true; } return super.handleEvent(event); // Delegate all other actions to super. } /** * update is implicitly called by repaint() * It calls paint(Graphics) **/ public void update(Graphics g) { paint(g); } /** * paint(Graphics) is is called by update(Graphics) **/ public void paint(Graphics g) { g.drawImage(image, 0, 0, null); g.setColor(Color.black); } // create an entry in String-Color correspondence table void addColor(String name, Color color) { colorChoice.addItem(name); allColors.addElement(new Pair(name, color)); } // return a Color object given a String naming the color public Color colorFromString(String string) { for( Enumeration e = allColors.elements(); e.hasMoreElements(); ) { Pair p = (Pair)(e.nextElement()); if( string.equals(p.first) ) return (Color)(p.second); } System.err.println("*** color not understood: " + string + " using black"); return Color.black; } // return a String from a Color object public String colorToString(Color color) { for( Enumeration e = allColors.elements(); e.hasMoreElements(); ) { Pair p = (Pair)(e.nextElement()); if( color.equals(p.second) ) return (String)(p.first); } return color.toString(); } static int findString(String toBeFound, String array[]) { for( int i = 0; i < array.length; i++ ) { if( toBeFound.equals(array[i]) ) return i; } return -1; } } // ObjectDraw // // The abstract class Shape // abstract class Shape extends java.awt.Rectangle { Color color; // the color of the shape boolean filled; // whether the shape is filled or outline // Constructor; width and height are set when the shape size is finally // determined Shape(int x, int y, Color color, boolean filled) { this.x = x; this.y = y; this.color = color; this.filled = filled; } Shape(Shape orig) // Copy constructor: Create a copy of a shape { x = orig.x; y = orig.y; width = orig.width; height = orig.height; color = orig.color; filled = orig.filled; } // move shape by indicated increment void moveBy(int dx, int dy) { x += dx; y += dy; } // complete deals with negative widths and heights when a shape is finished // It is overridden for Box and Oval and is a no-op for Line and Group. void complete() { } abstract Shape copy(); // returns copy of shape abstract void draw(int x, int y, Graphics g); // draws with added offset x, y abstract void describe(int indentation, StringBuffer buff, ObjectDraw app); void draw(Graphics g) // draw with no offset { draw(0, 0, g); } void describe(StringBuffer buff, ObjectDraw app) // print with no indentation { describe(0, buff, app); } static void indent(int indentation, StringBuffer buff) { for( int i = 0; i < indentation; i++ ) buff.append(" "); } } // Shape // // A Box shape // class Box extends Shape { Box(int x, int y, Color color, boolean filled) { super(x, y, color, filled); } Box(Box box) // copy constructor { super((Shape)box); } Shape copy() { return new Box(this); } void draw(int x, int y, Graphics g) // draw a Box { // note that x, y, width, and height are local x = this.x + x; // change from offset to absolute y = this.y + y; int width = this.width; int height = this.height; if( width < 0 ) // accomodate negative widths and heights { width = -width; x -= width; } if( height < 0 ) { height = -height; y -= height; } g.setColor(color); if( filled ) g.fillRect(x, y, width, height); else g.drawRect(x, y, width, height); } // complete fixes width and height if either is negative. void complete() { if( width < 0 ) { x += width; width = -width; } if( height < 0 ) { y += height; height = -height; } } void describe(int indentation, StringBuffer buff, ObjectDraw app) { indent(indentation, buff); buff.append("(" + (filled ? "filled " : "") + "rectangle " + app.colorToString(color) + " " + x + " " + y + " " + width + " " + height + ")\n"); } } // Box // // an Oval Shape // class Oval extends Shape { Oval(int x, int y, Color color, boolean filled) { super(x, y, color, filled); } Oval(Oval oval) // copy constructor { super((Shape)oval); } Shape copy() { return new Oval(this); } void draw(int x, int y, Graphics g) // draw an Oval { // note that x, y, width, and height are local x = this.x + x; // change from offset to absolute y = this.y + y; int width = this.width; int height = this.height; if( width < 0 ) // accomodate negative widths and heights { width = -width; x -= width; } if( height < 0 ) { height = -height; y -= height; } g.setColor(color); if( filled ) g.fillOval(x, y, width, height); else g.drawOval(x, y, width, height); } // complete fixes width and height if either is negative. void complete() { if( width < 0 ) { x += width; width = -width; } if( height < 0 ) { y += height; height = -height; } } void describe(int indentation, StringBuffer buff, ObjectDraw app) { indent(indentation, buff); buff.append("(" + (filled ? "filled " : "") + "oval " + app.colorToString(color) + " " + x + " " + y + " " + width + " " + height + ")\n"); } } // Oval // // A straight line // class Line extends Shape { Line(int x, int y, Color color) { super(x, y, color, false); // not filled } Line(Line line) // copy constructor { super((Shape)line); } Shape copy() { return new Line(this); } void draw(int x, int y, Graphics g) // draw a Line { g.setColor(color); g.drawLine(this.x + x, this.y + y, this.x+x+width, this.y+y+height); } // inside is determined specially for a line, since negative width and // height are permitted. public boolean inside(int x, int y) { int x0 = this.x; int y0 = this.y; int width = this.width; int height = this.height; if( width < 0 ) { width = -width; x0 -= width; } if( height < 0 ) { height = -height; y0 -= height; } return x >= x0 && x < x0 + width && y >= y0 && y < y0 + height; } void describe(int indentation, StringBuffer buff, ObjectDraw app) { indent(indentation, buff); buff.append("(line " + app.colorToString(color) + " " + x + " " + y + " " + width + " " + height + ")\n"); } } // Line // // A Group is a set of shapes treated as a single Shape. // Groups may have Groups as elements, etc. // Groups ignore the color attribute of Shape // class Group extends Shape { Vector members; // members of the group boolean shared = false; // indicates whether members shared Group(int x, int y, ObjectDraw app, Vector shapes) { super(x, y, null, false); // no color, not filled members = initMembers(shapes, app.currentShape); } Group(Group group) // copy constructor { // note: does not run initMembers super((Shape)group); members = group.members; shared = group.shared = true; // indicate sharing } Shape copy() { return new Group(this); } Group deepCopy() { Group result = (Group)copy(); Vector old = result.members; result.members = new Vector(); for( Enumeration e = old.elements(); e.hasMoreElements(); ) { result.members.addElement(((Shape)e.nextElement()).copy()); } return result; } // initialize the members of a group by setting their coordinates to be // relative. Vector initMembers(Vector members, Shape wrapper) { Vector v = new Vector(); // find maxes and mins int xmin = x+wrapper.width, ymin = y+wrapper.height, xmax = -1, ymax = -1; for( Enumeration e = members.elements(); e.hasMoreElements(); ) { Shape s = (Shape)e.nextElement(); if( s.width >= 0 ) { xmin = Math.min(xmin, s.x); xmax = Math.max(xmax, s.x+s.width-1); } else { xmin = Math.min(xmin, s.x+s.width+1); xmax = Math.max(xmax, s.x); } if( s.height >= 0 ) { ymin = Math.min(ymin, s.y); ymax = Math.max(ymax, s.y+s.height-1); } else { ymin = Math.min(ymin, s.y+s.height+1); ymax = Math.max(ymax, s.y); } } x = xmin; y = ymin; width = xmax - xmin + 1; height = ymax - ymin + 1; // adjust origins for( Enumeration e = members.elements(); e.hasMoreElements(); ) { Shape s = (Shape)e.nextElement(); s.x -= xmin; s.y -= ymin; v.addElement(s); } return v; } // draw a Group by drawing its members, with offset void draw(int x, int y, Graphics g) { for( Enumeration e = members.elements(); e.hasMoreElements(); ) { ((Shape)e.nextElement()).draw(this.x + x, this.y + y, g); } } void describe(int indentation, StringBuffer buff, ObjectDraw app) { indent(indentation, buff); buff.append("(group " + x + " " + y); if( shared ) { int i = app.descriptions.getRef(members); buff.append(" (ref " + i +"))\n"); } else { buff.append("\n"); for( Enumeration e = members.elements(); e.hasMoreElements(); ) { ((Shape)(e.nextElement())).describe(indentation+2, buff, app); } indent(indentation, buff); buff.append(")\n"); } } } // Group // Pair is just a pair of two objects class Pair { Object first; Object second; Pair(Object first, Object second) { this.first = first; this.second = second; } } // Pair /** * A DefTable is a table of object definitions **/ class DefTable extends java.util.Vector { // getRef gets a table index for a Vector of shapes int getRef(Vector members) { int i = indexOf(members); if( i < 0 ) { i = size(); // definition not already in table, put there now addElement(members); } return i; } // show all definitions in table void describe(StringBuffer buff, ObjectDraw app) { int i = 0; for( Enumeration e = elements(); e.hasMoreElements(); ) { buff.append("(def " + i +"\n"); describe1(i++, (Vector)e.nextElement(), buff, app); buff.append(")\n"); } } // show one definition in table void describe1(int i, Vector V, StringBuffer buff, ObjectDraw app) { for( Enumeration e = V.elements(); e.hasMoreElements(); ) { ((Shape)e.nextElement()).describe(2, buff, app); } } } // DefTable /** * A DescriptionFrame is a frame containing text descriptions **/ class DescriptionFrame extends Frame { StringBuffer shape; // description of a shape StringBuffer defs; // auxiliary definitions TextArea textArea = new TextArea(); // text area within frame Button clearButton = new Button("Clear"); // button to clear description Choice fontChoice = new Choice(); static int fontSize[] = {8, 10, 12, 14, 18, 24}; // font sizes available DescriptionFrame(ObjectDraw app) // constructor { super("Description"); setLayout(new FlowLayout(FlowLayout.LEFT)); add(clearButton); // establish fontChoice menu for( int i = 0; i < fontSize.length; i++ ) fontChoice.addItem(new Integer(fontSize[i]).toString()); fontChoice.select("18"); textArea.setFont(new Font("Helvetica", Font.BOLD, 18)); fontChoice.setFont(app.MainFont); // Font of the menu, not selected font add(fontChoice); // establish textArea setBackground(app.backgroundColor); add(textArea); resize(app.size().width, 2*app.size().height/3); clearButton.setFont(app.MainFont); // Font of the button } void clearText() { shape = new StringBuffer(); defs = new StringBuffer(); } void update() { textArea.appendText(defs.toString()); textArea.appendText(shape.toString()); show(); } public boolean action(Event event, Object arg) { if( event.target == clearButton ) { textArea.setText(""); // clear description text } else if( event.target == fontChoice ) { textArea.setFont(new Font("Helvetica", Font.BOLD, new Integer((String)arg).intValue())); } return super.action(event, arg); // Delegate all other actions to super. } } // DescriptionFrame /** * A Slider is a combination of a Label, a Scrollbar, and a TextField. **/ class Slider { double Value; // Value maintained by the slider Label label; // label for the slider Scrollbar scroll; // the slider itself TextField field; // field showing value of slider /** * Create a slider. **/ Slider(Applet app, // Applet in which to place slider String lab, // Label for the slider Font font, // Font for Label and TextField double initial, // Initial value int min, // minimum value of the slider int max, // maximum int fieldSize) // number of characters in TextField { label = new Label(lab); label.setFont(font); app.add(label); // Add the label. scroll = new Scrollbar(Scrollbar.VERTICAL, (int)initial, 100, min, max); scroll.setFont(font); app.add(scroll); // Add the scrollbar. this.Value = initial; // Initialize the value. field = new TextField(fieldSize); field.setText(new Double(initial).toString()); field.setFont(font); field.setEditable(false); app.add(field); // Add the TextField. } /** * Set the value of the slider programmatically. **/ void setValue(double Value) { this.Value = Value; field.setText(new Double(Value).toString()); } /** * Update the value of the slider when the scrollbar is adjusted * and return the value (must be called by handleEvent). **/ double getValue() { setValue(scroll.getValue()); return Value; } } // class Slider