X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/af1d24da230cc1ca3b2c2bfbf10d13887e73183f..2d956b58023a4955313a1c1531bc70a0deb0b831:/src/gtk/window.cpp diff --git a/src/gtk/window.cpp b/src/gtk/window.cpp index 17175ec63d..efc124ac8d 100644 --- a/src/gtk/window.cpp +++ b/src/gtk/window.cpp @@ -25,6 +25,7 @@ #include "wx/utils.h" #include "wx/dialog.h" #include "wx/msgdlg.h" +#include "wx/module.h" #if wxUSE_DRAG_AND_DROP #include "wx/dnd.h" @@ -39,7 +40,7 @@ #endif // wxUSE_CARET #if wxUSE_TEXTCTRL -#include "wx/textctrl.h" + #include "wx/textctrl.h" #endif #include "wx/menu.h" @@ -231,13 +232,17 @@ static bool g_captureWindowHasMouse = FALSE; // the last window which had the focus - this is normally never NULL (except // if we never had focus at all) as even when g_focusWindow is NULL it still // keeps its previous value -static wxWindowGTK *g_focusWindowLast = (wxWindowGTK *)NULL; +static wxWindowGTK *g_focusWindowLast = (wxWindowGTK*) NULL; // the frame that is currently active (i.e. its child has focus). It is // used to generate wxActivateEvents -static wxWindowGTK *g_activeFrame = (wxWindowGTK *)NULL; +static wxWindowGTK *g_activeFrame = (wxWindowGTK*) NULL; static bool g_activeFrameLostFocus = FALSE; +// If a window get the focus set but has not been realized +// yet, defer setting the focus to idle time. +wxWindowGTK *g_delayedFocus = (wxWindowGTK*) NULL; + // if we detect that the app has got/lost the focus, we set this variable to // either TRUE or FALSE and an activate event will be sent during the next // OnIdle() call and it is reset to -1: this value means that we shouldn't @@ -487,8 +492,8 @@ gint gtk_window_own_expose_callback( GtkWidget *widget, GdkEventExpose *gdk_even #ifdef __WXGTK20__ (* GTK_WIDGET_CLASS (pizza_parent_class)->expose_event) (widget, gdk_event); - -#endif + +#endif return TRUE; } @@ -503,261 +508,7 @@ static void gtk_window_own_draw_callback( GtkWidget *widget, GdkRectangle *WXUNU draw_frame( widget, win ); } -#endif - -//----------------------------------------------------------------------------- -// key code mapping routines -//----------------------------------------------------------------------------- - -static long map_to_unmodified_wx_keysym( GdkEventKey *event ) -{ - // VZ: it seems that GDK_KEY_RELEASE event doesn't set event->string - // but only event->keyval which is quite useless to us, so remember - // the last character from GDK_KEY_PRESS and resue it as last resort - // - // NB: should be MT-neutral as always called from main thread only - static struct - { - KeySym keysym; - long keycode; - } s_lastKeyPress = { 0, 0 }; - - KeySym keysym = event->keyval; - long key_code; - - switch ( keysym ) - { - case GDK_Shift_L: - case GDK_Shift_R: key_code = WXK_SHIFT; break; - case GDK_Control_L: - case GDK_Control_R: key_code = WXK_CONTROL; break; - case GDK_Meta_L: - case GDK_Meta_R: - case GDK_Alt_L: - case GDK_Alt_R: - case GDK_Super_L: - case GDK_Super_R: key_code = WXK_ALT; break; - case GDK_Menu: key_code = WXK_MENU; break; - case GDK_Help: key_code = WXK_HELP; break; - case GDK_BackSpace: key_code = WXK_BACK; break; - case GDK_ISO_Left_Tab: - case GDK_Tab: key_code = WXK_TAB; break; - case GDK_Linefeed: key_code = WXK_RETURN; break; - case GDK_Clear: key_code = WXK_CLEAR; break; - case GDK_Return: key_code = WXK_RETURN; break; - case GDK_Pause: key_code = WXK_PAUSE; break; - case GDK_Scroll_Lock: key_code = WXK_SCROLL; break; - case GDK_Escape: key_code = WXK_ESCAPE; break; - case GDK_Delete: key_code = WXK_DELETE; break; - case GDK_Home: key_code = WXK_HOME; break; - case GDK_Left: key_code = WXK_LEFT; break; - case GDK_Up: key_code = WXK_UP; break; - case GDK_Right: key_code = WXK_RIGHT; break; - case GDK_Down: key_code = WXK_DOWN; break; - case GDK_Prior: key_code = WXK_PRIOR; break; -// case GDK_Page_Up: key_code = WXK_PAGEUP; break; - case GDK_Next: key_code = WXK_NEXT; break; -// case GDK_Page_Down: key_code = WXK_PAGEDOWN; break; - case GDK_End: key_code = WXK_END; break; - case GDK_Begin: key_code = WXK_HOME; break; - case GDK_Select: key_code = WXK_SELECT; break; - case GDK_Print: key_code = WXK_PRINT; break; - case GDK_Execute: key_code = WXK_EXECUTE; break; - case GDK_Insert: key_code = WXK_INSERT; break; - case GDK_Num_Lock: key_code = WXK_NUMLOCK; break; - - case GDK_KP_0: key_code = WXK_NUMPAD0; break; - case GDK_KP_1: key_code = WXK_NUMPAD1; break; - case GDK_KP_2: key_code = WXK_NUMPAD2; break; - case GDK_KP_3: key_code = WXK_NUMPAD3; break; - case GDK_KP_4: key_code = WXK_NUMPAD4; break; - case GDK_KP_5: key_code = WXK_NUMPAD5; break; - case GDK_KP_6: key_code = WXK_NUMPAD6; break; - case GDK_KP_7: key_code = WXK_NUMPAD7; break; - case GDK_KP_8: key_code = WXK_NUMPAD8; break; - case GDK_KP_9: key_code = WXK_NUMPAD9; break; - case GDK_KP_Space: key_code = WXK_NUMPAD_SPACE; break; - case GDK_KP_Tab: key_code = WXK_NUMPAD_TAB; break; - case GDK_KP_Enter: key_code = WXK_NUMPAD_ENTER; break; - case GDK_KP_F1: key_code = WXK_NUMPAD_F1; break; - case GDK_KP_F2: key_code = WXK_NUMPAD_F2; break; - case GDK_KP_F3: key_code = WXK_NUMPAD_F3; break; - case GDK_KP_F4: key_code = WXK_NUMPAD_F4; break; - case GDK_KP_Home: key_code = WXK_NUMPAD_HOME; break; - case GDK_KP_Left: key_code = WXK_NUMPAD_LEFT; break; - case GDK_KP_Up: key_code = WXK_NUMPAD_UP; break; - case GDK_KP_Right: key_code = WXK_NUMPAD_RIGHT; break; - case GDK_KP_Down: key_code = WXK_NUMPAD_DOWN; break; - case GDK_KP_Prior: key_code = WXK_NUMPAD_PRIOR; break; -// case GDK_KP_Page_Up: key_code = WXK_NUMPAD_PAGEUP; break; - case GDK_KP_Next: key_code = WXK_NUMPAD_NEXT; break; -// case GDK_KP_Page_Down: key_code = WXK_NUMPAD_PAGEDOWN; break; - case GDK_KP_End: key_code = WXK_NUMPAD_END; break; - case GDK_KP_Begin: key_code = WXK_NUMPAD_BEGIN; break; - case GDK_KP_Insert: key_code = WXK_NUMPAD_INSERT; break; - case GDK_KP_Delete: key_code = WXK_NUMPAD_DELETE; break; - case GDK_KP_Equal: key_code = WXK_NUMPAD_EQUAL; break; - case GDK_KP_Multiply: key_code = WXK_NUMPAD_MULTIPLY; break; - case GDK_KP_Add: key_code = WXK_NUMPAD_ADD; break; - case GDK_KP_Separator: key_code = WXK_NUMPAD_SEPARATOR; break; - case GDK_KP_Subtract: key_code = WXK_NUMPAD_SUBTRACT; break; - case GDK_KP_Decimal: key_code = WXK_NUMPAD_DECIMAL; break; - case GDK_KP_Divide: key_code = WXK_NUMPAD_DIVIDE; break; - - case GDK_F1: key_code = WXK_F1; break; - case GDK_F2: key_code = WXK_F2; break; - case GDK_F3: key_code = WXK_F3; break; - case GDK_F4: key_code = WXK_F4; break; - case GDK_F5: key_code = WXK_F5; break; - case GDK_F6: key_code = WXK_F6; break; - case GDK_F7: key_code = WXK_F7; break; - case GDK_F8: key_code = WXK_F8; break; - case GDK_F9: key_code = WXK_F9; break; - case GDK_F10: key_code = WXK_F10; break; - case GDK_F11: key_code = WXK_F11; break; - case GDK_F12: key_code = WXK_F12; break; - default: - { - // do we have the translation? - if ( event->length == 1 ) - { - keysym = (KeySym)event->string[0]; - } - else if ( (keysym & 0xFF) != keysym ) - { - // non ASCII key, what to do? - - if ( event->type == GDK_KEY_RELEASE ) - { - // reuse the one from the last keypress if any - if ( keysym == s_lastKeyPress.keysym ) - { - key_code = s_lastKeyPress.keycode; - - // skip "return 0" - break; - } - } - - // ignore this one, we don't know it - return 0; - } - //else: ASCII key, ok - - guint upper = gdk_keyval_to_upper( (guint)keysym ); - key_code = upper ? upper : keysym; - - if ( event->type == GDK_KEY_PRESS ) - { - // remember it to be reused below later - s_lastKeyPress.keysym = keysym; - s_lastKeyPress.keycode = key_code; - } - } - } - - return key_code; -} - -static long map_to_wx_keysym( GdkEventKey *event ) -{ - KeySym keysym = event->keyval; - guint key_code = 0; - - switch (keysym) - { - case GDK_Menu: key_code = WXK_MENU; break; - case GDK_Help: key_code = WXK_HELP; break; - case GDK_BackSpace: key_code = WXK_BACK; break; - case GDK_ISO_Left_Tab: - case GDK_Tab: key_code = WXK_TAB; break; - case GDK_Linefeed: key_code = WXK_RETURN; break; - case GDK_Clear: key_code = WXK_CLEAR; break; - case GDK_Return: key_code = WXK_RETURN; break; - case GDK_Pause: key_code = WXK_PAUSE; break; - case GDK_Scroll_Lock: key_code = WXK_SCROLL; break; - case GDK_Escape: key_code = WXK_ESCAPE; break; - case GDK_Delete: key_code = WXK_DELETE; break; - case GDK_Home: key_code = WXK_HOME; break; - case GDK_Left: key_code = WXK_LEFT; break; - case GDK_Up: key_code = WXK_UP; break; - case GDK_Right: key_code = WXK_RIGHT; break; - case GDK_Down: key_code = WXK_DOWN; break; - case GDK_Prior: key_code = WXK_PRIOR; break; -// case GDK_Page_Up: key_code = WXK_PAGEUP; break; - case GDK_Next: key_code = WXK_NEXT; break; -// case GDK_Page_Down: key_code = WXK_PAGEDOWN; break; - case GDK_End: key_code = WXK_END; break; - case GDK_Begin: key_code = WXK_HOME; break; - case GDK_Select: key_code = WXK_SELECT; break; - case GDK_Print: key_code = WXK_PRINT; break; - case GDK_Execute: key_code = WXK_EXECUTE; break; - case GDK_Insert: key_code = WXK_INSERT; break; - case GDK_Num_Lock: key_code = WXK_NUMLOCK; break; - - case GDK_KP_0: key_code = '0'; break; - case GDK_KP_1: key_code = '1'; break; - case GDK_KP_2: key_code = '2'; break; - case GDK_KP_3: key_code = '3'; break; - case GDK_KP_4: key_code = '4'; break; - case GDK_KP_5: key_code = '5'; break; - case GDK_KP_6: key_code = '6'; break; - case GDK_KP_7: key_code = '7'; break; - case GDK_KP_8: key_code = '8'; break; - case GDK_KP_9: key_code = '9'; break; - case GDK_KP_Space: key_code = ' '; break; - case GDK_KP_Tab: key_code = WXK_TAB; break; /* or '\t' ??? */ - case GDK_KP_Enter: key_code = WXK_RETURN; break; /* or '\r' ??? */ - case GDK_KP_F1: key_code = WXK_NUMPAD_F1; break; - case GDK_KP_F2: key_code = WXK_NUMPAD_F2; break; - case GDK_KP_F3: key_code = WXK_NUMPAD_F3; break; - case GDK_KP_F4: key_code = WXK_NUMPAD_F4; break; - case GDK_KP_Home: key_code = WXK_HOME; break; - case GDK_KP_Left: key_code = WXK_LEFT; break; - case GDK_KP_Up: key_code = WXK_UP; break; - case GDK_KP_Right: key_code = WXK_RIGHT; break; - case GDK_KP_Down: key_code = WXK_DOWN; break; - case GDK_KP_Prior: key_code = WXK_PRIOR; break; -// case GDK_KP_Page_Up: key_code = WXK_PAGEUP; break; - case GDK_KP_Next: key_code = WXK_NEXT; break; -// case GDK_KP_Page_Down: key_code = WXK_PAGEDOWN; break; - case GDK_KP_End: key_code = WXK_END; break; - case GDK_KP_Begin: key_code = WXK_HOME; break; - case GDK_KP_Insert: key_code = WXK_INSERT; break; - case GDK_KP_Delete: key_code = WXK_DELETE; break; - case GDK_KP_Equal: key_code = '='; break; - case GDK_KP_Multiply: key_code = '*'; break; - case GDK_KP_Add: key_code = '+'; break; - case GDK_KP_Separator: key_code = ','; break; - case GDK_KP_Subtract: key_code = '-'; break; - case GDK_KP_Decimal: key_code = '.'; break; - case GDK_KP_Divide: key_code = '/'; break; - - case GDK_F1: key_code = WXK_F1; break; - case GDK_F2: key_code = WXK_F2; break; - case GDK_F3: key_code = WXK_F3; break; - case GDK_F4: key_code = WXK_F4; break; - case GDK_F5: key_code = WXK_F5; break; - case GDK_F6: key_code = WXK_F6; break; - case GDK_F7: key_code = WXK_F7; break; - case GDK_F8: key_code = WXK_F8; break; - case GDK_F9: key_code = WXK_F9; break; - case GDK_F10: key_code = WXK_F10; break; - case GDK_F11: key_code = WXK_F11; break; - case GDK_F12: key_code = WXK_F12; break; - default: - if (event->length == 1) - { - key_code = (unsigned char)*event->string; - } - else if ((keysym & 0xFF) == keysym) - { - key_code = (guint)keysym; - } - } - - return key_code; -} +#endif // GTK+ < 2.0 //----------------------------------------------------------------------------- // "size_request" of m_widget @@ -787,8 +538,8 @@ static int gtk_window_expose_callback( GtkWidget *widget, if (g_isIdle) wxapp_install_idle_handler(); -/* - if (win->GetName() == wxT("panel")) +#if 0 + if (win->GetName()) { wxPrintf( wxT("OnExpose from ") ); if (win->GetClassInfo() && win->GetClassInfo()->GetClassName()) @@ -798,7 +549,7 @@ static int gtk_window_expose_callback( GtkWidget *widget, (int)gdk_event->area.width, (int)gdk_event->area.height ); } -*/ +#endif #ifndef __WXUNIVERSAL__ GtkPizza *pizza = GTK_PIZZA (widget); @@ -829,12 +580,12 @@ static int gtk_window_expose_callback( GtkWidget *widget, // Actual redrawing takes place in idle time. win->Update(); - + #ifdef __WXGTK20__ (* GTK_WIDGET_CLASS (pizza_parent_class)->expose_event) (widget, gdk_event); - -#endif + +#endif return TRUE; } @@ -888,8 +639,8 @@ static void gtk_window_draw_callback( GtkWidget *widget, return; } -/* - if (win->GetName() == wxT("panel")) +#if 0 + if (win->GetName()) { wxPrintf( wxT("OnDraw from ") ); if (win->GetClassInfo() && win->GetClassInfo()->GetClassName()) @@ -899,7 +650,7 @@ static void gtk_window_draw_callback( GtkWidget *widget, (int)rect->width, (int)rect->height ); } -*/ +#endif #ifndef __WXUNIVERSAL__ GtkPizza *pizza = GTK_PIZZA (widget); @@ -934,7 +685,7 @@ static void gtk_window_draw_callback( GtkWidget *widget, win->GetUpdateRegion().Union( rect->x, rect->y, rect->width, rect->height ); // Actual redrawing takes place in idle time. - + win->Update(); #ifndef __WXUNIVERSAL__ @@ -960,52 +711,430 @@ static void gtk_window_draw_callback( GtkWidget *widget, // "key_press_event" from any window //----------------------------------------------------------------------------- -// turn on to see the key event codes on the console -#undef DEBUG_KEY_EVENTS +// set WXTRACE to this to see the key event codes on the console +#define TRACE_KEYS _T("keyevent") -static gint gtk_window_key_press_callback( GtkWidget *widget, - GdkEventKey *gdk_event, - wxWindow *win ) +// translates an X key symbol to WXK_XXX value +// +// if isChar is true it means that the value returned will be used for EVT_CHAR +// event and then we choose the logical WXK_XXX, i.e. '/' for GDK_KP_Divide, +// for example, while if it is false it means that the value is going to be +// used for KEY_DOWN/UP events and then we translate GDK_KP_Divide to +// WXK_NUMPAD_DIVIDE +static long wxTranslateKeySymToWXKey(KeySym keysym, bool isChar) { - DEBUG_MAIN_THREAD + long key_code; - if (g_isIdle) - wxapp_install_idle_handler(); + switch ( keysym ) + { + // Shift, Control and Alt don't generate the CHAR events at all + case GDK_Shift_L: + case GDK_Shift_R: + key_code = isChar ? 0 : WXK_SHIFT; + break; + case GDK_Control_L: + case GDK_Control_R: + key_code = isChar ? 0 : WXK_CONTROL; + break; + case GDK_Meta_L: + case GDK_Meta_R: + case GDK_Alt_L: + case GDK_Alt_R: + case GDK_Super_L: + case GDK_Super_R: + key_code = isChar ? 0 : WXK_ALT; + break; - if (!win->m_hasVMT) return FALSE; - if (g_blockEventsOnDrag) return FALSE; + // neither do the toggle modifies + case GDK_Scroll_Lock: + key_code = isChar ? 0 : WXK_SCROLL; + break; + case GDK_Caps_Lock: + key_code = isChar ? 0 : WXK_CAPITAL; + break; - int x = 0; - int y = 0; - GdkModifierType state; - if (gdk_event->window) - gdk_window_get_pointer(gdk_event->window, &x, &y, &state); + case GDK_Num_Lock: + key_code = isChar ? 0 : WXK_NUMLOCK; + break; + + + // various other special keys + case GDK_Menu: + key_code = WXK_MENU; + break; + + case GDK_Help: + key_code = WXK_HELP; + break; + + case GDK_BackSpace: + key_code = WXK_BACK; + break; + + case GDK_ISO_Left_Tab: + case GDK_Tab: + key_code = WXK_TAB; + break; + + case GDK_Linefeed: + case GDK_Return: + key_code = WXK_RETURN; + break; + + case GDK_Clear: + key_code = WXK_CLEAR; + break; + + case GDK_Pause: + key_code = WXK_PAUSE; + break; + + case GDK_Select: + key_code = WXK_SELECT; + break; + + case GDK_Print: + key_code = WXK_PRINT; + break; + + case GDK_Execute: + key_code = WXK_EXECUTE; + break; + + case GDK_Escape: + key_code = WXK_ESCAPE; + break; + + // cursor and other extended keyboard keys + case GDK_Delete: + key_code = WXK_DELETE; + break; + + case GDK_Home: + key_code = WXK_HOME; + break; + + case GDK_Left: + key_code = WXK_LEFT; + break; + + case GDK_Up: + key_code = WXK_UP; + break; + + case GDK_Right: + key_code = WXK_RIGHT; + break; + + case GDK_Down: + key_code = WXK_DOWN; + break; + + case GDK_Prior: // == GDK_Page_Up + key_code = WXK_PRIOR; + break; + + case GDK_Next: // == GDK_Page_Down + key_code = WXK_NEXT; + break; + + case GDK_End: + key_code = WXK_END; + break; + + case GDK_Begin: + key_code = WXK_HOME; + break; + + case GDK_Insert: + key_code = WXK_INSERT; + break; + + + // numpad keys + case GDK_KP_0: + case GDK_KP_1: + case GDK_KP_2: + case GDK_KP_3: + case GDK_KP_4: + case GDK_KP_5: + case GDK_KP_6: + case GDK_KP_7: + case GDK_KP_8: + case GDK_KP_9: + key_code = (isChar ? '0' : WXK_NUMPAD0) + keysym - GDK_KP_0; + break; + + case GDK_KP_Space: + key_code = isChar ? ' ' : WXK_NUMPAD_SPACE; + break; + + case GDK_KP_Tab: + key_code = isChar ? WXK_TAB : WXK_NUMPAD_TAB; + break; + + case GDK_KP_Enter: + key_code = isChar ? WXK_RETURN : WXK_NUMPAD_ENTER; + break; + + case GDK_KP_F1: + key_code = isChar ? WXK_F1 : WXK_NUMPAD_F1; + break; + + case GDK_KP_F2: + key_code = isChar ? WXK_F2 : WXK_NUMPAD_F2; + break; + + case GDK_KP_F3: + key_code = isChar ? WXK_F3 : WXK_NUMPAD_F3; + break; + + case GDK_KP_F4: + key_code = isChar ? WXK_F4 : WXK_NUMPAD_F4; + break; + + case GDK_KP_Home: + key_code = isChar ? WXK_HOME : WXK_NUMPAD_HOME; + break; + + case GDK_KP_Left: + key_code = isChar ? WXK_LEFT : WXK_NUMPAD_LEFT; + break; + + case GDK_KP_Up: + key_code = isChar ? WXK_UP : WXK_NUMPAD_UP; + break; + + case GDK_KP_Right: + key_code = isChar ? WXK_RIGHT : WXK_NUMPAD_RIGHT; + break; + + case GDK_KP_Down: + key_code = isChar ? WXK_DOWN : WXK_NUMPAD_DOWN; + break; - bool ret = FALSE; + case GDK_KP_Prior: // == GDK_KP_Page_Up + key_code = isChar ? WXK_PRIOR : WXK_NUMPAD_PRIOR; + break; + + case GDK_KP_Next: // == GDK_KP_Page_Down + key_code = isChar ? WXK_NEXT : WXK_NUMPAD_NEXT; + break; - long key_code = map_to_unmodified_wx_keysym( gdk_event ); + case GDK_KP_End: + key_code = isChar ? WXK_END : WXK_NUMPAD_END; + break; -#ifdef DEBUG_KEY_EVENTS - wxPrintf(_T("Key press event: %d => %ld\n"), gdk_event->keyval, key_code); -#endif // DEBUG_KEY_EVENTS + case GDK_KP_Begin: + key_code = isChar ? WXK_HOME : WXK_NUMPAD_BEGIN; + break; + + case GDK_KP_Insert: + key_code = isChar ? WXK_INSERT : WXK_NUMPAD_INSERT; + break; - /* sending unknown key events doesn't really make sense */ - if (key_code == 0) + case GDK_KP_Delete: + key_code = isChar ? WXK_DELETE : WXK_NUMPAD_DELETE; + break; + + case GDK_KP_Equal: + key_code = isChar ? '=' : WXK_NUMPAD_EQUAL; + break; + + case GDK_KP_Multiply: + key_code = isChar ? '*' : WXK_NUMPAD_MULTIPLY; + break; + + case GDK_KP_Add: + key_code = isChar ? '+' : WXK_NUMPAD_ADD; + break; + + case GDK_KP_Separator: + // FIXME: what is this? + key_code = isChar ? '.' : WXK_NUMPAD_SEPARATOR; + break; + + case GDK_KP_Subtract: + key_code = isChar ? '-' : WXK_NUMPAD_SUBTRACT; + break; + + case GDK_KP_Decimal: + key_code = isChar ? '.' : WXK_NUMPAD_DECIMAL; + break; + + case GDK_KP_Divide: + key_code = isChar ? '/' : WXK_NUMPAD_DIVIDE; + break; + + + // function keys + case GDK_F1: + case GDK_F2: + case GDK_F3: + case GDK_F4: + case GDK_F5: + case GDK_F6: + case GDK_F7: + case GDK_F8: + case GDK_F9: + case GDK_F10: + case GDK_F11: + case GDK_F12: + key_code = WXK_F1 + keysym - GDK_F1; + break; + + default: + key_code = 0; + } + + return key_code; +} + +static inline bool wxIsAsciiKeysym(KeySym ks) +{ + return ks < 256; +} + +static bool +wxTranslateGTKKeyEventToWx(wxKeyEvent& event, + wxWindowGTK *win, + GdkEventKey *gdk_event) +{ + // VZ: it seems that GDK_KEY_RELEASE event doesn't set event->string + // but only event->keyval which is quite useless to us, so remember + // the last character from GDK_KEY_PRESS and reuse it as last resort + // + // NB: should be MT-safe as we're always called from the main thread only + static struct + { + KeySym keysym; + long keycode; + } s_lastKeyPress = { 0, 0 }; + + KeySym keysym = gdk_event->keyval; + + wxLogTrace(TRACE_KEYS, _T("Key %s event: keysym = %d"), + event.GetEventType() == wxEVT_KEY_UP ? _T("release") + : _T("press"), + keysym); + + long key_code = wxTranslateKeySymToWXKey(keysym, FALSE /* !isChar */); + + if ( !key_code ) + { + // do we have the translation or is it a plain ASCII character? + if ( (gdk_event->length == 1) || wxIsAsciiKeysym(keysym) ) + { + // we should use keysym if it is ASCII as X does some translations + // like "I pressed while Control is down" => "Ctrl-I" == "TAB" + // which we don't want here (but which we do use for OnChar()) + if ( !wxIsAsciiKeysym(keysym) ) + { + keysym = (KeySym)gdk_event->string[0]; + } + + // we want to always get the same key code when the same key is + // pressed regardless of the state of the modifies, i.e. on a + // standard US keyboard pressing '5' or '%' ('5' key with + // Shift) should result in the same key code in OnKeyDown(): + // '5' (although OnChar() will get either '5' or '%'). + // + // to do it we first translate keysym to keycode (== scan code) + // and then back but always using the lower register + Display *dpy = (Display *)wxGetDisplay(); + KeyCode keycode = XKeysymToKeycode(dpy, keysym); + + wxLogTrace(TRACE_KEYS, _T("\t-> keycode %d"), keycode); + + KeySym keysymNormalized = XKeycodeToKeysym(dpy, keycode, 0); + + // use the normalized, i.e. lower register, keysym if we've + // got one + key_code = keysymNormalized ? keysymNormalized : keysym; + + // as explained above, we want to have lower register key codes + // normally but for the letter keys we want to have the upper ones + // + // NB: don't use XConvertCase() here, we want to do it for letters + // only + key_code = toupper(key_code); + } + else // non ASCII key, what to do? + { + // by default, ignore it + key_code = 0; + + // but if we have cached information from the last KEY_PRESS + if ( gdk_event->type == GDK_KEY_RELEASE ) + { + // then reuse it + if ( keysym == s_lastKeyPress.keysym ) + { + key_code = s_lastKeyPress.keycode; + } + } + } + + if ( gdk_event->type == GDK_KEY_PRESS ) + { + // remember it to be reused for KEY_UP event later + s_lastKeyPress.keysym = keysym; + s_lastKeyPress.keycode = key_code; + } + } + + wxLogTrace(TRACE_KEYS, _T("\t-> wxKeyCode %d"), key_code); + + // sending unknown key events doesn't really make sense + if ( !key_code ) return FALSE; - wxKeyEvent event( wxEVT_KEY_DOWN ); + // now fill all the other fields + int x = 0; + int y = 0; + GdkModifierType state; + if (gdk_event->window) + gdk_window_get_pointer(gdk_event->window, &x, &y, &state); + event.SetTimestamp( gdk_event->time ); - event.m_shiftDown = (gdk_event->state & GDK_SHIFT_MASK); - event.m_controlDown = (gdk_event->state & GDK_CONTROL_MASK); - event.m_altDown = (gdk_event->state & GDK_MOD1_MASK); - event.m_metaDown = (gdk_event->state & GDK_MOD2_MASK); + event.m_shiftDown = (gdk_event->state & GDK_SHIFT_MASK) != 0; + event.m_controlDown = (gdk_event->state & GDK_CONTROL_MASK) != 0; + event.m_altDown = (gdk_event->state & GDK_MOD1_MASK) != 0; + event.m_metaDown = (gdk_event->state & GDK_MOD2_MASK) != 0; event.m_keyCode = key_code; event.m_scanCode = gdk_event->keyval; + event.m_rawCode = (wxUint32) gdk_event->keyval; + event.m_rawFlags = 0; event.m_x = x; event.m_y = y; event.SetEventObject( win ); - ret = win->GetEventHandler()->ProcessEvent( event ); + + return TRUE; +} + +static gint gtk_window_key_press_callback( GtkWidget *widget, + GdkEventKey *gdk_event, + wxWindow *win ) +{ + DEBUG_MAIN_THREAD + + if (g_isIdle) + wxapp_install_idle_handler(); + + if (!win->m_hasVMT) + return FALSE; + if (g_blockEventsOnDrag) + return FALSE; + + wxKeyEvent event( wxEVT_KEY_DOWN ); + if ( !wxTranslateGTKKeyEventToWx(event, win, gdk_event) ) + { + // unknown key pressed, ignore (the event would be useless anyhow) + return FALSE; + } + + bool ret = win->GetEventHandler()->ProcessEvent( event ); #if wxUSE_ACCEL if (!ret) @@ -1031,15 +1160,26 @@ static gint gtk_window_key_press_callback( GtkWidget *widget, will only be sent if it is not in an accelerator table. */ if ( !ret ) { - key_code = map_to_wx_keysym( gdk_event ); + KeySym keysym = gdk_event->keyval; + long key_code = wxTranslateKeySymToWXKey(keysym, TRUE /* isChar */); + if ( !key_code ) + { + if ( gdk_event->length == 1 ) + { + key_code = (unsigned char)gdk_event->string[0]; + } + else if ( wxIsAsciiKeysym(keysym) ) + { + // ASCII key + key_code = (unsigned char)keysym; + } + } if ( key_code ) { -#ifdef DEBUG_KEY_EVENTS - wxPrintf(_T("Char event: %ld\n"), key_code); -#endif // DEBUG_KEY_EVENTS + wxLogTrace(TRACE_KEYS, _T("Char event: %ld"), key_code); - // reuse the ame event object, just change its type and use the + // reuse the same event object, just change its type and use the // translated keycode instead of the raw one event.SetEventType(wxEVT_CHAR); event.m_keyCode = key_code; @@ -1121,50 +1261,33 @@ static gint gtk_window_key_press_callback( GtkWidget *widget, // "key_release_event" from any window //----------------------------------------------------------------------------- -static gint gtk_window_key_release_callback( GtkWidget *widget, GdkEventKey *gdk_event, wxWindowGTK *win ) +static gint gtk_window_key_release_callback( GtkWidget *widget, + GdkEventKey *gdk_event, + wxWindowGTK *win ) { DEBUG_MAIN_THREAD if (g_isIdle) wxapp_install_idle_handler(); - if (!win->m_hasVMT) return FALSE; - if (g_blockEventsOnDrag) return FALSE; - - long key_code = map_to_unmodified_wx_keysym( gdk_event ); - -#ifdef DEBUG_KEY_EVENTS - wxPrintf(_T("Key release event: %d => %ld\n"), gdk_event->keyval, key_code); -#endif // DEBUG_KEY_EVENTS - - /* sending unknown key events doesn't really make sense */ - if (key_code == 0) return FALSE; + if (!win->m_hasVMT) + return FALSE; - int x = 0; - int y = 0; - GdkModifierType state; - if (gdk_event->window) - gdk_window_get_pointer(gdk_event->window, &x, &y, &state); + if (g_blockEventsOnDrag) + return FALSE; wxKeyEvent event( wxEVT_KEY_UP ); - event.SetTimestamp( gdk_event->time ); - event.m_shiftDown = (gdk_event->state & GDK_SHIFT_MASK); - event.m_controlDown = (gdk_event->state & GDK_CONTROL_MASK); - event.m_altDown = (gdk_event->state & GDK_MOD1_MASK); - event.m_metaDown = (gdk_event->state & GDK_MOD2_MASK); - event.m_keyCode = key_code; - event.m_scanCode = gdk_event->keyval; - event.m_x = x; - event.m_y = y; - event.SetEventObject( win ); - - if (win->GetEventHandler()->ProcessEvent( event )) + if ( !wxTranslateGTKKeyEventToWx(event, win, gdk_event) ) { - gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "key_release_event" ); - return TRUE; + // unknown key pressed, ignore (the event would be useless anyhow + return FALSE; } - return FALSE; + if ( !win->GetEventHandler()->ProcessEvent( event ) ) + return FALSE; + + gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "key_release_event" ); + return TRUE; } // ============================================================================ @@ -1678,11 +1801,11 @@ static gint gtk_window_focus_in_callback( GtkWidget *widget, g_focusWindow = win; #if 0 - wxLogDebug( wxT("OnSetFocus from %s\n"), win->GetName().c_str() ); + printf( "OnSetFocus 2 from %s\n", win->GetName().c_str() ); #endif - // notify the parent keeping track of focus for the kbd navigation - // purposes that we got it + // Notify the parent keeping track of focus for the kbd navigation + // purposes that we got it. wxChildFocusEvent eventFocus(win); (void)win->GetEventHandler()->ProcessEvent(eventFocus); @@ -1700,6 +1823,8 @@ static gint gtk_window_focus_in_callback( GtkWidget *widget, } #endif // wxUSE_CARET + g_activeFrameLostFocus = FALSE; + wxWindowGTK *active = wxGetTopLevelParent(win); if ( active != g_activeFrame ) { @@ -1716,8 +1841,11 @@ static gint gtk_window_focus_in_callback( GtkWidget *widget, wxActivateEvent event(wxEVT_ACTIVATE, TRUE, g_activeFrame->GetId()); event.SetEventObject(g_activeFrame); g_activeFrame->GetEventHandler()->ProcessEvent(event); + + // Don't send focus events in addition to activate + // if (win == g_activeFrame) + // return TRUE; } - g_activeFrameLostFocus = FALSE; wxFocusEvent event( wxEVT_SET_FOCUS, win->GetId() ); @@ -1729,7 +1857,6 @@ static gint gtk_window_focus_in_callback( GtkWidget *widget, return TRUE; } - return FALSE; } @@ -1867,13 +1994,13 @@ static gint gtk_window_leave_callback( GtkWidget *widget, GdkEventCrossing *gdk_ gdk_window_get_pointer( widget->window, &x, &y, &state ); - event.m_shiftDown = (state & GDK_SHIFT_MASK); - event.m_controlDown = (state & GDK_CONTROL_MASK); - event.m_altDown = (state & GDK_MOD1_MASK); - event.m_metaDown = (state & GDK_MOD2_MASK); - event.m_leftDown = (state & GDK_BUTTON1_MASK); - event.m_middleDown = (state & GDK_BUTTON2_MASK); - event.m_rightDown = (state & GDK_BUTTON3_MASK); + event.m_shiftDown = (state & GDK_SHIFT_MASK) != 0; + event.m_controlDown = (state & GDK_CONTROL_MASK) != 0; + event.m_altDown = (state & GDK_MOD1_MASK) != 0; + event.m_metaDown = (state & GDK_MOD2_MASK) != 0; + event.m_leftDown = (state & GDK_BUTTON1_MASK) != 0; + event.m_middleDown = (state & GDK_BUTTON2_MASK) != 0; + event.m_rightDown = (state & GDK_BUTTON3_MASK) != 0; wxPoint pt = win->GetClientAreaOrigin(); event.m_x = x + pt.x; @@ -2045,10 +2172,10 @@ gtk_window_realized_callback( GtkWidget *WXUNUSED(m_widget), wxWindow *win ) wxapp_install_idle_handler(); if (win->m_delayedBackgroundColour) - win->SetBackgroundColour( win->GetBackgroundColour() ); + win->GtkSetBackgroundColour( win->GetBackgroundColour() ); if (win->m_delayedForegroundColour) - win->SetForegroundColour( win->GetForegroundColour() ); + win->GtkSetForegroundColour( win->GetForegroundColour() ); wxWindowCreateEvent event( win ); event.SetEventObject( win ); @@ -2351,6 +2478,9 @@ bool wxWindowGTK::Create( wxWindow *parent, } m_insertCallback = wxInsertChildInWindow; + + // always needed for background clearing + m_delayedBackgroundColour = TRUE; m_widget = gtk_scrolled_window_new( (GtkAdjustment *) NULL, (GtkAdjustment *) NULL ); GTK_WIDGET_UNSET_FLAGS( m_widget, GTK_CAN_FOCUS ); @@ -2366,7 +2496,7 @@ bool wxWindowGTK::Create( wxWindow *parent, m_vAdjust = gtk_range_get_adjustment( GTK_RANGE(scrolledWindow->vscrollbar) ); m_wxwindow = gtk_pizza_new(); - + #ifndef __WXUNIVERSAL__ GtkPizza *pizza = GTK_PIZZA(m_wxwindow); @@ -2547,7 +2677,7 @@ void wxWindowGTK::PostCreation() #ifndef __WXGTK20__ gtk_signal_connect( GTK_OBJECT(m_wxwindow), "draw", GTK_SIGNAL_FUNC(gtk_window_draw_callback), (gpointer)this ); - + if (HasFlag(wxNO_FULL_REPAINT_ON_RESIZE)) { gtk_signal_connect( GTK_OBJECT(m_wxwindow), "event", @@ -2773,7 +2903,7 @@ void wxWindowGTK::OnInternalIdle() { // Update invalidated regions. Update(); - + // Synthetize activate events. if ( g_sendActivateEvent != -1 ) { @@ -2797,6 +2927,15 @@ void wxWindowGTK::OnInternalIdle() } g_activeFrameLostFocus = FALSE; } + + if (g_delayedFocus == this) + { + if (GTK_WIDGET_REALIZED(m_widget)) + { + gtk_widget_grab_focus( m_widget ); + g_delayedFocus = NULL; + } + } wxCursor cursor = m_cursor; if (g_globalCursor.Ok()) cursor = g_globalCursor; @@ -3152,13 +3291,6 @@ void wxWindowGTK::SetFocus() { wxCHECK_RET( (m_widget != NULL), wxT("invalid window") ); -#if 0 - wxPrintf( "SetFocus from " ); - if (GetClassInfo() && GetClassInfo()->GetClassName()) - wxPrintf( GetClassInfo()->GetClassName() ); - wxPrintf( ".\n" ); -#endif - if (m_wxwindow) { if (!GTK_WIDGET_HAS_FOCUS (m_wxwindow)) @@ -3170,7 +3302,10 @@ void wxWindowGTK::SetFocus() { if (GTK_WIDGET_CAN_FOCUS(m_widget) && !GTK_WIDGET_HAS_FOCUS (m_widget) ) { - gtk_widget_grab_focus (m_widget); + if (!GTK_WIDGET_REALIZED(m_widget)) + g_delayedFocus = this; + else + gtk_widget_grab_focus (m_widget); } else if (GTK_IS_CONTAINER(m_widget)) { @@ -3296,6 +3431,9 @@ void wxWindowGTK::Refresh( bool eraseBackground, const wxRect *rect ) if (!m_widget->window) return; #ifndef __WXGTK20__ + if (g_isIdle) + wxapp_install_idle_handler(); + if (eraseBackground && m_wxwindow && m_wxwindow->window) { if (rect) @@ -3382,23 +3520,28 @@ void wxWindowGTK::GtkSendPaintEvents() } m_clipPaintRegion = TRUE; - + // if (!m_clearRegion.IsEmpty()) // always send an erase event { wxWindowDC dc( (wxWindow*)this ); dc.SetClippingRegion( m_clearRegion ); - + wxEraseEvent erase_event( GetId(), &dc ); erase_event.SetEventObject( this ); - + if (!GetEventHandler()->ProcessEvent(erase_event)) { + if (!g_eraseGC) + { + g_eraseGC = gdk_gc_new( GTK_PIZZA(m_wxwindow)->bin_window ); + gdk_gc_set_fill( g_eraseGC, GDK_SOLID ); + } gdk_gc_set_foreground( g_eraseGC, m_backgroundColour.GetColor() ); - + wxRegionIterator upd( m_clearRegion ); while (upd) { - gdk_draw_rectangle( GTK_PIZZA(m_wxwindow)->bin_window, g_eraseGC, 0, + gdk_draw_rectangle( GTK_PIZZA(m_wxwindow)->bin_window, g_eraseGC, 1, upd.GetX(), upd.GetY(), upd.GetWidth(), upd.GetHeight() ); upd ++; } @@ -3421,26 +3564,26 @@ void wxWindowGTK::GtkSendPaintEvents() // The following code will result in all window-less widgets // being redrawn because the wxWindows class is allowed to // paint over the window-less widgets. - + GtkPizza *pizza = GTK_PIZZA(m_wxwindow); - + GList *children = pizza->children; while (children) { GtkPizzaChild *child = (GtkPizzaChild*) children->data; children = children->next; - + if (GTK_WIDGET_NO_WINDOW (child->widget) && GTK_WIDGET_DRAWABLE (child->widget)) { // Get intersection of widget area and update region wxRegion region( m_updateRegion ); - + GdkEventExpose gdk_event; gdk_event.type = GDK_EXPOSE; gdk_event.window = pizza->bin_window; gdk_event.count = 0; - + wxRegionIterator upd( m_updateRegion ); while (upd) { @@ -3449,12 +3592,12 @@ void wxWindowGTK::GtkSendPaintEvents() rect.y = upd.GetY(); rect.width = upd.GetWidth(); rect.height = upd.GetHeight(); - + if (gtk_widget_intersect (child->widget, &rect, &gdk_event.area)) { gtk_widget_event (child->widget, (GdkEvent*) &gdk_event); } - + upd ++; } } @@ -3469,11 +3612,14 @@ void wxWindowGTK::Clear() { wxCHECK_RET( m_widget != NULL, wxT("invalid window") ); - if (!m_widget->window) return; - if (m_wxwindow && m_wxwindow->window) { -// gdk_window_clear( m_wxwindow->window ); + m_clearRegion.Clear(); + wxSize size( GetClientSize() ); + m_clearRegion.Union( 0,0,size.x,size.y ); + + // Better do this in idle? + Update(); } } @@ -3492,16 +3638,37 @@ void wxWindowGTK::ApplyToolTip( GtkTooltips *tips, const wxChar *tip ) } #endif // wxUSE_TOOLTIPS +void wxWindowGTK::GtkSetBackgroundColour( const wxColour &colour ) +{ + GdkWindow *window = (GdkWindow*) NULL; + if (m_wxwindow) + window = GTK_PIZZA(m_wxwindow)->bin_window; + else + window = GetConnectWidget()->window; + + wxASSERT( window ); + + // We need the pixel value e.g. for background clearing. + m_backgroundColour.CalcPixel( gdk_window_get_colormap( window ) ); + + if ((m_wxwindow) && + (m_backgroundColour != wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE))) + { + /* wxMSW doesn't clear the window here. I don't do that either to + provide compatibility. call Clear() to do the job. */ + + gdk_window_set_background( window, m_backgroundColour.GetColor() ); + } + + ApplyWidgetStyle(); +} + bool wxWindowGTK::SetBackgroundColour( const wxColour &colour ) { wxCHECK_MSG( m_widget != NULL, FALSE, wxT("invalid window") ); if (!wxWindowBase::SetBackgroundColour(colour)) - { - // don't leave if the GTK widget has just - // been realized - if (!m_delayedBackgroundColour) return FALSE; - } + return FALSE; GdkWindow *window = (GdkWindow*) NULL; if (m_wxwindow) @@ -3515,24 +3682,29 @@ bool wxWindowGTK::SetBackgroundColour( const wxColour &colour ) // but it couldn't get applied as the // widget hasn't been realized yet. m_delayedBackgroundColour = TRUE; + return TRUE; } - - if ((m_wxwindow) && - (m_wxwindow->window) && - (m_backgroundColour != wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE))) + else { - /* wxMSW doesn't clear the window here. I don't do that either to - provide compatibility. call Clear() to do the job. */ - - m_backgroundColour.CalcPixel( gdk_window_get_colormap( window ) ); - gdk_window_set_background( window, m_backgroundColour.GetColor() ); + GtkSetBackgroundColour( colour ); } - ApplyWidgetStyle(); - return TRUE; } +void wxWindowGTK::GtkSetForegroundColour( const wxColour &colour ) +{ + GdkWindow *window = (GdkWindow*) NULL; + if (m_wxwindow) + window = GTK_PIZZA(m_wxwindow)->bin_window; + else + window = GetConnectWidget()->window; + + wxASSERT( window ); + + ApplyWidgetStyle(); +} + bool wxWindowGTK::SetForegroundColour( const wxColour &colour ) { wxCHECK_MSG( m_widget != NULL, FALSE, wxT("invalid window") ); @@ -3557,8 +3729,10 @@ bool wxWindowGTK::SetForegroundColour( const wxColour &colour ) // widget hasn't been realized yet. m_delayedForegroundColour = TRUE; } - - ApplyWidgetStyle(); + else + { + GtkSetForegroundColour( colour ); + } return TRUE; } @@ -4063,42 +4237,42 @@ void wxWindowGTK::ScrollWindow( int dx, int dy, const wxRect* WXUNUSED(rect) ) // No scrolling requested. if ((dx == 0) && (dy == 0)) return; - + #ifndef __WXGTK20__ if (!m_updateRegion.IsEmpty()) { m_updateRegion.Offset( dx, dy ); - + int cw = 0; int ch = 0; GetClientSize( &cw, &ch ); m_updateRegion.Intersect( 0, 0, cw, ch ); } - + if (!m_clearRegion.IsEmpty()) { m_clearRegion.Offset( dx, dy ); - + int cw = 0; int ch = 0; GetClientSize( &cw, &ch ); m_clearRegion.Intersect( 0, 0, cw, ch ); } - + m_clipPaintRegion = TRUE; - + gtk_pizza_scroll( GTK_PIZZA(m_wxwindow), -dx, -dy ); - + m_clipPaintRegion = FALSE; #else - gdk_window_scroll( GTK_PIZZA(m_wxwindow)->bin_window, -dx, -dy ); - - GTK_PIZZA(m_wxwindow)->xoffset -= dx; - GTK_PIZZA(m_wxwindow)->yoffset -= dy; - -#endif - + gdk_window_scroll( GTK_PIZZA(m_wxwindow)->bin_window, dx, dy ); + + GTK_PIZZA(m_wxwindow)->xoffset += dx; + GTK_PIZZA(m_wxwindow)->yoffset += dy; + +#endif + } @@ -4161,14 +4335,15 @@ IMPLEMENT_DYNAMIC_CLASS(wxWinModule, wxModule) bool wxWinModule::OnInit() { - g_eraseGC = gdk_gc_new( GDK_ROOT_PARENT() ); - gdk_gc_set_fill( g_eraseGC, GDK_SOLID ); - + // g_eraseGC = gdk_gc_new( GDK_ROOT_PARENT() ); + // gdk_gc_set_fill( g_eraseGC, GDK_SOLID ); + return TRUE; } void wxWinModule::OnExit() { - gdk_gc_unref( g_eraseGC ); + if (g_eraseGC) + gdk_gc_unref( g_eraseGC ); }