001    package javaeyes;
002    
003    import cs101.lang.*;
004    import cs101.util.*;
005    import java.awt.*;
006    import java.awt.event.*;
007    
008    /**
009     * The <code>JavaEyes</code> class is a Java-based implementation of the
010     * familiar "xeyes" program, available on many Unix-based systems. 
011     *
012     */
013    public class JavaEyes extends Frame implements Animate {
014    
015      // Class constants
016    
017      // Default background color
018      private static final Color DEFAULT_BG_COLOR = Color.white; 
019      // Default foreground color
020      private static final Color DEFAULT_FG_COLOR = Color.black; 
021    
022      // Non-varying instance data
023      private Color          bgColor;      // Actual background color
024      private Color          fgColor;      // Actual foreground color
025      private Panel          buttonPanel;  // A Panel used to hold the quit button
026      private Panel          eyesPanel;    // A Panel used to hold the eyes graphic
027      private Button         quitButton;   // The "quit" button
028    
029      // State variables:
030      private Point          mousePosition; // The current position of the mouse
031    
032      // Objects used to animate the eyes,
033      private AnimatorThread animator;     
034      private Image          offscreenImage;
035    
036    
037      /**
038       * Default class constructor.  Builds a <code>JavaEyes</code> object,
039       * and starts a <code>Thread</code> to animate the eyes in 
040       * response to the <code>mouseMoved</code> event. 
041       * Uses the default background and foreground colors.  
042       * 
043       */
044      public JavaEyes() { 
045        this(DEFAULT_BG_COLOR, DEFAULT_FG_COLOR);
046      }
047    
048      /**
049       * Class constructor.  Builds a <code>JavaEyes</code> object,
050       * and starts a <code>Thread</code> to animate the eyes in 
051       * response to the <code>mouseMoved</code> event. 
052       *
053       * @param bgColor  the background color of the <code>JavaEyes</code> window
054       * @param fgColor  the foreground color of the <code>JavaEyes</code> window
055       */
056      public JavaEyes(Color bgColor, Color fgColor) {    
057        super("JavaEyes");
058            
059        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++//
060        //                                                       //
061        //            Add components to the window               //
062        //                                                       //
063        // (In other words, add whatever buttons, icons, and     //
064        //  and graphics we'd like this window to contain).      //
065        //                                                       //
066        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++//
067            
068        // First, add a "quit" Button
069        //
070        this.buttonPanel = new Panel(); 
071        this.quitButton  = new Button("Quit");
072        this.buttonPanel.add(quitButton);
073        this.add("North", buttonPanel);
074    
075        // Next, add a Panel in which to display the "eyes" graphic
076        //
077        this.eyesPanel   = new Panel();
078        this.add("Center", eyesPanel);
079            
080        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++//
081        //                                                       //
082        //            Initialize the event handlers              //
083        //                                                       //
084        // (In other words, tell the window what to do when the  //
085        //  the user clicks a mouse on it, or types something    //
086        //  at the keyboard).                                    //
087        //                                                       //
088        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++//        
089                    
090        // Set up an event handler to listen for the mouseMoved event
091        // 
092        MouseMotionListener mouseMotionListener = new MouseMotionAdapter() {     
093          public void mouseMoved(MouseEvent e) {
094            JavaEyes.this.setMousePos(e.getX(),e.getY());
095          }
096        };
097                
098        this.addMouseMotionListener(mouseMotionListener);
099        this.eyesPanel.addMouseMotionListener(mouseMotionListener);
100        this.buttonPanel.addMouseMotionListener(mouseMotionListener);
101    
102        // Set up an event handler to detect when the user attempts
103        // to close the program by pressing the "quit" button
104        //
105                    
106        ActionListener actionListener = new ActionListener() {
107            public void actionPerformed(ActionEvent e) {
108                animator.stopExecution();
109                JavaEyes.this.dispose();
110                System.exit(0);
111            }
112        };
113                    
114        this.quitButton.addActionListener(actionListener);
115    
116        // Set up an event handler to detect when the user attempts
117        // to close the program by pressing the window's close button
118        //
119        WindowListener windowListener = new WindowAdapter() {
120            public void windowClosing(WindowEvent e) {
121                animator.stopExecution();
122                JavaEyes.this.dispose();
123                System.exit(0);
124            }
125        };
126                               
127        this.addWindowListener(windowListener);
128    
129        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++//
130        //                                                       //
131        //          Initialize the state of this class           //
132        //                                                       //
133        // (As a program runs, things about it will change.  For //
134        //  example, in the JavaEyes program, the position of    //
135        //  eyeballs changes.  This changeable data is called    //
136        //  "state".  When you start a program which uses state, //
137        //  it is generally necesssary to initialize it to some  //
138        //  default value.  In this case, we tell JavaEyes to    //
139        //  look at the top left corner of the window.  If we    //
140        //  failed to do this, the result of our program might   //
141        //  be unpredictable, and that is the last thing we      //
142        //  want!).                                              //
143        //                                                       //
144        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++//
145    
146            this.bgColor       = bgColor;
147            this.fgColor       = fgColor;
148        this.mousePosition = new Point(0,0);  // Tell the eyes where to look
149        this.setBackground(this.bgColor);     // Set the background color
150        this.setForeground(this.fgColor);     // Set the foreground color
151                    
152        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++//
153        //                                                       //
154        //                Standard window stuff                  //
155        //                                                       //
156        // (Don't worry about this for now).                     //
157        //                                                       //
158        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++//
159    
160        this.pack();
161        this.show();
162        this.setLocation(1,1);
163    
164        this.animator = new AnimatorThread(this, true, 0, 100);
165      }
166    
167      /**
168       * The standard entry point into a Java program.
169       *
170       * @param argv  command-line arguments passed to the program
171       */
172      public static void main(String argv[]) {
173        Color  bgColor = JavaEyes.DEFAULT_BG_COLOR;
174        Color  fgColor = JavaEyes.DEFAULT_FG_COLOR;
175        int    argnum  = 0;
176        String usage   = "Usage:  java JavaEyes [-fg <color>] [-bg <color>]";
177    
178        if (argv.length==0) { 
179          new JavaEyes(); 
180        } 
181        else { 
182          try { 
183            while (argnum < argv.length) { 
184              if (argv[argnum].equals("-bg")) { 
185                bgColor = Coerce.stringToColor(argv[++argnum]); 
186                argnum++;
187              }
188              else if (argv[argnum].equals("-fg")) { 
189                fgColor = Coerce.stringToColor(argv[++argnum]);
190                argnum++;
191              }
192              else { throw new Error(usage); }
193            }
194          }
195          catch (ArrayIndexOutOfBoundsException exc) { throw new Error(usage); }
196          
197          new JavaEyes(bgColor, fgColor);
198        }
199      }
200    
201      /**
202       * @deprecated  use <code>getPreferredSize()</code> instead.
203       */
204      public Dimension preferredSize() { 
205        return this.getPreferredSize(); 
206      } 
207            
208      /**
209       * Returns the preferred size of this component.  This method is
210       * implemented so that calling the <code>pack()</code> method on
211       * an instance of this class will not change the size of the
212       * window unpredictably. 
213       *
214       * @return the preferred size of this component. 
215       */
216      public Dimension getPreferredSize() { 
217        // You will comment the subsequent line out during your lab:
218        return new Dimension(400,300); 
219      }
220    
221      /**
222       * The <code>JavaEves</code> implementation of the 
223       * <code>cs101.lang.Animate</code> interface.  Continually updates
224       * the eyes graphic to point to the current location of the
225       * mouse pointer.  
226       */
227      public void act() {
228        this.updateEyes();
229      }
230    
231    
232      //+++++++++++++++++++++++++++++++++++++++++++++++++++++++//
233      //                                                       //
234      //                   Utility Methods                     //
235      //                                                       //
236      //+++++++++++++++++++++++++++++++++++++++++++++++++++++++//
237            
238      private synchronized void setMousePos(int x, int y) {
239        this.mousePosition.setLocation(x, y);
240      }
241    
242      private synchronized Point getMousePos() {
243        return this.mousePosition.getLocation();
244      }
245        
246         
247      /* 
248       * A lot of this is grungy math to position the eyes.
249       * This paints onto a graphic that is only 200 by 80 pixels, 
250       * but it is using the coordinates of the mouse in a much larger
251       * panel. Thus, we are going to be painting using coordinates
252       * within the (200,80) rectangle, but calculating the position 
253       * of the pupils using greater coordinates
254       */   
255      private void paintEyes(Graphics g) {
256    
257        // it's a 200 by 80 rectangle
258        int g_width=  200;
259        int g_height= 80;
260                    
261        // Get the dimensions of the central Panel
262        Dimension d= this.eyesPanel.getSize();
263        int w= d.width;
264        int h= d.height;
265    
266        // The offset of the (200,80) rectangle in the Panel
267        int x_offset= (w-g_width)/2;
268        int y_offset= h-g_height;
269                    
270        // Get the mouse position
271        Point p = this.getMousePos();
272    
273        // Set the color to the background color,
274        // and draw the background rectangle for the eyes
275        g.setColor(bgColor);
276        g.fillRect(0,0,g_width,g_height);
277    
278        // Set the color to the foreground color and draw the eyes
279        g.setColor(fgColor);
280    
281        // draw the outline of the eyes
282        g.drawOval(35,20,50,50);
283        // Set breakpoint on the subsequent line: 
284        g.drawOval(105,20,50,50);
285    
286        // the centers of the eyes, using the small rectangle coordinates
287        int eye1_x= 55 ;
288        int eye1_y= 40;
289        int eye2_x= 125;
290        int eye2_y= 40;
291    
292        // the centers of the eyes, with offsets, in the panel coordinates
293        int eye1_offset_x= eye1_x + x_offset;
294        int eye1_offset_y= eye1_y + y_offset;
295        int eye2_offset_x= eye2_x + x_offset;
296        int eye2_offset_y= eye2_y + y_offset;
297                    
298        int x1,y1,x2,y2,dist;
299    
300        dist= (int)Math.sqrt((p.x-eye1_offset_x)*(p.x-eye1_offset_x)+(p.y-eye1_offset_y)*(p.y-eye1_offset_y));
301        dist = (dist == 0) ? 1 : dist; // prevent arithmetic exception
302        x1=(int) ((p.x-eye1_offset_x) * 16 / dist);
303        y1=(int) ((p.y-eye1_offset_y) * 16 / dist);
304    
305        dist= (int)Math.sqrt((p.x-eye2_offset_x)*(p.x-eye2_offset_x)+(p.y-eye2_offset_y)*(p.y-eye2_offset_y));
306        dist = (dist == 0) ? 1 : dist; // prevent arithmetic exception
307        x2=(int) ((p.x-eye2_offset_x) * 16/dist);
308        y2=(int) ((p.y-eye2_offset_y) * 16/dist);
309    
310        // At last, we can draw the eyes!! 
311                    
312        // Set breakpoint on the subsequent line: 
313        g.fillOval(eye1_x+x1,eye1_y+y1,12,12);
314        g.fillOval(eye2_x+x2,eye2_y+y2,12,12);
315      }
316    
317      /* This implements a graphical technique called Double Buffering.
318       * You need not worry about what this does, although it will be
319       * explained later on in the course
320       */
321      private void updateEyes() {
322        // If no offscreen image has yet been created, create one.
323        if (this.offscreenImage==null) {
324          this.offscreenImage = this.createImage(200,80);
325        }
326    
327        paintEyes(this.offscreenImage.getGraphics());
328    
329        // paint the offscreen image onto the Panel
330        Dimension d = this.eyesPanel.getSize();
331        this.eyesPanel.getGraphics().drawImage(this.offscreenImage,d.width/2-100,d.height-80,this);
332        this.repaint();
333      }
334    }
335    
336    /*
337     * $Log: JavaEyes.java,v $
338     * Revision 1.5  2004/01/26 21:48:14  gus
339     * prevent arithmetic exception in paint method
340     *
341     * Revision 1.4  2004/01/26 21:32:39  gus
342     * Stop the animator before disposing the window to avoid NPEs
343     *
344     * Revision 1.3  2003/03/28 16:58:37  gus
345     * addign log comment
346     *
347     */