Back to index

lightning-sunbird  0.9+nobinonly
nsGtkEventHandler.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Peter Annema <disttsc@bart.nl>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsWidget.h"
00040 #include "keysym2ucs.h"
00041 #include "nsWindow.h"
00042 #include "nsAppShell.h"
00043 
00044 #include "nsGUIEvent.h"
00045 
00046 #include "nsTextWidget.h"
00047 #include "nsGtkIMEHelper.h"
00048 
00049 
00050 #include <stdio.h>
00051 
00052 #include <gtk/gtk.h>
00053 #include <gtk/gtkprivate.h>
00054 #include "nsGtkEventHandler.h"
00055 
00056 #include <gdk/gdkkeysyms.h>
00057 #include <X11/Xlib.h>
00058 #include <X11/Xutil.h>
00059 
00060 #ifdef DEBUG_pavlov
00061 //#define DEBUG_EVENTS 1
00062 #endif
00063 
00064 //#define DEBUG_MOVE
00065 
00066 static void
00067 dispatch_superwin_event(GdkEvent *event, nsWindow *window);
00068 
00069 static PRBool
00070 gdk_window_child_of_gdk_window(GdkWindow *window, GdkWindow *ancestor);
00071 
00072 // This is a flag that says if we should suppress the next key down
00073 // event.  This is used when that last key release event was
00074 // suppressed and we want the next key press event to not generate the
00075 // key down event since it is part of a key repeat sequence.
00076 static PRBool suppressNextKeyDown = PR_FALSE;
00077 
00078 //==============================================================
00079 void InitAllocationEvent(GtkAllocation *aAlloc,
00080                          nsSizeEvent &anEvent)
00081 {
00082   if (aAlloc != nsnull) {
00083     // HACK
00084     //    nsRect *foo = new nsRect(aAlloc->x, aAlloc->y, aAlloc->width, aAlloc->height);
00085     nsRect *foo = new nsRect(0, 0, aAlloc->width, aAlloc->height);
00086     anEvent.windowSize = foo;
00087     //    anEvent.point.x = aAlloc->x;
00088     //    anEvent.point.y = aAlloc->y;
00089     // HACK
00090     anEvent.mWinWidth = aAlloc->width;
00091     anEvent.mWinHeight = aAlloc->height;
00092   }
00093 
00094   anEvent.time = PR_IntervalNow();
00095 }
00096 
00097 //==============================================================
00098 struct nsKeyConverter {
00099   int vkCode; // Platform independent key code
00100   int keysym; // GDK keysym key code
00101 };
00102 
00103 //
00104 // Netscape keycodes are defined in widget/public/nsGUIEvent.h
00105 // GTK keycodes are defined in <gdk/gdkkeysyms.h>
00106 //
00107 struct nsKeyConverter nsKeycodes[] = {
00108   { NS_VK_CANCEL,     GDK_Cancel },
00109   { NS_VK_BACK,       GDK_BackSpace },
00110   { NS_VK_TAB,        GDK_Tab },
00111   { NS_VK_TAB,        GDK_ISO_Left_Tab },
00112   { NS_VK_CLEAR,      GDK_Clear },
00113   { NS_VK_RETURN,     GDK_Return },
00114   { NS_VK_SHIFT,      GDK_Shift_L },
00115   { NS_VK_SHIFT,      GDK_Shift_R },
00116   { NS_VK_CONTROL,    GDK_Control_L },
00117   { NS_VK_CONTROL,    GDK_Control_R },
00118   { NS_VK_ALT,        GDK_Alt_L },
00119   { NS_VK_ALT,        GDK_Alt_R },
00120   { NS_VK_META,       GDK_Meta_L },
00121   { NS_VK_META,       GDK_Meta_R },
00122   { NS_VK_PAUSE,      GDK_Pause },
00123   { NS_VK_CAPS_LOCK,  GDK_Caps_Lock },
00124   { NS_VK_ESCAPE,     GDK_Escape },
00125   { NS_VK_SPACE,      GDK_space },
00126   { NS_VK_PAGE_UP,    GDK_Page_Up },
00127   { NS_VK_PAGE_DOWN,  GDK_Page_Down },
00128   { NS_VK_END,        GDK_End },
00129   { NS_VK_HOME,       GDK_Home },
00130   { NS_VK_LEFT,       GDK_Left },
00131   { NS_VK_UP,         GDK_Up },
00132   { NS_VK_RIGHT,      GDK_Right },
00133   { NS_VK_DOWN,       GDK_Down },
00134   { NS_VK_PRINTSCREEN, GDK_Print },
00135   { NS_VK_INSERT,     GDK_Insert },
00136   { NS_VK_DELETE,     GDK_Delete },
00137 
00138   // keypad keys
00139   { NS_VK_LEFT,       GDK_KP_Left },
00140   { NS_VK_RIGHT,      GDK_KP_Right },
00141   { NS_VK_UP,         GDK_KP_Up },
00142   { NS_VK_DOWN,       GDK_KP_Down },
00143   { NS_VK_PAGE_UP,    GDK_KP_Page_Up },
00144     // Not sure what these are
00145     //{ NS_VK_,       GDK_KP_Prior },
00146     //{ NS_VK_,        GDK_KP_Next },
00147     // GDK_KP_Begin is the 5 on the non-numlock keypad
00148     //{ NS_VK_,        GDK_KP_Begin },
00149   { NS_VK_PAGE_DOWN,  GDK_KP_Page_Down },
00150   { NS_VK_HOME,       GDK_KP_Home },
00151   { NS_VK_END,        GDK_KP_End },
00152   { NS_VK_INSERT,     GDK_KP_Insert },
00153   { NS_VK_DELETE,     GDK_KP_Delete },
00154 
00155   { NS_VK_MULTIPLY,   GDK_KP_Multiply },
00156   { NS_VK_ADD,        GDK_KP_Add },
00157   { NS_VK_SEPARATOR,  GDK_KP_Separator },
00158   { NS_VK_SUBTRACT,   GDK_KP_Subtract },
00159   { NS_VK_DECIMAL,    GDK_KP_Decimal },
00160   { NS_VK_DIVIDE,     GDK_KP_Divide },
00161   { NS_VK_RETURN,     GDK_KP_Enter },
00162   { NS_VK_NUM_LOCK,   GDK_Num_Lock },
00163   { NS_VK_SCROLL_LOCK,GDK_Scroll_Lock },
00164 
00165   { NS_VK_COMMA,      GDK_comma },
00166   { NS_VK_PERIOD,     GDK_period },
00167   { NS_VK_SLASH,      GDK_slash },
00168   { NS_VK_BACK_SLASH, GDK_backslash },
00169   { NS_VK_BACK_QUOTE, GDK_grave },
00170   { NS_VK_OPEN_BRACKET, GDK_bracketleft },
00171   { NS_VK_CLOSE_BRACKET, GDK_bracketright },
00172   { NS_VK_SEMICOLON, GDK_colon },
00173   { NS_VK_QUOTE, GDK_apostrophe },
00174 
00175   // context menu key, keysym 0xff67, typically keycode 117 on 105-key (Microsoft) 
00176   // x86 keyboards, located between right 'Windows' key and right Ctrl key
00177   { NS_VK_CONTEXT_MENU, GDK_Menu },
00178 
00179   // NS doesn't have dash or equals distinct from the numeric keypad ones,
00180   // so we'll use those for now.  See bug 17008:
00181   { NS_VK_SUBTRACT, GDK_minus },
00182   { NS_VK_EQUALS, GDK_equal },
00183 
00184   // Some shifted keys, see bug 15463 as well as 17008.
00185   // These should be subject to different keyboard mappings.
00186   { NS_VK_QUOTE, GDK_quotedbl },
00187   { NS_VK_OPEN_BRACKET, GDK_braceleft },
00188   { NS_VK_CLOSE_BRACKET, GDK_braceright },
00189   { NS_VK_BACK_SLASH, GDK_bar },
00190   { NS_VK_SEMICOLON, GDK_semicolon },
00191   { NS_VK_BACK_QUOTE, GDK_asciitilde },
00192   { NS_VK_COMMA, GDK_less },
00193   { NS_VK_PERIOD, GDK_greater },
00194   { NS_VK_SLASH,      GDK_question },
00195   { NS_VK_1, GDK_exclam },
00196   { NS_VK_2, GDK_at },
00197   { NS_VK_3, GDK_numbersign },
00198   { NS_VK_4, GDK_dollar },
00199   { NS_VK_5, GDK_percent },
00200   { NS_VK_6, GDK_asciicircum },
00201   { NS_VK_7, GDK_ampersand },
00202   { NS_VK_8, GDK_asterisk },
00203   { NS_VK_9, GDK_parenleft },
00204   { NS_VK_0, GDK_parenright },
00205   { NS_VK_SUBTRACT, GDK_underscore },
00206   { NS_VK_EQUALS, GDK_plus }
00207 };
00208 
00209 #define IS_XSUN_XSERVER(dpy) \
00210     (strstr(XServerVendor(dpy), "Sun Microsystems") != NULL)
00211 
00212 // map Sun Keyboard special keysyms on to NS_VK keys
00213 struct nsKeyConverter nsSunKeycodes[] = {
00214   {NS_VK_ESCAPE, GDK_F11 }, //bug 57262, Sun Stop key generates F11 keysym
00215   {NS_VK_F11, 0x1005ff10 }, //Sun F11 key generates SunF36(0x1005ff10) keysym
00216   {NS_VK_F12, 0x1005ff11 }, //Sun F12 key generates SunF37(0x1005ff11) keysym
00217   {NS_VK_PAGE_UP,    GDK_F29 }, //KP_Prior
00218   {NS_VK_PAGE_DOWN,  GDK_F35 }, //KP_Next
00219   {NS_VK_HOME,       GDK_F27 }, //KP_Home
00220   {NS_VK_END,        GDK_F33 }, //KP_End
00221 };
00222 
00223 //==============================================================
00224 
00225 // Input keysym is in gtk format; output is in NS_VK format
00226 int nsPlatformToDOMKeyCode(GdkEventKey *aGEK)
00227 {
00228   int i, length = 0;
00229   int keysym = aGEK->keyval;
00230 
00231   // First, try to handle alphanumeric input, not listed in nsKeycodes:
00232   // most likely, more letters will be getting typed in than things in
00233   // the key list, so we will look through these first.
00234 
00235   // since X has different key symbols for upper and lowercase letters and
00236   // mozilla does not, convert gdk's to mozilla's
00237   if (keysym >= GDK_a && keysym <= GDK_z)
00238     return keysym - GDK_a + NS_VK_A;
00239   if (keysym >= GDK_A && keysym <= GDK_Z)
00240     return keysym - GDK_A + NS_VK_A;
00241 
00242   // numbers
00243   if (keysym >= GDK_0 && keysym <= GDK_9)
00244     return keysym - GDK_0 + NS_VK_0;
00245 
00246   // keypad numbers
00247   if (keysym >= GDK_KP_0 && keysym <= GDK_KP_9)
00248     return keysym - GDK_KP_0 + NS_VK_NUMPAD0;
00249 
00250   // map Sun Keyboard special keysyms
00251   if (IS_XSUN_XSERVER(GDK_DISPLAY())) {
00252     length = sizeof(nsSunKeycodes) / sizeof(struct nsKeyConverter);
00253     for (i = 0; i < length; i++) {
00254       if (nsSunKeycodes[i].keysym == keysym)
00255         return(nsSunKeycodes[i].vkCode);
00256     }
00257   }
00258 
00259   // misc other things
00260   length = sizeof(nsKeycodes) / sizeof(struct nsKeyConverter);
00261   for (i = 0; i < length; i++) {
00262     if (nsKeycodes[i].keysym == keysym)
00263       return(nsKeycodes[i].vkCode);
00264   }
00265 
00266   if (keysym >= GDK_F1 && keysym <= GDK_F24)
00267     return keysym - GDK_F1 + NS_VK_F1;
00268 
00269 #if defined(DEBUG_akkana) || defined(DEBUG_ftang)
00270   printf("No match in nsPlatformToDOMKeyCode: keysym is 0x%x, string is '%s', keyval = %d\n", keysym, aGEK->string, aGEK->keyval);
00271 #endif
00272 
00273   return((int)0);
00274 }
00275 
00276 //==============================================================
00277 
00278 // Convert gdk key event keyvals to char codes if printable, 0 otherwise
00279 PRUint32 nsConvertCharCodeToUnicode(GdkEventKey* aGEK)
00280 {
00281   // Anything above 0xf000 is considered a non-printable
00282   // Exception: directly encoded UCS characters
00283   if (aGEK->keyval > 0xf000 && (aGEK->keyval & 0xff000000) != 0x01000000) {
00284     // Keypad keys are an exception: they return a value different
00285     // from their non-keypad equivalents, but mozilla doesn't distinguish.
00286     switch (aGEK->keyval)
00287     {
00288       case GDK_KP_Space:
00289         return ' ';
00290       case GDK_KP_Equal:
00291         return '=';
00292       case GDK_KP_Multiply:
00293         return '*';
00294       case GDK_KP_Add:
00295         return '+';
00296       case GDK_KP_Separator:
00297         return ',';
00298       case GDK_KP_Subtract:
00299         return '-';
00300       case GDK_KP_Decimal:
00301         return '.';
00302       case GDK_KP_Divide:
00303         return '/';
00304       case GDK_KP_0:
00305         return '0';
00306       case GDK_KP_1:
00307         return '1';
00308       case GDK_KP_2:
00309         return '2';
00310       case GDK_KP_3:
00311         return '3';
00312       case GDK_KP_4:
00313         return '4';
00314       case GDK_KP_5:
00315         return '5';
00316       case GDK_KP_6:
00317         return '6';
00318       case GDK_KP_7:
00319         return '7';
00320       case GDK_KP_8:
00321         return '8';
00322       case GDK_KP_9:
00323         return '9';
00324     }
00325 
00326     // non-printables
00327     return 0;
00328   }
00329 
00330 #if defined(USE_XIM) && defined(_AIX)
00331   // On AIX, GDK doesn't get correct keysyms from XIM. Follow GDK 
00332   // reference recommending to use the 'string' member of GdkEventKey 
00333   // instead. See:
00334   //
00335   // developer.gnome.org/doc/API/gdk/gdk-event-structures.html#GDKEVENTKEY
00336 
00337   PRBool controlChar = (aGEK->state & GDK_CONTROL_MASK ||
00338                         aGEK->state & GDK_MOD1_MASK ||
00339                         aGEK->state & GDK_MOD4_MASK);
00340 
00341   // Use 'string' as opposed to 'keyval' when control, alt, or meta is 
00342   // not pressed. This allows keyboard shortcuts to continue to function
00343   // properly, while fixing input problems and mode switching in certain
00344   // locales where the 'keyval' does not correspond to the actual input.
00345   // See Bug #157397 and Bug #161581 for more details.
00346 
00347   if (!controlChar && gdk_im_ready() && aGEK->length > 0 
00348         && aGEK->keyval != (guint)*aGEK->string) {
00349     nsGtkIMEHelper* IMEHelper = nsGtkIMEHelper::GetSingleton();
00350     if (IMEHelper != nsnull) {
00351       PRUnichar* unichars = IMEHelper->GetUnichars();
00352       PRInt32 unilen = IMEHelper->GetUnicharsSize();
00353       PRInt32 unichar_size = IMEHelper->MultiByteToUnicode(
00354                                           aGEK->string, aGEK->length,
00355                                           &unichars, &unilen);
00356       if (unichar_size > 0) {
00357         IMEHelper->SetUnichars(unichars);
00358         IMEHelper->SetUnicharsSize(unilen);
00359         return (long)*unichars;
00360       }
00361     }
00362   }
00363 #endif
00364   // we're supposedly printable, let's try to convert
00365   long ucs = keysym2ucs(aGEK->keyval);
00366   if ((ucs != -1) && (ucs < 0x10000))
00367     return ucs;
00368 
00369   // I guess we couldn't convert
00370   return 0;
00371 }
00372 
00373 //==============================================================
00374 void InitKeyEvent(GdkEventKey *aGEK,
00375                             nsKeyEvent &anEvent)
00376 {
00377   if (aGEK != nsnull) {
00378     anEvent.keyCode = nsPlatformToDOMKeyCode(aGEK);
00379     anEvent.time = aGEK->time;
00380     anEvent.isShift = (aGEK->state & GDK_SHIFT_MASK) ? PR_TRUE : PR_FALSE;
00381     anEvent.isControl = (aGEK->state & GDK_CONTROL_MASK) ? PR_TRUE : PR_FALSE;
00382     anEvent.isAlt = (aGEK->state & GDK_MOD1_MASK) ? PR_TRUE : PR_FALSE;
00383     anEvent.isMeta = (aGEK->state & GDK_MOD4_MASK) ? PR_TRUE : PR_FALSE;
00384   }
00385 }
00386 
00387 void InitKeyPressEvent(GdkEventKey *aGEK,
00388                        nsKeyEvent &anEvent)
00389 {
00390   //
00391   // init the basic event fields
00392   //
00393 
00394   if (aGEK!=nsnull) {
00395     anEvent.isShift = (aGEK->state & GDK_SHIFT_MASK) ? PR_TRUE : PR_FALSE;
00396     anEvent.isControl = (aGEK->state & GDK_CONTROL_MASK) ? PR_TRUE : PR_FALSE;
00397     anEvent.isAlt = (aGEK->state & GDK_MOD1_MASK) ? PR_TRUE : PR_FALSE;
00398     anEvent.isMeta = (aGEK->state & GDK_MOD4_MASK) ? PR_TRUE : PR_FALSE;
00399 
00400     anEvent.charCode = nsConvertCharCodeToUnicode(aGEK);
00401     if (anEvent.charCode) {
00402       // if the control, meta, or alt key is down, then we should leave
00403       // the isShift flag alone (probably not a printable character)
00404       // if none of the other modifier keys are pressed then we need to
00405       // clear isShift so the character can be inserted in the editor
00406 
00407       if ( anEvent.isControl || anEvent.isAlt || anEvent.isMeta ) {
00408          // make Ctrl+uppercase functional as same as Ctrl+lowercase
00409          // when Ctrl+uppercase(eg.Ctrl+C) is pressed,convert the charCode
00410          // from uppercase to lowercase(eg.Ctrl+c),so do Alt and Meta Key
00411          // It is hack code for bug 61355, there is same code snip for
00412          // Windows platform in widget/src/windows/nsWindow.cpp: See bug 16486
00413          // Note: if Shift is pressed at the same time, do not to_lower()
00414          // Because Ctrl+Shift has different function with Ctrl
00415         if ( (anEvent.charCode >= GDK_A && anEvent.charCode <= GDK_Z) ||
00416              (anEvent.charCode >= GDK_a && anEvent.charCode <= GDK_z) ) {
00417           anEvent.charCode = (anEvent.isShift) ? 
00418             gdk_keyval_to_upper(anEvent.charCode) : 
00419             gdk_keyval_to_lower(anEvent.charCode);
00420         }
00421       }
00422     } else {
00423       anEvent.keyCode = nsPlatformToDOMKeyCode(aGEK);
00424     }
00425 
00426 #if defined(DEBUG_akkana_not) || defined (DEBUG_ftang)
00427     if (!aGEK->length) printf("!length, ");
00428     printf("Key Press event: gtk string = '%s', keyval = '%c' = %d,\n",
00429            aGEK->string, aGEK->keyval, aGEK->keyval);
00430     printf("    --> keyCode = 0x%x, char code = '%c'",
00431            anEvent.keyCode, anEvent.charCode);
00432     if (anEvent.isShift)
00433       printf(" [shift]");
00434     if (anEvent.isControl)
00435       printf(" [ctrl]");
00436     if (anEvent.isAlt)
00437       printf(" [alt]");
00438     if (anEvent.isMeta)
00439       printf(" [meta]");
00440     printf("\n");
00441 #endif
00442 
00443     anEvent.time = aGEK->time;
00444   }
00445 }
00446 
00447 /*==============================================================
00448   ==============================================================
00449   =============================================================
00450   ==============================================================*/
00451 
00452 void handle_size_allocate(GtkWidget *w, GtkAllocation *alloc, gpointer p)
00453 {
00454   nsWindow *widget = (nsWindow *)p;
00455   nsSizeEvent event(PR_TRUE, NS_SIZE, widget);
00456 
00457   InitAllocationEvent(alloc, event);
00458   NS_ADDREF(widget);
00459   widget->OnResize(&event);
00460   NS_RELEASE(widget);
00461 
00462   delete event.windowSize;
00463 }
00464 
00465 // GTK's text widget already does XIM, so we don't want to do this again
00466 gint handle_key_press_event_for_text(GtkObject *w, GdkEventKey* event,
00467                                      gpointer p)
00468 {
00469   nsTextWidget* win = (nsTextWidget*)p;
00470 
00471   // work around for annoying things.
00472   if (event->keyval == GDK_Tab)
00473     if (event->state & GDK_CONTROL_MASK)
00474       if (event->state & GDK_MOD1_MASK)
00475         return PR_FALSE;
00476 
00477   NS_ADDREF(win);
00478   nsKeyEvent keyDownEvent(PR_TRUE, NS_KEY_DOWN, win);
00479   InitKeyEvent(event, keyDownEvent);
00480   PRBool noDefault = win->OnKey(keyDownEvent);
00481 
00482   // Don't pass Shift, Control, Alt and Meta as NS_KEY_PRESS events.
00483   if (event->keyval == GDK_Shift_L
00484       || event->keyval == GDK_Shift_R
00485       || event->keyval == GDK_Control_L
00486       || event->keyval == GDK_Control_R
00487       || event->keyval == GDK_Alt_L
00488       || event->keyval == GDK_Alt_R
00489       || event->keyval == GDK_Meta_L
00490       || event->keyval == GDK_Meta_R
00491      )
00492     return PR_TRUE;
00493 
00494   //
00495   // Second, dispatch the Key event as a key press event w/ a Unicode
00496   //  character code.  Note we have to check for modifier keys, since
00497   // gtk returns a character value for them
00498   //
00499   nsKeyEvent keyPressEvent(PR_TRUE, NS_KEY_PRESS, win);
00500   InitKeyPressEvent(event, keyPressEvent);
00501   if (noDefault) {  // If prevent default set for onkeydown, do the same for onkeypress
00502    keyPressEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;
00503   }
00504 
00505   win->OnKey(keyPressEvent);
00506 
00507   NS_RELEASE(win);
00508   if (w)
00509   {
00510     gtk_signal_emit_stop_by_name (GTK_OBJECT(w), "key_press_event");
00511   }
00512 
00513   return PR_TRUE;
00514 }
00515 
00516 // GTK's text widget already does XIM, so we don't want to do this again
00517 gint handle_key_release_event_for_text(GtkObject *w, GdkEventKey* event,
00518                                        gpointer p)
00519 {
00520   nsTextWidget* win = (nsTextWidget*)p;
00521   nsKeyEvent kevent(PR_TRUE, NS_KEY_UP, win);
00522   InitKeyEvent(event, kevent);
00523   NS_ADDREF(win);
00524   win->OnKey(kevent);
00525   NS_RELEASE(win);
00526   
00527   if (w)
00528   {
00529     gtk_signal_emit_stop_by_name (GTK_OBJECT(w), "key_release_event");
00530   }
00531 
00532   return PR_TRUE;
00533 }
00534 
00535 
00536 //==============================================================
00537 gint handle_key_press_event(GtkObject *w, GdkEventKey* event, gpointer p)
00538 {
00539   nsWidget *win = (nsWidget*)p;
00540 
00541   // if there's a focused window rewrite the event to use that window.
00542   if (nsWidget::sFocusWindow)
00543     win = nsWidget::sFocusWindow;
00544 
00545   // work around for annoying things.
00546   if (event->keyval == GDK_Tab)
00547     if (event->state & GDK_CONTROL_MASK)
00548       if (event->state & GDK_MOD1_MASK)
00549         return PR_FALSE;
00550 
00551 
00552   NS_ADDREF(win);
00553 
00554   //
00555   // First, dispatch the Key event as a virtual key down event
00556   //   but lie about where it came from and say it is from the
00557   //   window that currently has focus inside our app...
00558   //
00559   PRBool noDefault = PR_FALSE;
00560   nsKeyEvent keyDownEvent(PR_TRUE, NS_KEY_DOWN, win);
00561   InitKeyEvent(event, keyDownEvent);
00562   // if we need to suppress this NS_KEY_DOWN event, reset the flag
00563   if (suppressNextKeyDown == PR_TRUE)
00564     suppressNextKeyDown = PR_FALSE;
00565   else
00566     noDefault = win->OnKey(keyDownEvent);
00567 
00568   // Don't pass Shift, Alt, Control and Meta as NS_KEY_PRESS events.
00569   if (event->keyval == GDK_Shift_L
00570       || event->keyval == GDK_Shift_R
00571       || event->keyval == GDK_Control_L
00572       || event->keyval == GDK_Control_R
00573       || event->keyval == GDK_Alt_L
00574       || event->keyval == GDK_Alt_R
00575       || event->keyval == GDK_Meta_L
00576       || event->keyval == GDK_Meta_R)
00577     return PR_TRUE;
00578 
00579   //
00580   // Second, dispatch the Key event as a key press event w/ a Unicode
00581   //  character code.  Note we have to check for modifier keys, since
00582   // gtk returns a character value for them
00583   //
00584 
00585   // Call nsConvertCharCodeToUnicode() here to get kevent.charCode 
00586   nsKeyEvent keyPressEvent(PR_TRUE, NS_KEY_PRESS, win);
00587   InitKeyPressEvent(event, keyPressEvent);
00588   if (noDefault) {  // If prevent default set for onkeydown, do the same for onkeypress
00589     keyPressEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;
00590   }
00591 
00592   if (event->length) {
00593     if (keyPressEvent.charCode || keyPressEvent.keyCode) {
00594       // keyPressEvent.charCode or keyPressEvent.keyCode is valid, just 
00595       // pass to OnKey()
00596       win->OnKey(keyPressEvent);
00597     } else if (nsGtkIMEHelper::GetSingleton()) {
00598       // commit request from IME
00599       win->IMECommitEvent(event);
00600     }
00601   } else { // for Home/End/Up/Down/Left/Right/PageUp/PageDown key
00602     win->OnKey(keyPressEvent);
00603   }
00604 
00605   NS_RELEASE(win);
00606 
00607   if (w)
00608   {
00609     gtk_signal_emit_stop_by_name (GTK_OBJECT(w), "key_press_event");
00610   }
00611 
00612   return PR_TRUE;
00613 }
00614 
00615 //==============================================================
00616 gint handle_key_release_event(GtkObject *w, GdkEventKey* event, gpointer p)
00617 {
00618   XEvent nextEvent;
00619   PRBool    shouldDrop = PR_FALSE;
00620   // According to the DOM spec if this is the result of a key repeat
00621   // event we don't let it through since the DOM wants the event
00622   // stream to look like this: press press press press release.  The
00623   // way that X does things is to do press release press release, etc.
00624   // We check to see if this is a key release by checking to see if
00625   // the next event in the queue is a key press event and it has the
00626   // exact same timestamp as the current event.
00627 
00628   // have a look in the X queue to see if there's another event in the
00629   // queue.
00630 
00631   if (XPending(GDK_DISPLAY())) {
00632     // get a copy of the next event
00633     XPeekEvent(GDK_DISPLAY(), &nextEvent);
00634     
00635     // see if it's a key press event and if it has the same time as
00636     // the last event.
00637     if ((nextEvent.xany.type == KeyPress) &&
00638         (nextEvent.xkey.time == event->time))
00639     {
00640       shouldDrop = PR_TRUE;
00641       // the next key press event shouldn't generate a key down event.
00642       // this is a global variable
00643       suppressNextKeyDown = PR_TRUE;
00644     }
00645   }
00646 
00647   // should we drop this event?
00648   if (shouldDrop)
00649     return PR_TRUE;
00650 
00651   nsWidget *win = (nsWidget *)p;
00652   if (nsWidget::sFocusWindow)
00653     win = nsWidget::sFocusWindow;
00654 
00655   nsKeyEvent kevent(PR_TRUE, NS_KEY_UP, win);
00656   InitKeyEvent(event, kevent);
00657 
00658   NS_ADDREF(win);
00659   win->OnKey(kevent);
00660   NS_RELEASE(win);
00661 
00662   if (w)
00663   {
00664     gtk_signal_emit_stop_by_name (GTK_OBJECT(w), "key_release_event");
00665   }
00666 
00667   return PR_TRUE;
00668 }
00669 
00670 //==============================================================
00671 void 
00672 handle_gdk_event (GdkEvent *event, gpointer data)
00673 {
00674   GtkObject *eventObject = nsnull;
00675 
00676   // set the last time that we got an event.  we need this for drag
00677   // and drop if you can believe that.
00678   guint32 event_time = gdk_event_get_time(event);
00679   if (event_time)
00680     nsWidget::SetLastEventTime(event_time);
00681 
00682   // Get the next X event serial ID and save it for later for event
00683   // processing.  If it stays zero then events won't be processed
00684   // later.  We're using this as a flag too but with a number this big
00685   // we're probably not going to overflow and even if we did there
00686   // wouldn't be any harm.
00687   unsigned long serial = 0;
00688 
00689   if (XPending(GDK_DISPLAY())) {
00690     XEvent temp_event;
00691     XPeekEvent(GDK_DISPLAY(), &temp_event);
00692     serial = temp_event.xany.serial - 1;
00693   }
00694 
00695   // try to get the user data for the event window.
00696   if (event->any.window)
00697     gdk_window_get_user_data (event->any.window, (void **)&eventObject);
00698 
00699   // If there is an event object and it's a superwin then we need to
00700   // make sure that the event either is handled locally or is passed
00701   // on with a legitimate GtkWidget object associated with it so the
00702   // Gtk mainloop doesn't go spastic.
00703 
00704   if (eventObject && GDK_IS_SUPERWIN(eventObject)) {
00705     // try to find the nsWindow object associated with this superwin
00706     nsWindow *window = (nsWindow *)gtk_object_get_data (eventObject,
00707                                                         "nsWindow");
00708 
00709     // If we don't have a window here anymore, we are probably in
00710     // the process of being or have been destroyed.  give up now.
00711     if (!window)
00712       goto end;
00713 
00714     // Find out if there's a grabbing widget.  If there is and it's a
00715     // regular gtk widget, and our current event window is not the
00716     // child of that grab widget, we need to rewrite the event to that
00717     // gtk grabbing widget.
00718     PRBool rewriteEvent = PR_FALSE;
00719     GtkWidget *grabWidget = gtk_grab_get_current();
00720     GtkWidget *owningWidget = window->GetOwningWidget();
00721 
00722     if (grabWidget &&
00723         !GTK_IS_MOZAREA(grabWidget) &&
00724         !gdk_window_child_of_gdk_window(owningWidget->window,
00725                                         grabWidget->window)) {
00726       rewriteEvent = PR_TRUE;
00727     }
00728 
00729 
00730     // There are a lot of events that are always dispatched to our
00731     // internal handler, no matter if there is a grab or not.
00732     switch(event->type)
00733       {
00734       case GDK_NOTHING:
00735         break;
00736 
00737       case GDK_DESTROY:
00738       case GDK_DELETE:
00739       case GDK_PROPERTY_NOTIFY:
00740       case GDK_EXPOSE:
00741       case GDK_NO_EXPOSE:
00742       case GDK_FOCUS_CHANGE:
00743       case GDK_CONFIGURE:
00744       case GDK_MAP:
00745       case GDK_UNMAP:
00746       case GDK_SELECTION_CLEAR:
00747       case GDK_SELECTION_REQUEST:
00748       case GDK_SELECTION_NOTIFY:
00749       case GDK_CLIENT_EVENT:
00750       case GDK_VISIBILITY_NOTIFY:
00751         dispatch_superwin_event(event, window);
00752         break;
00753 
00754       case GDK_BUTTON_PRESS:
00755       case GDK_2BUTTON_PRESS:
00756       case GDK_3BUTTON_PRESS:
00757       case GDK_KEY_PRESS:
00758       case GDK_KEY_RELEASE:
00759         // If we need to rewrite this to the nearest real gtk widget,
00760         // do it here.
00761         if (rewriteEvent) {
00762           gdk_window_unref(event->any.window);
00763           event->any.window = owningWidget->window;
00764           gdk_window_ref(event->any.window);
00765           gtk_main_do_event(event);
00766           break;
00767         }
00768 
00769         // Otherwise, just send it to our event handler
00770         if (GTK_WIDGET_IS_SENSITIVE(owningWidget))
00771           dispatch_superwin_event(event, window);
00772         break;
00773 
00774       case GDK_MOTION_NOTIFY:
00775       case GDK_BUTTON_RELEASE:
00776       case GDK_PROXIMITY_IN:
00777       case GDK_PROXIMITY_OUT:
00778         // See above.
00779         if (rewriteEvent) {
00780           gdk_window_unref(event->any.window);
00781           event->any.window = owningWidget->window;
00782           gdk_window_ref(event->any.window);
00783           gtk_propagate_event(grabWidget, event);
00784           break;
00785         }
00786 
00787         if (GTK_WIDGET_IS_SENSITIVE(owningWidget))
00788           dispatch_superwin_event(event, window);
00789         break;
00790 
00791       case GDK_ENTER_NOTIFY:
00792       case GDK_LEAVE_NOTIFY:
00793         // Always dispatch enter and leave notify events to the
00794         // windows that they happened on so that state can be properly
00795         // tracked.  The code that handles the enter and leave events
00796         // tracks sensitivity as well.
00797         dispatch_superwin_event(event, window);
00798         break;
00799 
00800       default:
00801         // XXX lots of DND events not handled?  I don't think that we have to.
00802         NS_WARNING("Odd, hit default case in handle_gdk_event()\n");
00803         break;
00804       }
00805   }
00806   else {
00807     nsWindow  *grabbingWindow = nsWindow::GetGrabWindow();
00808     
00809     nsCOMPtr<nsIWidget> grabbingWindowGuard(grabbingWindow);
00810     GtkWidget *tempWidget = NULL;
00811     
00812     if (grabbingWindow) {
00813       // get the GdkWindow that we are grabbing on
00814       GdkWindow *grabbingGdkWindow =
00815         NS_STATIC_CAST(GdkWindow *,
00816                        grabbingWindow->GetNativeData(NS_NATIVE_WINDOW));
00817       
00818       // If the object getting the event in question is a GtkWidget
00819       // and it's a child of the grabbing window, we need to put a
00820       // grab on that window before dispatching the event.  We do this
00821       // so that the gtk mainloop will recognize the widget in
00822       // question as having the grab.  If we didn't the mainloop
00823       // wouldn't think that the widget that got the event is the
00824       // child of the grabbing window, due to the superwin(s) in the
00825       // middle.
00826       if (GTK_IS_WIDGET(eventObject)) {
00827         tempWidget = GTK_WIDGET(eventObject);
00828         if (gdk_window_child_of_gdk_window(tempWidget->window,
00829                                            grabbingGdkWindow)) {
00830           // this is so awesome.  gtk_grab_add/gtk_grab_remove don't
00831           // have exact push and pop semantics.  if we call grab_add
00832           // on the object and as part of the dispatching of this
00833           // event another grab_add is called on it, we can end up
00834           // releasing the grab early because we always call
00835           // grab_remove on the object after dispatching the event.
00836           // we can end up with a widget that doesn't have a grab on
00837           // it, even though it should.  the scrollbar does this.
00838           //
00839           // so, what we do here is to check and see if the widget in
00840           // question has a parent.  if it does and it's a mozbox
00841           // object we slam a grab on that instead of the widget
00842           // itself.  we know that no one is doing grabs on those.
00843           if (tempWidget->parent) {
00844             if (GTK_IS_MOZBOX(tempWidget->parent)) {
00845               tempWidget = tempWidget->parent;
00846             }
00847           }
00848           gtk_grab_add(tempWidget);
00849         }
00850         else  {
00851           // if the gtk widget in question wasn't the child of the
00852           // grabbing window then the grabbing window gets it.
00853           dispatch_superwin_event(event, grabbingWindow);
00854           goto end;
00855         }
00856       }
00857     }
00858 
00859     gtk_main_do_event(event);
00860 
00861     if (tempWidget)
00862       gtk_grab_remove(tempWidget);
00863 
00864     if (event->type == GDK_BUTTON_RELEASE) {
00865       // Always clear the button motion target when sending a
00866       // button release event to a real gtk widget, otherwise
00867       // mozilla will still think it has the grab.  This happens
00868       // when there's a native gtk widget popup over a Mozilla
00869       // area.
00870       nsWidget::DropMotionTarget();
00871     }
00872   }
00873 
00874  end:
00875 
00876   // use the saved serial to process any pending events, now that all
00877   // the window events have been processed
00878   if (serial)
00879     nsAppShell::ProcessBeforeID(serial);
00880 
00881 }
00882 
00883 void
00884 dispatch_superwin_event(GdkEvent *event, nsWindow *window)
00885 {
00886   if (event->type == GDK_KEY_PRESS || event->type == GDK_KEY_RELEASE)
00887   {
00888     // Check to see whether or not we need to send this to the
00889     // toplevel window to get passed to the GtkWidget with focus.
00890     // This happens in the embedding case.
00891     if (!nsWidget::sFocusWindow)
00892     {
00893       GtkWidget *mozArea = window->GetOwningWidget();
00894       NS_ASSERTION(mozArea, "Failed to get GtkMozArea for superwin event!\n");
00895       // get the toplevel window for that widget
00896       GtkWidget *toplevel = gtk_widget_get_toplevel(mozArea);
00897       // pass it off to gtk's event system and return
00898       gboolean handled = gtk_widget_event(toplevel, event);
00899       if (handled)
00900         return;
00901     }
00902   }
00903 
00904   switch (event->type)
00905   {
00906   case GDK_KEY_PRESS:
00907     handle_key_press_event (NULL, &event->key, window);
00908     break;
00909   case GDK_KEY_RELEASE:
00910     handle_key_release_event (NULL, &event->key, window);
00911     break;
00912   default:
00913     window->HandleGDKEvent (event);
00914     break;
00915   }
00916 }
00917 
00918 PRBool
00919 gdk_window_child_of_gdk_window(GdkWindow *window, GdkWindow *ancestor)
00920 {
00921   GdkWindow *this_window = window;
00922   while (this_window)
00923   {
00924     if (this_window == ancestor)
00925       return PR_TRUE;
00926     this_window = gdk_window_get_parent(this_window);
00927   }
00928   return PR_FALSE;
00929 }
00930 
00931 
00932 //==============================================================
00933 void
00934 handle_xlib_shell_event(GdkSuperWin *superwin, XEvent *event, gpointer p)
00935 {
00936   nsWindow *window = (nsWindow *)p;
00937   switch(event->xany.type) {
00938   case ConfigureNotify:
00939     window->HandleXlibConfigureNotifyEvent(event);
00940     break;
00941   default:
00942     break;
00943   }
00944 }
00945 
00946 //==============================================================
00947 void
00948 handle_superwin_paint(gint aX, gint aY,
00949                       gint aWidth, gint aHeight, gpointer aData)
00950 {
00951   nsWindow *window = (nsWindow *)aData;
00952   nsRect    rect;
00953   rect.x = aX;
00954   rect.y = aY;
00955   rect.width = aWidth;
00956   rect.height = aHeight;
00957   window->Invalidate(rect, PR_FALSE);
00958 }
00959 
00960 void
00961 handle_superwin_flush(gpointer aData)
00962 {
00963   nsWindow *window = (nsWindow *)aData;
00964   window->Update();
00965 }
00966 
00967 
00968