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 */