/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * Peter Annema */ #include "nsWidget.h" #include "keysym2ucs.h" #include "nsWindow.h" #include "nsScrollbar.h" #include "nsGUIEvent.h" #include "nsTextWidget.h" #include "nsGtkIMEHelper.h" #include "stdio.h" #include "gtk/gtk.h" #include "nsGtkEventHandler.h" #include #include #include #include "nslog.h" NS_IMPL_LOG(nsGtkEventHandlerLog, 0) #define PRINTF NS_LOG_PRINTF(nsGtkEventHandlerLog) #define FLUSH NS_LOG_FLUSH(nsGtkEventHandlerLog) #ifdef DEBUG_pavlov //#define DEBUG_EVENTS 1 #endif //#define DEBUG_MOVE static void dispatch_superwin_event(GdkEvent *event, nsWindow *window); static PRBool superwin_child_of_gtk_widget(nsWindow *window, GtkWidget *widget); static PRBool gtk_widget_child_of_gdk_window(GtkWidget *widget, GdkWindow *window); struct EventInfo { nsWidget *widget; // the widget nsRect *rect; // the rect }; // This is a flag that says if we should suppress the next key down // event. This is used when that last key release event was // suppressed and we want the next key press event to not generate the // key down event since it is part of a key repeat sequence. static PRBool suppressNextKeyDown = PR_FALSE; //============================================================== void InitAllocationEvent(GtkAllocation *aAlloc, gpointer p, nsSizeEvent &anEvent, PRUint32 aEventType) { anEvent.message = aEventType; anEvent.widget = (nsWidget *) p; anEvent.eventStructType = NS_SIZE_EVENT; if (aAlloc != nsnull) { // HACK // nsRect *foo = new nsRect(aAlloc->x, aAlloc->y, aAlloc->width, aAlloc->height); nsRect *foo = new nsRect(0, 0, aAlloc->width, aAlloc->height); anEvent.windowSize = foo; // anEvent.point.x = aAlloc->x; // anEvent.point.y = aAlloc->y; // HACK anEvent.point.x = 0; anEvent.point.y = 0; anEvent.mWinWidth = aAlloc->width; anEvent.mWinHeight = aAlloc->height; } anEvent.time = PR_IntervalNow(); } //============================================================== void InitConfigureEvent(GdkEventConfigure *aConf, gpointer p, nsSizeEvent &anEvent, PRUint32 aEventType) { anEvent.message = aEventType; anEvent.widget = (nsWidget *) p; anEvent.eventStructType = NS_SIZE_EVENT; if (aConf != nsnull) { /* do we accually need to alloc a new rect, or can we just set the current one */ nsRect *foo = new nsRect(aConf->x, aConf->y, aConf->width, aConf->height); anEvent.windowSize = foo; anEvent.point.x = aConf->x; anEvent.point.y = aConf->y; anEvent.mWinWidth = aConf->width; anEvent.mWinHeight = aConf->height; } // this usually returns 0 anEvent.time = 0; } //============================================================== void InitExposeEvent(GdkEventExpose *aGEE, gpointer p, nsPaintEvent &anEvent, PRUint32 aEventType) { anEvent.message = aEventType; anEvent.widget = (nsWidget *) p; anEvent.eventStructType = NS_PAINT_EVENT; if (aGEE != nsnull) { #ifdef DEBUG_EVENTS g_print("expose event: x = %i , y = %i , w = %i , h = %i\n", aGEE->area.x, aGEE->area.y, aGEE->area.width, aGEE->area.height); #endif anEvent.point.x = aGEE->area.x; anEvent.point.y = aGEE->area.y; nsRect *rect = new nsRect(aGEE->area.x, aGEE->area.y, aGEE->area.width, aGEE->area.height); anEvent.rect = rect; anEvent.time = gdk_event_get_time((GdkEvent*)aGEE); } } //============================================================= void UninitExposeEvent(GdkEventExpose *aGEE, gpointer p, nsPaintEvent &anEvent, PRUint32 aEventType) { if (aGEE != nsnull) { delete anEvent.rect; } } struct nsKeyConverter { int vkCode; // Platform independent key code int keysym; // GDK keysym key code }; // // Netscape keycodes are defined in widget/public/nsGUIEvent.h // GTK keycodes are defined in // struct nsKeyConverter nsKeycodes[] = { { NS_VK_CANCEL, GDK_Cancel }, { NS_VK_BACK, GDK_BackSpace }, { NS_VK_TAB, GDK_Tab }, { NS_VK_TAB, GDK_ISO_Left_Tab }, { NS_VK_CLEAR, GDK_Clear }, { NS_VK_RETURN, GDK_Return }, { NS_VK_SHIFT, GDK_Shift_L }, { NS_VK_SHIFT, GDK_Shift_R }, { NS_VK_CONTROL, GDK_Control_L }, { NS_VK_CONTROL, GDK_Control_R }, { NS_VK_ALT, GDK_Alt_L }, { NS_VK_ALT, GDK_Alt_R }, { NS_VK_PAUSE, GDK_Pause }, { NS_VK_CAPS_LOCK, GDK_Caps_Lock }, { NS_VK_ESCAPE, GDK_Escape }, { NS_VK_SPACE, GDK_space }, { NS_VK_PAGE_UP, GDK_Page_Up }, { NS_VK_PAGE_DOWN, GDK_Page_Down }, { NS_VK_END, GDK_End }, { NS_VK_HOME, GDK_Home }, { NS_VK_LEFT, GDK_Left }, { NS_VK_UP, GDK_Up }, { NS_VK_RIGHT, GDK_Right }, { NS_VK_DOWN, GDK_Down }, { NS_VK_PRINTSCREEN, GDK_Print }, { NS_VK_INSERT, GDK_Insert }, { NS_VK_DELETE, GDK_Delete }, // keypad keys { NS_VK_LEFT, GDK_KP_Left }, { NS_VK_RIGHT, GDK_KP_Right }, { NS_VK_UP, GDK_KP_Up }, { NS_VK_DOWN, GDK_KP_Down }, { NS_VK_PAGE_UP, GDK_KP_Page_Up }, // Not sure what these are //{ NS_VK_, GDK_KP_Prior }, //{ NS_VK_, GDK_KP_Next }, // GDK_KP_Begin is the 5 on the non-numlock keypad //{ NS_VK_, GDK_KP_Begin }, { NS_VK_PAGE_DOWN, GDK_KP_Page_Down }, { NS_VK_HOME, GDK_KP_Home }, { NS_VK_END, GDK_KP_End }, { NS_VK_INSERT, GDK_KP_Insert }, { NS_VK_DELETE, GDK_KP_Delete }, { NS_VK_MULTIPLY, GDK_KP_Multiply }, { NS_VK_ADD, GDK_KP_Add }, { NS_VK_SEPARATOR, GDK_KP_Separator }, { NS_VK_SUBTRACT, GDK_KP_Subtract }, { NS_VK_DECIMAL, GDK_KP_Decimal }, { NS_VK_DIVIDE, GDK_KP_Divide }, { NS_VK_RETURN, GDK_KP_Enter }, { NS_VK_COMMA, GDK_comma }, { NS_VK_PERIOD, GDK_period }, { NS_VK_SLASH, GDK_slash }, { NS_VK_BACK_SLASH, GDK_backslash }, { NS_VK_BACK_QUOTE, GDK_grave }, { NS_VK_OPEN_BRACKET, GDK_bracketleft }, { NS_VK_CLOSE_BRACKET, GDK_bracketright }, { NS_VK_SEMICOLON, GDK_colon }, { NS_VK_QUOTE, GDK_apostrophe }, // NS doesn't have dash or equals distinct from the numeric keypad ones, // so we'll use those for now. See bug 17008: { NS_VK_SUBTRACT, GDK_minus }, { NS_VK_EQUALS, GDK_equal }, // Some shifted keys, see bug 15463 as well as 17008. // These should be subject to different keyboard mappings. { NS_VK_QUOTE, GDK_quotedbl }, { NS_VK_OPEN_BRACKET, GDK_braceleft }, { NS_VK_CLOSE_BRACKET, GDK_braceright }, { NS_VK_BACK_SLASH, GDK_bar }, { NS_VK_SEMICOLON, GDK_semicolon }, { NS_VK_BACK_QUOTE, GDK_asciitilde }, { NS_VK_COMMA, GDK_less }, { NS_VK_PERIOD, GDK_greater }, { NS_VK_SLASH, GDK_question }, { NS_VK_1, GDK_exclam }, { NS_VK_2, GDK_at }, { NS_VK_3, GDK_numbersign }, { NS_VK_4, GDK_dollar }, { NS_VK_5, GDK_percent }, { NS_VK_6, GDK_asciicircum }, { NS_VK_7, GDK_ampersand }, { NS_VK_8, GDK_asterisk }, { NS_VK_9, GDK_parenleft }, { NS_VK_0, GDK_parenright }, { NS_VK_SUBTRACT, GDK_underscore }, { NS_VK_EQUALS, GDK_plus } }; void nsGtkWidget_InitNSKeyEvent(int aEventType, nsKeyEvent& aKeyEvent, GtkWidget *w, gpointer p, GdkEventKey * event); //============================================================== // Input keysym is in gtk format; output is in NS_VK format int nsPlatformToDOMKeyCode(GdkEventKey *aGEK) { int i; int length = sizeof(nsKeycodes) / sizeof(struct nsKeyConverter); int keysym = aGEK->keyval; // First, try to handle alphanumeric input, not listed in nsKeycodes: // most likely, more letters will be getting typed in than things in // the key list, so we will look through these first. // since X has different key symbols for upper and lowercase letters and // mozilla does not, convert gdk's to mozilla's if (keysym >= GDK_a && keysym <= GDK_z) return keysym - GDK_a + NS_VK_A; if (keysym >= GDK_A && keysym <= GDK_Z) return keysym - GDK_A + NS_VK_A; // numbers if (keysym >= GDK_0 && keysym <= GDK_9) return keysym - GDK_0 + NS_VK_0; // keypad numbers if (keysym >= GDK_KP_0 && keysym <= GDK_KP_9) return keysym - GDK_KP_0 + NS_VK_NUMPAD0; // misc other things for (i = 0; i < length; i++) { if (nsKeycodes[i].keysym == keysym) return(nsKeycodes[i].vkCode); } // function keys if (keysym >= GDK_F1 && keysym <= GDK_F24) return keysym - GDK_F1 + NS_VK_F1; #if defined(DEBUG_akkana) || defined(DEBUG_ftang) PRINTF("No match in nsPlatformToDOMKeyCode: keysym is 0x%x, string is '%s', keyval = %d\n", keysym, aGEK->string, aGEK->keyval); #endif return((int)0); } //============================================================== // Convert gdk key event keyvals to char codes if printable, 0 otherwise PRUint32 nsConvertCharCodeToUnicode(GdkEventKey* aGEK) { // Anything above 0xf000 is considered a non-printable if (aGEK->keyval > 0xf000) { // Keypad keys are an exception: they return a value different // from their non-keypad equivalents, but mozilla doesn't distinguish. switch (aGEK->keyval) { case GDK_KP_Space: return ' '; case GDK_KP_Equal: return '='; case GDK_KP_Multiply: return '*'; case GDK_KP_Add: return '+'; case GDK_KP_Separator: return '|'; case GDK_KP_Subtract: return '-'; case GDK_KP_Decimal: return '.'; case GDK_KP_Divide: return '/'; case GDK_KP_0: return '0'; case GDK_KP_1: return '1'; case GDK_KP_2: return '2'; case GDK_KP_3: return '3'; case GDK_KP_4: return '4'; case GDK_KP_5: return '5'; case GDK_KP_6: return '6'; case GDK_KP_7: return '7'; case GDK_KP_8: return '8'; case GDK_KP_9: return '9'; } // non-printables return 0; } // we're supposedly printable, let's try to convert long ucs = keysym2ucs(aGEK->keyval); if ((ucs != -1) && (ucs < 0x10000)) return ucs; // I guess we couldn't convert return 0; } //============================================================== void InitKeyEvent(GdkEventKey *aGEK, gpointer p, nsKeyEvent &anEvent, PRUint32 aEventType) { anEvent.message = aEventType; anEvent.widget = (nsWidget *) p; anEvent.eventStructType = NS_KEY_EVENT; if (aGEK != nsnull) { anEvent.keyCode = nsPlatformToDOMKeyCode(aGEK); anEvent.charCode = 0; anEvent.time = aGEK->time; anEvent.isShift = (aGEK->state & GDK_SHIFT_MASK) ? PR_TRUE : PR_FALSE; anEvent.isControl = (aGEK->state & GDK_CONTROL_MASK) ? PR_TRUE : PR_FALSE; anEvent.isAlt = (aGEK->state & GDK_MOD1_MASK) ? PR_TRUE : PR_FALSE; // XXX For meta key, state is 0, and so are keyval and string. Sigh! anEvent.isMeta = PR_FALSE; //(aGEK->state & GDK_MOD2_MASK) ? PR_TRUE : PR_FALSE; anEvent.point.x = 0; anEvent.point.y = 0; } } void InitKeyPressEvent(GdkEventKey *aGEK, gpointer p, nsKeyEvent &anEvent) { // // init the basic event fields // anEvent.eventStructType = NS_KEY_EVENT; anEvent.message = NS_KEY_PRESS; anEvent.widget = (nsWidget*)p; if (aGEK!=nsnull) { anEvent.isShift = (aGEK->state & GDK_SHIFT_MASK) ? PR_TRUE : PR_FALSE; anEvent.isControl = (aGEK->state & GDK_CONTROL_MASK) ? PR_TRUE : PR_FALSE; anEvent.isAlt = (aGEK->state & GDK_MOD1_MASK) ? PR_TRUE : PR_FALSE; // XXX anEvent.isMeta = PR_FALSE; //(aGEK->state & GDK_MOD2_MASK) ? PR_TRUE : PR_FALSE; anEvent.charCode = nsConvertCharCodeToUnicode(aGEK); if (anEvent.charCode) { anEvent.keyCode = 0; // if the control, meta, or alt key is down, then we should leave // the isShift flag alone (probably not a printable character) // if none of the other modifier keys are pressed then we need to // clear isShift so the character can be inserted in the editor if (!anEvent.isControl && !anEvent.isAlt && !anEvent.isMeta) anEvent.isShift = PR_FALSE; } else { anEvent.keyCode = nsPlatformToDOMKeyCode(aGEK); } #if defined(DEBUG_akkana_not) || defined (DEBUG_ftang) if (!aGEK->length) PRINTF("!length, "); PRINTF("Key Press event: gtk string = '%s', keyval = '%c' = %d,\n", aGEK->string, aGEK->keyval, aGEK->keyval); PRINTF(" --> keyCode = 0x%x, char code = '%c'", anEvent.keyCode, anEvent.charCode); if (anEvent.isShift) PRINTF(" [shift]"); if (anEvent.isControl) PRINTF(" [ctrl]"); if (anEvent.isAlt) PRINTF(" [alt]"); if (anEvent.isMeta) PRINTF(" [meta]"); PRINTF("\n"); #endif anEvent.time = aGEK->time; anEvent.point.x = 0; anEvent.point.y = 0; } } //============================================================= void UninitKeyEvent(GdkEventKey *aGEK, gpointer p, nsKeyEvent &anEvent, PRUint32 aEventType) { } /*============================================================== ============================================================== ============================================================= ==============================================================*/ void handle_size_allocate(GtkWidget *w, GtkAllocation *alloc, gpointer p) { nsWindow *widget = (nsWindow *)p; nsSizeEvent event; InitAllocationEvent(alloc, p, event, NS_SIZE); NS_ADDREF(widget); widget->OnResize(event); NS_RELEASE(widget); delete event.windowSize; } gint handle_expose_event(GtkWidget *w, GdkEventExpose *event, gpointer p) { if (event->type == GDK_NO_EXPOSE) return PR_FALSE; nsPaintEvent pevent; InitExposeEvent(event, p, pevent, NS_PAINT); nsWindow *win = (nsWindow *)p; NS_ADDREF(win); win->OnExpose(pevent); NS_RELEASE(win); UninitExposeEvent(event, p, pevent, NS_PAINT); return PR_TRUE; } //============================================================== void handle_scrollbar_value_changed(GtkAdjustment *adj, gpointer p) { nsScrollbar *widget = (nsScrollbar*) p; nsScrollbarEvent sevent; sevent.message = NS_SCROLLBAR_POS; sevent.widget = (nsWidget *) p; sevent.eventStructType = NS_SCROLLBAR_EVENT; GdkWindow *win = (GdkWindow *)widget->GetNativeData(NS_NATIVE_WINDOW); gdk_window_get_pointer(win, &sevent.point.x, &sevent.point.y, nsnull); NS_ADDREF(widget); widget->OnScroll(sevent, adj->value); NS_RELEASE(widget); /* FIXME we need to set point.* from the event stuff. */ #if 0 nsWindow * widgetWindow = (nsWindow *) p ; XmScrollBarCallbackStruct * cbs = (XmScrollBarCallbackStruct*) call_data; sevent.widget = (nsWindow *) p; if (cbs->event != nsnull) { sevent.point.x = cbs->event->xbutton.x; sevent.point.y = cbs->event->xbutton.y; } else { sevent.point.x = 0; sevent.point.y = 0; } sevent.time = 0; //XXX Implement this switch (cbs->reason) { case XmCR_INCREMENT: sevent.message = NS_SCROLLBAR_LINE_NEXT; break; case XmCR_DECREMENT: sevent.message = NS_SCROLLBAR_LINE_PREV; break; case XmCR_PAGE_INCREMENT: sevent.message = NS_SCROLLBAR_PAGE_NEXT; break; case XmCR_PAGE_DECREMENT: sevent.message = NS_SCROLLBAR_PAGE_PREV; break; case XmCR_DRAG: sevent.message = NS_SCROLLBAR_POS; break; case XmCR_VALUE_CHANGED: sevent.message = NS_SCROLLBAR_POS; break; default: break; } #endif } // GTK's text widget already does XIM, so we don't want to do this again gint handle_key_press_event_for_text(GtkObject *w, GdkEventKey* event, gpointer p) { nsKeyEvent kevent; nsTextWidget* win = (nsTextWidget*)p; // work around for annoying things. if (event->keyval == GDK_Tab) if (event->state & GDK_CONTROL_MASK) if (event->state & GDK_MOD1_MASK) return PR_FALSE; // Don't pass shift, control and alt as key press events if (event->keyval == GDK_Shift_L || event->keyval == GDK_Shift_R || event->keyval == GDK_Control_L || event->keyval == GDK_Control_R || event->keyval == GDK_Alt_L || event->keyval == GDK_Alt_R ) return PR_TRUE; NS_ADDREF(win); InitKeyEvent(event, p, kevent, NS_KEY_DOWN); win->OnKey(kevent); // // Second, dispatch the Key event as a key press event w/ a Unicode // character code. Note we have to check for modifier keys, since // gtk returns a character value for them // InitKeyPressEvent(event,p, kevent); win->OnKey(kevent); NS_RELEASE(win); if (w) { gtk_signal_emit_stop_by_name (GTK_OBJECT(w), "key_press_event"); } return PR_TRUE; } // GTK's text widget already does XIM, so we don't want to do this again gint handle_key_release_event_for_text(GtkObject *w, GdkEventKey* event, gpointer p) { nsKeyEvent kevent; nsTextWidget* win = (nsTextWidget*)p; // Don't pass shift, control and alt as key release events if (event->keyval == GDK_Shift_L || event->keyval == GDK_Shift_R || event->keyval == GDK_Control_L || event->keyval == GDK_Control_R || event->keyval == GDK_Alt_L || event->keyval == GDK_Alt_R ) return PR_TRUE; InitKeyEvent(event, p, kevent, NS_KEY_UP); NS_ADDREF(win); win->OnKey(kevent); NS_RELEASE(win); if (w) { gtk_signal_emit_stop_by_name (GTK_OBJECT(w), "key_release_event"); } return PR_TRUE; } //============================================================== gint handle_key_press_event(GtkObject *w, GdkEventKey* event, gpointer p) { nsKeyEvent kevent; nsWidget *win = (nsWidget*)p; if (win->sFocusWindow) win = win->sFocusWindow; // work around for annoying things. if (event->keyval == GDK_Tab) if (event->state & GDK_CONTROL_MASK) if (event->state & GDK_MOD1_MASK) return PR_FALSE; // Don't pass shift and control as key press events if (event->keyval == GDK_Shift_L || event->keyval == GDK_Shift_R || event->keyval == GDK_Control_L || event->keyval == GDK_Control_R) return PR_TRUE; NS_ADDREF(win); // // First, dispatch the Key event as a virtual key down event // but lie about where it came from and say it is from the // window that currently has focus inside our app... // InitKeyEvent(event, win, kevent, NS_KEY_DOWN); // if we need to suppress this NS_KEY_DOWN event, reset the flag if (suppressNextKeyDown == PR_TRUE) suppressNextKeyDown = PR_FALSE; else win->OnKey(kevent); // // Second, dispatch the Key event as a key press event w/ a Unicode // character code. Note we have to check for modifier keys, since // gtk returns a character value for them // if (event->length) { if (nsGtkIMEHelper::GetSingleton() && (!kevent.keyCode)) { win->IMECommitEvent(event); } else { InitKeyPressEvent(event, win, kevent); win->OnKey(kevent); } } else { // for Home/End/Up/Down/Left/Right/PageUp/PageDown key InitKeyPressEvent(event, win, kevent); win->OnKey(kevent); } NS_RELEASE(win); if (w) { gtk_signal_emit_stop_by_name (GTK_OBJECT(w), "key_press_event"); } return PR_TRUE; } //============================================================== gint handle_key_release_event(GtkObject *w, GdkEventKey* event, gpointer p) { GdkEvent *nextEvent; PRBool shouldDrop = PR_FALSE; // According to the DOM spec if this is the result of a key repeat // event we don't let it through since the DOM wants the event // stream to look like this: press press press press release. The // way that X does things is to do press release press release, etc. // We check to see if this is a key release by checking to see if // the next event in the queue is a key press event and it has the // exact same timestamp as the current event. // get a copy of the next event nextEvent = gdk_event_get(); // see if it's a key press event and if the time matches. if (nextEvent) { if ((nextEvent->type == GDK_KEY_PRESS) && (nextEvent->key.time == event->time)) { shouldDrop = PR_TRUE; // the next key press event shouldn't generate a key down event. // this is a global variable suppressNextKeyDown = PR_TRUE; } // put makes a copy so we're safe doing this. gdk_event_put(nextEvent); // free the event since we just got a copy. gdk_event_free(nextEvent); } // should we drop this event? if (shouldDrop) return PR_TRUE; // Don't pass shift, control and alt as key release events if (event->keyval == GDK_Shift_L || event->keyval == GDK_Shift_R || event->keyval == GDK_Control_L || event->keyval == GDK_Control_R) return PR_TRUE; nsWidget *win = (nsWidget *)p; if (win->sFocusWindow) win = win->sFocusWindow; nsKeyEvent kevent; InitKeyEvent(event, win, kevent, NS_KEY_UP); NS_ADDREF(win); win->OnKey(kevent); NS_RELEASE(win); if (w) { gtk_signal_emit_stop_by_name (GTK_OBJECT(w), "key_release_event"); } return PR_TRUE; } //============================================================== void handle_gdk_event (GdkEvent *event, gpointer data) { GtkObject *object = nsnull; // set the last time that we got an event. we need this for drag // and drop if you can believe that. guint32 event_time = gdk_event_get_time(event); if (event_time) nsWidget::SetLastEventTime(event_time); // try to get the user data for the event window. if (event->any.window) gdk_window_get_user_data (event->any.window, (void **)&object); // check to see if this is an event on a superwin window. if it is, // we have to handle it because if it gets down to the standard gtk // event handler it will cause all sorts of mischief. if (object != nsnull && GDK_IS_SUPERWIN (object)) { // try to find the nsWindow object associated with this superwin nsWindow *window = (nsWindow *)gtk_object_get_data (object, "nsWindow"); // if we don't have a window here anymore, we are probably in // the process of being or have been destroyed. give up now. if (!window) return; // there are three possibilities here. // o there's a gtk grab in effect but no superwin grab // // if this is the case then we need to see if the superwin is a // child of the grabbed widget. if it is, then let the event // pass to the nsWindow associated with the superwin, as normal. // // if not, then rewrite the event widget window to the nearest // MozArea widget and let it pass to the default gtk event // handler. // // o there's a superwin grab in effect but no gtk grab // // easy. let the nsWindow associated with the superwin handle // the event. // // o there's both a gtk and superwin grab in effect // // this is where things get messy. we need to see if the // superwin is a child of the grabbing gtk widget. if it is, // pass it to the nsWindow to be handled // // if it in't then rewrite the event to the nearest MozArea // widget since it will get rewritten again in gtk. // find out what grabs are in effect GtkWidget *current_grab = gtk_grab_get_current(); PRBool superwin_grab = window->GrabInProgress(); // check to see if there are no grabs in effect if (!current_grab && !superwin_grab) { dispatch_superwin_event(event, window); return; } // o there's a gtk grab in effect but no superwin grab else if (current_grab && !superwin_grab) { // // if this is the case then we need to see if the superwin is a // child of the grabbed widget. if it is, then let the event // pass to the nsWindow associated with the superwin, as normal. if (superwin_child_of_gtk_widget(window, current_grab)) { dispatch_superwin_event(event, window); return; } // // if not, then rewrite the event widget window to the nearest // MozArea widget and let it pass to the default gtk event // handler. gdk_window_unref (event->any.window); event->any.window = GTK_WIDGET (window->GetMozArea())->window; gdk_window_ref (event->any.window); // dispatch it. gtk_main_do_event(event); return; } // o there's a superwin grab in effect but no gtk grab else if (!current_grab && superwin_grab) { // easy. let the nsWindow associated with the superwin handle // the event. dispatch_superwin_event(event, window); return; } // o there's both a gtk and superwin grab in effect else if (current_grab && superwin_grab) { // this is where things get messy. we need to see if the // superwin is a child of the grabbing gtk widget. if it is, // pass it to the nsWindow to be handled // nsWindow *grabbingWindow = nsWindow::GetGrabWindow(); GdkWindow *grabbingGdkWindow = (GdkWindow *)grabbingWindow->GetNativeData(NS_NATIVE_WINDOW); GtkWidget *grabbingMozArea = grabbingWindow->GetMozArea(); NS_RELEASE(grabbingWindow); if (gtk_widget_child_of_gdk_window(current_grab, grabbingGdkWindow)) { gdk_window_unref (event->any.window); event->any.window = grabbingMozArea->window; gdk_window_ref (event->any.window); // dispatch it. gtk_main_do_event(event); return; } // if it in't then rewrite the event to the nearest MozArea // widget since it will get rewritten again in gtk. else { dispatch_superwin_event(event, window); return; } } } else { // we need to make sure that if we're doing a gdk_pointer_grab on // a superwin that we check to make sure that we account for a // real GtkWidget being inside of it. the grab code in gtk that // checks to see if a window is contained within a grabbed window // only knows how to walk up a heirarchy of GtkWidget objects, not // GdkWindow objects. so, we have to fake it out. // the most obvious example of this is a popup scrolling window // with a gtk scrollbar in it. nsWindow *grabbingWindow = nsWindow::GetGrabWindow(); GtkWidget *tempWidget = NULL; if (grabbingWindow) { // get the GdkWindow that we are grabbing on GdkWindow *grabbingGdkWindow = (GdkWindow *)grabbingWindow->GetNativeData(NS_NATIVE_WINDOW); // release our nsIWidget NS_RELEASE(grabbingWindow); // only if this is a GtkWidget object if (GTK_IS_WIDGET(object)) { tempWidget = (GtkWidget *)object; // check to see if this widget is the child of the GdkWindow // that we are grabbing on. if (gtk_widget_child_of_gdk_window(tempWidget, grabbingGdkWindow)) { // this is so awesome. gtk_grab_add/gtk_grab_remove don't // have exact push and pop semantics. if we call grab_add // on the object and as part of the dispatching of this // event another grab_add is called on it, we can end up // releasing the grab early because we always call // grab_remove on the object after dispatching the event. // we can end up with a widget that doesn't have a grab on // it, even though it should. the scrollbar does this. // // so, what we do here is to check and see if the widget in // question has a parent. if it does and it's a mozbox // object we slam a grab on that instead of the widget // itself. we know that no one is doing grabs on those. if (tempWidget->parent) { if (GTK_IS_MOZBOX(tempWidget->parent)) { tempWidget = tempWidget->parent; } } gtk_grab_add(tempWidget); } } } gtk_main_do_event (event); if (tempWidget) gtk_grab_remove(tempWidget); } } void dispatch_superwin_event(GdkEvent *event, nsWindow *window) { if (event->type == GDK_KEY_PRESS || event->type == GDK_KEY_RELEASE) { // Check to see whether or not we need to send this to the // toplevel window to get passed to the GtkWidget with focus. // This happens in the embedding case. if (!window->sFocusWindow) { GtkWidget *mozArea = window->GetMozArea(); NS_ASSERTION(mozArea, "Failed to get GtkMozArea for superwin event!\n"); // pass it off to gtk's event system and return gtk_propagate_event(mozArea, event); return; } } switch (event->type) { case GDK_KEY_PRESS: handle_key_press_event (NULL, &event->key, window); break; case GDK_KEY_RELEASE: handle_key_release_event (NULL, &event->key, window); break; default: window->HandleGDKEvent (event); break; } } PRBool superwin_child_of_gtk_widget(nsWindow *window, GtkWidget *widget) { GtkWidget *this_widget = GTK_WIDGET(window->GetMozArea()); while (this_widget) { if (this_widget == widget) { return PR_TRUE; } this_widget = this_widget->parent; } return PR_FALSE; } PRBool gtk_widget_child_of_gdk_window(GtkWidget *widget, GdkWindow *window) { GdkWindow *gdk_window = widget->window; GdkWindow *this_window = gdk_window; while (this_window) { if (this_window == window) return PR_TRUE; this_window = gdk_window_get_parent(this_window); } return PR_FALSE; } //============================================================== void handle_xlib_shell_event(GdkSuperWin *superwin, XEvent *event, gpointer p) { nsWindow *window = (nsWindow *)p; switch(event->xany.type) { case ConfigureNotify: window->HandleXlibConfigureNotifyEvent(event); break; default: break; } } //============================================================== void handle_superwin_paint(gint aX, gint aY, gint aWidth, gint aHeight, gpointer aData) { nsWindow *window = (nsWindow *)aData; nsRect rect; rect.x = aX; rect.y = aY; rect.width = aWidth; rect.height = aHeight; window->Invalidate(rect, PR_FALSE); } void handle_superwin_flush(gpointer aData) { nsWindow *window = (nsWindow *)aData; window->Update(); } gboolean handle_configure_event(GtkWidget *w, GdkEventConfigure *conf, gpointer p) { // This event handler is only installed on toplevel windows nsWindow *widget = (nsWindow *)p; // Find out if the window position has changed nsRect oldBounds; widget->GetBounds(oldBounds); // this is really supposed to be get_origin, not get_root_origin // - bryner nscoord x,y; gdk_window_get_origin(w->window, &x, &y); if ((oldBounds.x == x) && (oldBounds.y == y)) { #ifdef DEBUG_MOVE PRINTF("Window: No position change\n"); #endif return PR_FALSE; } #ifdef DEBUG_MOVE PRINTF("Window: Move from (%d,%d) to (%d,%d)\n", oldBounds.x, oldBounds.y, x, y); #endif widget->OnMove(x, y); return PR_FALSE; }