X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/bd2e08d06528b2014a4acf878e91fee4696d0666..2399c87ccef4649135075265c2a271860e6df327:/src/gtk/window.cpp diff --git a/src/gtk/window.cpp b/src/gtk/window.cpp index b65a255afc..d795867315 100644 --- a/src/gtk/window.cpp +++ b/src/gtk/window.cpp @@ -40,6 +40,29 @@ #include #include +#if !GTK_CHECK_VERSION(2,10,0) + // GTK+ can reliably detect Meta key state only since 2.10 when + // GDK_META_MASK was introduced -- there wasn't any way to detect it + // in older versions. wxGTK used GDK_MOD2_MASK for this purpose, but + // GDK_MOD2_MASK is documented as: + // + // the fifth modifier key (it depends on the modifier mapping of the X + // server which key is interpreted as this modifier) + // + // In other words, it isn't guaranteed to map to Meta. This is a real + // problem: it is common to map NumLock to it (in fact, it's an exception + // if the X server _doesn't_ use it for NumLock). So the old code caused + // wxKeyEvent::MetaDown() to always return true as long as NumLock was on + // on many systems, which broke all applications using + // wxKeyEvent::GetModifiers() to check modifiers state (see e.g. here: + // http://tinyurl.com/56lsk2). + // + // Because of this, it's better to not detect Meta key state at all than + // to detect it incorrectly. Hence the following #define, which causes + // m_metaDown to be always set to false. + #define GDK_META_MASK 0 +#endif + //----------------------------------------------------------------------------- // documentation on internals //----------------------------------------------------------------------------- @@ -185,9 +208,11 @@ extern wxCursor g_globalCursor; static wxWindowGTK *g_captureWindow = (wxWindowGTK*) NULL; static bool g_captureWindowHasMouse = false; -// The window that currently has focus or is scheduled to get it in the next -// event loop iteration -static wxWindowGTK *gs_focusWindow = NULL; +// The window that currently has focus: +static wxWindowGTK *gs_currentFocus = NULL; +// The window that is scheduled to get focus in the next event loop iteration +// or NULL if there's no pending focus change: +static wxWindowGTK *gs_pendingFocus = NULL; // the window that has deferred focus-out event pending, if any (see // GTKAddDeferredFocusOut() for details) @@ -256,39 +281,15 @@ void wxgtk_window_size_request_callback(GtkWidget * WXUNUSED(widget), extern "C" { static gboolean -gtk_window_expose_callback( GtkWidget*, +gtk_window_expose_callback( GtkWidget* widget, GdkEventExpose *gdk_event, wxWindow *win ) { -#if 0 - if (win->GetName()) + if (gdk_event->window == widget->window) { - wxPrintf( wxT("OnExpose from ") ); - if (win->GetClassInfo() && win->GetClassInfo()->GetClassName()) - wxPrintf( win->GetClassInfo()->GetClassName() ); - wxPrintf( wxT(" %d %d %d %d\n"), (int)gdk_event->area.x, - (int)gdk_event->area.y, - (int)gdk_event->area.width, - (int)gdk_event->area.height ); + win->GetUpdateRegion() = wxRegion( gdk_event->region ); + win->GtkSendPaintEvents(); } - - gtk_paint_box - ( - win->m_wxwindow->style, - pizza->bin_window, - GTK_STATE_NORMAL, - GTK_SHADOW_OUT, - (GdkRectangle*) NULL, - win->m_wxwindow, - (char *)"button", // const_cast - 20,20,24,24 - ); -#endif - - win->GetUpdateRegion() = wxRegion( gdk_event->region ); - - win->GtkSendPaintEvents(); - // Let parent window draw window-less widgets return FALSE; } @@ -300,8 +301,6 @@ gtk_window_expose_callback( GtkWidget*, #ifndef __WXUNIVERSAL__ -GtkWidget* GetEntryWidget(); - extern "C" { static gboolean expose_event_border(GtkWidget* widget, GdkEventExpose* gdk_event, wxWindow* win) @@ -342,7 +341,7 @@ expose_event_border(GtkWidget* widget, GdkEventExpose* gdk_event, wxWindow* win) // for scrollable ones detail = "viewport"; - GtkWidget* styleWidget = GetEntryWidget(); + GtkWidget* styleWidget = wxGTKPrivate::GetEntryWidget(); gtk_paint_shadow( styleWidget->style, gdk_event->window, GTK_STATE_NORMAL, shadow, NULL, styleWidget, detail, x, y, w, h); @@ -522,11 +521,11 @@ static long wxTranslateKeySymToWXKey(KeySym keysym, bool isChar) case GDK_KP_7: case GDK_KP_8: case GDK_KP_9: - key_code = (isChar ? '0' : WXK_NUMPAD0) + keysym - GDK_KP_0; + key_code = (isChar ? '0' : int(WXK_NUMPAD0)) + keysym - GDK_KP_0; break; case GDK_KP_Space: - key_code = isChar ? ' ' : WXK_NUMPAD_SPACE; + key_code = isChar ? ' ' : int(WXK_NUMPAD_SPACE); break; case GDK_KP_Tab: @@ -598,32 +597,32 @@ static long wxTranslateKeySymToWXKey(KeySym keysym, bool isChar) break; case GDK_KP_Equal: - key_code = isChar ? '=' : WXK_NUMPAD_EQUAL; + key_code = isChar ? '=' : int(WXK_NUMPAD_EQUAL); break; case GDK_KP_Multiply: - key_code = isChar ? '*' : WXK_NUMPAD_MULTIPLY; + key_code = isChar ? '*' : int(WXK_NUMPAD_MULTIPLY); break; case GDK_KP_Add: - key_code = isChar ? '+' : WXK_NUMPAD_ADD; + key_code = isChar ? '+' : int(WXK_NUMPAD_ADD); break; case GDK_KP_Separator: // FIXME: what is this? - key_code = isChar ? '.' : WXK_NUMPAD_SEPARATOR; + key_code = isChar ? '.' : int(WXK_NUMPAD_SEPARATOR); break; case GDK_KP_Subtract: - key_code = isChar ? '-' : WXK_NUMPAD_SUBTRACT; + key_code = isChar ? '-' : int(WXK_NUMPAD_SUBTRACT); break; case GDK_KP_Decimal: - key_code = isChar ? '.' : WXK_NUMPAD_DECIMAL; + key_code = isChar ? '.' : int(WXK_NUMPAD_DECIMAL); break; case GDK_KP_Divide: - key_code = isChar ? '/' : WXK_NUMPAD_DIVIDE; + key_code = isChar ? '/' : int(WXK_NUMPAD_DIVIDE); break; @@ -670,7 +669,7 @@ static void wxFillOtherKeyEventFields(wxKeyEvent& event, 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_metaDown = (gdk_event->state & GDK_META_MASK) != 0; event.m_scanCode = gdk_event->keyval; event.m_rawCode = (wxUint32) gdk_event->keyval; event.m_rawFlags = 0; @@ -869,8 +868,18 @@ gtk_window_key_press_callback( GtkWidget *widget, int command = ancestor->GetAcceleratorTable()->GetCommand( event ); if (command != -1) { - wxCommandEvent command_event( wxEVT_COMMAND_MENU_SELECTED, command ); - ret = ancestor->HandleWindowEvent( command_event ); + wxCommandEvent menu_event( wxEVT_COMMAND_MENU_SELECTED, command ); + ret = ancestor->HandleWindowEvent( menu_event ); + + if ( !ret ) + { + // if the accelerator wasn't handled as menu event, try + // it as button click (for compatibility with other + // platforms): + wxCommandEvent button_event( wxEVT_COMMAND_BUTTON_CLICKED, command ); + ret = ancestor->HandleWindowEvent( button_event ); + } + break; } if (ancestor->IsTopLevel()) @@ -1062,7 +1071,7 @@ template void InitMouseEvent(wxWindowGTK *win, 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_metaDown = gdk_event->state & GDK_META_MASK; event.m_leftDown = gdk_event->state & GDK_BUTTON1_MASK; event.m_middleDown = gdk_event->state & GDK_BUTTON2_MASK; event.m_rightDown = gdk_event->state & GDK_BUTTON3_MASK; @@ -1372,7 +1381,7 @@ gtk_window_button_press_callback( GtkWidget *widget, return TRUE; if ((event_type == wxEVT_LEFT_DOWN) && !win->IsOfStandardClass() && - (gs_focusWindow != win) /* && win->IsFocusable() */) + (gs_currentFocus != win) /* && win->IsFocusable() */) { win->SetFocus(); } @@ -1891,33 +1900,23 @@ public: wxWindow *wxWindowBase::DoFindFocus() { + wxWindowGTK *focus = gs_pendingFocus ? gs_pendingFocus : gs_currentFocus; // the cast is necessary when we compile in wxUniversal mode - return wx_static_cast(wxWindow*, gs_focusWindow); + return wx_static_cast(wxWindow*, focus); } -//----------------------------------------------------------------------------- -// InsertChild for wxWindowGTK. -//----------------------------------------------------------------------------- - -/* Callback for wxWindowGTK. This very strange beast has to be used because - * C++ has no virtual methods in a constructor. We have to emulate a - * virtual function here as wxNotebook requires a different way to insert - * a child in it. I had opted for creating a wxNotebookPage window class - * which would have made this superfluous (such in the MDI window system), - * but no-one was listening to me... */ - -static void wxInsertChildInWindow( wxWindowGTK* parent, wxWindowGTK* child ) +void wxWindowGTK::AddChildGTK(wxWindowGTK* child) { /* the window might have been scrolled already, do we have to adapt the position */ - wxPizza* pizza = WX_PIZZA(parent->m_wxwindow); + wxPizza* pizza = WX_PIZZA(m_wxwindow); child->m_x += pizza->m_scroll_x; child->m_y += pizza->m_scroll_y; gtk_widget_set_size_request( child->m_widget, child->m_width, child->m_height); gtk_fixed_put( - GTK_FIXED(parent->m_wxwindow), child->m_widget, child->m_x, child->m_y); + GTK_FIXED(m_wxwindow), child->m_widget, child->m_x, child->m_y); } //----------------------------------------------------------------------------- @@ -1951,7 +1950,7 @@ wxMouseState wxGetMouseState() ms.SetControlDown(mask & GDK_CONTROL_MASK); ms.SetShiftDown(mask & GDK_SHIFT_MASK); ms.SetAltDown(mask & GDK_MOD1_MASK); - ms.SetMetaDown(mask & GDK_MOD2_MASK); + ms.SetMetaDown(mask & GDK_META_MASK); return ms; } @@ -2002,8 +2001,6 @@ void wxWindowGTK::Init() m_oldClientWidth = m_oldClientHeight = 0; - m_insertCallback = wxInsertChildInWindow; - m_clipPaintRegion = false; m_needsStyleChange = false; @@ -2121,12 +2118,15 @@ bool wxWindowGTK::Create( wxWindow *parent, gtk_widget_show( m_wxwindow ); } + g_object_ref(m_widget); if (m_parent) m_parent->DoAddChild( this ); m_focusWidget = m_wxwindow; + SetCanFocus(AcceptsFocus()); + PostCreation(); return true; @@ -2136,8 +2136,10 @@ wxWindowGTK::~wxWindowGTK() { SendDestroyEvent(); - if (gs_focusWindow == this) - gs_focusWindow = NULL; + if (gs_currentFocus == this) + gs_currentFocus = NULL; + if (gs_pendingFocus == this) + gs_pendingFocus = NULL; if ( gs_deferredFocusOut == this ) gs_deferredFocusOut = NULL; @@ -2166,17 +2168,17 @@ wxWindowGTK::~wxWindowGTK() // delete before the widgets to avoid a crash on solaris delete m_imData; - if (m_wxwindow && (m_wxwindow != m_widget)) - { - gtk_widget_destroy( m_wxwindow ); - m_wxwindow = (GtkWidget*) NULL; - } - if (m_widget) { - gtk_widget_destroy( m_widget ); - m_widget = (GtkWidget*) NULL; + // Note that gtk_widget_destroy() does not destroy the widget, it just + // emits the "destroy" signal. The widget is not actually destroyed + // until its reference count drops to zero. + gtk_widget_destroy(m_widget); + // Release our reference, should be the last one + g_object_unref(m_widget); + m_widget = NULL; } + m_wxwindow = NULL; } bool wxWindowGTK::PreCreation( wxWindowGTK *parent, const wxPoint &pos, const wxSize &size ) @@ -2426,8 +2428,6 @@ void wxWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags if (height != -1) m_height = height; - ConstrainSize(); - if (m_parent->m_wxwindow) { wxPizza* pizza = WX_PIZZA(m_parent->m_wxwindow); @@ -2907,9 +2907,8 @@ bool wxWindowGTK::GTKHandleFocusIn() if (m_imData) gtk_im_context_focus_in(m_imData->context); - // NB: SetFocus() does this assignment too, but not all focus changes - // originate from SetFocus() call - gs_focusWindow = this; + gs_currentFocus = this; + gs_pendingFocus = NULL; #if wxUSE_CARET // caret needs to be informed about focus change @@ -2922,7 +2921,7 @@ bool wxWindowGTK::GTKHandleFocusIn() // Notify the parent keeping track of focus for the kbd navigation // purposes that we got it. - wxChildFocusEvent eventChildFocus(this); + wxChildFocusEvent eventChildFocus(static_cast(this)); GTKProcessEvent(eventChildFocus); wxFocusEvent eventFocus(wxEVT_SET_FOCUS, GetId()); @@ -2971,22 +2970,22 @@ void wxWindowGTK::GTKHandleFocusOutNoDeferring() if (m_imData) gtk_im_context_focus_out(m_imData->context); - if ( gs_focusWindow != this ) + if ( gs_currentFocus != this ) { - // Something is terribly wrong, gs_focusWindow is out of sync with the + // Something is terribly wrong, gs_currentFocus is out of sync with the // real focus. We will reset it to NULL anyway, because after this // focus-out event is handled, one of the following with happen: // // * either focus will go out of the app altogether, in which case - // gs_focusWindow _should_ be NULL + // gs_currentFocus _should_ be NULL // // * or it goes to another control, in which case focus-in event will - // follow immediately and it will set gs_focusWindow to the right + // follow immediately and it will set gs_currentFocus to the right // value wxLogDebug("window %s(%p, %s) lost focus even though it didn't have it", GetClassInfo()->GetClassName(), this, GetLabel()); } - gs_focusWindow = NULL; + gs_currentFocus = NULL; #if wxUSE_CARET // caret needs to be informed about focus change @@ -3037,7 +3036,7 @@ void wxWindowGTK::SetFocus() // Because we want to FindFocus() call immediately following // foo->SetFocus() to return foo, we have to keep track of "pending" focus // ourselves. - gs_focusWindow = this; + gs_pendingFocus = this; GtkWidget *widget = m_wxwindow ? m_wxwindow : m_focusWidget; @@ -3088,13 +3087,8 @@ bool wxWindowGTK::Reparent( wxWindowBase *newParentBase ) wxASSERT( GTK_IS_WIDGET(m_widget) ); - /* prevent GTK from deleting the widget arbitrarily */ - gtk_widget_ref( m_widget ); - if (oldParent) - { gtk_container_remove( GTK_CONTAINER(m_widget->parent), m_widget ); - } wxASSERT( GTK_IS_WIDGET(m_widget) ); @@ -3105,14 +3099,10 @@ bool wxWindowGTK::Reparent( wxWindowBase *newParentBase ) m_showOnIdle = true; gtk_widget_hide( m_widget ); } - /* insert GTK representation */ - (*(newParent->m_insertCallback))(newParent, this); + newParent->AddChildGTK(this); } - /* reverse: prevent GTK from deleting the widget arbitrarily */ - gtk_widget_unref( m_widget ); - SetLayoutDirection(wxLayout_Default); return true; @@ -3127,7 +3117,7 @@ void wxWindowGTK::DoAddChild(wxWindowGTK *child) AddChild( child ); /* insert GTK representation */ - (*m_insertCallback)(this, child); + AddChildGTK(child); } void wxWindowGTK::AddChild(wxWindowBase *child) @@ -3579,7 +3569,7 @@ void wxWindowGTK::GtkSendPaintEvents() else { wxWindowDC dc( (wxWindow*)this ); - dc.SetClippingRegion( m_updateRegion ); + dc.SetDeviceClippingRegion( m_updateRegion ); // Work around gtk-qt <= 0.60 bug whereby the window colour // remains grey @@ -3827,6 +3817,103 @@ bool wxWindowGTK::SetBackgroundStyle(wxBackgroundStyle style) return true; } +// ---------------------------------------------------------------------------- +// Pop-up menu stuff +// ---------------------------------------------------------------------------- + +#if wxUSE_MENUS_NATIVE + +static void SetInvokingWindow( wxMenu *menu, wxWindow* win ) +{ + menu->SetInvokingWindow( win ); + + wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst(); + while (node) + { + wxMenuItem *menuitem = node->GetData(); + if (menuitem->IsSubMenu()) + { + SetInvokingWindow( menuitem->GetSubMenu(), win ); + } + + node = node->GetNext(); + } +} + +extern "C" { +static +void wxPopupMenuPositionCallback( GtkMenu *menu, + gint *x, gint *y, + gboolean * WXUNUSED(whatever), + gpointer user_data ) +{ + // ensure that the menu appears entirely on screen + GtkRequisition req; + gtk_widget_get_child_requisition(GTK_WIDGET(menu), &req); + + wxSize sizeScreen = wxGetDisplaySize(); + wxPoint *pos = (wxPoint*)user_data; + + gint xmax = sizeScreen.x - req.width, + ymax = sizeScreen.y - req.height; + + *x = pos->x < xmax ? pos->x : xmax; + *y = pos->y < ymax ? pos->y : ymax; +} +} + +void wxWindowGTK::DoPopupMenuUpdateUI(wxMenu* menu) +{ + menu->UpdateUI(); +} + +bool wxWindowGTK::DoPopupMenu( wxMenu *menu, int x, int y ) +{ + wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") ); + + wxCHECK_MSG( menu != NULL, false, wxT("invalid popup-menu") ); + + SetInvokingWindow( menu, this ); + + DoPopupMenuUpdateUI(menu); + + wxPoint pos; + gpointer userdata; + GtkMenuPositionFunc posfunc; + if ( x == -1 && y == -1 ) + { + // use GTK's default positioning algorithm + userdata = NULL; + posfunc = NULL; + } + else + { + pos = ClientToScreen(wxPoint(x, y)); + userdata = &pos; + posfunc = wxPopupMenuPositionCallback; + } + + menu->m_popupShown = true; + gtk_menu_popup( + GTK_MENU(menu->m_menu), + (GtkWidget *) NULL, // parent menu shell + (GtkWidget *) NULL, // parent menu item + posfunc, // function to position it + userdata, // client data + 0, // button used to activate it + gtk_get_current_event_time() + ); + + while (menu->m_popupShown) + { + gtk_main_iteration(); + } + + return true; +} + +#endif // wxUSE_MENUS_NATIVE + #if wxUSE_DRAG_AND_DROP void wxWindowGTK::SetDropTarget( wxDropTarget *dropTarget ) @@ -4200,16 +4287,68 @@ GdkWindow* wxWindowGTK::GTKGetDrawingWindow() const // freeze/thaw // ---------------------------------------------------------------------------- +extern "C" +{ + +// this is called if we attempted to freeze unrealized widget when it finally +// is realized (and so can be frozen): +static void wx_frozen_widget_realize(GtkWidget* w, void* WXUNUSED(data)) +{ + wxASSERT( w && !GTK_WIDGET_NO_WINDOW(w) ); + wxASSERT( GTK_WIDGET_REALIZED(w) ); + + g_signal_handlers_disconnect_by_func + ( + w, + (void*)wx_frozen_widget_realize, + NULL + ); + + gdk_window_freeze_updates(w->window); +} + +} // extern "C" + void wxWindowGTK::GTKFreezeWidget(GtkWidget *w) { - if ( w && !GTK_WIDGET_NO_WINDOW(w) ) - gdk_window_freeze_updates(w->window); + if ( !w || GTK_WIDGET_NO_WINDOW(w) ) + return; // window-less widget, cannot be frozen + + if ( !GTK_WIDGET_REALIZED(w) ) + { + // we can't thaw unrealized widgets because they don't have GdkWindow, + // so set it up to be done immediately after realization: + g_signal_connect_after + ( + w, + "realize", + G_CALLBACK(wx_frozen_widget_realize), + NULL + ); + return; + } + + gdk_window_freeze_updates(w->window); } void wxWindowGTK::GTKThawWidget(GtkWidget *w) { - if ( w && !GTK_WIDGET_NO_WINDOW(w) ) - gdk_window_thaw_updates(w->window); + if ( !w || GTK_WIDGET_NO_WINDOW(w) ) + return; // window-less widget, cannot be frozen + + if ( !GTK_WIDGET_REALIZED(w) ) + { + // the widget wasn't realized yet, no need to thaw + g_signal_handlers_disconnect_by_func + ( + w, + (void*)wx_frozen_widget_realize, + NULL + ); + return; + } + + gdk_window_thaw_updates(w->window); } void wxWindowGTK::DoFreeze()