X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/44dfb5ce92980483d077896c92cc5b388c33cf5e..0c8b041f147ff9bb6ce38f0e2da1fd82f6484523:/src/gtk1/window.cpp diff --git a/src/gtk1/window.cpp b/src/gtk1/window.cpp index 4b7a6ed8ae..fd76da760f 100644 --- a/src/gtk1/window.cpp +++ b/src/gtk1/window.cpp @@ -28,6 +28,7 @@ #include "wx/dialog.h" #include "wx/msgdlg.h" #include "wx/module.h" +#include "wx/combobox.h" #if wxUSE_DRAG_AND_DROP #include "wx/dnd.h" @@ -56,7 +57,7 @@ #include "wx/thread.h" #endif -#include +#include "wx/math.h" #include #include "wx/gtk/private.h" @@ -73,11 +74,6 @@ #include #endif -#ifdef __WXGTK20__ - #define SET_CONTAINER_FOCUS(w, d) gtk_widget_child_focus((w), (d)) -#else - #define SET_CONTAINER_FOCUS(w, d) gtk_container_focus(GTK_CONTAINER(w), (d)) -#endif #ifdef __WXGTK20__ #ifdef HAVE_XIM @@ -145,7 +141,7 @@ extern GtkContainerClass *pizza_parent_class; thw wxWindow class has a member variable called m_widget which holds a pointer to this widget. When the window class represents a GTK native widget, this is (in most cases) the only GTK widget the class manages. E.g. the - wxStatitText class handles only a GtkLabel widget a pointer to which you + wxStaticText class handles only a GtkLabel widget a pointer to which you can find in m_widget (defined in wxWindow) When the class has a client area for drawing into and for containing children @@ -235,28 +231,17 @@ static GdkGC *g_eraseGC = NULL; static wxWindowGTK *g_captureWindow = (wxWindowGTK*) NULL; static bool g_captureWindowHasMouse = FALSE; -/* extern */ wxWindowGTK *g_focusWindow = (wxWindowGTK*) NULL; +wxWindowGTK *g_focusWindow = (wxWindowGTK*) NULL; // 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; - -// 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; +wxWindowGTK *g_focusWindowLast = (wxWindowGTK*) NULL; // 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 -// send any activate events at all -static int g_sendActivateEvent = -1; - // hack: we need something to pass to gtk_menu_popup, so we store the time of // the last click here static guint32 gs_timeLastClick = 0; @@ -394,7 +379,7 @@ static void draw_frame( GtkWidget *widget, wxWindowGTK *win ) dh += hscroll_req.height; dh += scroll_class->scrollbar_spacing; } -} + } int dx = 0; int dy = 0; @@ -444,7 +429,8 @@ static void draw_frame( GtkWidget *widget, wxWindowGTK *win ) // "expose_event" of m_widget //----------------------------------------------------------------------------- -gint gtk_window_own_expose_callback( GtkWidget *widget, GdkEventExpose *gdk_event, wxWindowGTK *win ) +extern "C" { +static gint gtk_window_own_expose_callback( GtkWidget *widget, GdkEventExpose *gdk_event, wxWindowGTK *win ) { if (gdk_event->count > 0) return FALSE; @@ -457,6 +443,7 @@ gint gtk_window_own_expose_callback( GtkWidget *widget, GdkEventExpose *gdk_even #endif return TRUE; } +} //----------------------------------------------------------------------------- // "draw" of m_widget @@ -464,10 +451,12 @@ gint gtk_window_own_expose_callback( GtkWidget *widget, GdkEventExpose *gdk_even #ifndef __WXGTK20__ +extern "C" { static void gtk_window_own_draw_callback( GtkWidget *widget, GdkRectangle *WXUNUSED(rect), wxWindowGTK *win ) { draw_frame( widget, win ); } +} #endif // GTK+ < 2.0 @@ -475,8 +464,8 @@ static void gtk_window_own_draw_callback( GtkWidget *widget, GdkRectangle *WXUNU // "size_request" of m_widget //----------------------------------------------------------------------------- -// make it extern because wxStatitText needs to disconnect this one -extern "C" +// make it extern because wxStaticText needs to disconnect this one +extern "C" { void wxgtk_window_size_request_callback(GtkWidget *widget, GtkRequisition *requisition, wxWindow *win) @@ -491,11 +480,42 @@ void wxgtk_window_size_request_callback(GtkWidget *widget, requisition->height = h; requisition->width = w; } +} + +extern "C" { +static +void wxgtk_combo_size_request_callback(GtkWidget *widget, + GtkRequisition *requisition, + wxComboBox *win) +{ + // This callback is actually hooked into the text entry + // of the combo box, not the GtkHBox. + + int w, h; + win->GetSize( &w, &h ); + if (w < 2) + w = 2; + if (h < 2) + h = 2; + + GtkCombo *gcombo = GTK_COMBO(win->m_widget); + + GtkRequisition entry_req; + entry_req.width = 2; + entry_req.height = 2; + (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(gcombo->button) )->size_request ) + (gcombo->button, &entry_req ); + + requisition->width = w - entry_req.width; + requisition->height = entry_req.height+4; // TODO: why +4? +} +} //----------------------------------------------------------------------------- // "expose_event" of m_wxwindow //----------------------------------------------------------------------------- +extern "C" { static int gtk_window_expose_callback( GtkWidget *widget, GdkEventExpose *gdk_event, wxWindow *win ) @@ -565,17 +585,22 @@ static int gtk_window_expose_callback( GtkWidget *widget, return FALSE; } +} //----------------------------------------------------------------------------- // "event" of m_wxwindow //----------------------------------------------------------------------------- +#ifndef __WXGTK20__ + // GTK thinks it is clever and filters out a certain amount of "unneeded" // expose events. We need them, of course, so we override the main event // procedure in GtkWidget by giving our own handler for all system events. // There, we look for expose events ourselves whereas all other events are // handled normally. +extern "C" { +static gint gtk_window_event_event_callback( GtkWidget *widget, GdkEventExpose *event, wxWindow *win ) @@ -588,6 +613,9 @@ gint gtk_window_event_event_callback( GtkWidget *widget, return FALSE; } +} + +#endif // !GTK+ 2 //----------------------------------------------------------------------------- // "draw" of m_wxwindow @@ -598,6 +626,7 @@ gint gtk_window_event_event_callback( GtkWidget *widget, // This callback is a complete replacement of the gtk_pizza_draw() function, // which is disabled. +extern "C" { static void gtk_window_draw_callback( GtkWidget *widget, GdkRectangle *rect, wxWindow *win ) @@ -632,7 +661,7 @@ static void gtk_window_draw_callback( GtkWidget *widget, #ifndef __WXUNIVERSAL__ GtkPizza *pizza = GTK_PIZZA (widget); - if (win->GetThemeEnabled()) + if (win->GetThemeEnabled() && win->GetBackgroundStyle() == wxBG_STYLE_SYSTEM) { wxWindow *parent = win->GetParent(); while (parent && !parent->IsTopLevel()) @@ -673,6 +702,7 @@ static void gtk_window_draw_callback( GtkWidget *widget, } #endif } +} #endif @@ -965,6 +995,36 @@ static inline bool wxIsAsciiKeysym(KeySym ks) return ks < 256; } +static void wxFillOtherKeyEventFields(wxKeyEvent& event, + wxWindowGTK *win, + GdkEventKey *gdk_event) +{ + 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.SetId(win->GetId()); + 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_scanCode = gdk_event->keyval; + event.m_rawCode = (wxUint32) gdk_event->keyval; + event.m_rawFlags = 0; +#if wxUSE_UNICODE + event.m_uniChar = gdk_keyval_to_unicode(gdk_event->keyval); +#endif + wxGetMousePosition( &x, &y ); + win->ScreenToClient( &x, &y ); + event.m_x = x; + event.m_y = y; + event.SetEventObject( win ); +} + + static bool wxTranslateGTKKeyEventToWx(wxKeyEvent& event, wxWindowGTK *win, @@ -1060,29 +1120,33 @@ wxTranslateGTKKeyEventToWx(wxKeyEvent& event, return FALSE; // 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) != 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; + wxFillOtherKeyEventFields(event, win, gdk_event); + 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 ); return TRUE; } +#ifdef __WXGTK20__ +struct wxGtkIMData +{ + GtkIMContext *context; + GdkEventKey *lastKeyEvent; + + wxGtkIMData() + { + context = gtk_im_multicontext_new(); + lastKeyEvent = NULL; + } + ~wxGtkIMData() + { + g_object_unref(context); + } +}; +#endif + +extern "C" { static gint gtk_window_key_press_callback( GtkWidget *widget, GdkEventKey *gdk_event, wxWindow *win ) @@ -1099,14 +1163,110 @@ static gint gtk_window_key_press_callback( GtkWidget *widget, wxKeyEvent event( wxEVT_KEY_DOWN ); - if ( !wxTranslateGTKKeyEventToWx(event, win, gdk_event) ) + bool ret = false; + bool return_after_IM = false; + + if( wxTranslateGTKKeyEventToWx(event, win, gdk_event) ) { - // unknown key pressed, ignore (the event would be useless anyhow) - return FALSE; + // Emit KEY_DOWN event + ret = win->GetEventHandler()->ProcessEvent( event ); + } + else + { + // Return after IM processing as we cannot do + // anything with it anyhow. + return_after_IM = true; } - // Emit KEY_DOWN event - bool ret = win->GetEventHandler()->ProcessEvent( event ); +#ifdef __WXGTK20__ + // 2005.01.26 modified by Hong Jen Yee (hzysoft@sina.com.tw): + // When we get a key_press event here, it could be originate + // from the current widget or its child widgets. However, only the widget + // with the INPUT FOCUS can generate the INITIAL key_press event. That is, + // if the CURRENT widget doesn't have the FOCUS at all, this event definitely + // originated from its child widgets and shouldn't be passed to IM context. + // In fact, what a GTK+ IM should do is filtering keyEvents and convert them + // into text input ONLY WHEN THE WIDGET HAS INPUT FOCUS. Besides, when current + // widgets has both IM context and input focus, the event should be filtered + // by gtk_im_context_filter_keypress(). + // Then, we should, according to GTK+ 2.0 API doc, return whatever it returns. + if ((!ret) && (win->m_imData != NULL) && ( wxWindow::FindFocus() == win )) + { + // We should let GTK+ IM filter key event first. According to GTK+ 2.0 API + // docs, if IM filter returns true, no further processing should be done. + // we should send the key_down event anyway. + bool intercepted_by_IM = gtk_im_context_filter_keypress(win->m_imData->context, gdk_event); + win->m_imData->lastKeyEvent = NULL; + if (intercepted_by_IM) + { + wxLogTrace(TRACE_KEYS, _T("Key event intercepted by IM")); + return true; + } + } +#endif + if (return_after_IM) + return false; + +#ifndef __WXGTK20__ + // This is for GTK+ 1.2 only. The char event generatation for GTK+ 2.0 is done + // in the "commit" handler. + + // 2005.02.02 modified by Hong Jen Yee (hzysoft@sina.com.tw). + // In GTK+ 1.2, strings sent by IMs are also regarded as key_press events whose + // keyCodes cannot be recognized by wxWidgets. These MBCS strings, however, are + // composed of more than one character, which means gdk_event->length will always + // greater than one. When gtk_event->length == 1, this may be an ASCII character + // and can be translated by wx. However, when MBCS characters are sent by IM, + // gdk_event->length will >= 2. So neither should we pass it to accelerator table, + // nor should we pass it to controls. The following explanation was excerpted + // from GDK documentation. + // gint length : the length of string. + // gchar *string : a null-terminated multi-byte string containing the composed + // characters resulting from the key press. When text is being input, in a GtkEntry + // for example, it is these characters which should be added to the input buffer. + // When using Input Methods to support internationalized text input, the composed + // characters appear here after the pre-editing has been completed. + + if ( (!ret) && (gdk_event->length > 1) ) // If this event contains a pre-edited string from IM. + { + // We should translate this key event into wxEVT_CHAR not wxEVT_KEY_DOWN. + #if wxUSE_UNICODE // GTK+ 1.2 is not UTF-8 based. + const wxWCharBuffer string = wxConvLocal.cMB2WC( gdk_event->string ); + if( !string ) + return false; + #else + const char* string = gdk_event->string; + #endif + + // Implement OnCharHook by checking ancesteror top level windows + wxWindow *parent = win; + while (parent && !parent->IsTopLevel()) + parent = parent->GetParent(); + + for( const wxChar* pstr = string; *pstr; pstr++ ) + { + #if wxUSE_UNICODE + event.m_uniChar = *pstr; + // Backward compatible for ISO-8859-1 + event.m_keyCode = *pstr < 256 ? event.m_uniChar : 0; + #else + event.m_keyCode = *pstr; + #endif + if (parent) + { + event.SetEventType( wxEVT_CHAR_HOOK ); + ret = parent->GetEventHandler()->ProcessEvent( event ); + } + if (!ret) + { + event.SetEventType(wxEVT_CHAR); + win->GetEventHandler()->ProcessEvent( event ); + } + } + return true; + } + +#endif // #ifndef __WXGTK20__ #if wxUSE_ACCEL if (!ret) @@ -1134,60 +1294,50 @@ static gint gtk_window_key_press_callback( GtkWidget *widget, { long key_code; KeySym keysym = gdk_event->keyval; -#ifdef __WXGTK20__ - // In GTK 2.0, we need to hand over the key event to an input method - // and the IM will emit a "commit" event containing the actual utf8 - // character. In that case the EVT_CHAR events will be sent from - // there. But only do it this way for non-KeySym keys. - key_code = wxTranslateKeySymToWXKey(gdk_event->keyval, FALSE /* isChar */); - if ( !key_code && win->m_imContext ) + // Find key code for EVT_CHAR and EVT_CHAR_HOOK events + key_code = wxTranslateKeySymToWXKey(keysym, TRUE /* isChar */); + if ( !key_code ) { - gtk_im_context_filter_keypress ( (GtkIMContext*) win->m_imContext, gdk_event ); - ret = TRUE; - } - else -#endif - { - // Find key code for EVT_CHAR and EVT_CHAR_HOOK events - key_code = wxTranslateKeySymToWXKey(keysym, TRUE /* isChar */); - if ( !key_code ) + if ( wxIsAsciiKeysym(keysym) ) { - if ( gdk_event->length == 1 ) - { - key_code = (unsigned char)gdk_event->string[0]; - } - else if ( wxIsAsciiKeysym(keysym) ) - { - // ASCII key - key_code = (unsigned char)keysym; - } + // ASCII key + key_code = (unsigned char)keysym; } - - if ( key_code ) + // gdk_event->string is actually deprecated + else if ( gdk_event->length == 1 ) { - wxLogTrace(TRACE_KEYS, _T("Char event: %ld"), key_code); + key_code = (unsigned char)gdk_event->string[0]; + } + } - event.m_keyCode = key_code; + if ( key_code ) + { + wxLogTrace(TRACE_KEYS, _T("Char event: %ld"), key_code); - // Implement OnCharHook by checking ancesteror top level windows - wxWindow *parent = win; - while (parent && !parent->IsTopLevel()) - parent = parent->GetParent(); - if (parent) - { - event.SetEventType( wxEVT_CHAR_HOOK ); - ret = parent->GetEventHandler()->ProcessEvent( event ); - } + event.m_keyCode = key_code; - if (!ret) - { - event.SetEventType(wxEVT_CHAR); - ret = win->GetEventHandler()->ProcessEvent( event ); - } + // Implement OnCharHook by checking ancesteror top level windows + wxWindow *parent = win; + while (parent && !parent->IsTopLevel()) + parent = parent->GetParent(); + if (parent) + { + event.SetEventType( wxEVT_CHAR_HOOK ); + ret = parent->GetEventHandler()->ProcessEvent( event ); + } + + if (!ret) + { + event.SetEventType(wxEVT_CHAR); + ret = win->GetEventHandler()->ProcessEvent( event ); } } } + + + + // win is a control: tab can be propagated up if ( !ret && ((gdk_event->keyval == GDK_Tab) || (gdk_event->keyval == GDK_ISO_Left_Tab)) && @@ -1257,51 +1407,64 @@ static gint gtk_window_key_press_callback( GtkWidget *widget, return FALSE; } +} #ifdef __WXGTK20__ +extern "C" { static void gtk_wxwindow_commit_cb (GtkIMContext *context, const gchar *str, wxWindow *window) { - bool ret = FALSE; - wxKeyEvent event( wxEVT_KEY_DOWN ); -#if wxUSE_UNICODE - event.m_uniChar = g_utf8_get_char( str ); + // take modifiers, cursor position, timestamp etc. from the last + // key_press_event that was fed into Input Method: + if (window->m_imData->lastKeyEvent) + { + wxFillOtherKeyEventFields(event, + window, window->m_imData->lastKeyEvent); + } - // Backward compatible for ISO-8859 - if (event.m_uniChar < 256) - event.m_keyCode = event.m_uniChar; +#if wxUSE_UNICODE + const wxWCharBuffer data = wxConvUTF8.cMB2WC( (char*)str ); #else - gunichar uniChar = g_utf8_get_char( str ); - // We cannot handle Unicode in non-Unicode mode - if (uniChar > 255) return; - - event.m_keyCode = uniChar; -#endif - - - // TODO: We still need to set all the extra attributes of the - // event, modifiers and such... + const wxWCharBuffer wdata = wxConvUTF8.cMB2WC( (char*)str ); + const wxCharBuffer data = wxConvLocal.cWC2MB( wdata ); +#endif // wxUSE_UNICODE + if( !(const wxChar*)data ) + return; + bool ret = false; // Implement OnCharHook by checking ancestor top level windows wxWindow *parent = window; while (parent && !parent->IsTopLevel()) parent = parent->GetParent(); - if (parent) - { - event.SetEventType( wxEVT_CHAR_HOOK ); - ret = parent->GetEventHandler()->ProcessEvent( event ); - } - if (!ret) + for( const wxChar* pstr = data; *pstr; pstr++ ) { - event.SetEventType(wxEVT_CHAR); - ret = window->GetEventHandler()->ProcessEvent( event ); +#if wxUSE_UNICODE + event.m_uniChar = *pstr; + // Backward compatible for ISO-8859-1 + event.m_keyCode = *pstr < 256 ? event.m_uniChar : 0; + wxLogTrace(TRACE_KEYS, _T("IM sent character '%c'"), event.m_uniChar); +#else + event.m_keyCode = *pstr; +#endif // wxUSE_UNICODE + if (parent) + { + event.SetEventType( wxEVT_CHAR_HOOK ); + ret = parent->GetEventHandler()->ProcessEvent( event ); + } + + if (!ret) + { + event.SetEventType(wxEVT_CHAR); + ret = window->GetEventHandler()->ProcessEvent( event ); + } } } +} #endif @@ -1309,6 +1472,7 @@ static void gtk_wxwindow_commit_cb (GtkIMContext *context, // "key_release_event" from any window //----------------------------------------------------------------------------- +extern "C" { static gint gtk_window_key_release_callback( GtkWidget *widget, GdkEventKey *gdk_event, wxWindowGTK *win ) @@ -1337,6 +1501,7 @@ static gint gtk_window_key_release_callback( GtkWidget *widget, gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "key_release_event" ); return TRUE; } +} // ============================================================================ // the mouse events @@ -1362,6 +1527,7 @@ template void InitMouseEvent(wxWindowGTK *win, if (event.GetEventType() == wxEVT_MOUSEWHEEL) { event.m_linesPerAction = 3; + event.m_wheelDelta = 120; if (((GdkEventButton*)gdk_event)->button == 4) event.m_wheelRotation = 120; else if (((GdkEventButton*)gdk_event)->button == 5) @@ -1479,6 +1645,7 @@ wxWindowGTK *FindWindowForMouseEvent(wxWindowGTK *win, wxCoord& x, wxCoord& y) // "button_press_event" //----------------------------------------------------------------------------- +extern "C" { static gint gtk_window_button_press_callback( GtkWidget *widget, GdkEventButton *gdk_event, wxWindowGTK *win ) @@ -1534,8 +1701,8 @@ static gint gtk_window_button_press_callback( GtkWidget *widget, wxEventType event_type = wxEVT_NULL; - // GdkDisplay is a GTK+ 2.1.0 thing -#if defined(__WXGTK20__) && GTK_CHECK_VERSION(2, 1, 0) + // GdkDisplay is a GTK+ 2.2.0 thing +#if defined(__WXGTK20__) && GTK_CHECK_VERSION(2, 2, 0) if ( gdk_event->type == GDK_2BUTTON_PRESS && gdk_event->button >= 1 && gdk_event->button <= 3 ) { @@ -1638,13 +1805,6 @@ static gint gtk_window_button_press_callback( GtkWidget *widget, gs_timeLastClick = gdk_event->time; -/* - wxPrintf( wxT("2) OnButtonPress from ") ); - if (win->GetClassInfo() && win->GetClassInfo()->GetClassName()) - wxPrintf( win->GetClassInfo()->GetClassName() ); - wxPrintf( wxT(".\n") ); -*/ - #ifndef __WXGTK20__ if (event_type == wxEVT_LEFT_DCLICK) { @@ -1665,13 +1825,32 @@ static gint gtk_window_button_press_callback( GtkWidget *widget, return TRUE; } + if (event_type == wxEVT_RIGHT_DOWN) + { + // generate a "context menu" event: this is similar to right mouse + // click under many GUIs except that it is generated differently + // (right up under MSW, ctrl-click under Mac, right down here) and + // + // (a) it's a command event and so is propagated to the parent + // (b) under some ports it can be generated from kbd too + // (c) it uses screen coords (because of (a)) + wxContextMenuEvent evtCtx( + wxEVT_CONTEXT_MENU, + win->GetId(), + win->ClientToScreen(event.GetPosition())); + evtCtx.SetEventObject(win); + return win->GetEventHandler()->ProcessEvent(evtCtx); + } + return FALSE; } +} //----------------------------------------------------------------------------- // "button_release_event" //----------------------------------------------------------------------------- +extern "C" { static gint gtk_window_button_release_callback( GtkWidget *widget, GdkEventButton *gdk_event, wxWindowGTK *win ) @@ -1716,20 +1895,6 @@ static gint gtk_window_button_release_callback( GtkWidget *widget, // same wxListBox hack as above win->FixUpMouseEvent(widget, event.m_x, event.m_y); - if ( event_type == wxEVT_RIGHT_UP ) - { - // generate a "context menu" event: this is similar to wxEVT_RIGHT_UP - // except that: - // - // (a) it's a command event and so is propagated to the parent - // (b) under MSW it can be generated from kbd too - // (c) it uses screen coords (because of (a)) - wxContextMenuEvent evtCtx(wxEVT_CONTEXT_MENU, - win->GetId(), - win->ClientToScreen(event.GetPosition())); - (void)win->GetEventHandler()->ProcessEvent(evtCtx); - } - if ( !g_captureWindow ) win = FindWindowForMouseEvent(win, event.m_x, event.m_y); @@ -1741,11 +1906,13 @@ static gint gtk_window_button_release_callback( GtkWidget *widget, return FALSE; } +} //----------------------------------------------------------------------------- // "motion_notify_event" //----------------------------------------------------------------------------- +extern "C" { static gint gtk_window_motion_notify_callback( GtkWidget *widget, GdkEventMotion *gdk_event, wxWindowGTK *win ) @@ -1815,15 +1982,17 @@ static gint gtk_window_motion_notify_callback( GtkWidget *widget, return FALSE; } +} #ifdef __WXGTK20__ //----------------------------------------------------------------------------- // "mouse_wheel_event" //----------------------------------------------------------------------------- +extern "C" { static gint gtk_window_wheel_callback (GtkWidget * widget, - GdkEventScroll * gdk_event, - wxWindowGTK * win) + GdkEventScroll * gdk_event, + wxWindowGTK * win) { DEBUG_MAIN_THREAD @@ -1849,6 +2018,7 @@ static gint gtk_window_wheel_callback (GtkWidget * widget, event.m_middleDown = (gdk_event->state & GDK_BUTTON2_MASK); event.m_rightDown = (gdk_event->state & GDK_BUTTON3_MASK); event.m_linesPerAction = 3; + event.m_wheelDelta = 120; if (gdk_event->direction == GDK_SCROLL_UP) event.m_wheelRotation = 120; else @@ -1870,7 +2040,23 @@ static gint gtk_window_wheel_callback (GtkWidget * widget, return FALSE; } -#endif +} + +//----------------------------------------------------------------------------- +// "popup-menu" +//----------------------------------------------------------------------------- +extern "C" { +static gboolean wxgtk_window_popup_menu_callback(GtkWidget*, wxWindowGTK* win) +{ + wxContextMenuEvent event( + wxEVT_CONTEXT_MENU, + win->GetId(), + wxPoint(-1, -1)); + event.SetEventObject(win); + return win->GetEventHandler()->ProcessEvent(event); +} +} +#endif // __WXGTK20__ //----------------------------------------------------------------------------- // "focus_in_event" @@ -1891,31 +2077,20 @@ static bool DoSendFocusEvents(wxWindow *win) return win->GetEventHandler()->ProcessEvent(eventFocus); } +extern "C" { static gint gtk_window_focus_in_callback( GtkWidget *widget, GdkEvent *WXUNUSED(event), wxWindow *win ) { DEBUG_MAIN_THREAD - + if (g_isIdle) wxapp_install_idle_handler(); - if (!win->m_hasVMT) return FALSE; - if (g_blockEventsOnDrag) return FALSE; - - switch ( g_sendActivateEvent ) - { - case -1: - // we've got focus from outside, synthetize wxActivateEvent - g_sendActivateEvent = 1; - break; - - case 0: - // another our window just lost focus, it was already ours before - // - don't send any wxActivateEvent - g_sendActivateEvent = -1; - break; - } +#ifdef __WXGTK20__ + if (win->m_imData) + gtk_im_context_focus_in(win->m_imData->context); +#endif g_focusWindowLast = g_focusWindow = win; @@ -1937,36 +2112,12 @@ static gint gtk_window_focus_in_callback( GtkWidget *widget, } #endif // wxUSE_CARET - g_activeFrameLostFocus = FALSE; - - wxWindowGTK *active = wxGetTopLevelParent(win); - if ( active != g_activeFrame ) - { - 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); - } - - 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); - - // Don't send focus events in addition to activate - // if (win == g_activeFrame) - // return TRUE; - } - // does the window itself think that it has the focus? if ( !win->m_hasFocus ) { // not yet, notify it win->m_hasFocus = TRUE; - + if ( DoSendFocusEvents(win) ) { gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "focus_in_event" ); @@ -1976,11 +2127,13 @@ static gint gtk_window_focus_in_callback( GtkWidget *widget, return FALSE; } +} //----------------------------------------------------------------------------- // "focus_out_event" //----------------------------------------------------------------------------- +extern "C" { static gint gtk_window_focus_out_callback( GtkWidget *widget, GdkEventFocus *gdk_event, wxWindowGTK *win ) { DEBUG_MAIN_THREAD @@ -1988,29 +2141,14 @@ static gint gtk_window_focus_out_callback( GtkWidget *widget, GdkEventFocus *gdk if (g_isIdle) wxapp_install_idle_handler(); - if (!win->m_hasVMT) return FALSE; - if (g_blockEventsOnDrag) return FALSE; +#ifdef __WXGTK20__ + if (win->m_imData) + gtk_im_context_focus_out(win->m_imData->context); +#endif wxLogTrace( TRACE_FOCUS, _T("%s: focus out"), win->GetName().c_str() ); - 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 - g_sendActivateEvent = 0; wxWindowGTK *winFocus = wxFindFocusedChild(win); if ( winFocus ) @@ -2041,20 +2179,22 @@ static gint gtk_window_focus_out_callback( GtkWidget *widget, GdkEventFocus *gdk wxFocusEvent event( wxEVT_KILL_FOCUS, win->GetId() ); event.SetEventObject( win ); - if (win->GetEventHandler()->ProcessEvent( event )) - { - gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "focus_out_event" ); - return TRUE; - } + // even if we did process the event in wx code, still let GTK itself + // process it too as otherwise bad things happen, especially in GTK2 + // where the text control simply aborts the program if it doesn't get + // the matching focus out event + (void)win->GetEventHandler()->ProcessEvent( event ); } return FALSE; } +} //----------------------------------------------------------------------------- // "enter_notify_event" //----------------------------------------------------------------------------- +extern "C" { static gint gtk_window_enter_callback( GtkWidget *widget, GdkEventCrossing *gdk_event, @@ -2093,11 +2233,13 @@ gint gtk_window_enter_callback( GtkWidget *widget, return FALSE; } +} //----------------------------------------------------------------------------- // "leave_notify_event" //----------------------------------------------------------------------------- +extern "C" { static gint gtk_window_leave_callback( GtkWidget *widget, GdkEventCrossing *gdk_event, wxWindowGTK *win ) { DEBUG_MAIN_THREAD @@ -2143,11 +2285,13 @@ static gint gtk_window_leave_callback( GtkWidget *widget, GdkEventCrossing *gdk_ return FALSE; } +} //----------------------------------------------------------------------------- // "value_changed" from m_vAdjust //----------------------------------------------------------------------------- +extern "C" { static void gtk_window_vscroll_callback( GtkAdjustment *adjust, SCROLLBAR_CBACK_ARG wxWindowGTK *win ) @@ -2177,11 +2321,13 @@ static void gtk_window_vscroll_callback( GtkAdjustment *adjust, event.SetEventObject( win ); win->GetEventHandler()->ProcessEvent( event ); } +} //----------------------------------------------------------------------------- // "value_changed" from m_hAdjust //----------------------------------------------------------------------------- +extern "C" { static void gtk_window_hscroll_callback( GtkAdjustment *adjust, SCROLLBAR_CBACK_ARG wxWindowGTK *win ) @@ -2210,11 +2356,13 @@ static void gtk_window_hscroll_callback( GtkAdjustment *adjust, event.SetEventObject( win ); win->GetEventHandler()->ProcessEvent( event ); } +} //----------------------------------------------------------------------------- // "button_press_event" from scrollbar //----------------------------------------------------------------------------- +extern "C" { static gint gtk_scrollbar_button_press_callback( GtkRange *widget, GdkEventButton *gdk_event, wxWindowGTK *win) @@ -2234,11 +2382,13 @@ static gint gtk_scrollbar_button_press_callback( GtkRange *widget, return FALSE; } +} //----------------------------------------------------------------------------- // "button_release_event" from scrollbar //----------------------------------------------------------------------------- +extern "C" { static gint gtk_scrollbar_button_release_callback( GtkRange *widget, GdkEventButton *WXUNUSED(gdk_event), wxWindowGTK *win) @@ -2279,19 +2429,19 @@ static gint gtk_scrollbar_button_release_callback( GtkRange *widget, return FALSE; } +} // ---------------------------------------------------------------------------- // this wxWindowBase function is implemented here (in platform-specific file) // because it is static and so couldn't be made virtual // ---------------------------------------------------------------------------- -wxWindow *wxWindowBase::FindFocus() +wxWindow *wxWindowBase::DoFindFocus() { // the cast is necessary when we compile in wxUniversal mode return (wxWindow *)g_focusWindow; } - //----------------------------------------------------------------------------- // "realize" from m_widget //----------------------------------------------------------------------------- @@ -2299,6 +2449,7 @@ wxWindow *wxWindowBase::FindFocus() /* We cannot set colours and fonts before the widget has been realized, so we do this directly after realization. */ +extern "C" { static gint gtk_window_realized_callback( GtkWidget *m_widget, wxWindow *win ) { @@ -2307,17 +2458,12 @@ gtk_window_realized_callback( GtkWidget *m_widget, wxWindow *win ) if (g_isIdle) wxapp_install_idle_handler(); - if (win->m_delayedBackgroundColour) - win->GtkSetBackgroundColour( win->GetBackgroundColour() ); - - if (win->m_delayedForegroundColour) - win->GtkSetForegroundColour( win->GetForegroundColour() ); - #ifdef __WXGTK20__ - if (win->m_imContext) + if (win->m_imData) { GtkPizza *pizza = GTK_PIZZA( m_widget ); - gtk_im_context_set_client_window( (GtkIMContext*) win->m_imContext, pizza->bin_window ); + gtk_im_context_set_client_window( win->m_imData->context, + pizza->bin_window ); } #endif @@ -2327,11 +2473,13 @@ gtk_window_realized_callback( GtkWidget *m_widget, wxWindow *win ) return FALSE; } +} //----------------------------------------------------------------------------- // "size_allocate" //----------------------------------------------------------------------------- +extern "C" { static void gtk_window_size_callback( GtkWidget *WXUNUSED(widget), GtkAllocation *WXUNUSED(alloc), @@ -2358,6 +2506,7 @@ void gtk_window_size_callback( GtkWidget *WXUNUSED(widget), win->GetEventHandler()->ProcessEvent( event ); } } +} #ifdef HAVE_XIM @@ -2368,6 +2517,7 @@ void gtk_window_size_callback( GtkWidget *WXUNUSED(widget), /* Resize XIM window */ +extern "C" { static void gtk_wxwindow_size_callback( GtkWidget* WXUNUSED_UNLESS_XIM(widget), GtkAllocation* WXUNUSED_UNLESS_XIM(alloc), @@ -2391,6 +2541,7 @@ void gtk_wxwindow_size_callback( GtkWidget* WXUNUSED_UNLESS_XIM(widget), } #endif // HAVE_XIM } +} //----------------------------------------------------------------------------- // "realize" from m_wxwindow @@ -2398,6 +2549,7 @@ void gtk_wxwindow_size_callback( GtkWidget* WXUNUSED_UNLESS_XIM(widget), /* Initialize XIM support */ +extern "C" { static gint gtk_wxwindow_realized_callback( GtkWidget * WXUNUSED_UNLESS_XIM(widget), wxWindowGTK * WXUNUSED_UNLESS_XIM(win) ) @@ -2484,6 +2636,7 @@ gtk_wxwindow_realized_callback( GtkWidget * WXUNUSED_UNLESS_XIM(widget), return FALSE; } +} //----------------------------------------------------------------------------- // InsertChild for wxWindowGTK. @@ -2573,14 +2726,14 @@ void wxWindowGTK::Init() m_clipPaintRegion = FALSE; - m_cursor = *wxSTANDARD_CURSOR; + m_needsStyleChange = false; - m_delayedForegroundColour = FALSE; - m_delayedBackgroundColour = FALSE; + m_cursor = *wxSTANDARD_CURSOR; #ifdef __WXGTK20__ - m_imContext = NULL; + m_imData = NULL; m_x11Context = NULL; + m_dirtyTabOrder = false; #else #ifdef HAVE_XIM m_ic = (GdkIC*) NULL; @@ -2622,9 +2775,6 @@ 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 ); @@ -2707,17 +2857,6 @@ bool wxWindowGTK::Create( wxWindow *parent, gtk_signal_connect( GTK_OBJECT(m_vAdjust), "value_changed", (GtkSignalFunc) gtk_window_vscroll_callback, (gpointer) this ); -#ifdef __WXGTK20__ - // Create input method handler - m_imContext = (GtkIMMulticontext*) gtk_im_multicontext_new (); - - // Cannot handle drawing preedited text yet - gtk_im_context_set_use_preedit( (GtkIMContext*) m_imContext, FALSE ); - - g_signal_connect (G_OBJECT (m_imContext), "commit", - G_CALLBACK (gtk_wxwindow_commit_cb), this); -#endif - gtk_widget_show( m_wxwindow ); if (m_parent) @@ -2737,9 +2876,6 @@ wxWindowGTK::~wxWindowGTK() if (g_focusWindow == this) g_focusWindow = NULL; - if (g_activeFrame == this) - g_activeFrame = NULL; - if ( g_delayedFocus == this ) g_delayedFocus = NULL; @@ -2769,6 +2905,10 @@ wxWindowGTK::~wxWindowGTK() gtk_widget_destroy( m_widget ); m_widget = (GtkWidget*) NULL; } + +#ifdef __WXGTK20__ + delete m_imData; +#endif } bool wxWindowGTK::PreCreation( wxWindowGTK *parent, const wxPoint &pos, const wxSize &size ) @@ -2813,18 +2953,18 @@ void wxWindowGTK::PostCreation() #else // gtk_widget_set_redraw_on_allocate( GTK_WIDGET(m_wxwindow), !HasFlag( wxFULL_REPAINT_ON_RESIZE ) ); #endif + } #ifdef __WXGTK20__ // Create input method handler - m_imContext = (GtkIMMulticontext*) gtk_im_multicontext_new (); + m_imData = new wxGtkIMData; // Cannot handle drawing preedited text yet - gtk_im_context_set_use_preedit( (GtkIMContext*) m_imContext, FALSE ); + gtk_im_context_set_use_preedit( m_imData->context, FALSE ); - g_signal_connect (G_OBJECT (m_imContext), "commit", - G_CALLBACK (gtk_wxwindow_commit_cb), this); + g_signal_connect (G_OBJECT (m_imData->context), "commit", + G_CALLBACK (gtk_wxwindow_commit_cb), this); #endif - } // these are called when the "sunken" or "raised" borders are drawn gtk_signal_connect( GTK_OBJECT(m_widget), "expose_event", @@ -2838,14 +2978,17 @@ void wxWindowGTK::PostCreation() // focus handling - if (m_focusWidget == NULL) - m_focusWidget = m_widget; - - gtk_signal_connect( GTK_OBJECT(m_focusWidget), "focus_in_event", - GTK_SIGNAL_FUNC(gtk_window_focus_in_callback), (gpointer)this ); + if (!GTK_IS_WINDOW(m_widget)) + { + if (m_focusWidget == NULL) + m_focusWidget = m_widget; + + gtk_signal_connect( GTK_OBJECT(m_focusWidget), "focus_in_event", + GTK_SIGNAL_FUNC(gtk_window_focus_in_callback), (gpointer)this ); - gtk_signal_connect( GTK_OBJECT(m_focusWidget), "focus_out_event", - GTK_SIGNAL_FUNC(gtk_window_focus_out_callback), (gpointer)this ); + gtk_signal_connect_after( GTK_OBJECT(m_focusWidget), "focus_out_event", + GTK_SIGNAL_FUNC(gtk_window_focus_out_callback), (gpointer)this ); + } // connect to the various key and mouse handlers @@ -2873,18 +3016,27 @@ void wxWindowGTK::PostCreation() GTK_SIGNAL_FUNC(gtk_wxwindow_size_callback), (gpointer)this ); } - if ( !GTK_IS_COMBO(m_widget)) + if (GTK_IS_COMBO(m_widget)) + { + GtkCombo *gcombo = GTK_COMBO(m_widget); + + gtk_signal_connect( GTK_OBJECT(gcombo->entry), "size_request", + GTK_SIGNAL_FUNC(wxgtk_combo_size_request_callback), + (gpointer) this ); + } + else { // This is needed if we want to add our windows into native - // GTK control, such as the toolbar. With this callback, the + // GTK controls, such as the toolbar. With this callback, the // toolbar gets to know the correct size (the one set by the - // programmer). Sadly, it misbehaves for wxComboBox. FIXME - // when moving to GTK 2.0. + // programmer). Sadly, it misbehaves for wxComboBox. gtk_signal_connect( GTK_OBJECT(m_widget), "size_request", GTK_SIGNAL_FUNC(wxgtk_window_size_request_callback), (gpointer) this ); } + InheritAttributes(); + m_hasVMT = TRUE; // unless the window was created initially hidden (i.e. Hide() had been @@ -2913,6 +3065,8 @@ void wxWindowGTK::ConnectWidget( GtkWidget *widget ) #ifdef __WXGTK20__ gtk_signal_connect( GTK_OBJECT(widget), "scroll_event", GTK_SIGNAL_FUNC(gtk_window_wheel_callback), (gpointer)this ); + g_signal_connect(widget, "popup_menu", + G_CALLBACK(wxgtk_window_popup_menu_callback), this); #endif gtk_signal_connect( GTK_OBJECT(widget), "enter_notify_event", @@ -3004,22 +3158,37 @@ void wxWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags if ((maxWidth != -1) && (m_width > maxWidth)) m_width = maxWidth; if ((maxHeight != -1) && (m_height > maxHeight)) m_height = maxHeight; - int border = 0; + int left_border = 0; + int right_border = 0; + int top_border = 0; int bottom_border = 0; -#ifndef __WXGTK20__ + /* the default button has a border around it */ if (GTK_WIDGET_CAN_DEFAULT(m_widget)) { - /* the default button has a border around it */ - border = 6; +#ifdef __WXGTK20__ + GtkBorder *default_border = NULL; + gtk_widget_style_get( m_widget, "default_border", &default_border, NULL ); + if (default_border) + { + left_border += default_border->left; + right_border += default_border->right; + top_border += default_border->top; + bottom_border += default_border->bottom; + g_free( default_border ); + } +#else + left_border = 6; + right_border = 6; + top_border = 6; bottom_border = 5; - } #endif + } - DoMoveWindow( m_x-border, - m_y-border, - m_width+2*border, - m_height+border+bottom_border ); + DoMoveWindow( m_x-top_border, + m_y-left_border, + m_width+left_border+right_border, + m_height+top_border+bottom_border ); } if (m_hasScrolling) @@ -3052,32 +3221,20 @@ void wxWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags void wxWindowGTK::OnInternalIdle() { - // Update invalidated regions. - GtkUpdate(); - - // Synthetize activate events. - if ( g_sendActivateEvent != -1 ) +#ifdef __WXGTK20__ + if ( m_dirtyTabOrder ) + RealizeTabOrder(); +#endif + // Update style if the window was not yet realized + // and SetBackgroundStyle(wxBG_STYLE_CUSTOM) was called + if (m_needsStyleChange) { - bool activate = g_sendActivateEvent != 0; - - // do it only once - g_sendActivateEvent = -1; - - wxTheApp->SetActive(activate, (wxWindow *)g_focusWindowLast); + SetBackgroundStyle(GetBackgroundStyle()); + m_needsStyleChange = false; } - 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; - } + // Update invalidated regions. + GtkUpdate(); wxCursor cursor = m_cursor; if (g_globalCursor.Ok()) cursor = g_globalCursor; @@ -3351,7 +3508,7 @@ bool wxWindowGTK::Show( bool show ) gtk_widget_hide( m_widget ); wxShowEvent eventShow(GetId(), show); - eventShow.m_eventObject = this; + eventShow.SetEventObject(this); GetEventHandler()->ProcessEvent(eventShow); @@ -3420,7 +3577,7 @@ int wxWindowGTK::GetCharHeight() const g_object_unref( G_OBJECT( layout ) ); - return (int) (rect.height / PANGO_SCALE); + return (int) PANGO_PIXELS(rect.height); #else GdkFont *gfont = font.GetInternalFont( 1.0 ); @@ -3454,7 +3611,7 @@ int wxWindowGTK::GetCharWidth() const g_object_unref( G_OBJECT( layout ) ); - return (int) (rect.width / PANGO_SCALE); + return (int) PANGO_PIXELS(rect.width); #else GdkFont *gfont = font.GetInternalFont( 1.0 ); @@ -3505,17 +3662,18 @@ void wxWindowGTK::GetTextExtent( const wxString& string, pango_layout_set_text(layout, (const char*) data, strlen( (const char*) data )); #endif } - PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data; PangoRectangle rect; - pango_layout_line_get_extents(line, NULL, &rect); + pango_layout_get_extents(layout, NULL, &rect); - if (x) (*x) = (wxCoord) (rect.width / PANGO_SCALE); - if (y) (*y) = (wxCoord) (rect.height / PANGO_SCALE); + if (x) (*x) = (wxCoord) PANGO_PIXELS(rect.width); + if (y) (*y) = (wxCoord) PANGO_PIXELS(rect.height); if (descent) { - // Do something about metrics here - (*descent) = 0; + PangoLayoutIter *iter = pango_layout_get_iter(layout); + int baseline = pango_layout_iter_get_baseline(iter); + pango_layout_iter_free(iter); + *descent = *y - PANGO_PIXELS(baseline); } if (externalLeading) (*externalLeading) = 0; // ?? @@ -3532,7 +3690,6 @@ void wxWindowGTK::GetTextExtent( const wxString& string, void wxWindowGTK::SetFocus() { wxCHECK_RET( m_widget != NULL, wxT("invalid window") ); - if ( m_hasFocus ) { // don't do anything if we already have focus @@ -3548,8 +3705,16 @@ void wxWindowGTK::SetFocus() } else if (m_widget) { +#ifdef __WXGTK20__ + if (GTK_IS_CONTAINER(m_widget)) + { + gtk_widget_child_focus( m_widget, GTK_DIR_TAB_FORWARD ); + } + else +#endif if (GTK_WIDGET_CAN_FOCUS(m_widget) && !GTK_WIDGET_HAS_FOCUS (m_widget) ) { + if (!GTK_WIDGET_REALIZED(m_widget)) { // we can't set the focus to the widget now so we remember that @@ -3570,11 +3735,14 @@ void wxWindowGTK::SetFocus() gtk_widget_grab_focus (m_widget); } } - else if (GTK_IS_CONTAINER(m_widget)) + else +#ifndef __WXGTK20__ + if (GTK_IS_CONTAINER(m_widget)) { - SET_CONTAINER_FOCUS( m_widget, GTK_DIR_TAB_FORWARD ); + gtk_container_focus( GTK_CONTAINER(m_widget), GTK_DIR_TAB_FORWARD ); } else +#endif { wxLogTrace(TRACE_FOCUS, _T("Can't set focus to %s(%s)"), @@ -3639,22 +3807,88 @@ void wxWindowGTK::DoAddChild(wxWindowGTK *child) (*m_insertCallback)(this, child); } +#ifdef __WXGTK20__ + +void wxWindowGTK::AddChild(wxWindowBase *child) +{ + wxWindowBase::AddChild(child); + m_dirtyTabOrder = true; + if (g_isIdle) + wxapp_install_idle_handler(); +} + +void wxWindowGTK::RemoveChild(wxWindowBase *child) +{ + wxWindowBase::RemoveChild(child); + m_dirtyTabOrder = true; + if (g_isIdle) + wxapp_install_idle_handler(); +} + +void wxWindowGTK::DoMoveInTabOrder(wxWindow *win, MoveKind move) +{ + wxWindowBase::DoMoveInTabOrder(win, move); + m_dirtyTabOrder = true; + if (g_isIdle) + wxapp_install_idle_handler(); +} + +void wxWindowGTK::RealizeTabOrder() +{ + if (m_wxwindow) + { + if (m_children.size() > 0) + { + GList *chain = NULL; + + for (wxWindowList::const_iterator i = m_children.begin(); + i != m_children.end(); ++i) + { + chain = g_list_prepend(chain, (*i)->m_widget); + } + + chain = g_list_reverse(chain); + + gtk_container_set_focus_chain(GTK_CONTAINER(m_wxwindow), chain); + g_list_free(chain); + } + else + { + gtk_container_unset_focus_chain(GTK_CONTAINER(m_wxwindow)); + } + } + + m_dirtyTabOrder = false; +} + +#endif // __WXGTK20__ + void wxWindowGTK::Raise() { wxCHECK_RET( (m_widget != NULL), wxT("invalid window") ); - if (!m_widget->window) return; - - gdk_window_raise( m_widget->window ); + if (m_wxwindow && m_wxwindow->window) + { + gdk_window_raise( m_wxwindow->window ); + } + else if (m_widget->window) + { + gdk_window_raise( m_widget->window ); + } } void wxWindowGTK::Lower() { wxCHECK_RET( (m_widget != NULL), wxT("invalid window") ); - if (!m_widget->window) return; - - gdk_window_lower( m_widget->window ); + if (m_wxwindow && m_wxwindow->window) + { + gdk_window_lower( m_wxwindow->window ); + } + else if (m_widget->window) + { + gdk_window_lower( m_widget->window ); + } } bool wxWindowGTK::SetCursor( const wxCursor &cursor ) @@ -3693,35 +3927,38 @@ void wxWindowGTK::WarpPointer( int x, int y ) void wxWindowGTK::Refresh( bool eraseBackground, const wxRect *rect ) { - if (!m_widget) return; - if (!m_widget->window) return; + if (!m_widget) + return; + if (!m_widget->window) + return; #ifndef __WXGTK20__ if (g_isIdle) wxapp_install_idle_handler(); - wxRect myRect(0,0,0,0); + wxRect myRect; if (m_wxwindow && rect) { myRect.SetSize(wxSize( m_wxwindow->allocation.width, m_wxwindow->allocation.height)); - myRect.Intersect(*rect); - if (!myRect.width || !myRect.height) + if ( myRect.Intersect(*rect).IsEmpty() ) + { // nothing to do, rectangle is empty return; + } + rect = &myRect; } + // schedule the area for later updating in GtkUpdate() if (eraseBackground && m_wxwindow && m_wxwindow->window) { if (rect) { - // Schedule for later Updating in ::Update() or ::OnInternalIdle(). m_clearRegion.Union( rect->x, rect->y, rect->width, rect->height ); } else { - // Schedule for later Updating in ::Update() or ::OnInternalIdle(). m_clearRegion.Clear(); m_clearRegion.Union( 0, 0, m_wxwindow->allocation.width, m_wxwindow->allocation.height ); } @@ -3731,7 +3968,6 @@ void wxWindowGTK::Refresh( bool eraseBackground, const wxRect *rect ) { if (m_wxwindow) { - // Schedule for later Updating in ::Update() or ::OnInternalIdle(). m_updateRegion.Union( rect->x, rect->y, rect->width, rect->height ); } else @@ -3748,7 +3984,6 @@ void wxWindowGTK::Refresh( bool eraseBackground, const wxRect *rect ) { if (m_wxwindow) { - // Schedule for later Updating in ::Update() or ::OnInternalIdle(). m_updateRegion.Clear(); m_updateRegion.Union( 0, 0, m_wxwindow->allocation.width, m_wxwindow->allocation.height ); } @@ -3757,29 +3992,38 @@ void wxWindowGTK::Refresh( bool eraseBackground, const wxRect *rect ) gtk_widget_draw( m_widget, (GdkRectangle*) NULL ); } } -#else +#else // GTK+ 2 if (m_wxwindow) { + GdkRectangle gdk_rect, + *p; if (rect) { - GdkRectangle gdk_rect; gdk_rect.x = rect->x; gdk_rect.y = rect->y; gdk_rect.width = rect->width; gdk_rect.height = rect->height; - gdk_window_invalidate_rect( GTK_PIZZA(m_wxwindow)->bin_window, &gdk_rect, TRUE ); + p = &gdk_rect; } - else + else // invalidate everything { - gdk_window_invalidate_rect( GTK_PIZZA(m_wxwindow)->bin_window, NULL, TRUE ); + p = NULL; } + + gdk_window_invalidate_rect( GTK_PIZZA(m_wxwindow)->bin_window, p, TRUE ); } -#endif +#endif // GTK+ 1/2 } void wxWindowGTK::Update() { GtkUpdate(); + + // when we call Update() we really want to update the window immediately on + // screen, even if itmeans flushing the entire queue and hence slowing down + // everything -- but it should still be done, it's just that Update() should + // be called very rarely + gdk_flush(); } void wxWindowGTK::GtkUpdate() @@ -3791,6 +4035,16 @@ void wxWindowGTK::GtkUpdate() if (!m_updateRegion.IsEmpty()) GtkSendPaintEvents(); #endif + + // for consistency with other platforms (and also because it's convenient + // to be able to update an entire TLW by calling Update() only once), we + // should also update all our children here + for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst(); + node; + node = node->GetNext() ) + { + node->GetData()->GtkUpdate(); + } } void wxWindowGTK::GtkSendPaintEvents() @@ -3810,34 +4064,35 @@ void wxWindowGTK::GtkSendPaintEvents() // widget to draw on GtkPizza *pizza = GTK_PIZZA (m_wxwindow); - if (GetThemeEnabled()) + if (GetThemeEnabled() && (GetBackgroundStyle() == wxBG_STYLE_SYSTEM)) { // find ancestor from which to steal background - wxWindow *parent = GetParent(); - while (parent && !parent->IsTopLevel()) - parent = parent->GetParent(); + wxWindow *parent = wxGetTopLevelParent((wxWindow *)this); if (!parent) parent = (wxWindow*)this; - wxRegionIterator upd( m_updateRegion ); - while (upd) + if (GTK_WIDGET_MAPPED(parent->m_widget)) { - GdkRectangle rect; - rect.x = upd.GetX(); - rect.y = upd.GetY(); - rect.width = upd.GetWidth(); - rect.height = upd.GetHeight(); - - gtk_paint_flat_box( parent->m_widget->style, - pizza->bin_window, - (GtkStateType)GTK_WIDGET_STATE(m_wxwindow), - GTK_SHADOW_NONE, - &rect, - parent->m_widget, - (char *)"base", - 0, 0, -1, -1 ); - - upd ++; + wxRegionIterator upd( m_updateRegion ); + while (upd) + { + GdkRectangle rect; + rect.x = upd.GetX(); + rect.y = upd.GetY(); + rect.width = upd.GetWidth(); + rect.height = upd.GetHeight(); + + gtk_paint_flat_box( parent->m_widget->style, + pizza->bin_window, + (GtkStateType)GTK_WIDGET_STATE(m_wxwindow), + GTK_SHADOW_NONE, + &rect, + parent->m_widget, + (char *)"base", + 0, 0, -1, -1 ); + + ++upd; + } } } else @@ -3864,7 +4119,7 @@ void wxWindowGTK::GtkSendPaintEvents() wxEraseEvent erase_event( GetId(), &dc ); erase_event.SetEventObject( this ); - if (!GetEventHandler()->ProcessEvent(erase_event)) + if (!GetEventHandler()->ProcessEvent(erase_event) && GetBackgroundStyle() != wxBG_STYLE_CUSTOM) { if (!g_eraseGC) { @@ -3975,74 +4230,25 @@ 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 ); - - // This will work around the fact that I don't know what to do to reset to - // theme settings when colour == wxNullColour, GetBackgroundColour will - // fetch the default if needed, giving us a valid colour to use below. - // Vaclav needs to help here to implement the RightThing... - wxColour newColour = GetBackgroundColour(); - - // We need the pixel value e.g. for background clearing. - newColour.CalcPixel( gdk_window_get_colormap( window ) ); - - if (m_wxwindow) - { - // wxMSW doesn't clear the window here, either. - gdk_window_set_background( window, newColour.GetColor() ); - } - - ApplyWidgetStyle(); -} - bool wxWindowGTK::SetBackgroundColour( const wxColour &colour ) { - wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") ); + wxCHECK_MSG( m_widget != NULL, FALSE, wxT("invalid window") ); if (!wxWindowBase::SetBackgroundColour(colour)) return false; - GdkWindow *window = (GdkWindow*) NULL; - if (m_wxwindow) - window = GTK_PIZZA(m_wxwindow)->bin_window; - else - window = GetConnectWidget()->window; - - if (!window) + if (colour.Ok()) { - // indicate that a new style has been set - // but it couldn't get applied as the - // widget hasn't been realized yet. - m_delayedBackgroundColour = true; - return true; - } - else - { - GtkSetBackgroundColour( colour ); + // We need the pixel value e.g. for background clearing. + m_backgroundColour.CalcPixel(gtk_widget_get_colormap(m_widget)); } - 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 ); + // apply style change (forceStyle=true so that new style is applied + // even if the bg colour changed from valid to wxNullColour) + if (GetBackgroundStyle() != wxBG_STYLE_CUSTOM) + ApplyWidgetStyle(true); - ApplyWidgetStyle(); + return true; } bool wxWindowGTK::SetForegroundColour( const wxColour &colour ) @@ -4051,29 +4257,19 @@ bool wxWindowGTK::SetForegroundColour( const wxColour &colour ) if (!wxWindowBase::SetForegroundColour(colour)) { - // don't leave if the GTK widget has just - // been realized - if (!m_delayedForegroundColour) return false; - } - - GdkWindow *window = (GdkWindow*) NULL; - if (m_wxwindow) - window = GTK_PIZZA(m_wxwindow)->bin_window; - else - window = GetConnectWidget()->window; - - if (!window) - { - // indicate that a new style has been set - // but it couldn't get applied as the - // widget hasn't been realized yet. - m_delayedForegroundColour = true; + return false; } - else + + if (colour.Ok()) { - GtkSetForegroundColour( colour ); + // We need the pixel value e.g. for background clearing. + m_foregroundColour.CalcPixel(gtk_widget_get_colormap(m_widget)); } + // apply style change (forceStyle=true so that new style is applied + // even if the bg colour changed from valid to wxNullColour): + ApplyWidgetStyle(true); + return true; } @@ -4092,18 +4288,19 @@ PangoContext *wxWindowGTK::GtkGetPangoX11Context() } #endif -GtkRcStyle *wxWindowGTK::CreateWidgetStyle() +GtkRcStyle *wxWindowGTK::CreateWidgetStyle(bool forceStyle) { // do we need to apply any changes at all? - if ( !m_hasFont && !m_hasFgCol && - (!m_hasBgCol || !m_backgroundColour.Ok()) ) + if ( !forceStyle && + !m_font.Ok() && + !m_foregroundColour.Ok() && !m_backgroundColour.Ok() ) { return NULL; } GtkRcStyle *style = gtk_rc_style_new(); - if ( m_hasFont ) + if ( m_font.Ok() ) { #ifdef __WXGTK20__ style->font_desc = @@ -4114,41 +4311,41 @@ GtkRcStyle *wxWindowGTK::CreateWidgetStyle() #endif } - if ( m_hasFgCol ) + if ( m_foregroundColour.Ok() ) { - m_foregroundColour.CalcPixel( gtk_widget_get_colormap( m_widget ) ); + GdkColor *fg = m_foregroundColour.GetColor(); - style->fg[GTK_STATE_NORMAL] = *m_foregroundColour.GetColor(); + style->fg[GTK_STATE_NORMAL] = *fg; style->color_flags[GTK_STATE_NORMAL] = GTK_RC_FG; - style->fg[GTK_STATE_PRELIGHT] = *m_foregroundColour.GetColor(); + style->fg[GTK_STATE_PRELIGHT] = *fg; style->color_flags[GTK_STATE_PRELIGHT] = GTK_RC_FG; - style->fg[GTK_STATE_ACTIVE] = *m_foregroundColour.GetColor(); + style->fg[GTK_STATE_ACTIVE] = *fg; style->color_flags[GTK_STATE_ACTIVE] = GTK_RC_FG; } - if ( m_hasBgCol && m_backgroundColour.Ok() ) + if ( m_backgroundColour.Ok() ) { - m_backgroundColour.CalcPixel( gtk_widget_get_colormap( m_widget ) ); - - style->bg[GTK_STATE_NORMAL] = *m_backgroundColour.GetColor(); - style->base[GTK_STATE_NORMAL] = *m_backgroundColour.GetColor(); + GdkColor *bg = m_backgroundColour.GetColor(); + + style->bg[GTK_STATE_NORMAL] = *bg; + style->base[GTK_STATE_NORMAL] = *bg; style->color_flags[GTK_STATE_NORMAL] = (GtkRcFlags) (style->color_flags[GTK_STATE_NORMAL] | GTK_RC_BG | GTK_RC_BASE); - style->bg[GTK_STATE_PRELIGHT] = *m_backgroundColour.GetColor(); - style->base[GTK_STATE_PRELIGHT] = *m_backgroundColour.GetColor(); + style->bg[GTK_STATE_PRELIGHT] = *bg; + style->base[GTK_STATE_PRELIGHT] = *bg; style->color_flags[GTK_STATE_PRELIGHT] = (GtkRcFlags) (style->color_flags[GTK_STATE_PRELIGHT] | GTK_RC_BG | GTK_RC_BASE); - style->bg[GTK_STATE_ACTIVE] = *m_backgroundColour.GetColor(); - style->base[GTK_STATE_ACTIVE] = *m_backgroundColour.GetColor(); + style->bg[GTK_STATE_ACTIVE] = *bg; + style->base[GTK_STATE_ACTIVE] = *bg; style->color_flags[GTK_STATE_ACTIVE] = (GtkRcFlags) (style->color_flags[GTK_STATE_ACTIVE] | GTK_RC_BG | GTK_RC_BASE); - style->bg[GTK_STATE_INSENSITIVE] = *m_backgroundColour.GetColor(); - style->base[GTK_STATE_INSENSITIVE] = *m_backgroundColour.GetColor(); + style->bg[GTK_STATE_INSENSITIVE] = *bg; + style->base[GTK_STATE_INSENSITIVE] = *bg; style->color_flags[GTK_STATE_INSENSITIVE] = (GtkRcFlags) (style->color_flags[GTK_STATE_INSENSITIVE] | GTK_RC_BG | GTK_RC_BASE); } @@ -4156,8 +4353,62 @@ GtkRcStyle *wxWindowGTK::CreateWidgetStyle() return style; } -void wxWindowGTK::ApplyWidgetStyle() +void wxWindowGTK::ApplyWidgetStyle(bool forceStyle) +{ + GtkRcStyle *style = CreateWidgetStyle(forceStyle); + if ( style ) + { + DoApplyWidgetStyle(style); + gtk_rc_style_unref(style); + } + + // Style change may affect GTK+'s size calculation: + InvalidateBestSize(); +} + +void wxWindowGTK::DoApplyWidgetStyle(GtkRcStyle *style) { + if (m_wxwindow) + gtk_widget_modify_style(m_wxwindow, style); + gtk_widget_modify_style(m_widget, style); +} + +bool wxWindowGTK::SetBackgroundStyle(wxBackgroundStyle style) +{ + wxWindowBase::SetBackgroundStyle(style); + + if (style == wxBG_STYLE_CUSTOM) + { + GdkWindow *window = (GdkWindow*) NULL; + if (m_wxwindow) + window = GTK_PIZZA(m_wxwindow)->bin_window; + else + window = GetConnectWidget()->window; + + if (window) + { + // Make sure GDK/X11 doesn't refresh the window + // automatically. + gdk_window_set_back_pixmap( window, None, False ); +#ifdef __X__ + Display* display = GDK_WINDOW_DISPLAY(window); + XFlush(display); +#endif + m_needsStyleChange = false; + } + else + // Do in OnIdle, because the window is not yet available + m_needsStyleChange = true; + + // Don't apply widget style, or we get a grey background + } + else + { + // apply style change (forceStyle=true so that new style is applied + // even if the bg colour changed from valid to wxNullColour): + ApplyWidgetStyle(true); + } + return true; } //----------------------------------------------------------------------------- @@ -4172,9 +4423,10 @@ void gtk_pop_hide_callback( GtkWidget *WXUNUSED(widget), bool* is_waiting ) *is_waiting = FALSE; } -static void SetInvokingWindow( wxMenu *menu, wxWindowGTK *win ) +void SetInvokingWindow( wxMenu *menu, wxWindow* win ) { menu->SetInvokingWindow( win ); + wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst(); while (node) { @@ -4215,16 +4467,20 @@ bool wxWindowGTK::DoPopupMenu( wxMenu *menu, int x, int y ) wxCHECK_MSG( menu != NULL, false, wxT("invalid popup-menu") ); + // NOTE: if you change this code, you need to update + // the same code in taskbar.cpp as well. This + // is ugly code duplication, I know, + SetInvokingWindow( menu, this ); menu->UpdateUI(); bool is_waiting = true; - gtk_signal_connect( GTK_OBJECT(menu->m_menu), - "hide", - GTK_SIGNAL_FUNC(gtk_pop_hide_callback), - (gpointer)&is_waiting ); + gulong handler = gtk_signal_connect( GTK_OBJECT(menu->m_menu), + "hide", + GTK_SIGNAL_FUNC(gtk_pop_hide_callback), + (gpointer)&is_waiting ); wxPoint pos; gpointer userdata; @@ -4261,6 +4517,8 @@ bool wxWindowGTK::DoPopupMenu( wxMenu *menu, int x, int y ) gtk_main_iteration(); } + gtk_signal_disconnect(GTK_OBJECT(menu->m_menu), handler); + return true; } @@ -4302,24 +4560,16 @@ bool wxWindowGTK::IsOwnGtkWindow( GdkWindow *window ) bool wxWindowGTK::SetFont( const wxFont &font ) { - if (!wxWindowBase::SetFont(font) || !m_widget) - { - return FALSE; - } + wxCHECK_MSG( m_widget != NULL, FALSE, wxT("invalid window") ); - wxColour sysbg = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ); - if ( sysbg == m_backgroundColour ) - { - m_backgroundColour = wxNullColour; - ApplyWidgetStyle(); - m_backgroundColour = sysbg; - } - else - { - ApplyWidgetStyle(); - } + if (!wxWindowBase::SetFont(font)) + return false; - return TRUE; + // apply style change (forceStyle=true so that new style is applied + // even if the font changed from valid to wxNullFont): + ApplyWidgetStyle(true); + + return true; } void wxWindowGTK::DoCaptureMouse()