/* * Copyright 1996-2004 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. */ #ifdef HEADLESS #error This file should not be included in headless library #endif #include "awt_p.h" #include #include #include #include "color.h" #include "java_awt_PopupMenu.h" #include "java_awt_Component.h" #include "java_awt_Event.h" #include "sun_awt_motif_MPopupMenuPeer.h" #include "sun_awt_motif_MComponentPeer.h" #include "awt_PopupMenu.h" #include "awt_MenuItem.h" #include "awt_Component.h" #include "awt_MenuComponent.h" #include "awt_Menu.h" #include "awt_Event.h" #include "multi_font.h" #include #include extern struct MMenuItemPeerIDs mMenuItemPeerIDs; extern struct MComponentPeerIDs mComponentPeerIDs; extern struct MenuComponentIDs menuComponentIDs; extern struct MenuItemIDs menuItemIDs; extern struct MenuIDs menuIDs; extern AwtGraphicsConfigDataPtr getGraphicsConfigFromComponentPeer(JNIEnv *env, jobject parentPeer); extern Boolean keyboardGrabbed; Boolean poppingDown = False; struct MPopupMenuPeerIDs mPopupMenuPeerIDs; static Widget activePopup; void removePopupMenus() { if (activePopup != NULL && XtIsManaged(activePopup)) { XtUnmanageChild(activePopup); activePopup = NULL; } } Boolean awtMenuIsActive() { return ((activePopup != NULL) || (awt_util_focusIsOnMenu(awt_display))); } struct ClientDataStruct { struct ComponentData *wdata; jobject mMenuItemPeerIDs; }; /* * Class: sun_awt_motif_MPopupMenuPeer * Method: initIDs * Signature: ()V */ /* This function gets called from the static initializer for MPopupMenuPeer.java to initialize the methodIDs for methods that may be accessed from C */ JNIEXPORT void JNICALL Java_sun_awt_motif_MPopupMenuPeer_initIDs (JNIEnv *env, jclass cls) { mPopupMenuPeerIDs.destroyNativeWidgetAfterGettingTreeLock = (*env)->GetMethodID(env, cls, "destroyNativeWidgetAfterGettingTreeLock", "()V"); } extern Boolean skipNextNotifyWhileGrabbed; static void Popup_popUpCB(Widget w, XtPointer client_data, XtPointer calldata) { skipNextNotifyWhileGrabbed = True; } /* * client_data is MPopupMenuPeer instance */ static void Popup_popdownCB(Widget w, XtPointer client_data, XtPointer calldata) { JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); jobject target = NULL; /* * Fix for 4394847. Due to the race keyboard remains grabbed after menu * was disposed. Clear the grab status here instead of processOneEvent. */ poppingDown = True; keyboardGrabbed = False; skipNextNotifyWhileGrabbed = True; XtRemoveCallback(w, XtNpopdownCallback, Popup_popdownCB, (XtPointer) client_data); (*env)->CallVoidMethod(env, (jobject) client_data, mPopupMenuPeerIDs.destroyNativeWidgetAfterGettingTreeLock); if ((*env)->ExceptionOccurred(env)) { (*env)->ExceptionDescribe(env); (*env)->ExceptionClear(env); } } /* * Class: sun_awt_motif_MPopupMenuPeer * Method: createMenu * Signature: (Lsun/awt/motif/MComponentPeer;)V */ JNIEXPORT void JNICALL Java_sun_awt_motif_MPopupMenuPeer_createMenu (JNIEnv *env, jobject this, jobject parent) { struct ComponentData *wdata; struct MenuData *mdata; struct FontData *fdata; char *ctitle = NULL; int32_t argc; #define MAX_ARGC 10 Arg args[MAX_ARGC]; Pixel bg; Pixel fg; XmFontList fontlist = NULL; XmString mfstr = NULL; jobject font; jobject target; jobject targetFont; jobject label; jboolean IsMultiFont; jboolean tearOff; jobject globalRef = (*env)->NewGlobalRef(env, this); AwtGraphicsConfigDataPtr adata; JNU_SetLongFieldFromPtr(env, this, mMenuItemPeerIDs.jniGlobalRef, globalRef); AWT_LOCK(); if (JNU_IsNull(env, parent)) { JNU_ThrowNullPointerException(env, "NullPointerException"); AWT_UNLOCK(); return; } target = (*env)->GetObjectField(env, this, mMenuItemPeerIDs.target); wdata = (struct ComponentData *) JNU_GetLongFieldAsPtr(env, parent, mComponentPeerIDs.pData); if (wdata == NULL || JNU_IsNull(env, target)) { JNU_ThrowNullPointerException(env, "NullPointerException"); AWT_UNLOCK(); return; } mdata = ZALLOC(MenuData); if (mdata == NULL) { JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError"); AWT_UNLOCK(); return; } JNU_SetLongFieldFromPtr(env, this, mMenuItemPeerIDs.pData, mdata); adata = getGraphicsConfigFromComponentPeer(env, parent); /* * Why are these different? */ font = JNU_CallMethodByName(env, NULL, target, "getFont_NoClientCode", "()Ljava/awt/Font;").l; targetFont = (*env)->GetObjectField(env, target, menuComponentIDs.font); if (!JNU_IsNull(env, targetFont) && (fdata = awtJNI_GetFontData(env, targetFont, NULL)) != NULL) { IsMultiFont = awtJNI_IsMultiFont(env, targetFont); } else { IsMultiFont = awtJNI_IsMultiFont(env, font); } label = (*env)->GetObjectField(env, target, menuItemIDs.label); if (JNU_IsNull(env, label)) { mfstr = XmStringCreateLocalized(""); ctitle = ""; } else { if (IsMultiFont) { mfstr = awtJNI_MakeMultiFontString(env, label, font); } else { ctitle = (char *) JNU_GetStringPlatformChars(env, label, NULL); } } XtVaGetValues(wdata->widget, XmNbackground, &bg, NULL); XtVaGetValues(wdata->widget, XmNforeground, &fg, NULL); argc = 0; XtSetArg(args[argc], XmNbackground, bg); argc++; XtSetArg(args[argc], XmNforeground, fg); argc++; tearOff = (*env)->GetBooleanField(env, target, menuIDs.tearOff); if (tearOff) { XtSetArg(args[argc], XmNtearOffModel, XmTEAR_OFF_ENABLED); argc++; } if (!JNU_IsNull(env, targetFont) && (fdata = awtJNI_GetFontData(env, targetFont, NULL)) != NULL) { if (IsMultiFont) { fontlist = awtJNI_GetFontList(env, targetFont); } else { fontlist = XmFontListCreate(fdata->xfont, "labelFont"); } XtSetArg(args[argc], XmNfontList, fontlist); argc++; } else { if (IsMultiFont) { fontlist = awtJNI_GetFontList(env, font); XtSetArg(args[argc], XmNfontList, fontlist); argc++; } } XtSetArg(args[argc], XmNvisual, adata->awt_visInfo.visual); argc++; XtSetArg (args[argc], XmNscreen, ScreenOfDisplay(awt_display, adata->awt_visInfo.screen)); argc++; if (IsMultiFont) { DASSERT(!(argc > MAX_ARGC)); mdata->itemData.comp.widget = XmCreatePopupMenu(wdata->widget, "", args, argc); } else { DASSERT(!(argc > MAX_ARGC)); mdata->itemData.comp.widget = XmCreatePopupMenu(wdata->widget, ctitle, args, argc); } awt_addMenuWidget(mdata->itemData.comp.widget); /* * Fix for bug 4180147 - * screen can be frozen when interacting with MB3 using AWT on Motif */ XtUngrabButton(wdata->widget, AnyButton, AnyModifier); XtUngrabPointer(wdata->widget, CurrentTime); /* fix for bug #4169155: Popup menus get a leading separator on Motif system. Additional check that title string is not empty*/ if (!JNU_IsNull(env, label) && (*env)->GetStringUTFLength( env, label) != (jsize)0 ) { if (IsMultiFont) { XtVaCreateManagedWidget("", xmLabelWidgetClass, mdata->itemData.comp.widget, XmNfontList, fontlist, XmNlabelString, mfstr, XmNbackground, bg, XmNforeground, fg, XmNhighlightColor, fg, NULL); XmStringFree(mfstr); } else { XmString xmstr = XmStringCreateLocalized(ctitle); XtVaCreateManagedWidget(ctitle, xmLabelWidgetClass, mdata->itemData.comp.widget, XmNlabelString, xmstr, XmNbackground, bg, XmNforeground, fg, XmNhighlightColor, fg, NULL); XmStringFree(xmstr); JNU_ReleaseStringPlatformChars(env, label, (const char *) ctitle); } /* Create separator */ XtVaCreateManagedWidget("", xmSeparatorWidgetClass, mdata->itemData.comp.widget, XmNbackground, bg, XmNforeground, fg, NULL); } if (tearOff) { Widget tearOffWidget = XmGetTearOffControl(mdata->itemData.comp.widget); XtVaSetValues(tearOffWidget, XmNbackground, bg, XmNforeground, fg, XmNhighlightColor, fg, NULL); } mdata->comp.widget = mdata->itemData.comp.widget; if (!JNU_IsNull(env, targetFont)) { XmFontListFree(fontlist); } XtSetSensitive(mdata->comp.widget, ((*env)->GetBooleanField(env, target, menuItemIDs.enabled) ? True : False)); AWT_UNLOCK(); } /* * Class: sun_awt_motif_MPopupMenuPeer * Method: pShow * Signature: (Ljava/awt/Event;IILsun/awt/motif/MComponentPeer;)V */ JNIEXPORT void JNICALL Java_sun_awt_motif_MPopupMenuPeer_pShow (JNIEnv *env, jobject this, jobject event, jint x, jint y, jobject origin) { struct MenuData *mdata; struct ComponentData *wdata; XButtonEvent *bevent; XButtonEvent *newEvent = NULL; void *data; AWT_LOCK(); mdata = (struct MenuData *) JNU_GetLongFieldAsPtr(env, this, mMenuItemPeerIDs.pData); if (mdata == NULL || JNU_IsNull(env, event)) { JNU_ThrowNullPointerException(env, "NullPointerException"); AWT_UNLOCK(); return; } wdata = (struct ComponentData *) JNU_GetLongFieldAsPtr(env, origin, mComponentPeerIDs.pData); if ( wdata == NULL || wdata->widget == NULL ) { /* 425598 */ JNU_ThrowNullPointerException(env, "NullPointerException"); /* 425598 */ AWT_UNLOCK(); /* 425598 */ return; /* 425598 */ } /* 425598 */ if (!XtIsRealized(wdata->widget)) { JNU_ThrowInternalError(env, "widget not visible on screen"); AWT_UNLOCK(); return; } /* * Fix for BugTraq ID 4186663 - Pural PopupMenus appear at the same time. * If another popup is currently visible hide it. */ if (activePopup != NULL && activePopup != mdata->comp.widget && XtIsObject(activePopup) && XtIsManaged(activePopup)) { removePopupMenus(); } /* If the raw x event is not available, then we must use an unfortunate * round-trip call to XTranslateCoordiates to get the root coordinates. */ data = JNU_GetLongFieldAsPtr(env, event, eventIDs.data); if (data == NULL || ((XEvent *) data)->type != ButtonPress) { int32_t rx, ry; Window root, win; root = RootWindowOfScreen(XtScreen(wdata->widget)); XTranslateCoordinates(awt_display, XtWindow(wdata->widget), root, (int32_t) x, (int32_t) y, &rx, &ry, &win); /* printf("translated coords %d,%d to root %d,%d\n", x, y, rx, ry); */ newEvent = (XButtonEvent *) malloc(sizeof(XButtonEvent)); newEvent->type = ButtonPress; newEvent->display = awt_display; newEvent->window = XtWindow(wdata->widget); newEvent->time = awt_util_getCurrentServerTime(); newEvent->x = (int32_t) x; newEvent->y = (int32_t) y; newEvent->x_root = rx; newEvent->y_root = ry; bevent = newEvent; } else { bevent = (XButtonEvent *) data; } XtAddCallback(XtParent(mdata->comp.widget), XtNpopdownCallback, Popup_popdownCB, (XtPointer) JNU_GetLongFieldAsPtr(env, this, mMenuItemPeerIDs.jniGlobalRef)); XtAddCallback(XtParent(mdata->comp.widget), XtNpopupCallback, Popup_popUpCB, (XtPointer) JNU_GetLongFieldAsPtr(env, this, mMenuItemPeerIDs.jniGlobalRef)); XmMenuPosition(mdata->comp.widget, bevent); XtManageChild(mdata->comp.widget); /* * Fix for BugTraq ID 4186663 - Pural PopupMenus appear at the same time. * Store the pointer to the currently showing popup. */ activePopup = mdata->comp.widget; if (newEvent) { free((void *) newEvent); } AWT_UNLOCK(); } /* * Class: sun_awt_motif_MPopupMenuPeer * Method: pDispose * Signature: ()V */ JNIEXPORT void JNICALL Java_sun_awt_motif_MPopupMenuPeer_pDispose (JNIEnv *env, jobject this) { struct MenuData *mdata; AWT_LOCK(); mdata = (struct MenuData *) JNU_GetLongFieldAsPtr(env, this, mMenuItemPeerIDs.pData); if (mdata == NULL) { AWT_UNLOCK(); return; } /* * Fix for BugTraq ID 4186663 - Pural PopupMenus appear at the same time. * Clear the pointer to the currently showing popup. */ if (activePopup == mdata->comp.widget) { activePopup = NULL; } awt_delMenuWidget(mdata->itemData.comp.widget); XtUnmanageChild(mdata->comp.widget); awt_util_consumeAllXEvents(mdata->comp.widget); XtDestroyWidget(mdata->comp.widget); free((void *) mdata); (*env)->SetLongField(env, this, mMenuItemPeerIDs.pData, (jlong)0); awtJNI_DeleteGlobalMenuRef(env, this); poppingDown = False; AWT_UNLOCK(); }