1 /*
   2  * Copyright (c) 2003, 2015, 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             robot.waitForIdle();
 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     class TestKFM extends DefaultKeyboardFocusManager {
 282         protected synchronized void enqueueKeyEvents(long after,
 283                                                      Component untilFocused)
 284         {
 285             super.enqueueKeyEvents(after, untilFocused);
 286 
 287             if (untilFocused == TestDialogTypeAhead.this.ok) {
 288                 TestDialogTypeAhead.this.robotSema.raise();
 289             }
 290         }
 291     }
 292 }// class TestDialogTypeAhead
 293 
 294 
 295 /****************************************************
 296  Standard Test Machinery
 297  DO NOT modify anything below -- it's a standard
 298   chunk of code whose purpose is to make user
 299   interaction uniform, and thereby make it simpler
 300   to read and understand someone else's test.
 301  ****************************************************/
 302 
 303 /**
 304  This is part of the standard test machinery.
 305  It creates a dialog (with the instructions), and is the interface
 306   for sending text messages to the user.
 307  To print the instructions, send an array of strings to Sysout.createDialog
 308   WithInstructions method.  Put one line of instructions per array entry.
 309  To display a message for the tester to see, simply call Sysout.println
 310   with the string to be displayed.
 311  This mimics System.out.println but works within the test harness as well
 312   as standalone.
 313  */
 314 
 315 class Sysout
 316 {
 317     private static TestDialog dialog;
 318 
 319     public static void createDialogWithInstructions( String[] instructions )
 320     {
 321         dialog = new TestDialog( new Frame(), "Instructions" );
 322         dialog.printInstructions( instructions );
 323         dialog.setVisible(true);
 324         println( "Any messages for the tester will display here." );
 325     }
 326 
 327     public static void createDialog( )
 328     {
 329         dialog = new TestDialog( new Frame(), "Instructions" );
 330         String[] defInstr = { "Instructions will appear here. ", "" } ;
 331         dialog.printInstructions( defInstr );
 332         dialog.setVisible(true);
 333         println( "Any messages for the tester will display here." );
 334     }
 335 
 336 
 337     public static void printInstructions( String[] instructions )
 338     {
 339         dialog.printInstructions( instructions );
 340     }
 341 
 342 
 343     public static void println( String messageIn )
 344     {
 345         dialog.displayMessage( messageIn );
 346     }
 347 
 348 }// Sysout  class
 349 
 350 /**
 351   This is part of the standard test machinery.  It provides a place for the
 352    test instructions to be displayed, and a place for interactive messages
 353    to the user to be displayed.
 354   To have the test instructions displayed, see Sysout.
 355   To have a message to the user be displayed, see Sysout.
 356   Do not call anything in this dialog directly.
 357   */
 358 class TestDialog extends Dialog
 359 {
 360 
 361     TextArea instructionsText;
 362     TextArea messageText;
 363     int maxStringLength = 80;
 364 
 365     //DO NOT call this directly, go through Sysout
 366     public TestDialog( Frame frame, String name )
 367     {
 368         super( frame, name );
 369         int scrollBoth = TextArea.SCROLLBARS_BOTH;
 370         instructionsText = new TextArea( "", 15, maxStringLength, scrollBoth );
 371         add( "North", instructionsText );
 372 
 373         messageText = new TextArea( "", 5, maxStringLength, scrollBoth );
 374         add("Center", messageText);
 375 
 376         pack();
 377 
 378         show();
 379     }// TestDialog()
 380 
 381     //DO NOT call this directly, go through Sysout
 382     public void printInstructions( String[] instructions )
 383     {
 384         //Clear out any current instructions
 385         instructionsText.setText( "" );
 386 
 387         //Go down array of instruction strings
 388 
 389         String printStr, remainingStr;
 390         for( int i=0; i < instructions.length; i++ )
 391         {
 392             //chop up each into pieces maxSringLength long
 393             remainingStr = instructions[ i ];
 394             while( remainingStr.length() > 0 )
 395             {
 396                 //if longer than max then chop off first max chars to print
 397                 if( remainingStr.length() >= maxStringLength )
 398                 {
 399                     //Try to chop on a word boundary
 400                     int posOfSpace = remainingStr.
 401                         lastIndexOf( ' ', maxStringLength - 1 );
 402 
 403                     if( posOfSpace <= 0 ) posOfSpace = maxStringLength - 1;
 404 
 405                     printStr = remainingStr.substring( 0, posOfSpace + 1 );
 406                     remainingStr = remainingStr.substring( posOfSpace + 1 );
 407                 }
 408                 //else just print
 409                 else
 410                 {
 411                     printStr = remainingStr;
 412                     remainingStr = "";
 413                 }
 414 
 415                 instructionsText.append( printStr + "\n" );
 416 
 417             }// while
 418 
 419         }// for
 420 
 421     }//printInstructions()
 422 
 423     //DO NOT call this directly, go through Sysout
 424     public void displayMessage( String messageIn )
 425     {
 426         messageText.append( messageIn + "\n" );
 427         System.out.println(messageIn);
 428     }
 429 
 430 }// TestDialog  class