1 /*
   2  * Copyright 2003-2007 Sun Microsystems, Inc.  All Rights Reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  20  * CA 95054 USA or visit www.sun.com if you need additional information or
  21  * have any questions.
  22  */
  23 
  24 /*
  25 test
  26 @bug 4799136
  27 @summary Tests that type-ahead for dialog works and doesn't block program
  28 @author  area=awt.focus
  29 @run applet TestDialogTypeAhead.html
  30 */
  31 
  32 // Note there is no @ in front of test above.  This is so that the
  33 //  harness will not mistake this file as a test file.  It should
  34 //  only see the html file as a test file. (the harness runs all
  35 //  valid test files, so it would run this test twice if this file
  36 //  were valid as well as the html file.)
  37 // Also, note the area= after Your Name in the author tag.  Here, you
  38 //  should put which functional area the test falls in.  See the
  39 //  AWT-core home page -> test areas and/or -> AWT team  for a list of
  40 //  areas.
  41 // Note also the 'TestDialogTypeAhead.html' in the run tag.  This should
  42 //  be changed to the name of the test.
  43 
  44 
  45 /**
  46  * TestDialogTypeAhead.java
  47  *
  48  * summary:
  49  */
  50 
  51 import java.applet.Applet;
  52 import java.awt.*;
  53 import java.lang.reflect.InvocationTargetException;
  54 import java.awt.event.*;
  55 import java.awt.peer.DialogPeer;
  56 import java.awt.peer.ComponentPeer;
  57 import java.lang.reflect.Method;
  58 import java.lang.reflect.Proxy;
  59 import java.lang.reflect.InvocationHandler;
  60 import java.lang.reflect.InvocationTargetException;
  61 import test.java.awt.regtesthelpers.Util;
  62 
  63 //Automated tests should run as applet tests if possible because they
  64 // get their environments cleaned up, including AWT threads, any
  65 // test created threads, and any system resources used by the test
  66 // such as file descriptors.  (This is normally not a problem as
  67 // main tests usually run in a separate VM, however on some platforms
  68 // such as the Mac, separate VMs are not possible and non-applet
  69 // tests will cause problems).  Also, you don't have to worry about
  70 // synchronisation stuff in Applet tests they way you do in main
  71 // tests...
  72 
  73 
  74 public class TestDialogTypeAhead extends Applet
  75 {
  76     //Declare things used in the test, like buttons and labels here
  77     static Frame f;
  78     static Button b;
  79     static Dialog d;
  80     static Button ok;
  81     static Semaphore pressSema = new Semaphore();
  82     static Semaphore robotSema = new Semaphore();
  83     static volatile boolean gotFocus = false;
  84     static Robot robot;
  85     public void init()
  86     {
  87         //Create instructions for the user here, as well as set up
  88         // the environment -- set the layout manager, add buttons,
  89         // etc.
  90 
  91         Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
  92                 public void eventDispatched(AWTEvent e) {
  93                     System.err.println(e.toString());
  94                 }
  95             }, AWTEvent.KEY_EVENT_MASK);
  96 
  97         this.setLayout (new BorderLayout ());
  98 
  99         f = new Frame("frame");
 100         b = new Button("press");
 101         d = new TestDialog(f, "dialog", true, robotSema);
 102         ok = new Button("ok");
 103         d.add(ok);
 104         d.pack();
 105 
 106         ok.addKeyListener(new KeyAdapter() {
 107                 public void keyPressed(KeyEvent e) {
 108                     System.err.println("OK pressed");
 109                     d.dispose();
 110                     f.dispose();
 111                     // Typed-ahead key events should only be accepted if
 112                     // they arrive after FOCUS_GAINED
 113                     if (gotFocus) {
 114                         pressSema.raise();
 115                     }
 116                 }
 117             });
 118         ok.addFocusListener(new FocusAdapter() {
 119                 public void focusGained(FocusEvent e) {
 120                     gotFocus = true;
 121                     System.err.println("Ok got focus");
 122                 }
 123             });
 124         f.add(b);
 125         f.pack();
 126         b.addActionListener(new ActionListener() {
 127                 public void actionPerformed(ActionEvent e) {
 128                     System.err.println("B pressed");
 129 
 130                     EventQueue.invokeLater(new Runnable() {
 131                             public void run() {
 132                                 waitTillShown(d);
 133                                 TestDialogTypeAhead.this.d.toFront();
 134                                 TestDialogTypeAhead.this.moveMouseOver(d);
 135                             }
 136                         });
 137 
 138                     d.setVisible(true);
 139                 }
 140             });
 141 
 142     }//End  init()
 143 
 144     public void start ()
 145     {
 146         //Get things going.  Request focus, set size, et cetera
 147         setSize (200,200);
 148         setVisible(true);
 149         validate();
 150         try {
 151             robot = new Robot();
 152         } catch (Exception e) {
 153             throw new RuntimeException("Can't create robot:" + e);
 154         }
 155 
 156         f.setVisible(true);
 157         waitTillShown(b);
 158         System.err.println("b is shown");
 159         f.toFront();
 160         moveMouseOver(f);
 161         waitForIdle();
 162         makeFocused(b);
 163         waitForIdle();
 164         System.err.println("b is focused");
 165 
 166         robot.keyPress(KeyEvent.VK_SPACE);
 167         robot.keyRelease(KeyEvent.VK_SPACE);
 168         try {
 169             robotSema.doWait(1000);
 170         } catch (InterruptedException ie) {
 171             throw new RuntimeException("Interrupted!");
 172         }
 173         robot.keyPress(KeyEvent.VK_SPACE);
 174         robot.keyRelease(KeyEvent.VK_SPACE);
 175         waitForIdle();
 176         try {
 177             pressSema.doWait(3000);
 178         } catch (InterruptedException ie) {
 179             throw new RuntimeException("Interrupted!");
 180         }
 181         if (!pressSema.getState()) {
 182             throw new RuntimeException("Type-ahead doesn't work");
 183         }
 184 
 185     }// start()
 186 
 187     private void moveMouseOver(Container c) {
 188         Point p = c.getLocationOnScreen();
 189         Dimension d = c.getSize();
 190         robot.mouseMove(p.x + (int)(d.getWidth()/2), p.y + (int)(d.getHeight()/2));
 191     }
 192     private void waitForIdle() {
 193         try {
 194             Toolkit.getDefaultToolkit().sync();
 195             sun.awt.SunToolkit.flushPendingEvents();
 196             EventQueue.invokeAndWait( new Runnable() {
 197                                             public void run() {
 198                                                 // dummy implementation
 199                                             }
 200                                         } );
 201         } catch(InterruptedException ite) {
 202             System.err.println("Robot.waitForIdle, non-fatal exception caught:");
 203             ite.printStackTrace();
 204         } catch(InvocationTargetException ine) {
 205             System.err.println("Robot.waitForIdle, non-fatal exception caught:");
 206             ine.printStackTrace();
 207         }
 208     }
 209 
 210     private void waitTillShown(Component c) {
 211         while (true) {
 212             try {
 213                 Thread.sleep(100);
 214                 c.getLocationOnScreen();
 215                 break;
 216             } catch (InterruptedException ie) {
 217                 ie.printStackTrace();
 218                 break;
 219             } catch (Exception e) {
 220             }
 221         }
 222     }
 223     private void makeFocused(Component comp) {
 224         if (comp.isFocusOwner()) {
 225             return;
 226         }
 227         final Semaphore sema = new Semaphore();
 228         final FocusAdapter fa = new FocusAdapter() {
 229                 public void focusGained(FocusEvent fe) {
 230                     sema.raise();
 231                 }
 232             };
 233         comp.addFocusListener(fa);
 234         comp.requestFocusInWindow();
 235         if (comp.isFocusOwner()) {
 236             return;
 237         }
 238         try {
 239             sema.doWait(3000);
 240         } catch (InterruptedException ie) {
 241             ie.printStackTrace();
 242         }
 243         comp.removeFocusListener(fa);
 244         if (!comp.isFocusOwner()) {
 245             throw new RuntimeException("Can't make " + comp + " focused, current owner is " + KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner());
 246         }
 247     }
 248 
 249     static class Semaphore {
 250         boolean state = false;
 251         int waiting = 0;
 252         public Semaphore() {
 253         }
 254         public synchronized void doWait() throws InterruptedException {
 255             if (state) {
 256                 return;
 257             }
 258             waiting++;
 259             wait();
 260             waiting--;
 261         }
 262         public synchronized void doWait(int timeout) throws InterruptedException {
 263             if (state) {
 264                 return;
 265             }
 266             waiting++;
 267             wait(timeout);
 268             waiting--;
 269         }
 270         public synchronized void raise() {
 271             state = true;
 272             if (waiting > 0) {
 273                 notifyAll();
 274             }
 275         }
 276         public synchronized boolean getState() {
 277             return state;
 278         }
 279     }
 280 
 281     // Fix for 6446952.
 282     // In the process of showing the dialog we have to catch peer.show() call
 283     // so that to trigger key events just before it gets invoked.
 284     // We base on the fact that a modal dialog sets type-ahead markers
 285     // before it calls 'show' on the peer.
 286     // Posting the key events before dialog.setVisible(true) would be actually not
 287     // good because it would be Ok to dispatch them to the current focus owner,
 288     // not to the dialog.
 289     class TestDialog extends Dialog {
 290         ComponentPeer origDialogPeer;
 291         ComponentPeer proxyInstPeer;
 292         Semaphore trigger;
 293 
 294         TestDialog(Frame owner, String title, boolean modal, Semaphore trigger) {
 295             super(owner, title, modal);
 296             this.trigger = trigger;
 297         }
 298 
 299         public void addNotify() {
 300             super.addNotify();
 301         }
 302     }
 303 }// class TestDialogTypeAhead
 304 
 305 
 306 /****************************************************
 307  Standard Test Machinery
 308  DO NOT modify anything below -- it's a standard
 309   chunk of code whose purpose is to make user
 310   interaction uniform, and thereby make it simpler
 311   to read and understand someone else's test.
 312  ****************************************************/
 313 
 314 /**
 315  This is part of the standard test machinery.
 316  It creates a dialog (with the instructions), and is the interface
 317   for sending text messages to the user.
 318  To print the instructions, send an array of strings to Sysout.createDialog
 319   WithInstructions method.  Put one line of instructions per array entry.
 320  To display a message for the tester to see, simply call Sysout.println
 321   with the string to be displayed.
 322  This mimics System.out.println but works within the test harness as well
 323   as standalone.
 324  */
 325 
 326 class Sysout
 327 {
 328     private static TestDialog dialog;
 329 
 330     public static void createDialogWithInstructions( String[] instructions )
 331     {
 332         dialog = new TestDialog( new Frame(), "Instructions" );
 333         dialog.printInstructions( instructions );
 334         dialog.setVisible(true);
 335         println( "Any messages for the tester will display here." );
 336     }
 337 
 338     public static void createDialog( )
 339     {
 340         dialog = new TestDialog( new Frame(), "Instructions" );
 341         String[] defInstr = { "Instructions will appear here. ", "" } ;
 342         dialog.printInstructions( defInstr );
 343         dialog.setVisible(true);
 344         println( "Any messages for the tester will display here." );
 345     }
 346 
 347 
 348     public static void printInstructions( String[] instructions )
 349     {
 350         dialog.printInstructions( instructions );
 351     }
 352 
 353 
 354     public static void println( String messageIn )
 355     {
 356         dialog.displayMessage( messageIn );
 357     }
 358 
 359 }// Sysout  class
 360 
 361 /**
 362   This is part of the standard test machinery.  It provides a place for the
 363    test instructions to be displayed, and a place for interactive messages
 364    to the user to be displayed.
 365   To have the test instructions displayed, see Sysout.
 366   To have a message to the user be displayed, see Sysout.
 367   Do not call anything in this dialog directly.
 368   */
 369 class TestDialog extends Dialog
 370 {
 371 
 372     TextArea instructionsText;
 373     TextArea messageText;
 374     int maxStringLength = 80;
 375 
 376     //DO NOT call this directly, go through Sysout
 377     public TestDialog( Frame frame, String name )
 378     {
 379         super( frame, name );
 380         int scrollBoth = TextArea.SCROLLBARS_BOTH;
 381         instructionsText = new TextArea( "", 15, maxStringLength, scrollBoth );
 382         add( "North", instructionsText );
 383 
 384         messageText = new TextArea( "", 5, maxStringLength, scrollBoth );
 385         add("Center", messageText);
 386 
 387         pack();
 388 
 389         show();
 390     }// TestDialog()
 391 
 392     //DO NOT call this directly, go through Sysout
 393     public void printInstructions( String[] instructions )
 394     {
 395         //Clear out any current instructions
 396         instructionsText.setText( "" );
 397 
 398         //Go down array of instruction strings
 399 
 400         String printStr, remainingStr;
 401         for( int i=0; i < instructions.length; i++ )
 402         {
 403             //chop up each into pieces maxSringLength long
 404             remainingStr = instructions[ i ];
 405             while( remainingStr.length() > 0 )
 406             {
 407                 //if longer than max then chop off first max chars to print
 408                 if( remainingStr.length() >= maxStringLength )
 409                 {
 410                     //Try to chop on a word boundary
 411                     int posOfSpace = remainingStr.
 412                         lastIndexOf( ' ', maxStringLength - 1 );
 413 
 414                     if( posOfSpace <= 0 ) posOfSpace = maxStringLength - 1;
 415 
 416                     printStr = remainingStr.substring( 0, posOfSpace + 1 );
 417                     remainingStr = remainingStr.substring( posOfSpace + 1 );
 418                 }
 419                 //else just print
 420                 else
 421                 {
 422                     printStr = remainingStr;
 423                     remainingStr = "";
 424                 }
 425 
 426                 instructionsText.append( printStr + "\n" );
 427 
 428             }// while
 429 
 430         }// for
 431 
 432     }//printInstructions()
 433 
 434     //DO NOT call this directly, go through Sysout
 435     public void displayMessage( String messageIn )
 436     {
 437         messageText.append( messageIn + "\n" );
 438         System.out.println(messageIn);
 439     }
 440 
 441 }// TestDialog  class