blob: afd186b7e5af729a1f68464496f22dc35432ac01 [file] [log] [blame]
/*
* 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 <Xm/Separator.h>
#include <Xm/MenuShell.h>
#include <Xm/RowColumn.h>
#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 <jni.h>
#include <jni_util.h>
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();
}