1 /*
   2  * Copyright (c) 2005, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 /*
  27  * A sample Assistive Technology which queries the JavaVM (if any) underneath
  28  * the mouse pointer to get the Java Accessibility information available
  29  * for the Java UI object underneath the mouse, using the Java
  30  * Accessibility Bridge, AccessBridge.DLL
  31  */
  32 
  33 /*
  34  TO DO
  35 
  36  - update copyright
  37  - add logging to JavaFerret docs
  38  - add /NO_LOGGING flag to JavaFerret docs
  39 
  40  */
  41 
  42 #include <windows.h>   // includes basic windows functionality
  43 #include <jni.h>
  44 
  45 #include "ferretResource.h"
  46 #include "AccessBridgeCalls.h"
  47 #include "AccessBridgeCallbacks.h"
  48 
  49 #include <stdio.h>
  50 #include <time.h>
  51 #include <string.h>
  52 
  53 #include "JavaFerret.h"
  54 #include "AccessInfo.h"
  55 
  56 #define TIMER_ID 1
  57 #define DISPLAY_INFO_MESSAGE WM_USER+1
  58 #define DISPLAY_HWND_INFO_MESSAGE WM_USER+2
  59 
  60 HWND theDialogWindow;
  61 HINSTANCE theInstance;
  62 BOOL theAccessBridgeLoadedFlag;
  63 
  64 HHOOK prevKbdHook;
  65 HHOOK prevMouseHook;
  66 
  67 BOOL updateMouse;
  68 BOOL updateF1;
  69 BOOL updateF2;
  70 
  71 BOOL trackMouse;
  72 BOOL trackFocus;
  73 BOOL trackCaret;
  74 BOOL trackShutdown;
  75 
  76 BOOL trackMenuSelected;
  77 BOOL trackMenuDeselected;
  78 BOOL trackMenuCanceled;
  79 
  80 BOOL trackPopupVisible;
  81 BOOL trackPopupInvisible;
  82 BOOL trackPopupCanceled;
  83 
  84 //BOOL trackPropertyChange;
  85 
  86 BOOL trackPropertyNameChange;
  87 BOOL trackPropertyDescriptionChange;
  88 BOOL trackPropertyStateChange;
  89 BOOL trackPropertyValueChange;
  90 BOOL trackPropertySelectionChange;
  91 BOOL trackPropertyTextChange;
  92 BOOL trackPropertyCaretChange;
  93 BOOL trackPropertyVisibleDataChange;
  94 BOOL trackPropertyChildChange;
  95 BOOL trackPropertyActiveDescendentChange;
  96 BOOL trackPropertyTableModelChange;
  97 
  98 
  99 FILE *logfile = NULL;
 100 
 101 /**
 102  * WinMain
 103  *
 104  */
 105 int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nShowCmd) {
 106     MSG msg;
 107 
 108     theInstance = hInst;
 109 
 110     updateF1 = FALSE;
 111     updateF2 = FALSE;
 112     updateMouse = FALSE;
 113 
 114     trackMouse = FALSE;
 115     trackShutdown = FALSE;
 116     trackFocus = FALSE;
 117     trackCaret = FALSE;
 118     trackMenuSelected = FALSE;
 119     trackMenuDeselected = FALSE;
 120     trackMenuCanceled = FALSE;
 121     trackPopupVisible = FALSE;
 122     trackPopupInvisible = FALSE;
 123     trackPopupCanceled = FALSE;
 124 
 125     trackPropertyNameChange = FALSE;
 126     trackPropertyDescriptionChange = FALSE;
 127     trackPropertyStateChange = FALSE;
 128     trackPropertyValueChange = FALSE;
 129     trackPropertySelectionChange = FALSE;
 130     trackPropertyTextChange = FALSE;
 131     trackPropertyCaretChange = FALSE;
 132     trackPropertyVisibleDataChange = FALSE;
 133     trackPropertyChildChange = FALSE;
 134     trackPropertyActiveDescendentChange = FALSE;
 135     trackPropertyTableModelChange = FALSE;
 136 
 137 
 138     theAccessBridgeLoadedFlag = FALSE;
 139 
 140 
 141     if (InitWindow(hInst)) {
 142         if (initializeAccessBridge() == TRUE) {
 143             theAccessBridgeLoadedFlag = TRUE;
 144             while (GetMessage(&msg, NULL, 0, 0)) {
 145                 TranslateMessage(&msg);
 146                 DispatchMessage(&msg);
 147             }
 148             if (theAccessBridgeLoadedFlag == TRUE) {
 149                 shutdownAccessBridge();
 150             }
 151         }
 152     }
 153     return(0);
 154 }
 155 
 156 char szAppName [] = "FERRETWINDOW";
 157 
 158 /**
 159  * Real(tm) MS-Windows window initialization
 160  *
 161  */
 162 BOOL InitWindow (HANDLE hInstance) {
 163     theDialogWindow = CreateDialog ((struct HINSTANCE__ *)hInstance,
 164                                     szAppName,
 165                                     NULL,
 166                                     (DLGPROC)FerretDialogProc);
 167 
 168     // If window could not be created, return "failure".
 169     if (!theDialogWindow)
 170         return (FALSE);
 171 
 172     if (logfile == null) {
 173         logfile = fopen(JAVA_FERRET_LOG, "w"); // overwrite existing log file
 174         logString(logfile, "Starting JavaFerret.exe %s\n", getTimeAndDate());
 175     }
 176 
 177     // Make the window visible, update its client area, & return "success".
 178     SetWindowText (theDialogWindow, "Java Ferret");
 179     ShowWindow (theDialogWindow, SW_SHOWNORMAL);
 180     UpdateWindow (theDialogWindow);
 181 
 182     return (TRUE);
 183 }
 184 
 185 /**
 186  * Display Accessible information about the object under the mouse
 187  */
 188 void displayAccessibleInfo(long vmID, AccessibleContext ac, int x, int y) {
 189     char buffer[HUGE_BUFSIZE];
 190 
 191     getAccessibleInfo(vmID, ac, x, y, buffer, (int)(sizeof(buffer)));
 192     displayAndLog(theDialogWindow, cFerretText, logfile, (char *)buffer);
 193 }
 194 
 195 /**
 196  * Display Java event info
 197  */
 198 void displayJavaEvent(long vmID, AccessibleContext ac, char *announcement) {
 199     char buffer[HUGE_BUFSIZE];
 200     char *bufOffset;
 201 
 202     strncpy(buffer, announcement, sizeof(buffer));
 203 
 204     bufOffset = (char *) (buffer + strlen(buffer));
 205     getAccessibleInfo(vmID, ac, -1, -1, bufOffset, (int)(sizeof(buffer) - strlen(buffer)));
 206     displayAndLog(theDialogWindow, cFerretText, logfile, (char *)buffer);
 207 }
 208 
 209 
 210 /**
 211  * Display Accessible propertyChange event info
 212  */
 213 void displayAccessiblePropertyChange(long vmID, AccessibleContext ac,
 214                                      char *announcement) {
 215     char buffer[HUGE_BUFSIZE];
 216     char *bufOffset;
 217 
 218     strncpy(buffer, announcement, sizeof(buffer));
 219 
 220     bufOffset = (char *) (buffer + strlen(buffer));
 221     getAccessibleInfo(vmID, ac, -1, -1, bufOffset, (int)(sizeof(buffer) - strlen(buffer)));
 222     displayAndLog(theDialogWindow, cFerretText, logfile, (char *)buffer);
 223 }
 224 
 225 
 226 /**
 227  * Update display under mouse when it moves
 228  */
 229 void echoMouseObject() {
 230     long vmID;
 231     AccessibleContext acParent;
 232     AccessibleContext ac;
 233     POINT p;
 234     HWND hwnd;
 235     RECT windowRect;
 236 
 237     GetCursorPos(&p);
 238     hwnd = WindowFromPoint(p);
 239     if (GetAccessibleContextFromHWND(hwnd, &vmID, &acParent)) {
 240         GetWindowRect(hwnd, &windowRect);
 241         // send the point in global coordinates; Java will handle it!
 242         if (GetAccessibleContextAt(vmID, acParent, (jint) p.x, (jint) p.y, &ac)) {
 243             displayAccessibleInfo(vmID, ac, p.x, p.y);          // can handle null
 244             ReleaseJavaObject(vmID, ac);
 245         }
 246     }
 247 }
 248 
 249 
 250 /**
 251  * Update display under HWND the mouse is in
 252  */
 253 void echoMouseHWNDObject() {
 254     long vmID;
 255     AccessibleContext ac;
 256     POINT p;
 257     HWND hwnd;
 258 
 259     GetCursorPos(&p);
 260     hwnd = WindowFromPoint(p);
 261 
 262     if (GetAccessibleContextFromHWND(hwnd, &vmID, &ac)) {
 263         displayAccessibleInfo(vmID, ac, 0, 0);          // can handle null
 264         ReleaseJavaObject(vmID, ac);
 265     }
 266 }
 267 
 268 /**
 269  * Display Accessible information about the object that has focus in
 270  * the topmost Java HWND
 271  *
 272  */
 273 void displayFocusedObject() {
 274     HWND hWnd;
 275     long vmID;
 276     AccessibleContext ac;
 277 
 278     hWnd = GetTopWindow(NULL);
 279     while (hWnd != NULL) {
 280         if (IsJavaWindow(hWnd)) {
 281             if (GetAccessibleContextWithFocus(hWnd, &vmID, &ac) == TRUE) {
 282                 displayAccessibleInfo(vmID, ac, 0, 0);
 283                 ReleaseJavaObject(vmID, ac);
 284             }
 285             return;
 286         } else {
 287             hWnd = GetNextWindow(hWnd, GW_HWNDNEXT);
 288         }
 289     }
 290 }
 291 
 292 /*
 293  * Handle notification of the Java application shutting down
 294  */
 295 void HandleJavaShutdown(long vmID) {
 296     char s[1024];
 297     wsprintf(s, "Java VM 0x%X terminated\r\n\r\n", vmID);
 298 
 299     displayJavaEvent(vmID, null, s); // intentially passing null AccessibleContext
 300     displayAndLog(theDialogWindow, cFerretText, logfile, (char *)s);
 301 }
 302 
 303 /**
 304  * Handle a FocusGained event
 305  */
 306 void HandleJavaFocusGained(long vmID, FocusEvent event, AccessibleContext ac) {
 307 
 308     char s[1024];
 309     wsprintf(s, "FocusGained\r\n\r\n");
 310 
 311     if (ac != (AccessibleContext) 0) {
 312         displayJavaEvent(vmID, ac, s);
 313     }
 314 
 315     ReleaseJavaObject(vmID, ac);        // must always release, to stave off memory leaks
 316     ReleaseJavaObject(vmID, event);     // must always release, to stave off memory leaks
 317 }
 318 
 319 /**
 320  * Handle a FocusLost event
 321  */
 322 void HandleJavaFocusLost(long vmID, FocusEvent event, AccessibleContext ac) {
 323 
 324     // NOTE: calling GetAccessibleContextWithFocus() after a FocusLost event
 325     //       would result in a null AccessibleContext being returned, since
 326     //       at that point, no object has the focus.  If the topmost window
 327     //       does not belong to a JavaVM, then no component within a JavaVM
 328     //       will have the focus (and again, GetAccessibleContextWithFocus()
 329     //       will return a null AccessibleContext).  You should always get
 330     //       a FocusLost event when a window not belonging to a JavaVM becomes
 331     //       topmost.
 332 
 333     char s[1024];
 334     wsprintf(s, "FocusLost\r\n\r\n");
 335 
 336     if (ac != (AccessibleContext) 0) {
 337         displayJavaEvent(vmID, ac, s);
 338     }
 339     /*
 340     if (ac != (AccessibleContext) 0) {
 341         displayAccessibleInfo(vmID, ac, 0, 0);
 342     }
 343     */
 344     ReleaseJavaObject(vmID, ac);        // must always release, to stave off memory leaks
 345     ReleaseJavaObject(vmID, event);     // must always release, to stave off memory leaks
 346 }
 347 
 348 /**
 349  * Handle a CaretUpdate event
 350  */
 351 void HandleJavaCaretUpdate(long vmID, CaretEvent event, AccessibleContext ac) {
 352     if (ac != (AccessibleContext) 0) {
 353         displayAccessibleInfo(vmID, ac, 0, 0);
 354     }
 355     ReleaseJavaObject(vmID, ac);        // must always release, to stave off memory leaks
 356     ReleaseJavaObject(vmID, event);     // must always release, to stave off memory leaks
 357 }
 358 
 359 /**
 360  * Handle a MouseClicked event
 361  */
 362 void HandleMouseClicked(long vmID, MouseEvent event, AccessibleContext ac) {
 363     if (ac != (AccessibleContext) 0) {
 364         displayAccessibleInfo(vmID, ac, 0, 0);
 365     }
 366     ReleaseJavaObject(vmID, ac);        // must always release, to stave off memory leaks
 367     ReleaseJavaObject(vmID, event);     // must always release, to stave off memory leaks
 368 }
 369 
 370 /**
 371  * Handle a MouseEntered event
 372  */
 373 void HandleMouseEntered(long vmID, MouseEvent event, AccessibleContext ac) {
 374     if (ac != (AccessibleContext) 0) {
 375         displayAccessibleInfo(vmID, ac, 0, 0);
 376     }
 377 
 378     ReleaseJavaObject(vmID, ac);        // must always release, to stave off memory leaks
 379     ReleaseJavaObject(vmID, event);     // must always release, to stave off memory leaks
 380 }
 381 
 382 /**
 383  * Handle a MouseExited event
 384  */
 385 void HandleMouseExited(long vmID, MouseEvent event, AccessibleContext ac) {
 386     if (ac != (AccessibleContext) 0) {
 387         displayAccessibleInfo(vmID, ac, 0, 0);
 388     }
 389     ReleaseJavaObject(vmID, ac);        // must always release, to stave off memory leaks
 390     ReleaseJavaObject(vmID, event);     // must always release, to stave off memory leaks
 391 }
 392 
 393 /**
 394  * Handle a MousePressed event
 395  */
 396 void HandleMousePressed(long vmID, MouseEvent event, AccessibleContext ac) {
 397     if (ac != (AccessibleContext) 0) {
 398         displayAccessibleInfo(vmID, ac, 0, 0);
 399     }
 400     ReleaseJavaObject(vmID, ac);        // must always release, to stave off memory leaks
 401     ReleaseJavaObject(vmID, event);     // must always release, to stave off memory leaks
 402 }
 403 
 404 /**
 405  * Handle a MouseReleased event
 406  */
 407 void HandleMouseReleased(long vmID, MouseEvent event, AccessibleContext ac) {
 408     if (ac != (AccessibleContext) 0) {
 409         displayAccessibleInfo(vmID, ac, 0, 0);
 410     }
 411     ReleaseJavaObject(vmID, ac);        // must always release, to stave off memory leaks
 412     ReleaseJavaObject(vmID, event);     // must always release, to stave off memory leaks
 413 }
 414 
 415 /**
 416  * Handle a MenuCanceled event
 417  */
 418 void HandleMenuCanceled(long vmID, MenuEvent event, AccessibleContext ac) {
 419     if (ac != (AccessibleContext) 0) {
 420         displayAccessibleInfo(vmID, ac, 0, 0);
 421     }
 422     ReleaseJavaObject(vmID, ac);        // must always release, to stave off memory leaks
 423     ReleaseJavaObject(vmID, event);     // must always release, to stave off memory leaks
 424 }
 425 
 426 /**
 427  * Handle a MenuDeselected event
 428  */
 429 void HandleMenuDeselected(long vmID, MenuEvent event, AccessibleContext ac) {
 430     if (ac != (AccessibleContext) 0) {
 431         displayAccessibleInfo(vmID, ac, 0, 0);
 432     }
 433     ReleaseJavaObject(vmID, ac);        // must always release, to stave off memory leaks
 434     ReleaseJavaObject(vmID, event);     // must always release, to stave off memory leaks
 435 }
 436 
 437 /**
 438  * Handle a MenuSelected event
 439  */
 440 void HandleMenuSelected(long vmID, MenuEvent event, AccessibleContext ac) {
 441     if (ac != (AccessibleContext) 0) {
 442         displayAccessibleInfo(vmID, ac, 0, 0);
 443     }
 444     ReleaseJavaObject(vmID, ac);        // must always release, to stave off memory leaks
 445     ReleaseJavaObject(vmID, event);     // must always release, to stave off memory leaks
 446 }
 447 
 448 /**
 449  * Handle a PopupMenuCanceled event
 450  */
 451 void HandlePopupMenuCanceled(long vmID, MenuEvent event, AccessibleContext ac) {
 452     if (ac != (AccessibleContext) 0) {
 453         displayAccessibleInfo(vmID, ac, 0, 0);
 454     }
 455     ReleaseJavaObject(vmID, ac);        // must always release, to stave off memory leaks
 456     ReleaseJavaObject(vmID, event);     // must always release, to stave off memory leaks
 457 }
 458 
 459 /**
 460  * Handle a PopupMenuWillBecomeInvisible event
 461  */
 462 void HandlePopupMenuWillBecomeInvisible(long vmID, MenuEvent event, AccessibleContext ac) {
 463     if (ac != (AccessibleContext) 0) {
 464         displayAccessibleInfo(vmID, ac, 0, 0);
 465     }
 466     ReleaseJavaObject(vmID, ac);        // must always release, to stave off memory leaks
 467     ReleaseJavaObject(vmID, event);     // must always release, to stave off memory leaks
 468 }
 469 
 470 /**
 471  * Handle a PopupMenuWillBecomeVisible event
 472  */
 473 void HandlePopupMenuWillBecomeVisible(long vmID, MenuEvent event, AccessibleContext ac) {
 474     if (ac != (AccessibleContext) 0) {
 475         displayAccessibleInfo(vmID, ac, 0, 0);
 476     }
 477     ReleaseJavaObject(vmID, ac);        // must always release, to stave off memory leaks
 478     ReleaseJavaObject(vmID, event);     // must always release, to stave off memory leaks
 479 }
 480 
 481 
 482 
 483 
 484 /**
 485  * Handle a HandlePropertyNameChange event
 486  */
 487 void HandlePropertyNameChange(long vmID, PropertyChangeEvent event, AccessibleContext ac,
 488                               wchar_t *oldName, wchar_t *newName) {
 489     char s[1024];
 490     wsprintf(s, "Name changed event: old = %ls; new = %ls\r\n\r\n", oldName, newName);
 491 
 492     if (ac != (AccessibleContext) 0) {
 493         displayAccessiblePropertyChange(vmID, ac, s);
 494     }
 495     ReleaseJavaObject(vmID, ac);        // must always release, to stave off memory leaks
 496     ReleaseJavaObject(vmID, event);     // must always release, to stave off memory leaks
 497 }
 498 
 499 /**
 500  * Handle a HandlePropertyDescriptionChange event
 501  */
 502 void HandlePropertyDescriptionChange(long vmID, PropertyChangeEvent event, AccessibleContext ac,
 503                                      wchar_t *oldDescription, wchar_t *newDescription) {
 504     char s[1024];
 505     wsprintf(s, "Description changed event: old = %ls; new = %ls\r\n\r\n", oldDescription, newDescription);
 506 
 507     if (ac != (AccessibleContext) 0) {
 508         displayAccessiblePropertyChange(vmID, ac, s);
 509     }
 510     ReleaseJavaObject(vmID, ac);        // must always release, to stave off memory leaks
 511     ReleaseJavaObject(vmID, event);     // must always release, to stave off memory leaks
 512 }
 513 
 514 /**
 515  * Handle a HandlePropertyStateChange event
 516  */
 517 void HandlePropertyStateChange(long vmID, PropertyChangeEvent event, AccessibleContext ac,
 518                                wchar_t *oldState, wchar_t *newState) {
 519     char s[1024];
 520     wsprintf(s, "State changed event: old = %ls; new = %ls\r\n\r\n", oldState, newState);
 521 
 522     if (ac != (AccessibleContext) 0) {
 523         displayAccessiblePropertyChange(vmID, ac, s);
 524     }
 525     ReleaseJavaObject(vmID, ac);        // must always release, to stave off memory leaks
 526     ReleaseJavaObject(vmID, event);     // must always release, to stave off memory leaks
 527 }
 528 
 529 /**
 530  * Handle a HandlePropertyValueChange event
 531  */
 532 void HandlePropertyValueChange(long vmID, PropertyChangeEvent event, AccessibleContext ac,
 533                                wchar_t *oldValue, wchar_t *newValue) {
 534     char s[1024];
 535     wsprintf(s, "Value changed event: old = %ls; new = %ls\r\n\r\n", oldValue, newValue);
 536 
 537     if (ac != (AccessibleContext) 0) {
 538         displayAccessiblePropertyChange(vmID, ac, s);
 539     }
 540     ReleaseJavaObject(vmID, ac);        // must always release, to stave off memory leaks
 541     ReleaseJavaObject(vmID, event);     // must always release, to stave off memory leaks
 542 }
 543 
 544 /**
 545  * Handle a HandlePropertySelectionChange event
 546  */
 547 void HandlePropertySelectionChange(long vmID, PropertyChangeEvent event, AccessibleContext ac) {
 548     if (ac != (AccessibleContext) 0) {
 549         displayAccessiblePropertyChange(vmID, ac, "Selection changed event\r\n\r\n");
 550     }
 551     ReleaseJavaObject(vmID, ac);        // must always release, to stave off memory leaks
 552     ReleaseJavaObject(vmID, event);     // must always release, to stave off memory leaks
 553 }
 554 
 555 /**
 556  * Handle a HandlePropertyTextChange event
 557  */
 558 void HandlePropertyTextChange(long vmID, PropertyChangeEvent event, AccessibleContext ac) {
 559     if (ac != (AccessibleContext) 0) {
 560         displayAccessiblePropertyChange(vmID, ac, "Text changed event\r\n\r\n");
 561     }
 562     ReleaseJavaObject(vmID, ac);        // must always release, to stave off memory leaks
 563     ReleaseJavaObject(vmID, event);     // must always release, to stave off memory leaks
 564 }
 565 
 566 /**
 567  * Handle a HandlePropertyCaretChange event
 568  */
 569 void HandlePropertyCaretChange(long vmID, PropertyChangeEvent event, AccessibleContext ac,
 570                                int oldPosition, int newPosition) {
 571     char s[1024];
 572 
 573     wsprintf(s, "Caret changed event: oldPosition = %d; newPosition = %d\r\n\r\n", oldPosition, newPosition);
 574 
 575     if (ac != (AccessibleContext) 0) {
 576         displayAccessiblePropertyChange(vmID, ac, s);
 577     }
 578     ReleaseJavaObject(vmID, ac);        // must always release, to stave off memory leaks
 579     ReleaseJavaObject(vmID, event);     // must always release, to stave off memory leaks
 580 }
 581 
 582 /**
 583  * Handle a HandlePropertyVisibleDataChange event
 584  */
 585 void HandlePropertyVisibleDataChange(long vmID, PropertyChangeEvent event, AccessibleContext ac) {
 586     if (ac != (AccessibleContext) 0) {
 587         displayAccessiblePropertyChange(vmID, ac, "VisibleData changed event\r\n\r\n");
 588     }
 589     ReleaseJavaObject(vmID, ac);        // must always release, to stave off memory leaks
 590     ReleaseJavaObject(vmID, event);     // must always release, to stave off memory leaks
 591 }
 592 
 593 /**
 594  * Handle a HandlePropertyChildChange event
 595  */
 596 void HandlePropertyChildChange(long vmID, PropertyChangeEvent event, AccessibleContext ac,
 597                                JOBJECT64 oldChild, JOBJECT64 newChild) {
 598     char buffer[HUGE_BUFSIZE];
 599     char *bufOffset;
 600 
 601     sprintf(buffer, "Child property changed event:\r\n=======================\r\n\r\n");
 602 
 603     if (oldChild != 0) {
 604         strncat(buffer, "Old Accessible Child info:\r\n\r\n", sizeof(buffer));
 605         bufOffset = (char *) (buffer + strlen(buffer));
 606         getAccessibleInfo(vmID, oldChild, 0, 0, bufOffset, (int)(sizeof(buffer) - strlen(buffer)));
 607         strncat(buffer, "\r\n\r\n", sizeof(buffer));
 608     }
 609 
 610     if (newChild != 0) {
 611         strncat(buffer, "New Accessible Child info:\r\n\r\n", sizeof(buffer));
 612         bufOffset = (char *) (buffer + strlen(buffer));
 613         getAccessibleInfo(vmID, newChild, 0, 0, bufOffset, (int)(sizeof(buffer) - strlen(buffer)));
 614         strncat(buffer, "\r\n\r\n", sizeof(buffer));
 615     }
 616 
 617     if (ac != (AccessibleContext) 0) {
 618         displayAccessiblePropertyChange(vmID, ac, buffer);
 619     }
 620 
 621     ReleaseJavaObject(vmID, ac);        // must always release, to stave off memory leaks
 622     ReleaseJavaObject(vmID, event);     // must always release, to stave off memory leaks
 623     ReleaseJavaObject(vmID, oldChild);  // must always release, to stave off memory leaks
 624     ReleaseJavaObject(vmID, newChild);  // must always release, to stave off memory leaks
 625 }
 626 
 627 /**
 628  * Handle a HandlePropertyActiveDescendentChange event
 629  */
 630 void HandlePropertyActiveDescendentChange(long vmID, PropertyChangeEvent event,
 631                                           AccessibleContext ac,
 632                                           JOBJECT64 oldActiveDescendent,
 633                                           JOBJECT64 newActiveDescendent) {
 634     char buffer[HUGE_BUFSIZE];
 635 
 636     sprintf(buffer, "ActiveDescendent property changed event:\r\n=======================\r\n\r\n");
 637 
 638 #ifdef _notdef
 639     char *bufOffset;
 640     if (oldActiveDescendent != 0) {
 641         strncat(buffer, "Old Accessible ActiveDescendent info:\r\n\r\n", sizeof(buffer));
 642         bufOffset = (char *) (buffer + strlen(buffer));
 643         getAccessibleInfo(vmID, oldActiveDescendent, 0, 0, bufOffset, (int)(sizeof(buffer) - strlen(buffer)));
 644         strncat(buffer, "\r\n\r\n", sizeof(buffer));
 645     }
 646 
 647     if (newActiveDescendent != 0) {
 648         strncat(buffer, "New Accessible ActiveDescendent info:\r\n\r\n", sizeof(buffer));
 649         bufOffset = (char *) (buffer + strlen(buffer));
 650         getAccessibleInfo(vmID, newActiveDescendent, 0, 0, bufOffset, (int)(sizeof(buffer) - strlen(buffer)));
 651         strncat(buffer, "\r\n\r\n", sizeof(buffer));
 652     }
 653 #endif _notdef
 654 
 655 
 656     if (newActiveDescendent != (AccessibleContext) 0) {
 657         displayAccessiblePropertyChange(vmID, newActiveDescendent, buffer);
 658     }
 659 
 660     ReleaseJavaObject(vmID, ac);        // must always release, to stave off memory leaks
 661     ReleaseJavaObject(vmID, event);     // must always release, to stave off memory leaks
 662     ReleaseJavaObject(vmID, oldActiveDescendent);       // must always release, to stave off memory leaks
 663     ReleaseJavaObject(vmID, newActiveDescendent);       // must always release, to stave off memory leaks
 664 }
 665 
 666 
 667 /**
 668  * Handle a HandlePropertyTableModelChange event
 669  */
 670 void HandlePropertyTableModelChange(long vmID, PropertyChangeEvent event,
 671                                     AccessibleContext ac,
 672                                     wchar_t *oldValue, wchar_t *newValue) {
 673 
 674     char s[1024];
 675     wsprintf(s, "Table Model Change: old = %ls; new = %ls\r\n\r\n", oldValue, newValue);
 676 
 677     if (ac != (AccessibleContext) 0) {
 678         displayAccessiblePropertyChange(vmID, ac, s);
 679     }
 680     ReleaseJavaObject(vmID, ac);        // must always release, to stave off memory leaks
 681     ReleaseJavaObject(vmID, event);     // must always release, to stave off memory leaks
 682 }
 683 
 684 
 685 
 686 #define DOWN_UP_FLAG 1<<31
 687 
 688 void CALLBACK TimerProc(HWND hWnd, UINT uTimerMsg, UINT uTimerID, DWORD dwTime) {
 689     // when mouse settles from movement
 690     KillTimer(hWnd, uTimerID);
 691     if (updateMouse == TRUE) {
 692         PostMessage(theDialogWindow, DISPLAY_INFO_MESSAGE, 0, 0);
 693     }
 694 }
 695 
 696 LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam) {
 697     // on mouse-up of F1
 698     if (code < 0) {
 699         CallNextHookEx(prevKbdHook, code, wParam, lParam);
 700     } else if (wParam == VK_F1 && lParam & DOWN_UP_FLAG) {
 701         PostMessage(theDialogWindow, DISPLAY_INFO_MESSAGE, wParam, lParam);
 702     } else if (wParam == VK_F2 && lParam & DOWN_UP_FLAG) {
 703         PostMessage(theDialogWindow, DISPLAY_HWND_INFO_MESSAGE, wParam, lParam);
 704     }
 705     return 0;
 706 }
 707 
 708 LRESULT CALLBACK MouseProc(int code, WPARAM wParam, LPARAM lParam) {
 709     // when mouse settles from movement
 710     if (code < 0) {
 711         CallNextHookEx(prevMouseHook, code, wParam, lParam);
 712     } else {
 713         // reset the timer every time the mouse moves
 714         KillTimer(theDialogWindow, TIMER_ID);
 715         SetTimer(theDialogWindow, TIMER_ID, 1000, (TIMERPROC)TimerProc);
 716     }
 717     return 0;
 718 }
 719 
 720 void exitJavaFerret(HWND hWnd) {
 721     EndDialog(hWnd, TRUE);
 722     PostQuitMessage (0);
 723 }
 724 
 725 #define INSTALL_EVENT_LISTENER(toggleVal, setFP, handler)   \
 726     if (toggleVal) {                                        \
 727         setFP(handler);                                     \
 728     }
 729 
 730 void reinstallEventListeners() {
 731     INSTALL_EVENT_LISTENER(trackMouse, SetMouseEntered, HandleMouseEntered);
 732     INSTALL_EVENT_LISTENER(trackShutdown, SetJavaShutdown, HandleJavaShutdown);
 733     INSTALL_EVENT_LISTENER(trackFocus, SetFocusGained, HandleJavaFocusGained);
 734     INSTALL_EVENT_LISTENER(trackFocus, SetFocusGained, HandleJavaFocusGained);
 735     INSTALL_EVENT_LISTENER(trackCaret, SetCaretUpdate, HandleJavaCaretUpdate);
 736 
 737     INSTALL_EVENT_LISTENER(trackMenuSelected, SetMenuSelected, HandleMenuSelected);
 738     INSTALL_EVENT_LISTENER(trackMenuDeselected, SetMenuDeselected, HandleMenuDeselected);
 739     INSTALL_EVENT_LISTENER(trackMenuCanceled, SetMenuCanceled, HandleMenuCanceled);
 740 
 741     INSTALL_EVENT_LISTENER(trackPopupVisible, SetPopupMenuWillBecomeVisible, HandlePopupMenuWillBecomeVisible);
 742     INSTALL_EVENT_LISTENER(trackPopupInvisible, SetPopupMenuWillBecomeInvisible, HandlePopupMenuWillBecomeInvisible);
 743     INSTALL_EVENT_LISTENER(trackPopupCanceled, SetPopupMenuCanceled, HandlePopupMenuCanceled);
 744 
 745     INSTALL_EVENT_LISTENER(trackPropertyNameChange, SetPropertyNameChange, HandlePropertyNameChange);
 746     INSTALL_EVENT_LISTENER(trackPropertyDescriptionChange, SetPropertyDescriptionChange, HandlePropertyDescriptionChange);
 747     INSTALL_EVENT_LISTENER(trackPropertyStateChange, SetPropertyStateChange, HandlePropertyStateChange);
 748     INSTALL_EVENT_LISTENER(trackPropertyValueChange, SetPropertyValueChange, HandlePropertyValueChange);
 749     INSTALL_EVENT_LISTENER(trackPropertySelectionChange, SetPropertySelectionChange, HandlePropertySelectionChange);
 750     INSTALL_EVENT_LISTENER(trackPropertyTextChange, SetPropertyTextChange, HandlePropertyTextChange);
 751     INSTALL_EVENT_LISTENER(trackPropertyCaretChange, SetPropertyCaretChange, HandlePropertyCaretChange);
 752     INSTALL_EVENT_LISTENER(trackPropertyVisibleDataChange, SetPropertyVisibleDataChange, HandlePropertyVisibleDataChange);
 753     INSTALL_EVENT_LISTENER(trackPropertyChildChange, SetPropertyChildChange, HandlePropertyChildChange);
 754     INSTALL_EVENT_LISTENER(trackPropertyActiveDescendentChange, SetPropertyActiveDescendentChange, HandlePropertyActiveDescendentChange);
 755     INSTALL_EVENT_LISTENER(trackPropertyTableModelChange, SetPropertyTableModelChange, HandlePropertyTableModelChange);
 756 }
 757 
 758 
 759 #define TRACK_EVENT_TOGGLE(menuItem, toggleVal, setFP, handler) \
 760         case menuItem:                                                                                          \
 761                 menu = GetMenu(hWnd);                                                                   \
 762                 if (toggleVal) {                                                                                \
 763                         toggleVal = FALSE;                                                                      \
 764                         CheckMenuItem(menu, menuItem,                                           \
 765                                                   MF_BYCOMMAND | MF_UNCHECKED);                 \
 766                         setFP(NULL);                                                                            \
 767                 } else {                                                                                                \
 768                         toggleVal = TRUE;                                                                       \
 769                         CheckMenuItem(menu, menuItem,                                           \
 770                                                   MF_BYCOMMAND | MF_CHECKED);                   \
 771                         setFP(handler);                                                                         \
 772                 }                                                                                                               \
 773                 return(TRUE)
 774 
 775 
 776 BOOL APIENTRY FerretDialogProc (HWND hWnd, UINT message, UINT wParam, LONG lParam) {
 777     int command;
 778     short width, height;
 779     HWND dlgItem;
 780     HMENU menu;
 781 
 782     switch (message) {
 783 
 784     case WM_CREATE:
 785         return 0;
 786 
 787     case WM_INITDIALOG:
 788         CheckMenuItem(GetMenu(hWnd), cAccessBridgeDLLLoaded, MF_BYCOMMAND | MF_CHECKED);
 789         return(TRUE);
 790 
 791     case WM_CLOSE:
 792         exitJavaFerret(hWnd);
 793         return(TRUE);
 794         break;
 795 
 796     case WM_SIZE:
 797         width = LOWORD(lParam);
 798         height = HIWORD(lParam);
 799         dlgItem = GetDlgItem(theDialogWindow, cFerretText);
 800         SetWindowPos(dlgItem, NULL, 0, 0, width, height, 0);
 801         return(FALSE);                  // let windows finish handling this
 802         break;
 803 
 804     case WM_COMMAND:
 805         command = LOWORD(wParam);
 806 
 807         switch(command) {
 808         case cAccessBridgeDLLLoaded:    // toggle; unload or load AccessBridge
 809             if (theAccessBridgeLoadedFlag) {
 810                 shutdownAccessBridge();
 811                 theAccessBridgeLoadedFlag = FALSE;
 812                 CheckMenuItem(GetMenu(hWnd), cAccessBridgeDLLLoaded, MF_BYCOMMAND | MF_UNCHECKED);
 813             } else {
 814                 theAccessBridgeLoadedFlag = initializeAccessBridge();
 815                 if (theAccessBridgeLoadedFlag) {
 816                     CheckMenuItem(GetMenu(hWnd), cAccessBridgeDLLLoaded, MF_BYCOMMAND | MF_CHECKED);
 817                     reinstallEventListeners();
 818                 }
 819             }
 820             return(TRUE);
 821 
 822         case cExitMenuItem:
 823             exitJavaFerret(hWnd);
 824             return(TRUE);
 825 
 826             TRACK_EVENT_TOGGLE(cTrackMouseMenuItem, trackMouse, SetMouseEntered, HandleMouseEntered);
 827             TRACK_EVENT_TOGGLE(cTrackShutdownMenuItem, trackShutdown, SetJavaShutdown, HandleJavaShutdown);
 828             TRACK_EVENT_TOGGLE(cTrackFocusMenuItem, trackFocus, SetFocusGained, HandleJavaFocusGained);
 829             TRACK_EVENT_TOGGLE(cTrackCaretMenuItem, trackCaret, SetCaretUpdate, HandleJavaCaretUpdate);
 830 
 831             TRACK_EVENT_TOGGLE(cTrackMenuSelectedMenuItem, trackMenuSelected, SetMenuSelected, HandleMenuSelected);
 832             TRACK_EVENT_TOGGLE(cTrackMenuDeselectedMenuItem, trackMenuDeselected, SetMenuDeselected, HandleMenuDeselected);
 833             TRACK_EVENT_TOGGLE(cTrackMenuCanceledItem, trackMenuCanceled, SetMenuCanceled, HandleMenuCanceled);
 834 
 835             TRACK_EVENT_TOGGLE(cTrackPopupBecomeVisibleMenuItem, trackPopupVisible, SetPopupMenuWillBecomeVisible, HandlePopupMenuWillBecomeVisible);
 836             TRACK_EVENT_TOGGLE(cTrackPopupBecomeInvisibleMenuItem, trackPopupInvisible, SetPopupMenuWillBecomeInvisible, HandlePopupMenuWillBecomeInvisible);
 837             TRACK_EVENT_TOGGLE(cTrackPopupCanceledItem, trackPopupCanceled, SetPopupMenuCanceled, HandlePopupMenuCanceled);
 838 
 839             TRACK_EVENT_TOGGLE(cTrackPropertyNameItem, trackPropertyNameChange, SetPropertyNameChange, HandlePropertyNameChange);
 840             TRACK_EVENT_TOGGLE(cTrackPropertyDescriptionItem, trackPropertyDescriptionChange, SetPropertyDescriptionChange, HandlePropertyDescriptionChange);
 841             TRACK_EVENT_TOGGLE(cTrackPropertyStateItem, trackPropertyStateChange, SetPropertyStateChange, HandlePropertyStateChange);
 842             TRACK_EVENT_TOGGLE(cTrackPropertyValueItem, trackPropertyValueChange, SetPropertyValueChange, HandlePropertyValueChange);
 843             TRACK_EVENT_TOGGLE(cTrackPropertySelectionItem, trackPropertySelectionChange, SetPropertySelectionChange, HandlePropertySelectionChange);
 844             TRACK_EVENT_TOGGLE(cTrackPropertyTextItem, trackPropertyTextChange, SetPropertyTextChange, HandlePropertyTextChange);
 845             TRACK_EVENT_TOGGLE(cTrackPropertyCaretItem, trackPropertyCaretChange, SetPropertyCaretChange, HandlePropertyCaretChange);
 846             TRACK_EVENT_TOGGLE(cTrackPropertyVisibleDataItem, trackPropertyVisibleDataChange, SetPropertyVisibleDataChange, HandlePropertyVisibleDataChange);
 847             TRACK_EVENT_TOGGLE(cTrackPropertyChildItem, trackPropertyChildChange, SetPropertyChildChange, HandlePropertyChildChange);
 848             TRACK_EVENT_TOGGLE(cTrackPropertyActiveDescendentItem, trackPropertyActiveDescendentChange, SetPropertyActiveDescendentChange, HandlePropertyActiveDescendentChange);
 849             TRACK_EVENT_TOGGLE(cTrackPropertyTableModelChangeItem, trackPropertyTableModelChange, SetPropertyTableModelChange, HandlePropertyTableModelChange);
 850 
 851         case cUpdateFromMouseMenuItem:
 852             menu = GetMenu(hWnd);
 853             if (updateMouse) {
 854                 updateMouse = FALSE;
 855                 CheckMenuItem(menu, cUpdateFromMouseMenuItem,
 856                               MF_BYCOMMAND | MF_UNCHECKED);
 857                 UnhookWindowsHookEx((HHOOK)MouseProc);
 858                 KillTimer(hWnd, TIMER_ID);
 859             } else {
 860                 updateMouse = TRUE;
 861                 CheckMenuItem(menu, cUpdateFromMouseMenuItem,
 862                               MF_BYCOMMAND | MF_CHECKED);
 863                 prevMouseHook = SetWindowsHookEx(WH_MOUSE, MouseProc, theInstance, 0);
 864             }
 865             return(TRUE);
 866 
 867         case cUpdateWithF1Item:
 868             menu = GetMenu(hWnd);
 869             if (updateF1) {
 870                 updateF1 = FALSE;
 871                 CheckMenuItem(menu, cUpdateWithF1Item,
 872                               MF_BYCOMMAND | MFS_UNCHECKED);
 873                 UnhookWindowsHookEx((HHOOK)KeyboardProc);
 874             } else {
 875                 updateF1 = TRUE;
 876                 CheckMenuItem(menu, cUpdateWithF1Item,
 877                               MF_BYCOMMAND | MFS_CHECKED);
 878                 prevKbdHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, theInstance, 0);
 879             }
 880             return(TRUE);
 881 
 882         case cUpdateWithF2Item:
 883             menu = GetMenu(hWnd);
 884             if (updateF2) {
 885                 updateF2 = FALSE;
 886                 CheckMenuItem(menu, cUpdateWithF2Item,
 887                               MF_BYCOMMAND | MFS_UNCHECKED);
 888                 UnhookWindowsHookEx((HHOOK)KeyboardProc);
 889             } else {
 890                 updateF2 = TRUE;
 891                 CheckMenuItem(menu, cUpdateWithF2Item,
 892                               MF_BYCOMMAND | MFS_CHECKED);
 893                 prevKbdHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, theInstance, 0);
 894             }
 895             return(TRUE);
 896 
 897         }
 898         break;
 899 
 900     case DISPLAY_INFO_MESSAGE:
 901         echoMouseObject();
 902         break;
 903 
 904     case DISPLAY_HWND_INFO_MESSAGE:
 905         echoMouseHWNDObject();
 906         break;
 907     }
 908 
 909     return (FALSE);
 910 }