X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/496beb3fc0244c36bdb0e37055aa868012b52a23..51072df23ffcf5bdd4651dbe0ad5143b1e360119:/src/gtk1/window.cpp diff --git a/src/gtk1/window.cpp b/src/gtk1/window.cpp index 7af1eff2e3..05a2f829cb 100644 --- a/src/gtk1/window.cpp +++ b/src/gtk1/window.cpp @@ -216,6 +216,11 @@ static bool g_captureWindowHasMouse = FALSE; // keeps its previous value 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 bool g_activeFrameLostFocus = FALSE; + // 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 @@ -358,6 +363,16 @@ wxWindow *wxFindFocusedChild(wxWindowGTK *win) return (wxWindow *)NULL; } +// Returns toplevel grandparent of given window: +static wxWindowGTK* wxGetTopLevelParent(wxWindowGTK *win) +{ + wxWindowGTK *p = win; + while (p && !p->IsTopLevel()) + p = p->GetParent(); + return p; +} + + static void draw_frame( GtkWidget *widget, wxWindowGTK *win ) { // wxUniversal widgets draw the borders and scrollbars themselves @@ -471,10 +486,21 @@ static void gtk_window_own_draw_callback( GtkWidget *widget, GdkRectangle *WXUNU 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; - guint key_code = 0; + long key_code; - switch (keysym) + switch ( keysym ) { case GDK_Shift_L: case GDK_Shift_R: key_code = WXK_SHIFT; break; @@ -567,20 +593,45 @@ static long map_to_unmodified_wx_keysym( GdkEventKey *event ) case GDK_F12: key_code = WXK_F12; break; default: { - if (event->length == 1) - { - key_code = toupper( (unsigned char)*event->string ); - } - else if ((keysym & 0xFF) == keysym) + // do we have the translation? + if ( event->length == 1 ) { - guint upper = gdk_keyval_to_upper( (guint)keysym ); - keysym = (upper != 0 ? upper : keysym ); /* to be MSW compatible */ - key_code = (guint)keysym; + 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); + return key_code; } static long map_to_wx_keysym( GdkEventKey *event ) @@ -670,19 +721,17 @@ static long map_to_wx_keysym( GdkEventKey *event ) 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) + if (event->length == 1) + { + key_code = (unsigned char)*event->string; + } + else if ((keysym & 0xFF) == keysym) { key_code = (guint)keysym; } - } } - return (key_code); + return key_code; } //----------------------------------------------------------------------------- @@ -952,6 +1001,9 @@ 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 + static gint gtk_window_key_press_callback( GtkWidget *widget, GdkEventKey *gdk_event, wxWindow *win ) @@ -965,23 +1017,23 @@ static gint gtk_window_key_press_callback( GtkWidget *widget, if (g_blockEventsOnDrag) return FALSE; -/* - wxString tmp; - tmp += (char)gdk_event->keyval; - printf( "KeyDown-Code is: %s.\n", tmp.c_str() ); - printf( "KeyDown-ScanCode is: %d.\n", gdk_event->keyval ); -*/ - int x = 0; int y = 0; GdkModifierType state; - if (gdk_event->window) gdk_window_get_pointer(gdk_event->window, &x, &y, &state); + if (gdk_event->window) + gdk_window_get_pointer(gdk_event->window, &x, &y, &state); bool ret = FALSE; long key_code = map_to_unmodified_wx_keysym( gdk_event ); + +#ifdef DEBUG_KEY_EVENTS + wxPrintf(_T("Key press 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 (key_code == 0) + return FALSE; wxKeyEvent event( wxEVT_KEY_DOWN ); event.SetTimestamp( gdk_event->time ); @@ -1016,39 +1068,37 @@ static gint gtk_window_key_press_callback( GtkWidget *widget, } #endif // wxUSE_ACCEL - /* wxMSW doesn't send char events with Alt pressed */ /* Only send wxEVT_CHAR event if not processed yet. Thus, ALT-x will only be sent if it is not in an accelerator table. */ - key_code = map_to_wx_keysym( gdk_event ); + if ( !ret ) + { + key_code = map_to_wx_keysym( gdk_event ); - if ( (!ret) && - (key_code != 0)) - { - wxKeyEvent event2( wxEVT_CHAR ); - event2.SetTimestamp( gdk_event->time ); - event2.m_shiftDown = (gdk_event->state & GDK_SHIFT_MASK); - event2.m_controlDown = (gdk_event->state & GDK_CONTROL_MASK); - event2.m_altDown = (gdk_event->state & GDK_MOD1_MASK); - event2.m_metaDown = (gdk_event->state & GDK_MOD2_MASK); - event2.m_keyCode = key_code; - event2.m_scanCode = gdk_event->keyval; - event2.m_x = x; - event2.m_y = y; - event2.SetEventObject( win ); - ret = win->GetEventHandler()->ProcessEvent( event2 ); + if ( key_code ) + { +#ifdef DEBUG_KEY_EVENTS + wxPrintf(_T("Char event: %ld\n"), key_code); +#endif // DEBUG_KEY_EVENTS + + // reuse the ame 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; + + ret = win->GetEventHandler()->ProcessEvent( event ); + } } /* win is a control: tab can be propagated up */ - if ( (!ret) && + if ( !ret && ((gdk_event->keyval == GDK_Tab) || (gdk_event->keyval == GDK_ISO_Left_Tab)) && // VZ: testing for wxTE_PROCESS_TAB shouldn't be done here the control may // have this style, yet choose not to process this particular TAB in which // case TAB must still work as a navigational character #if 0 - (!win->HasFlag(wxTE_PROCESS_TAB)) && + !win->HasFlag(wxTE_PROCESS_TAB) && #endif // 0 - (win->GetParent()) && - (win->GetParent()->HasFlag( wxTAB_TRAVERSAL)) ) + win->GetParent() && (win->GetParent()->HasFlag( wxTAB_TRAVERSAL)) ) { wxNavigationKeyEvent new_event; new_event.SetEventObject( win->GetParent() ); @@ -1061,7 +1111,7 @@ static gint gtk_window_key_press_callback( GtkWidget *widget, } /* generate wxID_CANCEL if has been pressed (typically in dialogs) */ - if ( (!ret) && + if ( !ret && (gdk_event->keyval == GDK_Escape) ) { wxCommandEvent new_event(wxEVT_COMMAND_BUTTON_CLICKED,wxID_CANCEL); @@ -1069,10 +1119,9 @@ static gint gtk_window_key_press_callback( GtkWidget *widget, ret = win->GetEventHandler()->ProcessEvent( new_event ); } -#if (GTK_MINOR_VERSION > 0) - /* Pressing F10 will activate the menu bar of the top frame. */ /* Doesn't work. */ -/* +#if 0 // (GTK_MINOR_VERSION > 0) + /* Pressing F10 will activate the menu bar of the top frame. */ if ( (!ret) && (gdk_event->keyval == GDK_F10) ) { @@ -1098,8 +1147,7 @@ static gint gtk_window_key_press_callback( GtkWidget *widget, ancestor = ancestor->GetParent(); } } -*/ -#endif +#endif // 0 if (ret) { @@ -1124,28 +1172,20 @@ static gint gtk_window_key_release_callback( GtkWidget *widget, GdkEventKey *gdk if (!win->m_hasVMT) return FALSE; if (g_blockEventsOnDrag) return FALSE; -/* - printf( "KeyUp-ScanCode is: %d.\n", gdk_event->keyval ); - if (gdk_event->state & GDK_SHIFT_MASK) - printf( "ShiftDown.\n" ); - else - printf( "ShiftUp.\n" ); - if (gdk_event->state & GDK_CONTROL_MASK) - printf( "ControlDown.\n" ); - else - printf( "ControlUp.\n" ); - printf( "\n" ); -*/ - 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; int x = 0; int y = 0; GdkModifierType state; - if (gdk_event->window) gdk_window_get_pointer(gdk_event->window, &x, &y, &state); + if (gdk_event->window) + gdk_window_get_pointer(gdk_event->window, &x, &y, &state); wxKeyEvent event( wxEVT_KEY_UP ); event.SetTimestamp( gdk_event->time ); @@ -1293,6 +1333,14 @@ static gint gtk_window_button_press_callback( GtkWidget *widget, GdkEventButton event.m_y = (wxCoord)gdk_event->y; AdjustEventButtonState(event); + + // wxListBox actually get mouse events from the item + + if (win->m_isListBox) + { + event.m_x += widget->allocation.x; + event.m_y += widget->allocation.y; + } // Some control don't have their own X window and thus cannot get // any events. @@ -1426,6 +1474,14 @@ static gint gtk_window_button_release_callback( GtkWidget *widget, GdkEventButto AdjustEventButtonState(event); + // wxListBox actually get mouse events from the item + + if (win->m_isListBox) + { + event.m_x += widget->allocation.x; + event.m_y += widget->allocation.y; + } + // Some control don't have their own X window and thus cannot get // any events. @@ -1709,14 +1765,25 @@ static gint gtk_window_focus_in_callback( GtkWidget *widget, } #endif // wxUSE_CARET - if (win->IsTopLevel()) + wxWindowGTK *active = wxGetTopLevelParent(win); + if ( active != g_activeFrame ) { - wxActivateEvent event( wxEVT_ACTIVATE, TRUE, win->GetId() ); - event.SetEventObject( win ); + if ( g_activeFrame ) + { + wxLogTrace(wxT("activate"), wxT("Deactivating frame %p (from focus_in)"), g_activeFrame); + wxActivateEvent event(wxEVT_ACTIVATE, FALSE, g_activeFrame->GetId()); + event.SetEventObject(g_activeFrame); + g_activeFrame->GetEventHandler()->ProcessEvent(event); + } - // ignore return value - win->GetEventHandler()->ProcessEvent( event ); + wxLogTrace(wxT("activate"), wxT("Activating frame %p (from focus_in)"), active); + g_activeFrame = active; + wxActivateEvent event(wxEVT_ACTIVATE, TRUE, g_activeFrame->GetId()); + event.SetEventObject(g_activeFrame); + g_activeFrame->GetEventHandler()->ProcessEvent(event); } + g_activeFrameLostFocus = FALSE; + wxFocusEvent event( wxEVT_SET_FOCUS, win->GetId() ); event.SetEventObject( win ); @@ -1759,6 +1826,19 @@ static gint gtk_window_focus_out_callback( GtkWidget *widget, GdkEvent *WXUNUSED return FALSE; } + if ( !g_activeFrameLostFocus && g_activeFrame ) + { + // VZ: commenting this out because it does happen (although not easy + // to reproduce, I only see it when using wxMiniFrame and not + // always) and makes using Mahogany quite annoying +#if 0 + wxASSERT_MSG( wxGetTopLevelParent(win) == g_activeFrame, + wxT("unfocusing window that hasn't gained focus properly") ) +#endif // 0 + + g_activeFrameLostFocus = TRUE; + } + // if the focus goes out of our app alltogether, OnIdle() will send // wxActivateEvent, otherwise gtk_window_focus_in_callback() will reset // g_sendActivateEvent to -1 @@ -1791,15 +1871,6 @@ static gint gtk_window_focus_out_callback( GtkWidget *widget, GdkEvent *WXUNUSED } #endif // wxUSE_CARET - if (win->IsTopLevel()) - { - wxActivateEvent event( wxEVT_ACTIVATE, FALSE, win->GetId() ); - event.SetEventObject( win ); - - // ignore return value - win->GetEventHandler()->ProcessEvent( event ); - } - wxFocusEvent event( wxEVT_KILL_FOCUS, win->GetId() ); event.SetEventObject( win ); @@ -2318,6 +2389,7 @@ void wxWindowGTK::Init() m_isStaticBox = FALSE; m_isRadioButton = FALSE; + m_isListBox = FALSE; m_isFrame = FALSE; m_acceptsFocus = FALSE; @@ -2491,6 +2563,9 @@ wxWindowGTK::~wxWindowGTK() if (g_focusWindow == this) g_focusWindow = NULL; + if (g_activeFrame == this) + g_activeFrame = NULL; + m_isBeingDeleted = TRUE; m_hasVMT = FALSE; @@ -2810,6 +2885,19 @@ void wxWindowGTK::OnInternalIdle() wxTheApp->SetActive(activate, (wxWindow *)g_focusWindowLast); } + if ( g_activeFrameLostFocus ) + { + if ( g_activeFrame ) + { + wxLogTrace(wxT("activate"), wxT("Deactivating frame %p (from idle)"), g_activeFrame); + wxActivateEvent event(wxEVT_ACTIVATE, FALSE, g_activeFrame->GetId()); + event.SetEventObject(g_activeFrame); + g_activeFrame->GetEventHandler()->ProcessEvent(event); + g_activeFrame = NULL; + } + g_activeFrameLostFocus = FALSE; + } + wxCursor cursor = m_cursor; if (g_globalCursor.Ok()) cursor = g_globalCursor; @@ -3646,16 +3734,29 @@ static void SetInvokingWindow( wxMenu *menu, wxWindowGTK *win ) } } +// used to pass the coordinates from wxWindowGTK::DoPopupMenu() to +// wxPopupMenuPositionCallback() +// +// should be safe even in the MT case as the user can hardly popup 2 menus +// simultaneously, can he? static gint gs_pop_x = 0; static gint gs_pop_y = 0; -static void pop_pos_callback( GtkMenu * WXUNUSED(menu), - gint *x, gint *y, - wxWindowGTK *win ) +static void wxPopupMenuPositionCallback( GtkMenu *menu, + gint *x, gint *y, + gpointer WXUNUSED(user_data) ) { - win->ClientToScreen( &gs_pop_x, &gs_pop_y ); - *x = gs_pop_x; - *y = gs_pop_y; + // ensure that the menu appears entirely on screen + GtkRequisition req; + gtk_widget_get_child_requisition(GTK_WIDGET(menu), &req); + + wxSize sizeScreen = wxGetDisplaySize(); + + gint xmax = sizeScreen.x - req.width, + ymax = sizeScreen.y - req.height; + + *x = gs_pop_x < xmax ? gs_pop_x : xmax; + *y = gs_pop_y < ymax ? gs_pop_y : ymax; } bool wxWindowGTK::DoPopupMenu( wxMenu *menu, int x, int y ) @@ -3670,6 +3771,7 @@ bool wxWindowGTK::DoPopupMenu( wxMenu *menu, int x, int y ) gs_pop_x = x; gs_pop_y = y; + ClientToScreen( &gs_pop_x, &gs_pop_y ); bool is_waiting = TRUE; @@ -3678,12 +3780,12 @@ bool wxWindowGTK::DoPopupMenu( wxMenu *menu, int x, int y ) gtk_menu_popup( GTK_MENU(menu->m_menu), - (GtkWidget *) NULL, // parent menu shell - (GtkWidget *) NULL, // parent menu item - (GtkMenuPositionFunc) pop_pos_callback, - (gpointer) this, // client data - 0, // button used to activate it - gs_timeLastClick // the time of activation + (GtkWidget *) NULL, // parent menu shell + (GtkWidget *) NULL, // parent menu item + wxPopupMenuPositionCallback, // function to position it + NULL, // client data + 0, // button used to activate it + gs_timeLastClick // the time of activation ); while (is_waiting)