Solution for
Programming Exercise 7.8


THIS PAGE DISCUSSES ONE POSSIBLE SOLUTION to the following exercise from this on-line Java textbook.

Exercise 7.8: Turn your applet from the previous exercise into a stand-alone application that runs in a Frame. This is not an easy exercise, since the material on frames in Section 7.7 is sort of sketchy. The information is there if you read carefully. (But I won't think too badly of you if you just look at the solution.)

As another improvement, you can add an "Undo" button. When the user clicks on the "Undo" button, the previous drawing operation will be undone. This just means returning to the image as it was before the drawing operation took place. This is easy to implement, as long as we allow just one operation to be undone. When the off-screen canvas, OSC, is created, make a second off-screen canvas, undoBuffer, of the same size. Before starting any drawing operation, copy the image from OSC to undoBuffer. You can do this with the commands

          Graphics undoGr = undoBuffer.getGraphics();
          undoGr.drawImage(OSC, 0, 0, null);

When the user clicks "Undo", just swap the values of OSC and undoBuffer and repaint. The previous image will appear on the screen. Clicking on "Undo" again will "undo the undo".

Here is a button that opens the paint program in its own window. (You don't have to write an applet like this one. Just open the frame in the program's main() routine.)


Discussion

Let's start by discussing the Undo operation, since there is not much more to say about it beyond what is said in the exercise. The off-screen canvas for the undo operation, undoBuffer, is created in the setupOSC() method where OSC is created. It is filled with the background color, just like OSC. (Ordinarily, the user won't see the initial contents of the undo buffer. But if the user clicks the Undo button before drawing anything, the undo buffer will be swapped in and displayed. So, it should have some well-defined content. Since, in fact, both OSC and undoBuffer are initially filled with the background color, the user won't see any difference when they are swapped.

In the actionPerformed method, in response to a click on the Undo button, the values of OSC and undoBuffer are swapped with the statements

         Image temp = OSC;
         OSC = undoBuffer;
         undoBuffer = temp;
         repaint();

The values stored in the variables OSC and undoBuffer are swapped. But remember that the values are only pointers to the Image objects. The Images themselves are not copied. After the swap, the Images themselves have not moved, but OSC and undoBuffer are pointing to different images. When the screen is repainted, and OSC is drawn to the screen, it's the former undo buffer that appears on the screen. The former OSC is now the undo buffer.

In the mousePressed() routine, before a drawing operation starts, the current image is saved in the undo buffer with the commands

        Graphics undoGr = undoBuffer.getGraphics();  
        undoGr.drawImage(OSC, 0, 0, null);
        undoGr.dispose();

(Note that the mysterious fourth parameter of the drawImage() routine -- the ImageObserver -- is set to null instead of the usual this. This value is OK when copying from one off-screen canvas to another.)

In the source code below, all changes having to do with the Undo command are shown in red.


In changing the program from an applet to a frame, the first line, which was

    public class KaleidaPaint extends Applet {

is changed to

    public class KaleidaPaintFrame extends Frame {

The init() method of the applet becomes the constructor of the frame. The body of the constructor is unchanged, except that the following lines are added at the end:

          addWindowListener( new WindowAdapter() {
                  public void windowClosing(WindowEvent evt) {
                          // If user clicks the window's close box, close
                          // the window.  Call System.exit(0) to end the program.
                     dispose();
                     System.exit(0);
                  }
               }
            );
          
          pack();              // Use the frame's preferred size;
          setLocation(40,60);  // Put the frame at (40,60) on the screen.
          show();              // Make the window visible.

The first statement here adds a window listener that closes the window when the user clicks on its close box. This is taken directly from Section 7.7. (Except that I've added the statement "System.exit(0);". The System.exit(0) is a good idea when the frame is being run as an independent program. It will end the program properly. Without it, the Java interpreter might keep running even though the window has been closed.)

The pack() command sets the frame to its preferred size. The show() command is important, since without it the frame would never appear on the screen. The show() command does not have to occur in the constructor. If the constructor does not show the frame, then the frame remains invisible until some other routine calls the frame's show() method. Sometimes it's good to be able to show and hide a frame at will.

Since I use the pack() command to set the size of the frame, all the components in the frame must define a reasonable preferred size. My original canvas class did not do this, so I added a preferredSize() method to the canvas class. This (and a getMinimumSize() method) is the only change to the canvas class. As an alternative, I could have set the size of the frame by calling its setSize() or setBounds() method, instead of pack().

The frame class has a main() routine that makes it possible to run the frame as a standalone program. The main() routine just needs to say "new KaleidaPaintFrame()" to create the frame:

       public static void main(String[] args) {
          new KaleidaPaintFrame();
       }

Note that the main() routine ends immediately after it opens the frame. It does not wait for the window to close. The main() routine ends even while the frame continues to exist. The main() routine is, in fact, a separate thread from the user interface thread that runs the frame. The main() routine could even go on to perform other tasks independently of the frame, such as interacting with the user via TextIO.

The only other change I made was to remove the getInsets() method from the original program. The frame has its own borders anyway. And, as explained in Section 7.7, using insets with frames requires some care.

This program could be improved by removing all the Button and Choice controls and adding a menu bar to provide the same functionality.


The Solution

Changes related to the Undo operation are shown in red.


    /*
       A simple program where the user can sketch curves and shapes in a 
       variety of colors on a variety of background colors.  The user selects
       a drawing color form a pop-up menu at the top of the
       applet.  If the user clicks "Set Background", the background
       color is set to the current drawing color and the drawing
       area is filled with that color.  If the user clicks "Clear",
       the drawing area is just filled with the current background color.
       
       The user selects the shape to draw from another pop-up menu at the
       top of the applet.  The user can draw free-hand curves, straight
       lines, and one of six different types of shapes.
       
       The user can specify symmetric drawing of three types, by selecting
       from a pop-up menu.  In 2-way Symmetry, the horizontal reflection of
       each figure is drawn along with the figure.  4-way Symmetry adds the
       vertical reflections.  8-way symmetry adds the diagonal reflections.
       This gives pretty kaleidascope-like pictures.
       
       An undo button can be used to undo just the most recent operation.
    
       The user's drawing is saved in an off-screen canvas, which is
       used to refresh the screen when repainting.  The picture is
       lost if the applet changes size, however.
       
       This file defines two classes, KaleidaPaint itself, and a non-public
       class, KaleidaCanvas, that is used by KaleidaPaint.
    */
    
    
    import java.awt.*;
    import java.awt.event.*;
    
    public class KaleidaPaintFrame extends Frame {
    
            // The main Frame class simply sets up the Frame.  Most of the
            // work is done in the canvas.  The canvas listens for events from
            // the buttons.
            
       public KaleidaPaintFrame() {
       
          setBackground(Color.gray);         // Background color of applet, shows
                                             //   in borders and between components.
          setLayout(new BorderLayout(3,3));  // Layout manager for applet.
          
          KaleidaFrameCanvas canvas = new KaleidaFrameCanvas();  // The drawing area.
          add(canvas,BorderLayout.CENTER);
          
          Panel buttonBar = new Panel();       // A panel to hold the buttons.
          buttonBar.setBackground(Color.lightGray);
          add(buttonBar, BorderLayout.SOUTH);
          
          Panel choiceBar = new Panel();       // A panel to hole the pop-up menus
          choiceBar.setBackground(Color.lightGray);
          add(choiceBar, BorderLayout.NORTH);
          
          Button fill = new Button("Set Background");  // The first button.
          fill.addActionListener(canvas);
          fill.setBackground(Color.lightGray);
          buttonBar.add(fill);
          
          Button clear = new Button("Clear");   // The second button.
          clear.addActionListener(canvas);
          clear.setBackground(Color.lightGray);
          buttonBar.add(clear);
          
          Button undo = new Button("Undo");    // Third button.
          undo.addActionListener(canvas);
          undo.setBackground(Color.lightGray);
          buttonBar.add(undo);
          
          Choice choice = new Choice();  // The pop-up menu of colors.
          choice.addItem("Black");
          choice.addItem("Red");
          choice.addItem("Green");
          choice.addItem("Blue");
          choice.addItem("Cyan");
          choice.addItem("Magenta");
          choice.addItem("Yellow");
          choice.addItem("White");
          choice.setBackground(Color.white);
          choiceBar.add(choice);
          
          Choice choice2 = new Choice();  // The pop-up menu of shapes.
          choice2.addItem("Curve");
          choice2.addItem("Straight Line");
          choice2.addItem("Rectangle");
          choice2.addItem("Oval");
          choice2.addItem("RoundRect");
          choice2.addItem("Filled Rectangle");
          choice2.addItem("Filled Oval");
          choice2.addItem("Filled RoundRect");
          choice2.setBackground(Color.white);
          choiceBar.add(choice2);
          
          Choice choice3 = new Choice();   // The pop-up menu of symmetry styles.
          choice3.add("No symmetry");
          choice3.add("2-way symmetry");
          choice3.add("4-way symmetry");
          choice3.add("8-way symmetry");
          choice3.setBackground(Color.white);
          choiceBar.add(choice3);
          
          canvas.colorChoice = choice;      // Canvas needs access to Choice menus,
          canvas.figureChoice = choice2;    //   so it can check it to find out what
          canvas.symmetryChoice = choice3;  //   color, shape, and symmetry to use.
          
          addWindowListener( new WindowAdapter() {
                  public void windowClosing(WindowEvent evt) {
                          // If user clicks the window's close box, close
                          // the window.  Call System.exit(0) to end
                          // the program.
                     dispose();
                     System.exit(0);
                  }
               }
            );
          
          pack();              // Use the frame's preferred size;
          setLocation(40,60);  // Put the frame at (40,60) on the screen.
          show();              // Make the window visible.
          
       }  // end constructor
    
       
       public static void main(String[] args) {
             // If this class is run as a stand-along program, then
             // it just opens a frame and ends.  (The frame remains
             // open until the user closes it.)
          new KaleidaPaintFrame();
       }
     
    
    } // end class KaleidaPaintFrame
    
    
    
    class KaleidaFrameCanvas extends Canvas 
                     implements MouseListener, MouseMotionListener, ActionListener {
                     
            // A KaleidaFrameCanvas lets the user use the mouse to draw colored curves
            // and shapes.  Depending on the setting of the symmetryChoice menu, some
            // reflections are also drawn.  The current color is specified by a pop-up
            // colorChoice.   The current shape is specified by another pop-up menu,
            // figureChoice.  The applet listens for action events from buttons
            // named "Clear", "Set Background", and "Undo".  The "Clear" button fills
            // the canvas with the current background color.  The "Set Background"
            // sets the background color to the current drawing color and
            // then clears.  The Undo button undoes just the previous operation.
                   
    
       private final static int 
                   BLACK = 0,
                   RED = 1,            // Some constants to make
                   GREEN = 2,          // the code more readable.
                   BLUE = 3,           // These numbers code for
                   CYAN = 4,           // the differnet drawing colors.
                   MAGENTA = 5,
                   YELLOW = 6,
                   WHITE = 7;
    
       Choice colorChoice;  // A Choice object, containing the possible drawing
                            // colors, which must be created by the applet.
                            
       private final static int
                  CURVE = 0,
                  LINE = 1,
                  RECT = 2,               // Some constants that code
                  OVAL = 3,               // for the different types of
                  ROUNDRECT = 4,          // figure the program can draw.
                  FILLED_RECT = 5,
                  FILLED_OVAL = 6,
                  FILLED_ROUNDRECT = 7;
                  
       Choice figureChoice;  // A Choice object containing the possible figures.
       
       private final static int
                  NO_SYMMETRY = 0,       // Some constants that code for 
                  SYMMETRY_2 = 1,        // the different symmetry styles.
                  SYMMETRY_4 = 2,        
                  SYMMETRY_8 = 3;
                  
       Choice symmetryChoice;  // A Choice object containing the possible 
                               //   symmetry styles.
                            
    
       /* Some variables used for double-buffering. */
       
       Image OSC;  // The off-screen canvas (created in setupOSC()).
       
       int widthOfOSC, heightOfOSC;  // Current width and height of OSC.  These
                                     // are checked against the size of the applet,
                                     // to detect any change in the applet's size.
                                     // If the size has changed, a new OSC is created.
                                     // The picture in the off-screen canvas is lost
                                     // when that happens.
                                     
       Image undoBuffer;  // Another buffer for saving a copy of OSC to
                          // implement the undo operation.  The OSC is copied
                          // to the undoBuffer at the start of a draw operation.
                          // If the user clicks Undo, the buffers are swapped.
       
    
       /* The following variables are used when the user is sketching a
          curve while dragging a mouse. */
    
       private int prevX, prevY;     // The previous location of the mouse.
       
       private int startX, startY;   // The starting position of the mouse.
                                     // (Not used for drawing curves.)
       
       private boolean dragging;     // This is set to true when the user is drawing.
       
       private int figure;    // What type of figure is being drawn.  This is
                              //    specified by the figureChoice menu.
                              
       private int symmetry;  // What type of symmetry style is being used.  This is
                              //    specified by the symmetryChoice menu.
       
       private Graphics graphicsForDrawing;  // A graphics context for the applet.
       
       private Graphics offscreenGraphics;   // A graphics context for the off-screen canvas.
                                             
    
       KaleidaFrameCanvas() {
              // Constructor.  When the canvas is first created, it is set to
              // listen for mouse events and mouse motion events from
              // itself.  The initial background color is white.
          addMouseListener(this);
          addMouseMotionListener(this);
          setBackground(Color.white);
       }
       
       
       private void setupOSC() {
            // This method is responsible for creating the off-screen canvas. 
            // It should be called before using the OSC.  It will make a new OSC if
            // the size of the canvas changes.  Also make an undo buffer.
            
          if (OSC == null || widthOfOSC != getSize().width || heightOfOSC != getSize().height) {
                 // Create the OSC, or make a new one if canvas size has changed.
                 
             OSC = null;  // (If OSC & undoBuffer already exist, this frees up the memory.)
             undoBuffer = null;
             
             OSC = createImage(getSize().width, getSize().height);
             widthOfOSC = getSize().width;
             heightOfOSC = getSize().height;
             Graphics OSG = OSC.getGraphics();  // Graphics context for drawing to OSC.
             OSG.setColor(getBackground());
             OSG.fillRect(0, 0, widthOfOSC, heightOfOSC);
             OSG.dispose();
             
             undoBuffer = createImage(widthOfOSC, heightOfOSC);
             OSG = undoBuffer.getGraphics();  // Graphics context for drawing to the undoBuffer.
             OSG.setColor(getBackground());
             OSG.fillRect(0, 0, widthOfOSC, heightOfOSC);
             OSG.dispose();
             
          }
          
       } // end setupOSC()
       
       
       private Graphics getOSG() {
             // Return a graphics context for drawing to the off-screen canvas.
             // A new canvas is created if necessary.  Note that the graphics
             // context should not be kept for any length of time, in case the
             // size of the canvas changes.
           setupOSC();
           return OSC.getGraphics();
       }
    
    
       private void clearOSC() {
             // Fill the off-screen canvas with the background color.
             // (First, save the current image in the undo buffer.)
           Graphics undoGr = undoBuffer.getGraphics();
           undoGr.drawImage(OSC, 0, 0, null);
           undoGr.dispose(); 
           Graphics OSG = OSC.getGraphics();
           OSG.setColor(getBackground());
           OSG.fillRect(0, 0, widthOfOSC, heightOfOSC);
           OSG.dispose();
       }
    
       public void update(Graphics g) {
             // Redefine update so it doesn't clear the canvas before calling paint().
          paint(g);
       }
       
    
       public void paint(Graphics g) {
            // Just copy the off-screen canvas to the screen.
          setupOSC();
          g.drawImage(OSC, 0, 0, this);
       }
    
    
       public void actionPerformed(ActionEvent evt) {
               // Respond when the user clicks on a button.
          String command = evt.getActionCommand();
          if (command.equals("Clear")) {
                 // Clear the off-screen canvas to current background color,
                 // then call repaint() so the screen is also cleared.
             clearOSC();
             repaint();
          }
          else if (command.equals("Set Background")) {
                 // Set background color, then clear.
             setBackground(getCurrentColor());
             clearOSC();
             repaint();
          }
          else if (command.equals("Undo")) {
                 // Swap the off-screen canvas with the undoBuffer and repaint.
              Image temp = OSC;
              OSC = undoBuffer;
              undoBuffer = temp;
              repaint();
          }
       }
    
    
       private Color getCurrentColor() {
                // Check the colorChoice menu to find the currently
                // selected color, and return the appropriate color
                // object.
          int currentColor = colorChoice.getSelectedIndex();
          switch (currentColor) {
             case BLACK:
                return Color.black;
             case RED:
                return Color.red;
             case GREEN:
                return Color.green;
             case BLUE:
                return Color.blue;
             case CYAN:
                return Color.cyan;
             case MAGENTA:
                return Color.magenta;
             case YELLOW:
                return Color.yellow;
             default:
                return Color.white;
          }
       }
       
       
       private void putFigure(Graphics g, int kind,
                                  int x1, int y1, int x2, int y2, boolean outlineOnly) {
             // Draws a figure with corners at (x1,y1) and (x2,y2).  The 
             // parameter "kind" codes for the type of figure to draw.  If the
             // figure is LINE, a line is drawn between the points.  For the
             // other shapes, we need the top-left corner, the width, and the
             // height of the shape.  We have to figure out whether x1 or x2
             // is the left edge of the shape and compute the width accordingly.
             // Similarly for y1 and y2.  If outlineOnly is true, then filled shapes
             // are drawn in outline only.
          if (kind == LINE)
             g.drawLine(x1, y1, x2, y2);
          else {
             int x, y, w, h;  // Top-left corner, width, and height.
             if (x2 >= x1) {  // x1 is left edge
                x = x1;
                w = x2 - x1;
             }
             else {          // x2 is left edge
                x = x2;
                w = x1 - x2;
             }
             if (y2 >= y1) {  // y1 is top edge
                y = y1;
                h = y2 - y1;
             }
             else {          // y2 is top edge.
                y = y2;
                h = y1 - y2;
             }
             switch (kind) {   // Draw the appropriate figure.
                case RECT:
                   g.drawRect(x, y, w, h);
                   break;
                case OVAL:
                   g.drawOval(x, y, w, h);
                   break;
                case ROUNDRECT:
                   g.drawRoundRect(x, y, w, h, 20, 20);
                   break;
                case FILLED_RECT:
                   if (outlineOnly)
                      g.drawRect(x, y, w, h);
                   else 
                      g.fillRect(x, y, w, h);
                   break;
                case FILLED_OVAL:
                   if (outlineOnly)
                      g.drawOval(x, y, w, h);
                   else 
                   g.fillOval(x, y, w, h);
                   break;
                case FILLED_ROUNDRECT:
                   if (outlineOnly)
                      g.drawRoundRect(x, y, w, h, 20, 20);
                   else 
                   g.fillRoundRect(x, y, w, h, 20, 20);
                   break;
             }
          }
       }
       
       
       private void putMultiFigure(Graphics g, int kind, int sym,
                                  int x1, int y1, int x2, int y2, boolean outlineOnly) {
             // Draws a figure with corners at (x1,y1) and (x2,y2) and possibly
             // some of its reflections.  The reflections that are drawn depend
             // on the sym parameter, which has one of the values NO_SYMMETRY,
             // SYMMETRY_2, SYMMETRY_4, or SYMMETRY_8.  The
             // parameter "kind" codes for the type of figure to draw.
          
          int width = getSize().width;
          int height = getSize().height;
          
          putFigure(g, kind, x1, y1, x2, y2, outlineOnly);
          
          if (sym >= SYMMETRY_2) {  // Draw the horizontal reflection.
             putFigure(g, kind, width - x1, y1, width - x2, y2, outlineOnly);
          }
    
          if (sym >= SYMMETRY_4) {  // Draw the two vertical reflections.
             putFigure(g, kind, x1, height - y1, x2, height - y2, outlineOnly);
             putFigure(g, kind, width - x1, height - y1, width - x2, height - y2, outlineOnly);
          }
          
          if (sym == SYMMETRY_8) {  // Draw the four diagonal reflections.
             int a1 = (int)( ((double)y1 / height) * width );
             int b1 = (int)( ((double)x1 / width) * height );
             int a2 = (int)( ((double)y2 / height) * width );
             int b2 = (int)( ((double)x2 / width) * height );
             putFigure(g, kind, a1, b1, a2, b2, outlineOnly);
             putFigure(g, kind, width - a1, b1, width - a2, b2, outlineOnly);
             putFigure(g, kind, a1, height - b1, a2, height - b2, outlineOnly);
             putFigure(g, kind, width - a1, height - b1, width - a2, height - b2, outlineOnly);
          }
    
       } // end putMultiFigure
       
    
       public void mousePressed(MouseEvent evt) {
               // This is called when the user presses the mouse on the
               // canvas.  This begins a draw operation in which the user
               // sketches a curve or draws a shape.
               
          if (dragging == true)  // Ignore mouse presses that occur
              return;            //    when user is already drawing a curve.
                                 //    (This can happen if the user presses
                                 //    two mouse buttons at the same time.)
    
          prevX = startX = evt.getX();  // Save mouse coordinates.
          prevY = startY = evt.getY();
          
          figure = figureChoice.getSelectedIndex(); // Type of figure being drawn.
          
          symmetry = symmetryChoice.getSelectedIndex(); // Symmetry style for drawing.
    
          graphicsForDrawing = getGraphics();  // For drawing on the screen.
          graphicsForDrawing.setColor(getCurrentColor());
    
          offscreenGraphics = getOSG();  // For drawing on the canvas.
          offscreenGraphics.setColor(getCurrentColor());
    
          // Copy the current image to the undo buffer.
          
          Graphics undoGr = undoBuffer.getGraphics();  
          undoGr.drawImage(OSC, 0, 0, null);
          undoGr.dispose();
    
          if (figure != CURVE) {
                 // Shapes are drawn in XOR mode so they can be erased by redrawing.
                 // Curves are drawn directly to both the screen and to the OSC.
             graphicsForDrawing.setXORMode(getBackground());
             putMultiFigure(graphicsForDrawing, figure, symmetry, 
                                              startX, startY, startX, startY, true);
          }
          
          dragging = true;  // Start drawing.
          
       } // end mousePressed()
       
    
       public void mouseReleased(MouseEvent evt) {
               // Called whenever the user releases the mouse button.
               // If the use was drawing a shape, we make the shape
               // permanent by drawing it in paintMode on both the screen
               // and the off-screen canvas (but only if the mouse is not
               // at its starting location.)  Then get rid of the graphics
               // contexts that we were using while dragging.
           if (dragging == false)
              return;  // Nothing to do because the user isn't drawing.
           dragging = false;
           if (figure != CURVE) {
                  // Erase the last XOR mode shape by redrawing it in XOR mode.
                  // Then, if the mouse is not back where it started from,
                  // Draw the final shape on both the screen and the off-screen
                  // canvas in paintMode.
              putMultiFigure(graphicsForDrawing, figure, symmetry, 
                                                      startX, startY, prevX, prevY, true);
              if (startX != prevX || startY != prevY) {
                 graphicsForDrawing.setPaintMode();
                 putMultiFigure(graphicsForDrawing, figure, symmetry, 
                                                           startX, startY, prevX, prevY, false);
                 putMultiFigure(offscreenGraphics, figure, symmetry, 
                                                           startX, startY, prevX, prevY, false);
              }
           }
           graphicsForDrawing.dispose();
           offscreenGraphics.dispose();
           graphicsForDrawing = null;
           offscreenGraphics = null;
       }
       
    
       public void mouseDragged(MouseEvent evt) {
                // Called whenever the user moves the mouse while a mouse button
                // is down.  If the user is drawing a curve, draw a segment of
                // the curve on both the screen and the off-screen canvas.
                // If the user is drawing a shape, erase the previous shape
                // by redrawing it (in XOR mode), then draw the new shape.
    
           if (dragging == false)
              return;  // Nothing to do because the user isn't drawing.
              
           int x = evt.getX();   // x-coordinate of mouse.
           int y = evt.getY();   // y=coordinate of mouse.
           
           if (figure == CURVE) {
                  // Draw the line on the applet and on the off-screen canvas.
              putMultiFigure(graphicsForDrawing, LINE, symmetry, prevX, prevY, x, y, false); 
              putMultiFigure(offscreenGraphics, LINE, symmetry, prevX, prevY, x, y, false);
           }
           else {  
                 // Erase previous figure and draw a new one using the new mouse position.
              putMultiFigure(graphicsForDrawing, figure, symmetry, 
                                                     startX, startY, prevX, prevY, true);
              putMultiFigure(graphicsForDrawing, figure, symmetry, 
                                                     startX, startY, x, y, true);
           }
           
           prevX = x;  // Save coords for the next call to mouseDragged or mouseReleased.
           prevY = y;
           
       } // end mouseDragged.
       
    
       public void mouseEntered(MouseEvent evt) { }   // Some empty routines.
       public void mouseExited(MouseEvent evt) { }    //    (Required by the MouseListener
       public void mouseClicked(MouseEvent evt) { }   //    and MouseMotionListener
       public void mouseMoved(MouseEvent evt) { }     //    interfaces).
       
       
       public Dimension getMinimumSize() {
          return new Dimension(250,200);
       }
       
       
       public Dimension getPreferredSize() {
           return new Dimension(320,320);
       }
      
                    
    } // end class KaleidaFrameCanvas


[ Exercises | Chapter Index | Main Index ]