1 /*
   2  * Copyright (c) 2003, 2007, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * 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.awt.event.*;
  54 import java.lang.reflect.InvocationTargetException;
  55 import test.java.awt.regtesthelpers.Util;
  56 
  57 //Automated tests should run as applet tests if possible because they
  58 // get their environments cleaned up, including AWT threads, any
  59 // test created threads, and any system resources used by the test
  60 // such as file descriptors.  (This is normally not a problem as
  61 // main tests usually run in a separate VM, however on some platforms
  62 // such as the Mac, separate VMs are not possible and non-applet
  63 // tests will cause problems).  Also, you don't have to worry about
  64 // synchronisation stuff in Applet tests they way you do in main
  65 // tests...
  66 
  67 
  68 public class TestDialogTypeAhead extends Applet
  69 {
  70     //Declare things used in the test, like buttons and labels here
  71     static Frame f;
  72     static Button b;
  73     static Dialog d;
  74     static Button ok;
  75     static Semaphore pressSema = new Semaphore();
  76     static Semaphore robotSema = new Semaphore();
  77     static volatile boolean gotFocus = false;
  78     static Robot robot;
  79     public void init()
  80     {
  81         //Create instructions for the user here, as well as set up
  82         // the environment -- set the layout manager, add buttons,
  83         // etc.
  84 
  85         Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
  86                 public void eventDispatched(AWTEvent e) {
  87                     System.err.println(e.toString());
  88                 }
  89             }, AWTEvent.KEY_EVENT_MASK);
  90 
  91         KeyboardFocusManager.setCurrentKeyboardFocusManager(new TestKFM());
  92 
  93         this.setLayout (new BorderLayout ());
  94 
  95         f = new Frame("frame");
  96         b = new Button("press");
  97         d = new Dialog(f, "dialog", true);
  98         ok = new Button("ok");
  99         d.add(ok);
 100         d.pack();
 101 
 102         ok.addKeyListener(new KeyAdapter() {
 103                 public void keyPressed(KeyEvent e) {
 104                     System.err.println("OK pressed");
 105                     d.dispose();
 106                     f.dispose();
 107                     // Typed-ahead key events should only be accepted if
 108                     // they arrive after FOCUS_GAINED
 109                     if (gotFocus) {
 110                         pressSema.raise();
 111                     }
 112                 }
 113             });
 114         ok.addFocusListener(new FocusAdapter() {
 115                 public void focusGained(FocusEvent e) {
 116                     gotFocus = true;
 117                     System.err.println("Ok got focus");
 118                 }
 119             });
 120         f.add(b);
 121         f.pack();
 122         b.addActionListener(new ActionListener() {
 123                 public void actionPerformed(ActionEvent e) {
 124                     System.err.println("B pressed");
 125 
 126                     EventQueue.invokeLater(new Runnable() {
 127                             public void run() {
 128                                 waitTillShown(d);
 129                                 TestDialogTypeAhead.this.d.toFront();
 130                                 TestDialogTypeAhead.this.moveMouseOver(d);
 131                             }
 132                         });
 133 
 134                     d.setVisible(true);
 135                 }
 136             });
 137 
 138     }//End  init()
 139 
 140     public void start ()
 141     {
 142         //Get things going.  Request focus, set size, et cetera
 143         setSize (200,200);
 144         setVisible(true);
 145         validate();
 146         try {
 147             robot = new Robot();
 148         } catch (Exception e) {
 149             throw new RuntimeException("Can't create robot:" + e);
 150         }
 151 
 152         f.setVisible(true);
 153         waitTillShown(b);
 154         System.err.println("b is shown");
 155         f.toFront();
 156         moveMouseOver(f);
 157         waitForIdle();
 158         makeFocused(b);
 159         waitForIdle();
 160         System.err.println("b is focused");
 161 
 162         robot.keyPress(KeyEvent.VK_SPACE);
 163         robot.keyRelease(KeyEvent.VK_SPACE);
 164         try {
 165             robotSema.doWait(1000);
 166         } catch (InterruptedException ie) {
 167             throw new RuntimeException("Interrupted!");
 168         }
 169         if (!robotSema.getState()) {
 170             throw new RuntimeException("robotSema hasn't been triggered");
 171         }
 172 
 173         System.err.println("typing ahead");
 174         robot.keyPress(KeyEvent.VK_SPACE);
 175         robot.keyRelease(KeyEvent.VK_SPACE);
 176         waitForIdle();
 177         try {
 178             pressSema.doWait(3000);
 179         } catch (InterruptedException ie) {
 180             throw new RuntimeException("Interrupted!");
 181         }
 182         if (!pressSema.getState()) {
 183             throw new RuntimeException("Type-ahead doesn't work");
 184         }
 185 
 186     }// start()
 187 
 188     private void moveMouseOver(Container c) {
 189         Point p = c.getLocationOnScreen();
 190         Dimension d = c.getSize();
 191         robot.mouseMove(p.x + (int)(d.getWidth()/2), p.y + (int)(d.getHeight()/2));
 192     }
 193     private void waitForIdle() {
 194         try {
 195             Toolkit.getDefaultToolkit().sync();
 196             sun.awt.SunToolkit.flushPendingEvents();
 197             EventQueue.invokeAndWait( new Runnable() {
 198                                             public void run() {
 199                                                 // dummy implementation
 200                                             }
 201                                         } );
 202         } catch(InterruptedException ite) {
 203             System.err.println("Robot.waitForIdle, non-fatal exception caught:");
 204             ite.printStackTrace();
 205         } catch(InvocationTargetException ine) {
 206             System.err.println("Robot.waitForIdle, non-fatal exception caught:");
 207             ine.printStackTrace();
 208         }
 209     }
 210 
 211     private void waitTillShown(Component c) {
 212         while (true) {
 213             try {
 214                 Thread.sleep(100);
 215                 c.getLocationOnScreen();
 216                 break;
 217             } catch (InterruptedException ie) {
 218                 ie.printStackTrace();
 219                 break;
 220             } catch (Exception e) {
 221             }
 222         }
 223     }
 224     private void makeFocused(Component comp) {
 225         if (comp.isFocusOwner()) {
 226             return;
 227         }
 228         final Semaphore sema = new Semaphore();
 229         final FocusAdapter fa = new FocusAdapter() {
 230                 public void focusGained(FocusEvent fe) {
 231                     sema.raise();
 232                 }
 233             };
 234         comp.addFocusListener(fa);
 235         comp.requestFocusInWindow();
 236         if (comp.isFocusOwner()) {
 237             return;
 238         }
 239         try {
 240             sema.doWait(3000);
 241         } catch (InterruptedException ie) {
 242             ie.printStackTrace();
 243         }
 244         comp.removeFocusListener(fa);
 245         if (!comp.isFocusOwner()) {
 246             throw new RuntimeException("Can't make " + comp + " focused, current owner is " + KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner());
 247         }
 248     }
 249 
 250     static class Semaphore {
 251         boolean state = false;
 252         int waiting = 0;
 253         public Semaphore() {
 254         }
 255         public synchronized void doWait() throws InterruptedException {
 256             if (state) {
 257                 return;
 258             }
 259             waiting++;
 260             wait();
 261             waiting--;
 262         }
 263         public synchronized void doWait(int timeout) throws InterruptedException {
 264             if (state) {
 265                 return;
 266             }
 267             waiting++;
 268             wait(timeout);
 269             waiting--;
 270         }
 271         public synchronized void raise() {
 272             state = true;
 273             if (waiting > 0) {
 274                 notifyAll();
 275             }
 276         }
 277         public synchronized boolean getState() {
 278             return state;
 279         }
 280     }
 281 
 282     class TestKFM extends DefaultKeyboardFocusManager {
 283         protected synchronized void enqueueKeyEvents(long after,
 284                                                      Component untilFocused)
 285         {
 286             super.enqueueKeyEvents(after, untilFocused);
 287 
 288             if (untilFocused == TestDialogTypeAhead.this.ok) {
 289                 TestDialogTypeAhead.this.robotSema.raise();
 290             }
 291         }
 292     }
 293 }// class TestDialogTypeAhead
 294 
 295 
 296 /****************************************************
 297  Standard Test Machinery
 298  DO NOT modify anything below -- it's a standard
 299   chunk of code whose purpose is to make user
 300   interaction uniform, and thereby make it simpler
 301   to read and understand someone else's test.
 302  ****************************************************/
 303 
 304 /**
 305  This is part of the standard test machinery.
 306  It creates a dialog (with the instructions), and is the interface
 307   for sending text messages to the user.
 308  To print the instructions, send an array of strings to Sysout.createDialog
 309   WithInstructions method.  Put one line of instructions per array entry.
 310  To display a message for the tester to see, simply call Sysout.println
 311   with the string to be displayed.
 312  This mimics System.out.println but works within the test harness as well
 313   as standalone.
 314  */
 315 
 316 class Sysout
 317 {
 318     private static TestDialog dialog;
 319 
 320     public static void createDialogWithInstructions( String[] instructions )
 321     {
 322         dialog = new TestDialog( new Frame(), "Instructions" );
 323         dialog.printInstructions( instructions );
 324         dialog.setVisible(true);
 325         println( "Any messages for the tester will display here." );
 326     }
 327 
 328     public static void createDialog( )
 329     {
 330         dialog = new TestDialog( new Frame(), "Instructions" );
 331         String[] defInstr = { "Instructions will appear here. ", "" } ;
 332         dialog.printInstructions( defInstr );
 333         dialog.setVisible(true);
 334         println( "Any messages for the tester will display here." );
 335     }
 336 
 337 
 338     public static void printInstructions( String[] instructions )
 339     {
 340         dialog.printInstructions( instructions );
 341     }
 342 
 343 
 344     public static void println( String messageIn )
 345     {
 346         dialog.displayMessage( messageIn );
 347     }
 348 
 349 }// Sysout  class
 350 
 351 /**
 352   This is part of the standard test machinery.  It provides a place for the
 353    test instructions to be displayed, and a place for interactive messages
 354    to the user to be displayed.
 355   To have the test instructions displayed, see Sysout.
 356   To have a message to the user be displayed, see Sysout.
 357   Do not call anything in this dialog directly.
 358   */
 359 class TestDialog extends Dialog
 360 {
 361 
 362     TextArea instructionsText;
 363     TextArea messageText;
 364     int maxStringLength = 80;
 365 
 366     //DO NOT call this directly, go through Sysout
 367     public TestDialog( Frame frame, String name )
 368     {
 369         super( frame, name );
 370         int scrollBoth = TextArea.SCROLLBARS_BOTH;
 371         instructionsText = new TextArea( "", 15, maxStringLength, scrollBoth );
 372         add( "North", instructionsText );
 373 
 374         messageText = new TextArea( "", 5, maxStringLength, scrollBoth );
 375         add("Center", messageText);
 376 
 377         pack();
 378 
 379         show();
 380     }// TestDialog()
 381 
 382     //DO NOT call this directly, go through Sysout
 383     public void printInstructions( String[] instructions )
 384     {
 385         //Clear out any current instructions
 386         instructionsText.setText( "" );
 387 
 388         //Go down array of instruction strings
 389 
 390         String printStr, remainingStr;
 391         for( int i=0; i < instructions.length; i++ )
 392         {
 393             //chop up each into pieces maxSringLength long
 394             remainingStr = instructions[ i ];
 395             while( remainingStr.length() > 0 )
 396             {
 397                 //if longer than max then chop off first max chars to print
 398                 if( remainingStr.length() >= maxStringLength )
 399                 {
 400                     //Try to chop on a word boundary
 401                     int posOfSpace = remainingStr.
 402                         lastIndexOf( ' ', maxStringLength - 1 );
 403 
 404                     if( posOfSpace <= 0 ) posOfSpace = maxStringLength - 1;
 405 
 406                     printStr = remainingStr.substring( 0, posOfSpace + 1 );
 407                     remainingStr = remainingStr.substring( posOfSpace + 1 );
 408                 }
 409                 //else just print
 410                 else
 411                 {
 412                     printStr = remainingStr;
 413                     remainingStr = "";
 414                 }
 415 
 416                 instructionsText.append( printStr + "\n" );
 417 
 418             }// while
 419 
 420         }// for
 421 
 422     }//printInstructions()
 423 
 424     //DO NOT call this directly, go through Sysout
 425     public void displayMessage( String messageIn )
 426     {
 427         messageText.append( messageIn + "\n" );
 428         System.out.println(messageIn);
 429     }
 430 
 431 }// TestDialog  class