X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/c0d6c58bd0d7d6b7dae707b2a78c7f917f98da77..2d956b58023a4955313a1c1531bc70a0deb0b831:/src/gtk/window.cpp diff --git a/src/gtk/window.cpp b/src/gtk/window.cpp index d09ee10763..efc124ac8d 100644 --- a/src/gtk/window.cpp +++ b/src/gtk/window.cpp @@ -1,5 +1,5 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: window.cpp +// Name: gtk/window.cpp // Purpose: // Author: Robert Roebling // Id: $Id$ @@ -12,15 +12,20 @@ #pragma implementation "window.h" #endif +#ifdef __VMS +#define XWarpPointer XWARPPOINTER +#endif + #include "wx/defs.h" #include "wx/window.h" -#include "wx/dc.h" +#include "wx/dcclient.h" #include "wx/frame.h" #include "wx/app.h" #include "wx/layout.h" #include "wx/utils.h" #include "wx/dialog.h" #include "wx/msgdlg.h" +#include "wx/module.h" #if wxUSE_DRAG_AND_DROP #include "wx/dnd.h" @@ -30,21 +35,51 @@ #include "wx/tooltip.h" #endif +#if wxUSE_CARET + #include "wx/caret.h" +#endif // wxUSE_CARET + +#if wxUSE_TEXTCTRL + #include "wx/textctrl.h" +#endif + #include "wx/menu.h" #include "wx/statusbr.h" #include "wx/intl.h" #include "wx/settings.h" #include "wx/log.h" +#ifdef __WXDEBUG__ + #include "wx/thread.h" +#endif + #include -#include "gdk/gdk.h" -#include "gtk/gtk.h" -#include "gdk/gdkprivate.h" -#include "gdk/gdkkeysyms.h" +#include "wx/gtk/private.h" +#include +#include +#include + +#include +#include + #include "wx/gtk/win_gtk.h" -#include "gdk/gdkx.h" +#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 + #undef HAVE_XIM + #endif +#endif + +#ifdef __WXGTK20__ +extern GtkContainerClass *pizza_parent_class; +#endif //----------------------------------------------------------------------------- // documentation on internals @@ -184,9 +219,29 @@ extern wxList wxPendingDelete; extern bool g_blockEventsOnDrag; extern bool g_blockEventsOnScroll; extern wxCursor g_globalCursor; -static wxWindow *g_captureWindow = (wxWindow*) NULL; -/* extern */ wxWindow *g_focusWindow = (wxWindow*) NULL; +static GdkGC *g_eraseGC = NULL; + +// mouse capture state: the window which has it and if the mouse is currently +// inside it +static wxWindowGTK *g_captureWindow = (wxWindowGTK*) NULL; +static bool g_captureWindowHasMouse = FALSE; + +/* extern */ 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; + +// 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 @@ -198,12 +253,24 @@ static int g_sendActivateEvent = -1; the last click here */ static guint32 gs_timeLastClick = 0; +extern bool g_mainThreadLocked; + //----------------------------------------------------------------------------- // debug //----------------------------------------------------------------------------- +#ifndef __WXGTK20__ +#define DISABLE_STYLE_IF_BROKEN_THEME 1 +#endif + #ifdef __WXDEBUG__ +#if wxUSE_THREADS +# define DEBUG_MAIN_THREAD if (wxThread::IsMain() && g_mainThreadLocked) printf("gui reentrance"); +#else +# define DEBUG_MAIN_THREAD +#endif + static gint gtk_debug_focus_in_callback( GtkWidget *WXUNUSED(widget), GdkEvent *WXUNUSED(event), const wxChar *WXUNUSED(name) ) @@ -223,18 +290,25 @@ static gint gtk_debug_focus_in_callback( GtkWidget *WXUNUSED(widget), void debug_focus_in( GtkWidget* widget, const wxChar* name, const wxChar *window ) { - wxString tmp = name; - tmp += wxT(" FROM "); - tmp += window; + // suppress warnings about gtk_debug_focus_in_callback being unused with + // this "if ( 0 )" + if ( 0 ) + { + wxString tmp = name; + tmp += wxT(" FROM "); + tmp += window; - wxChar *s = new wxChar[tmp.Length()+1]; + wxChar *s = new wxChar[tmp.Length()+1]; - wxStrcpy( s, tmp ); + wxStrcpy( s, tmp ); - gtk_signal_connect( GTK_OBJECT(widget), "focus_in_event", - GTK_SIGNAL_FUNC(gtk_debug_focus_in_callback), (gpointer)s ); + gtk_signal_connect( GTK_OBJECT(widget), "focus_in_event", + GTK_SIGNAL_FUNC(gtk_debug_focus_in_callback), (gpointer)s ); + } } +#else +#define DEBUG_MAIN_THREAD #endif // Debug //----------------------------------------------------------------------------- @@ -246,11 +320,23 @@ gdk_window_warp_pointer (GdkWindow *window, gint x, gint y) { +#ifndef __WXGTK20__ GdkWindowPrivate *priv; +#endif if (!window) - window = (GdkWindow*) &gdk_root_parent; + window = GDK_ROOT_PARENT(); +#ifdef __WXGTK20__ + if (!GDK_WINDOW_DESTROYED(window)) + { + XWarpPointer (GDK_WINDOW_XDISPLAY(window), + None, /* not source window -> move from anywhere */ + GDK_WINDOW_XID(window), /* dest window */ + 0, 0, 0, 0, /* not source window -> move from anywhere */ + x, y ); + } +#else priv = (GdkWindowPrivate*) window; if (!priv->destroyed) @@ -261,6 +347,7 @@ gdk_window_warp_pointer (GdkWindow *window, 0, 0, 0, 0, /* not source window -> move from anywhere */ x, y ); } +#endif } //----------------------------------------------------------------------------- @@ -274,38 +361,79 @@ extern bool g_isIdle; // local code (see below) //----------------------------------------------------------------------------- -static void draw_frame( GtkWidget *widget, wxWindow *win ) +// returns the child of win which currently has focus or NULL if not found +// +// Note: can't be static, needed by textctrl.cpp. +wxWindow *wxFindFocusedChild(wxWindowGTK *win) +{ + wxWindow *winFocus = wxWindowGTK::FindFocus(); + if ( !winFocus ) + return (wxWindow *)NULL; + + if ( winFocus == win ) + return (wxWindow *)win; + + for ( wxWindowList::Node *node = win->GetChildren().GetFirst(); + node; + node = node->GetNext() ) + { + wxWindow *child = wxFindFocusedChild(node->GetData()); + if ( child ) + return child; + } + + 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 +#ifndef __WXUNIVERSAL__ if (!win->m_hasVMT) return; int dw = 0; int dh = 0; - if (win->HasScrolling()) + if (win->m_hasScrolling) { GtkScrolledWindow *scroll_window = GTK_SCROLLED_WINDOW(widget); - GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT(widget)->klass ); -/* - GtkWidget *hscrollbar = scroll_window->hscrollbar; - GtkWidget *vscrollbar = scroll_window->vscrollbar; + GtkRequisition vscroll_req; + vscroll_req.width = 2; + vscroll_req.height = 2; + (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->vscrollbar) )->size_request ) + (scroll_window->vscrollbar, &vscroll_req ); - we use this instead: range.slider_width = 11 + 2*2pts edge -*/ + GtkRequisition hscroll_req; + hscroll_req.width = 2; + hscroll_req.height = 2; + (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->hscrollbar) )->size_request ) + (scroll_window->hscrollbar, &hscroll_req ); + + GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(widget) ); if (scroll_window->vscrollbar_visible) { - dw += 15; /* dw += vscrollbar->allocation.width; */ + dw += vscroll_req.width; dw += scroll_class->scrollbar_spacing; } if (scroll_window->hscrollbar_visible) { - dh += 15; /* dh += hscrollbar->allocation.height; */ + dh += hscroll_req.height; dh += scroll_class->scrollbar_spacing; } - } +} int dx = 0; int dy = 0; @@ -322,7 +450,7 @@ static void draw_frame( GtkWidget *widget, wxWindow *win ) GTK_STATE_NORMAL, GTK_SHADOW_OUT, dx, dy, - win->m_width-dw, win->m_height-dh ); + widget->allocation.width-dw, widget->allocation.height-dh ); return; } @@ -333,7 +461,7 @@ static void draw_frame( GtkWidget *widget, wxWindow *win ) GTK_STATE_NORMAL, GTK_SHADOW_IN, dx, dy, - win->m_width-dw, win->m_height-dh ); + widget->allocation.width-dw, widget->allocation.height-dh ); return; } @@ -344,368 +472,674 @@ static void draw_frame( GtkWidget *widget, wxWindow *win ) gdk_gc_set_foreground( gc, &widget->style->black ); gdk_draw_rectangle( widget->window, gc, FALSE, dx, dy, - win->m_width-dw-1, win->m_height-dh-1 ); + widget->allocation.width-dw-1, widget->allocation.height-dh-1 ); gdk_gc_unref( gc ); return; } +#endif // __WXUNIVERSAL__ } //----------------------------------------------------------------------------- // "expose_event" of m_widget //----------------------------------------------------------------------------- -static void gtk_window_own_expose_callback( GtkWidget *widget, GdkEventExpose *gdk_event, wxWindow *win ) +gint gtk_window_own_expose_callback( GtkWidget *widget, GdkEventExpose *gdk_event, wxWindowGTK *win ) { - if (gdk_event->count > 0) return; + if (gdk_event->count > 0) return FALSE; + draw_frame( widget, win ); + +#ifdef __WXGTK20__ + + (* GTK_WIDGET_CLASS (pizza_parent_class)->expose_event) (widget, gdk_event); + +#endif + return TRUE; } //----------------------------------------------------------------------------- // "draw" of m_widget //----------------------------------------------------------------------------- -static void gtk_window_own_draw_callback( GtkWidget *widget, GdkRectangle *WXUNUSED(rect), wxWindow *win ) +#ifndef __WXGTK20__ + +static void gtk_window_own_draw_callback( GtkWidget *widget, GdkRectangle *WXUNUSED(rect), wxWindowGTK *win ) { draw_frame( widget, win ); } +#endif // GTK+ < 2.0 + //----------------------------------------------------------------------------- -// key code mapping routines +// "size_request" of m_widget //----------------------------------------------------------------------------- -static long map_to_unmodified_wx_keysym( KeySym keysym ) +static void gtk_window_size_request_callback( GtkWidget *widget, GtkRequisition *requisition, wxWindow *win ) { - guint key_code = 0; + int w,h; + win->GetSize( &w, &h ); + if (w < 2) w = 2; + if (h < 2) h = 2; - switch (keysym) - { - case GDK_Shift_L: - case GDK_Shift_R: key_code = WXK_SHIFT; break; - case GDK_Control_L: - case GDK_Control_R: key_code = WXK_CONTROL; break; - case GDK_Meta_L: - case GDK_Meta_R: - case GDK_Alt_L: - case GDK_Alt_R: - case GDK_Super_L: - case GDK_Super_R: key_code = WXK_ALT; break; - case GDK_Menu: key_code = WXK_MENU; break; - case GDK_Help: key_code = WXK_HELP; break; - case GDK_BackSpace: key_code = WXK_BACK; break; - case GDK_ISO_Left_Tab: - case GDK_Tab: key_code = WXK_TAB; break; - case GDK_Linefeed: key_code = WXK_RETURN; break; - case GDK_Clear: key_code = WXK_CLEAR; break; - case GDK_Return: key_code = WXK_RETURN; break; - case GDK_Pause: key_code = WXK_PAUSE; break; - case GDK_Scroll_Lock: key_code = WXK_SCROLL; break; - case GDK_Escape: key_code = WXK_ESCAPE; break; - case GDK_Delete: key_code = WXK_DELETE; break; - case GDK_Home: key_code = WXK_HOME; break; - case GDK_Left: key_code = WXK_LEFT; break; - case GDK_Up: key_code = WXK_UP; break; - case GDK_Right: key_code = WXK_RIGHT; break; - case GDK_Down: key_code = WXK_DOWN; break; - case GDK_Prior: key_code = WXK_PRIOR; break; -// case GDK_Page_Up: key_code = WXK_PAGEUP; break; - case GDK_Next: key_code = WXK_NEXT; break; -// case GDK_Page_Down: key_code = WXK_PAGEDOWN; break; - case GDK_End: key_code = WXK_END; break; - case GDK_Begin: key_code = WXK_HOME; break; - case GDK_Select: key_code = WXK_SELECT; break; - case GDK_Print: key_code = WXK_PRINT; break; - case GDK_Execute: key_code = WXK_EXECUTE; break; - case GDK_Insert: key_code = WXK_INSERT; break; - case GDK_Num_Lock: key_code = WXK_NUMLOCK; break; - - case GDK_KP_0: key_code = WXK_NUMPAD0; break; - case GDK_KP_1: key_code = WXK_NUMPAD1; break; - case GDK_KP_2: key_code = WXK_NUMPAD2; break; - case GDK_KP_3: key_code = WXK_NUMPAD3; break; - case GDK_KP_4: key_code = WXK_NUMPAD4; break; - case GDK_KP_5: key_code = WXK_NUMPAD5; break; - case GDK_KP_6: key_code = WXK_NUMPAD6; break; - case GDK_KP_7: key_code = WXK_NUMPAD7; break; - case GDK_KP_8: key_code = WXK_NUMPAD8; break; - case GDK_KP_9: key_code = WXK_NUMPAD9; break; - case GDK_KP_Space: key_code = WXK_NUMPAD_SPACE; break; - case GDK_KP_Tab: key_code = WXK_NUMPAD_TAB; break; - case GDK_KP_Enter: key_code = WXK_NUMPAD_ENTER; break; - case GDK_KP_F1: key_code = WXK_NUMPAD_F1; break; - case GDK_KP_F2: key_code = WXK_NUMPAD_F2; break; - case GDK_KP_F3: key_code = WXK_NUMPAD_F3; break; - case GDK_KP_F4: key_code = WXK_NUMPAD_F4; break; - case GDK_KP_Home: key_code = WXK_NUMPAD_HOME; break; - case GDK_KP_Left: key_code = WXK_NUMPAD_LEFT; break; - case GDK_KP_Up: key_code = WXK_NUMPAD_UP; break; - case GDK_KP_Right: key_code = WXK_NUMPAD_RIGHT; break; - case GDK_KP_Down: key_code = WXK_NUMPAD_DOWN; break; - case GDK_KP_Prior: key_code = WXK_NUMPAD_PRIOR; break; -// case GDK_KP_Page_Up: key_code = WXK_NUMPAD_PAGEUP; break; - case GDK_KP_Next: key_code = WXK_NUMPAD_NEXT; break; -// case GDK_KP_Page_Down: key_code = WXK_NUMPAD_PAGEDOWN; break; - case GDK_KP_End: key_code = WXK_NUMPAD_END; break; - case GDK_KP_Begin: key_code = WXK_NUMPAD_BEGIN; break; - case GDK_KP_Insert: key_code = WXK_NUMPAD_INSERT; break; - case GDK_KP_Delete: key_code = WXK_NUMPAD_DELETE; break; - case GDK_KP_Equal: key_code = WXK_NUMPAD_EQUAL; break; - case GDK_KP_Multiply: key_code = WXK_NUMPAD_MULTIPLY; break; - case GDK_KP_Add: key_code = WXK_NUMPAD_ADD; break; - case GDK_KP_Separator: key_code = WXK_NUMPAD_SEPARATOR; break; - case GDK_KP_Subtract: key_code = WXK_NUMPAD_SUBTRACT; break; - case GDK_KP_Decimal: key_code = WXK_NUMPAD_DECIMAL; break; - case GDK_KP_Divide: key_code = WXK_NUMPAD_DIVIDE; break; - - case GDK_F1: key_code = WXK_F1; break; - case GDK_F2: key_code = WXK_F2; break; - case GDK_F3: key_code = WXK_F3; break; - case GDK_F4: key_code = WXK_F4; break; - case GDK_F5: key_code = WXK_F5; break; - case GDK_F6: key_code = WXK_F6; break; - case GDK_F7: key_code = WXK_F7; break; - case GDK_F8: key_code = WXK_F8; break; - case GDK_F9: key_code = WXK_F9; break; - case GDK_F10: key_code = WXK_F10; break; - case GDK_F11: key_code = WXK_F11; break; - case GDK_F12: key_code = WXK_F12; break; - default: - { - if (keysym <= 0xFF) - { - guint upper = gdk_keyval_to_upper( (guint)keysym ); - keysym = (upper != 0 ? upper : keysym ); /* to be MSW compatible */ - key_code = (guint)keysym; - } - } - } - - return (key_code); + requisition->height = h; + requisition->width = w; } -static long map_to_wx_keysym( KeySym keysym ) +//----------------------------------------------------------------------------- +// "expose_event" of m_wxwindow +//----------------------------------------------------------------------------- + +static int gtk_window_expose_callback( GtkWidget *widget, + GdkEventExpose *gdk_event, + wxWindow *win ) { - guint key_code = 0; + DEBUG_MAIN_THREAD + + if (g_isIdle) + wxapp_install_idle_handler(); - switch (keysym) +#if 0 + if (win->GetName()) { - case GDK_Menu: key_code = WXK_MENU; break; - case GDK_Help: key_code = WXK_HELP; break; - case GDK_BackSpace: key_code = WXK_BACK; break; - case GDK_ISO_Left_Tab: - case GDK_Tab: key_code = WXK_TAB; break; - case GDK_Linefeed: key_code = WXK_RETURN; break; - case GDK_Clear: key_code = WXK_CLEAR; break; - case GDK_Return: key_code = WXK_RETURN; break; - case GDK_Pause: key_code = WXK_PAUSE; break; - case GDK_Scroll_Lock: key_code = WXK_SCROLL; break; - case GDK_Escape: key_code = WXK_ESCAPE; break; - case GDK_Delete: key_code = WXK_DELETE; break; - case GDK_Home: key_code = WXK_HOME; break; - case GDK_Left: key_code = WXK_LEFT; break; - case GDK_Up: key_code = WXK_UP; break; - case GDK_Right: key_code = WXK_RIGHT; break; - case GDK_Down: key_code = WXK_DOWN; break; - case GDK_Prior: key_code = WXK_PRIOR; break; -// case GDK_Page_Up: key_code = WXK_PAGEUP; break; - case GDK_Next: key_code = WXK_NEXT; break; -// case GDK_Page_Down: key_code = WXK_PAGEDOWN; break; - case GDK_End: key_code = WXK_END; break; - case GDK_Begin: key_code = WXK_HOME; break; - case GDK_Select: key_code = WXK_SELECT; break; - case GDK_Print: key_code = WXK_PRINT; break; - case GDK_Execute: key_code = WXK_EXECUTE; break; - case GDK_Insert: key_code = WXK_INSERT; break; - case GDK_Num_Lock: key_code = WXK_NUMLOCK; break; - - case GDK_KP_0: key_code = '0'; break; - case GDK_KP_1: key_code = '1'; break; - case GDK_KP_2: key_code = '2'; break; - case GDK_KP_3: key_code = '3'; break; - case GDK_KP_4: key_code = '4'; break; - case GDK_KP_5: key_code = '5'; break; - case GDK_KP_6: key_code = '6'; break; - case GDK_KP_7: key_code = '7'; break; - case GDK_KP_8: key_code = '8'; break; - case GDK_KP_9: key_code = '9'; break; - case GDK_KP_Space: key_code = ' '; break; - case GDK_KP_Tab: key_code = WXK_TAB; break; /* or '\t' ??? */ - case GDK_KP_Enter: key_code = WXK_RETURN; break; /* or '\r' ??? */ - case GDK_KP_F1: key_code = WXK_NUMPAD_F1; break; - case GDK_KP_F2: key_code = WXK_NUMPAD_F2; break; - case GDK_KP_F3: key_code = WXK_NUMPAD_F3; break; - case GDK_KP_F4: key_code = WXK_NUMPAD_F4; break; - case GDK_KP_Home: key_code = WXK_HOME; break; - case GDK_KP_Left: key_code = WXK_LEFT; break; - case GDK_KP_Up: key_code = WXK_UP; break; - case GDK_KP_Right: key_code = WXK_RIGHT; break; - case GDK_KP_Down: key_code = WXK_DOWN; break; - case GDK_KP_Prior: key_code = WXK_PRIOR; break; -// case GDK_KP_Page_Up: key_code = WXK_PAGEUP; break; - case GDK_KP_Next: key_code = WXK_NEXT; break; -// case GDK_KP_Page_Down: key_code = WXK_PAGEDOWN; break; - case GDK_KP_End: key_code = WXK_END; break; - case GDK_KP_Begin: key_code = WXK_HOME; break; - case GDK_KP_Insert: key_code = WXK_INSERT; break; - case GDK_KP_Delete: key_code = WXK_DELETE; break; - case GDK_KP_Equal: key_code = '='; break; - case GDK_KP_Multiply: key_code = '*'; break; - case GDK_KP_Add: key_code = '+'; break; - case GDK_KP_Separator: key_code = ','; break; - case GDK_KP_Subtract: key_code = '-'; break; - case GDK_KP_Decimal: key_code = '.'; break; - case GDK_KP_Divide: key_code = '/'; break; - - case GDK_F1: key_code = WXK_F1; break; - case GDK_F2: key_code = WXK_F2; break; - case GDK_F3: key_code = WXK_F3; break; - case GDK_F4: key_code = WXK_F4; break; - case GDK_F5: key_code = WXK_F5; break; - case GDK_F6: key_code = WXK_F6; break; - case GDK_F7: key_code = WXK_F7; break; - case GDK_F8: key_code = WXK_F8; break; - case GDK_F9: key_code = WXK_F9; break; - case GDK_F10: key_code = WXK_F10; break; - case GDK_F11: key_code = WXK_F11; break; - case GDK_F12: key_code = WXK_F12; break; - default: - { - if (keysym <= 0xFF) - { - key_code = (guint)keysym; - } - } + 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 ); } +#endif - return (key_code); -} - -//----------------------------------------------------------------------------- -// "expose_event" of m_wxwindow -//----------------------------------------------------------------------------- +#ifndef __WXUNIVERSAL__ + GtkPizza *pizza = GTK_PIZZA (widget); -static void gtk_window_expose_callback( GtkWidget *WXUNUSED(widget), GdkEventExpose *gdk_event, wxWindow *win ) -{ - if (!win->m_hasVMT) - return; + if (win->GetThemeEnabled()) + { + wxWindow *parent = win->GetParent(); + while (parent && !parent->IsTopLevel()) + parent = parent->GetParent(); + if (!parent) + parent = win; + + gtk_paint_flat_box (parent->m_widget->style, + pizza->bin_window, + GTK_STATE_NORMAL, + GTK_SHADOW_NONE, + &gdk_event->area, + parent->m_widget, + (char *)"base", + 0, 0, -1, -1); + } +#endif win->GetUpdateRegion().Union( gdk_event->area.x, gdk_event->area.y, gdk_event->area.width, gdk_event->area.height ); - if (gdk_event->count > 0) - return; + // Actual redrawing takes place in idle time. + win->Update(); -/* - wxPrintf( "OnExpose from " ); - if (win->GetClassInfo() && win->GetClassInfo()->GetClassName()) - wxPrintf( win->GetClassInfo()->GetClassName() ); - wxPrintf( " %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 ); -*/ +#ifdef __WXGTK20__ - wxEraseEvent eevent( win->GetId() ); - eevent.SetEventObject( win ); - win->GetEventHandler()->ProcessEvent(eevent); + (* GTK_WIDGET_CLASS (pizza_parent_class)->expose_event) (widget, gdk_event); - wxPaintEvent event( win->GetId() ); - event.SetEventObject( win ); - win->GetEventHandler()->ProcessEvent( event ); +#endif + + return TRUE; +} + +//----------------------------------------------------------------------------- +// "event" of m_wxwindow +//----------------------------------------------------------------------------- - win->GetUpdateRegion().Clear(); +// 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. + +gint gtk_window_event_event_callback( GtkWidget *widget, + GdkEventExpose *event, + wxWindow *win ) +{ + if (event->type == GDK_EXPOSE) + { + gint ret = gtk_window_expose_callback( widget, event, win ); + return ret; + } + + return FALSE; } //----------------------------------------------------------------------------- // "draw" of m_wxwindow //----------------------------------------------------------------------------- -static void gtk_window_draw_callback( GtkWidget *WXUNUSED(widget), - GdkRectangle *rect, wxWindow *win ) +#ifndef __WXGTK20__ + +// This callback is a complete replacement of the gtk_pizza_draw() function, +// which is disabled. + +static void gtk_window_draw_callback( GtkWidget *widget, + GdkRectangle *rect, + wxWindow *win ) { + DEBUG_MAIN_THREAD + if (g_isIdle) wxapp_install_idle_handler(); - if (!win->m_hasVMT) + // The wxNO_FULL_REPAINT_ON_RESIZE flag only works if + // there are no child windows. + if ((win->HasFlag(wxNO_FULL_REPAINT_ON_RESIZE)) && + (win->GetChildren().GetCount() == 0)) + { return; + } - win->GetUpdateRegion().Union( rect->x, rect->y, - rect->width, rect->height ); +#if 0 + if (win->GetName()) + { + wxPrintf( wxT("OnDraw from ") ); + if (win->GetClassInfo() && win->GetClassInfo()->GetClassName()) + wxPrintf( win->GetClassInfo()->GetClassName() ); + wxPrintf( wxT(" %d %d %d %d\n"), (int)rect->x, + (int)rect->y, + (int)rect->width, + (int)rect->height ); + } +#endif -/* - wxPrintf( "OnDraw from " ); - if (win->GetClassInfo() && win->GetClassInfo()->GetClassName()) - printf( win->GetClassInfo()->GetClassName() ); - wxPrintf( " %d %d %d %d\n", (int)rect->x, - (int)rect->y, - (int)rect->width, - (int)rect->height ); -*/ +#ifndef __WXUNIVERSAL__ + GtkPizza *pizza = GTK_PIZZA (widget); - wxEraseEvent eevent( win->GetId() ); - eevent.SetEventObject( win ); - win->GetEventHandler()->ProcessEvent(eevent); + if (win->GetThemeEnabled()) + { + wxWindow *parent = win->GetParent(); + while (parent && !parent->IsTopLevel()) + parent = parent->GetParent(); + if (!parent) + parent = win; + + gtk_paint_flat_box (parent->m_widget->style, + pizza->bin_window, + GTK_STATE_NORMAL, + GTK_SHADOW_NONE, + rect, + parent->m_widget, + (char *)"base", + 0, 0, -1, -1); + } - wxPaintEvent event( win->GetId() ); - event.SetEventObject( win ); - win->GetEventHandler()->ProcessEvent( event ); - win->GetUpdateRegion().Clear(); + if (!(GTK_WIDGET_APP_PAINTABLE (widget)) && + (pizza->clear_on_draw)) + { + gdk_window_clear_area( pizza->bin_window, + rect->x, rect->y, rect->width, rect->height); + } +#endif + + win->GetUpdateRegion().Union( rect->x, rect->y, rect->width, rect->height ); + + // Actual redrawing takes place in idle time. + + win->Update(); + +#ifndef __WXUNIVERSAL__ + // Redraw child widgets + GList *children = pizza->children; + while (children) + { + GtkPizzaChild *child = (GtkPizzaChild*) children->data; + children = children->next; + + GdkRectangle child_area; + if (gtk_widget_intersect (child->widget, rect, &child_area)) + { + gtk_widget_draw (child->widget, &child_area /* (GdkRectangle*) NULL*/ ); + } + } +#endif } +#endif + //----------------------------------------------------------------------------- // "key_press_event" from any window //----------------------------------------------------------------------------- -static gint gtk_window_key_press_callback( GtkWidget *widget, GdkEventKey *gdk_event, wxWindow *win ) +// set WXTRACE to this to see the key event codes on the console +#define TRACE_KEYS _T("keyevent") + +// translates an X key symbol to WXK_XXX value +// +// if isChar is true it means that the value returned will be used for EVT_CHAR +// event and then we choose the logical WXK_XXX, i.e. '/' for GDK_KP_Divide, +// for example, while if it is false it means that the value is going to be +// used for KEY_DOWN/UP events and then we translate GDK_KP_Divide to +// WXK_NUMPAD_DIVIDE +static long wxTranslateKeySymToWXKey(KeySym keysym, bool isChar) { - if (g_isIdle) - wxapp_install_idle_handler(); + long key_code; - if (!win->m_hasVMT) return FALSE; - if (g_blockEventsOnDrag) return FALSE; + switch ( keysym ) + { + // Shift, Control and Alt don't generate the CHAR events at all + case GDK_Shift_L: + case GDK_Shift_R: + key_code = isChar ? 0 : WXK_SHIFT; + break; + case GDK_Control_L: + case GDK_Control_R: + key_code = isChar ? 0 : WXK_CONTROL; + break; + case GDK_Meta_L: + case GDK_Meta_R: + case GDK_Alt_L: + case GDK_Alt_R: + case GDK_Super_L: + case GDK_Super_R: + key_code = isChar ? 0 : WXK_ALT; + break; -/* - printf( "KeyDown-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" ); -*/ + // neither do the toggle modifies + case GDK_Scroll_Lock: + key_code = isChar ? 0 : WXK_SCROLL; + break; + + case GDK_Caps_Lock: + key_code = isChar ? 0 : WXK_CAPITAL; + break; + + case GDK_Num_Lock: + key_code = isChar ? 0 : WXK_NUMLOCK; + break; + + + // various other special keys + case GDK_Menu: + key_code = WXK_MENU; + break; + + case GDK_Help: + key_code = WXK_HELP; + break; + + case GDK_BackSpace: + key_code = WXK_BACK; + break; + + case GDK_ISO_Left_Tab: + case GDK_Tab: + key_code = WXK_TAB; + break; + + case GDK_Linefeed: + case GDK_Return: + key_code = WXK_RETURN; + break; + + case GDK_Clear: + key_code = WXK_CLEAR; + break; + + case GDK_Pause: + key_code = WXK_PAUSE; + break; + + case GDK_Select: + key_code = WXK_SELECT; + break; + + case GDK_Print: + key_code = WXK_PRINT; + break; + + case GDK_Execute: + key_code = WXK_EXECUTE; + break; + + case GDK_Escape: + key_code = WXK_ESCAPE; + break; + + // cursor and other extended keyboard keys + case GDK_Delete: + key_code = WXK_DELETE; + break; + + case GDK_Home: + key_code = WXK_HOME; + break; + + case GDK_Left: + key_code = WXK_LEFT; + break; + + case GDK_Up: + key_code = WXK_UP; + break; + + case GDK_Right: + key_code = WXK_RIGHT; + break; + + case GDK_Down: + key_code = WXK_DOWN; + break; + + case GDK_Prior: // == GDK_Page_Up + key_code = WXK_PRIOR; + break; + + case GDK_Next: // == GDK_Page_Down + key_code = WXK_NEXT; + break; + + case GDK_End: + key_code = WXK_END; + break; + + case GDK_Begin: + key_code = WXK_HOME; + break; + + case GDK_Insert: + key_code = WXK_INSERT; + break; + + + // numpad keys + case GDK_KP_0: + case GDK_KP_1: + case GDK_KP_2: + case GDK_KP_3: + case GDK_KP_4: + case GDK_KP_5: + case GDK_KP_6: + case GDK_KP_7: + case GDK_KP_8: + case GDK_KP_9: + key_code = (isChar ? '0' : WXK_NUMPAD0) + keysym - GDK_KP_0; + break; + + case GDK_KP_Space: + key_code = isChar ? ' ' : WXK_NUMPAD_SPACE; + break; + + case GDK_KP_Tab: + key_code = isChar ? WXK_TAB : WXK_NUMPAD_TAB; + break; + + case GDK_KP_Enter: + key_code = isChar ? WXK_RETURN : WXK_NUMPAD_ENTER; + break; + + case GDK_KP_F1: + key_code = isChar ? WXK_F1 : WXK_NUMPAD_F1; + break; + + case GDK_KP_F2: + key_code = isChar ? WXK_F2 : WXK_NUMPAD_F2; + break; + + case GDK_KP_F3: + key_code = isChar ? WXK_F3 : WXK_NUMPAD_F3; + break; + + case GDK_KP_F4: + key_code = isChar ? WXK_F4 : WXK_NUMPAD_F4; + break; + + case GDK_KP_Home: + key_code = isChar ? WXK_HOME : WXK_NUMPAD_HOME; + break; + + case GDK_KP_Left: + key_code = isChar ? WXK_LEFT : WXK_NUMPAD_LEFT; + break; + + case GDK_KP_Up: + key_code = isChar ? WXK_UP : WXK_NUMPAD_UP; + break; + + case GDK_KP_Right: + key_code = isChar ? WXK_RIGHT : WXK_NUMPAD_RIGHT; + break; + + case GDK_KP_Down: + key_code = isChar ? WXK_DOWN : WXK_NUMPAD_DOWN; + break; + + case GDK_KP_Prior: // == GDK_KP_Page_Up + key_code = isChar ? WXK_PRIOR : WXK_NUMPAD_PRIOR; + break; + + case GDK_KP_Next: // == GDK_KP_Page_Down + key_code = isChar ? WXK_NEXT : WXK_NUMPAD_NEXT; + break; + + case GDK_KP_End: + key_code = isChar ? WXK_END : WXK_NUMPAD_END; + break; + + case GDK_KP_Begin: + key_code = isChar ? WXK_HOME : WXK_NUMPAD_BEGIN; + break; + + case GDK_KP_Insert: + key_code = isChar ? WXK_INSERT : WXK_NUMPAD_INSERT; + break; + + case GDK_KP_Delete: + key_code = isChar ? WXK_DELETE : WXK_NUMPAD_DELETE; + break; + + case GDK_KP_Equal: + key_code = isChar ? '=' : WXK_NUMPAD_EQUAL; + break; + + case GDK_KP_Multiply: + key_code = isChar ? '*' : WXK_NUMPAD_MULTIPLY; + break; + + case GDK_KP_Add: + key_code = isChar ? '+' : WXK_NUMPAD_ADD; + break; + + case GDK_KP_Separator: + // FIXME: what is this? + key_code = isChar ? '.' : WXK_NUMPAD_SEPARATOR; + break; + + case GDK_KP_Subtract: + key_code = isChar ? '-' : WXK_NUMPAD_SUBTRACT; + break; + + case GDK_KP_Decimal: + key_code = isChar ? '.' : WXK_NUMPAD_DECIMAL; + break; + + case GDK_KP_Divide: + key_code = isChar ? '/' : WXK_NUMPAD_DIVIDE; + break; + + + // function keys + case GDK_F1: + case GDK_F2: + case GDK_F3: + case GDK_F4: + case GDK_F5: + case GDK_F6: + case GDK_F7: + case GDK_F8: + case GDK_F9: + case GDK_F10: + case GDK_F11: + case GDK_F12: + key_code = WXK_F1 + keysym - GDK_F1; + break; + + default: + key_code = 0; + } + + return key_code; +} + +static inline bool wxIsAsciiKeysym(KeySym ks) +{ + return ks < 256; +} + +static bool +wxTranslateGTKKeyEventToWx(wxKeyEvent& event, + wxWindowGTK *win, + GdkEventKey *gdk_event) +{ + // VZ: it seems that GDK_KEY_RELEASE event doesn't set event->string + // but only event->keyval which is quite useless to us, so remember + // the last character from GDK_KEY_PRESS and reuse it as last resort + // + // NB: should be MT-safe as we're always called from the main thread only + static struct + { + KeySym keysym; + long keycode; + } s_lastKeyPress = { 0, 0 }; + + KeySym keysym = gdk_event->keyval; + + wxLogTrace(TRACE_KEYS, _T("Key %s event: keysym = %d"), + event.GetEventType() == wxEVT_KEY_UP ? _T("release") + : _T("press"), + keysym); + + long key_code = wxTranslateKeySymToWXKey(keysym, FALSE /* !isChar */); + + if ( !key_code ) + { + // do we have the translation or is it a plain ASCII character? + if ( (gdk_event->length == 1) || wxIsAsciiKeysym(keysym) ) + { + // we should use keysym if it is ASCII as X does some translations + // like "I pressed while Control is down" => "Ctrl-I" == "TAB" + // which we don't want here (but which we do use for OnChar()) + if ( !wxIsAsciiKeysym(keysym) ) + { + keysym = (KeySym)gdk_event->string[0]; + } + + // we want to always get the same key code when the same key is + // pressed regardless of the state of the modifies, i.e. on a + // standard US keyboard pressing '5' or '%' ('5' key with + // Shift) should result in the same key code in OnKeyDown(): + // '5' (although OnChar() will get either '5' or '%'). + // + // to do it we first translate keysym to keycode (== scan code) + // and then back but always using the lower register + Display *dpy = (Display *)wxGetDisplay(); + KeyCode keycode = XKeysymToKeycode(dpy, keysym); + + wxLogTrace(TRACE_KEYS, _T("\t-> keycode %d"), keycode); + + KeySym keysymNormalized = XKeycodeToKeysym(dpy, keycode, 0); + + // use the normalized, i.e. lower register, keysym if we've + // got one + key_code = keysymNormalized ? keysymNormalized : keysym; + + // as explained above, we want to have lower register key codes + // normally but for the letter keys we want to have the upper ones + // + // NB: don't use XConvertCase() here, we want to do it for letters + // only + key_code = toupper(key_code); + } + else // non ASCII key, what to do? + { + // by default, ignore it + key_code = 0; + + // but if we have cached information from the last KEY_PRESS + if ( gdk_event->type == GDK_KEY_RELEASE ) + { + // then reuse it + if ( keysym == s_lastKeyPress.keysym ) + { + key_code = s_lastKeyPress.keycode; + } + } + } + + if ( gdk_event->type == GDK_KEY_PRESS ) + { + // remember it to be reused for KEY_UP event later + s_lastKeyPress.keysym = keysym; + s_lastKeyPress.keycode = key_code; + } + } + + wxLogTrace(TRACE_KEYS, _T("\t-> wxKeyCode %d"), key_code); + + // sending unknown key events doesn't really make sense + if ( !key_code ) + return FALSE; + + // 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); - - bool ret = FALSE; - - long key_code = map_to_unmodified_wx_keysym( gdk_event->keyval ); - /* sending unknown key events doesn't really make sense */ - if (key_code == 0) return FALSE; + if (gdk_event->window) + gdk_window_get_pointer(gdk_event->window, &x, &y, &state); - wxKeyEvent event( wxEVT_KEY_DOWN ); event.SetTimestamp( gdk_event->time ); - event.m_shiftDown = (gdk_event->state & GDK_SHIFT_MASK); - event.m_controlDown = (gdk_event->state & GDK_CONTROL_MASK); - event.m_altDown = (gdk_event->state & GDK_MOD1_MASK); - event.m_metaDown = (gdk_event->state & GDK_MOD2_MASK); + event.m_shiftDown = (gdk_event->state & GDK_SHIFT_MASK) != 0; + event.m_controlDown = (gdk_event->state & GDK_CONTROL_MASK) != 0; + event.m_altDown = (gdk_event->state & GDK_MOD1_MASK) != 0; + event.m_metaDown = (gdk_event->state & GDK_MOD2_MASK) != 0; event.m_keyCode = key_code; event.m_scanCode = gdk_event->keyval; + event.m_rawCode = (wxUint32) gdk_event->keyval; + event.m_rawFlags = 0; event.m_x = x; event.m_y = y; event.SetEventObject( win ); - ret = win->GetEventHandler()->ProcessEvent( event ); + + return TRUE; +} + +static gint gtk_window_key_press_callback( GtkWidget *widget, + GdkEventKey *gdk_event, + wxWindow *win ) +{ + DEBUG_MAIN_THREAD + + if (g_isIdle) + wxapp_install_idle_handler(); + + if (!win->m_hasVMT) + return FALSE; + if (g_blockEventsOnDrag) + return FALSE; + + wxKeyEvent event( wxEVT_KEY_DOWN ); + if ( !wxTranslateGTKKeyEventToWx(event, win, gdk_event) ) + { + // unknown key pressed, ignore (the event would be useless anyhow) + return FALSE; + } + + bool ret = win->GetEventHandler()->ProcessEvent( event ); #if wxUSE_ACCEL if (!ret) { - wxWindow *ancestor = win; + wxWindowGTK *ancestor = win; while (ancestor) { int command = ancestor->GetAcceleratorTable()->GetCommand( event ); @@ -715,54 +1149,68 @@ static gint gtk_window_key_press_callback( GtkWidget *widget, GdkEventKey *gdk_e ret = ancestor->GetEventHandler()->ProcessEvent( command_event ); break; } - if (ancestor->m_isFrame) + if (ancestor->IsTopLevel()) break; ancestor = ancestor->GetParent(); } } #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->keyval ); + if ( !ret ) + { + KeySym keysym = gdk_event->keyval; + long key_code = wxTranslateKeySymToWXKey(keysym, TRUE /* isChar */); + if ( !key_code ) + { + if ( gdk_event->length == 1 ) + { + key_code = (unsigned char)gdk_event->string[0]; + } + else if ( wxIsAsciiKeysym(keysym) ) + { + // ASCII key + key_code = (unsigned char)keysym; + } + } - if ( (!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 ) + { + wxLogTrace(TRACE_KEYS, _T("Char event: %ld"), key_code); + + // reuse the same event object, just change its type and use the + // translated keycode instead of the raw one + event.SetEventType(wxEVT_CHAR); + event.m_keyCode = key_code; + + 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)) && - (!win->HasFlag(wxTE_PROCESS_TAB)) && - (win->GetParent()) && - (win->GetParent()->HasFlag( wxTAB_TRAVERSAL)) ) +// 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) && +#endif // 0 + win->GetParent() && (win->GetParent()->HasFlag( wxTAB_TRAVERSAL)) ) { wxNavigationKeyEvent new_event; - new_event.SetEventObject( win ); + new_event.SetEventObject( win->GetParent() ); /* GDK reports GDK_ISO_Left_Tab for SHIFT-TAB */ new_event.SetDirection( (gdk_event->keyval == GDK_Tab) ); /* CTRL-TAB changes the (parent) window, i.e. switch notebook page */ new_event.SetWindowChange( (gdk_event->state & GDK_CONTROL_MASK) ); new_event.SetCurrentFocus( win ); - ret = win->GetEventHandler()->ProcessEvent( new_event ); + ret = win->GetParent()->GetEventHandler()->ProcessEvent( new_event ); } /* 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); @@ -770,14 +1218,13 @@ static gint gtk_window_key_press_callback( GtkWidget *widget, GdkEventKey *gdk_e 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) ) { - wxWindow *ancestor = win; + wxWindowGTK *ancestor = win; while (ancestor) { if (wxIsKindOf(ancestor,wxFrame)) @@ -799,8 +1246,7 @@ static gint gtk_window_key_press_callback( GtkWidget *widget, GdkEventKey *gdk_e ancestor = ancestor->GetParent(); } } -*/ -#endif +#endif // 0 if (ret) { @@ -815,64 +1261,100 @@ static gint gtk_window_key_press_callback( GtkWidget *widget, GdkEventKey *gdk_e // "key_release_event" from any window //----------------------------------------------------------------------------- -static gint gtk_window_key_release_callback( GtkWidget *widget, GdkEventKey *gdk_event, wxWindow *win ) +static gint gtk_window_key_release_callback( GtkWidget *widget, + GdkEventKey *gdk_event, + wxWindowGTK *win ) { + DEBUG_MAIN_THREAD + if (g_isIdle) wxapp_install_idle_handler(); - if (!win->m_hasVMT) return FALSE; - if (g_blockEventsOnDrag) return FALSE; + if (!win->m_hasVMT) + 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" ); -*/ + if (g_blockEventsOnDrag) + return FALSE; - long key_code = map_to_unmodified_wx_keysym( gdk_event->keyval ); + wxKeyEvent event( wxEVT_KEY_UP ); + if ( !wxTranslateGTKKeyEventToWx(event, win, gdk_event) ) + { + // unknown key pressed, ignore (the event would be useless anyhow + return FALSE; + } - /* sending unknown key events doesn't really make sense */ - if (key_code == 0) return FALSE; + if ( !win->GetEventHandler()->ProcessEvent( event ) ) + return FALSE; - int x = 0; - int y = 0; - GdkModifierType state; - if (gdk_event->window) gdk_window_get_pointer(gdk_event->window, &x, &y, &state); + gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "key_release_event" ); + return TRUE; +} - wxKeyEvent event( wxEVT_KEY_UP ); - event.SetTimestamp( gdk_event->time ); - event.m_shiftDown = (gdk_event->state & GDK_SHIFT_MASK); - event.m_controlDown = (gdk_event->state & GDK_CONTROL_MASK); - event.m_altDown = (gdk_event->state & GDK_MOD1_MASK); - event.m_metaDown = (gdk_event->state & GDK_MOD2_MASK); - event.m_keyCode = key_code; - event.m_scanCode = gdk_event->keyval; - event.m_x = x; - event.m_y = y; - event.SetEventObject( win ); +// ============================================================================ +// the mouse events +// ============================================================================ + +// init wxMouseEvent with the info from gdk_event +#define InitMouseEvent(win, event, gdk_event) \ + { \ + event.SetTimestamp( gdk_event->time ); \ + event.m_shiftDown = (gdk_event->state & GDK_SHIFT_MASK); \ + event.m_controlDown = (gdk_event->state & GDK_CONTROL_MASK); \ + event.m_altDown = (gdk_event->state & GDK_MOD1_MASK); \ + event.m_metaDown = (gdk_event->state & GDK_MOD2_MASK); \ + event.m_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); \ +\ + wxPoint pt = win->GetClientAreaOrigin(); \ + event.m_x = (wxCoord)gdk_event->x - pt.x; \ + event.m_y = (wxCoord)gdk_event->y - pt.y; \ + } - if (win->GetEventHandler()->ProcessEvent( event )) +// ---------------------------------------------------------------------------- +// mouse event processing helper +// ---------------------------------------------------------------------------- + +static void AdjustEventButtonState(wxMouseEvent& event) +{ + // GDK reports the old state of the button for a button press event, but + // for compatibility with MSW and common sense we want m_leftDown be TRUE + // for a LEFT_DOWN event, not FALSE, so we will invert + // left/right/middleDown for the corresponding click events + + if ((event.GetEventType() == wxEVT_LEFT_DOWN) || + (event.GetEventType() == wxEVT_LEFT_DCLICK) || + (event.GetEventType() == wxEVT_LEFT_UP)) { - gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "key_release_event" ); - return TRUE; + event.m_leftDown = !event.m_leftDown; + return; } - return FALSE; + if ((event.GetEventType() == wxEVT_MIDDLE_DOWN) || + (event.GetEventType() == wxEVT_MIDDLE_DCLICK) || + (event.GetEventType() == wxEVT_MIDDLE_UP)) + { + event.m_middleDown = !event.m_middleDown; + return; + } + + if ((event.GetEventType() == wxEVT_RIGHT_DOWN) || + (event.GetEventType() == wxEVT_RIGHT_DCLICK) || + (event.GetEventType() == wxEVT_RIGHT_UP)) + { + event.m_rightDown = !event.m_rightDown; + return; + } } //----------------------------------------------------------------------------- // "button_press_event" //----------------------------------------------------------------------------- -static gint gtk_window_button_press_callback( GtkWidget *widget, GdkEventButton *gdk_event, wxWindow *win ) +static gint gtk_window_button_press_callback( GtkWidget *widget, GdkEventButton *gdk_event, wxWindowGTK *win ) { + DEBUG_MAIN_THREAD + if (g_isIdle) wxapp_install_idle_handler(); @@ -888,23 +1370,18 @@ static gint gtk_window_button_press_callback( GtkWidget *widget, GdkEventButton if (!win->IsOwnGtkWindow( gdk_event->window )) return FALSE; - if (win->m_wxwindow) + if (win->m_wxwindow && (g_focusWindow != win) && win->AcceptsFocus()) { - if (GTK_WIDGET_CAN_FOCUS(win->m_wxwindow) && !GTK_WIDGET_HAS_FOCUS (win->m_wxwindow) ) - { - gtk_widget_grab_focus (win->m_wxwindow); - + gtk_widget_grab_focus( win->m_wxwindow ); /* - wxPrintf( wxT("GrabFocus from ") ); - if (win->GetClassInfo() && win->GetClassInfo()->GetClassName()) - wxPrintf( win->GetClassInfo()->GetClassName() ); - wxPrintf( wxT(".\n") ); + wxPrintf( wxT("GrabFocus from ") ); + if (win->GetClassInfo() && win->GetClassInfo()->GetClassName()) + wxPrintf( win->GetClassInfo()->GetClassName() ); + wxPrintf( wxT(".\n") ); */ - - } } - wxEventType event_type = wxEVT_LEFT_DOWN; + wxEventType event_type = wxEVT_NULL; if (gdk_event->button == 1) { @@ -934,18 +1411,24 @@ static gint gtk_window_button_press_callback( GtkWidget *widget, GdkEventButton } } + if ( event_type == wxEVT_NULL ) + { + // unknown mouse button or click type + return FALSE; + } + wxMouseEvent event( event_type ); - event.SetTimestamp( gdk_event->time ); - event.m_shiftDown = (gdk_event->state & GDK_SHIFT_MASK); - event.m_controlDown = (gdk_event->state & GDK_CONTROL_MASK); - event.m_altDown = (gdk_event->state & GDK_MOD1_MASK); - event.m_metaDown = (gdk_event->state & GDK_MOD2_MASK); - event.m_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); + InitMouseEvent( win, event, gdk_event ); + + AdjustEventButtonState(event); - event.m_x = (long)gdk_event->x; - event.m_y = (long)gdk_event->y; + // 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. @@ -964,7 +1447,7 @@ static gint gtk_window_button_press_callback( GtkWidget *widget, GdkEventButton wxNode *node = win->GetChildren().First(); while (node) { - wxWindow *child = (wxWindow*)node->Data(); + wxWindowGTK *child = (wxWindowGTK*)node->Data(); node = node->Next(); if (!child->IsShown()) @@ -1035,8 +1518,10 @@ static gint gtk_window_button_press_callback( GtkWidget *widget, GdkEventButton // "button_release_event" //----------------------------------------------------------------------------- -static gint gtk_window_button_release_callback( GtkWidget *widget, GdkEventButton *gdk_event, wxWindow *win ) +static gint gtk_window_button_release_callback( GtkWidget *widget, GdkEventButton *gdk_event, wxWindowGTK *win ) { + DEBUG_MAIN_THREAD + if (g_isIdle) wxapp_install_idle_handler(); @@ -1060,19 +1545,21 @@ static gint gtk_window_button_release_callback( GtkWidget *widget, GdkEventButto case 1: event_type = wxEVT_LEFT_UP; break; case 2: event_type = wxEVT_MIDDLE_UP; break; case 3: event_type = wxEVT_RIGHT_UP; break; + default: return FALSE; } wxMouseEvent event( event_type ); - event.SetTimestamp( gdk_event->time ); - event.m_shiftDown = (gdk_event->state & GDK_SHIFT_MASK); - event.m_controlDown = (gdk_event->state & GDK_CONTROL_MASK); - event.m_altDown = (gdk_event->state & GDK_MOD1_MASK); - event.m_metaDown = (gdk_event->state & GDK_MOD2_MASK); - event.m_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); - event.m_x = (long)gdk_event->x; - event.m_y = (long)gdk_event->y; + InitMouseEvent( win, event, gdk_event ); + + 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. @@ -1091,7 +1578,7 @@ static gint gtk_window_button_release_callback( GtkWidget *widget, GdkEventButto wxNode *node = win->GetChildren().First(); while (node) { - wxWindow *child = (wxWindow*)node->Data(); + wxWindowGTK *child = (wxWindowGTK*)node->Data(); node = node->Next(); if (!child->IsShown()) @@ -1153,8 +1640,12 @@ static gint gtk_window_button_release_callback( GtkWidget *widget, GdkEventButto // "motion_notify_event" //----------------------------------------------------------------------------- -static gint gtk_window_motion_notify_callback( GtkWidget *widget, GdkEventMotion *gdk_event, wxWindow *win ) +static gint gtk_window_motion_notify_callback( GtkWidget *widget, + GdkEventMotion *gdk_event, + wxWindowGTK *win ) { + DEBUG_MAIN_THREAD + if (g_isIdle) wxapp_install_idle_handler(); @@ -1182,23 +1673,30 @@ static gint gtk_window_motion_notify_callback( GtkWidget *widget, GdkEventMotion */ wxMouseEvent event( wxEVT_MOTION ); - event.SetTimestamp( gdk_event->time ); - event.m_shiftDown = (gdk_event->state & GDK_SHIFT_MASK); - event.m_controlDown = (gdk_event->state & GDK_CONTROL_MASK); - event.m_altDown = (gdk_event->state & GDK_MOD1_MASK); - event.m_metaDown = (gdk_event->state & GDK_MOD2_MASK); - event.m_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); + InitMouseEvent(win, event, gdk_event); - event.m_x = (long)gdk_event->x; - event.m_y = (long)gdk_event->y; - - // Some control don't have their own X window and thus cannot get - // any events. - - if (!g_captureWindow) + if ( g_captureWindow ) + { + // synthetize a mouse enter or leave event if needed + GdkWindow *winUnderMouse = gdk_window_at_pointer(NULL, NULL); + bool hasMouse = winUnderMouse == gdk_event->window; + if ( hasMouse != g_captureWindowHasMouse ) + { + // the mouse changed window + g_captureWindowHasMouse = hasMouse; + + wxMouseEvent event(g_captureWindowHasMouse ? wxEVT_ENTER_WINDOW + : wxEVT_LEAVE_WINDOW); + InitMouseEvent(win, event, gdk_event); + event.SetEventObject(win); + win->GetEventHandler()->ProcessEvent(event); + } + } + else // no capture { + // Some control don't have their own X window and thus cannot get + // any events. + wxCoord x = event.m_x; wxCoord y = event.m_y; if (win->m_wxwindow) @@ -1211,7 +1709,7 @@ static gint gtk_window_motion_notify_callback( GtkWidget *widget, GdkEventMotion wxNode *node = win->GetChildren().First(); while (node) { - wxWindow *child = (wxWindow*)node->Data(); + wxWindowGTK *child = (wxWindowGTK*)node->Data(); node = node->Next(); if (!child->IsShown()) @@ -1273,8 +1771,12 @@ static gint gtk_window_motion_notify_callback( GtkWidget *widget, GdkEventMotion // "focus_in_event" //----------------------------------------------------------------------------- -static gint gtk_window_focus_in_callback( GtkWidget *widget, GdkEvent *WXUNUSED(event), wxWindow *win ) +static gint gtk_window_focus_in_callback( GtkWidget *widget, + GdkEvent *WXUNUSED(event), + wxWindow *win ) { + DEBUG_MAIN_THREAD + if (g_isIdle) wxapp_install_idle_handler(); @@ -1284,7 +1786,7 @@ static gint gtk_window_focus_in_callback( GtkWidget *widget, GdkEvent *WXUNUSED( switch ( g_sendActivateEvent ) { case -1: - // we've got focus from outside, synthtize wxActivateEvent + // we've got focus from outside, synthetize wxActivateEvent g_sendActivateEvent = 1; break; @@ -1295,30 +1797,64 @@ static gint gtk_window_focus_in_callback( GtkWidget *widget, GdkEvent *WXUNUSED( break; } + g_focusWindowLast = g_focusWindow = win; -/* - printf( "OnSetFocus from " ); - if (win->GetClassInfo() && win->GetClassInfo()->GetClassName()) - printf( win->GetClassInfo()->GetClassName() ); - printf( " " ); - printf( WXSTRINGCAST win->GetLabel() ); - printf( ".\n" ); -*/ +#if 0 + printf( "OnSetFocus 2 from %s\n", win->GetName().c_str() ); +#endif + + // Notify the parent keeping track of focus for the kbd navigation + // purposes that we got it. + wxChildFocusEvent eventFocus(win); + (void)win->GetEventHandler()->ProcessEvent(eventFocus); + +#ifdef HAVE_XIM + if (win->m_ic) + gdk_im_begin(win->m_ic, win->m_wxwindow->window); +#endif + +#if wxUSE_CARET + // caret needs to be informed about focus change + wxCaret *caret = win->GetCaret(); + if ( caret ) + { + caret->OnSetFocus(); + } +#endif // wxUSE_CARET - wxPanel *panel = wxDynamicCast(win->GetParent(), wxPanel); - if (panel) + g_activeFrameLostFocus = FALSE; + + wxWindowGTK *active = wxGetTopLevelParent(win); + if ( active != g_activeFrame ) { - panel->SetLastFocus(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); + } + + 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; } + wxFocusEvent event( wxEVT_SET_FOCUS, win->GetId() ); event.SetEventObject( win ); if (win->GetEventHandler()->ProcessEvent( event )) { - gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "focus_in_event" ); - return TRUE; + gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "focus_in_event" ); + return TRUE; } return FALSE; @@ -1328,27 +1864,57 @@ static gint gtk_window_focus_in_callback( GtkWidget *widget, GdkEvent *WXUNUSED( // "focus_out_event" //----------------------------------------------------------------------------- -static gint gtk_window_focus_out_callback( GtkWidget *widget, GdkEvent *WXUNUSED(event), wxWindow *win ) +static gint gtk_window_focus_out_callback( GtkWidget *widget, GdkEventFocus *gdk_event, wxWindowGTK *win ) { + DEBUG_MAIN_THREAD + if (g_isIdle) wxapp_install_idle_handler(); if (!win->m_hasVMT) return FALSE; if (g_blockEventsOnDrag) return FALSE; +#if 0 + wxLogDebug( wxT("OnKillFocus from %s"), win->GetName().c_str() ); +#endif + + 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; - g_focusWindow = (wxWindow *)NULL; + wxWindowGTK *winFocus = wxFindFocusedChild(win); + if ( winFocus ) + win = winFocus; -/* - printf( "OnKillFocus from " ); - if (win->GetClassInfo() && win->GetClassInfo()->GetClassName()) - printf( win->GetClassInfo()->GetClassName() ); - printf( ".\n" ); -*/ + g_focusWindow = (wxWindowGTK *)NULL; + +#ifdef HAVE_XIM + if (win->m_ic) + gdk_im_end(); +#endif + +#if wxUSE_CARET + // caret needs to be informed about focus change + wxCaret *caret = win->GetCaret(); + if ( caret ) + { + caret->OnKillFocus(); + } +#endif // wxUSE_CARET wxFocusEvent event( wxEVT_KILL_FOCUS, win->GetId() ); event.SetEventObject( win ); @@ -1366,8 +1932,10 @@ static gint gtk_window_focus_out_callback( GtkWidget *widget, GdkEvent *WXUNUSED // "enter_notify_event" //----------------------------------------------------------------------------- -static gint gtk_window_enter_callback( GtkWidget *widget, GdkEventCrossing *gdk_event, wxWindow *win ) +static gint gtk_window_enter_callback( GtkWidget *widget, GdkEventCrossing *gdk_event, wxWindowGTK *win ) { + DEBUG_MAIN_THREAD + if (g_isIdle) wxapp_install_idle_handler(); @@ -1377,9 +1945,7 @@ static gint gtk_window_enter_callback( GtkWidget *widget, GdkEventCrossing *gdk_ if (!win->IsOwnGtkWindow( gdk_event->window )) return FALSE; wxMouseEvent event( wxEVT_ENTER_WINDOW ); -#if (GTK_MINOR_VERSION > 0) event.SetTimestamp( gdk_event->time ); -#endif event.SetEventObject( win ); int x = 0; @@ -1388,16 +1954,10 @@ static gint gtk_window_enter_callback( GtkWidget *widget, GdkEventCrossing *gdk_ gdk_window_get_pointer( widget->window, &x, &y, &state ); - event.m_shiftDown = (state & GDK_SHIFT_MASK); - event.m_controlDown = (state & GDK_CONTROL_MASK); - event.m_altDown = (state & GDK_MOD1_MASK); - event.m_metaDown = (state & GDK_MOD2_MASK); - event.m_leftDown = (state & GDK_BUTTON1_MASK); - event.m_middleDown = (state & GDK_BUTTON2_MASK); - event.m_rightDown = (state & GDK_BUTTON3_MASK); - - event.m_x = (long)x; - event.m_y = (long)y; + InitMouseEvent(win, event, gdk_event); + wxPoint pt = win->GetClientAreaOrigin(); + event.m_x = x + pt.x; + event.m_y = y + pt.y; if (win->GetEventHandler()->ProcessEvent( event )) { @@ -1412,8 +1972,10 @@ static gint gtk_window_enter_callback( GtkWidget *widget, GdkEventCrossing *gdk_ // "leave_notify_event" //----------------------------------------------------------------------------- -static gint gtk_window_leave_callback( GtkWidget *widget, GdkEventCrossing *gdk_event, wxWindow *win ) +static gint gtk_window_leave_callback( GtkWidget *widget, GdkEventCrossing *gdk_event, wxWindowGTK *win ) { + DEBUG_MAIN_THREAD + if (g_isIdle) wxapp_install_idle_handler(); @@ -1423,9 +1985,7 @@ static gint gtk_window_leave_callback( GtkWidget *widget, GdkEventCrossing *gdk_ if (!win->IsOwnGtkWindow( gdk_event->window )) return FALSE; wxMouseEvent event( wxEVT_LEAVE_WINDOW ); -#if (GTK_MINOR_VERSION > 0) event.SetTimestamp( gdk_event->time ); -#endif event.SetEventObject( win ); int x = 0; @@ -1434,16 +1994,17 @@ static gint gtk_window_leave_callback( GtkWidget *widget, GdkEventCrossing *gdk_ gdk_window_get_pointer( widget->window, &x, &y, &state ); - event.m_shiftDown = (state & GDK_SHIFT_MASK); - event.m_controlDown = (state & GDK_CONTROL_MASK); - event.m_altDown = (state & GDK_MOD1_MASK); - event.m_metaDown = (state & GDK_MOD2_MASK); - event.m_leftDown = (state & GDK_BUTTON1_MASK); - event.m_middleDown = (state & GDK_BUTTON2_MASK); - event.m_rightDown = (state & GDK_BUTTON3_MASK); + event.m_shiftDown = (state & GDK_SHIFT_MASK) != 0; + event.m_controlDown = (state & GDK_CONTROL_MASK) != 0; + event.m_altDown = (state & GDK_MOD1_MASK) != 0; + event.m_metaDown = (state & GDK_MOD2_MASK) != 0; + event.m_leftDown = (state & GDK_BUTTON1_MASK) != 0; + event.m_middleDown = (state & GDK_BUTTON2_MASK) != 0; + event.m_rightDown = (state & GDK_BUTTON3_MASK) != 0; - event.m_x = (long)x; - event.m_y = (long)y; + wxPoint pt = win->GetClientAreaOrigin(); + event.m_x = x + pt.x; + event.m_y = y + pt.y; if (win->GetEventHandler()->ProcessEvent( event )) { @@ -1458,8 +2019,12 @@ static gint gtk_window_leave_callback( GtkWidget *widget, GdkEventCrossing *gdk_ // "value_changed" from m_vAdjust //----------------------------------------------------------------------------- -static void gtk_window_vscroll_callback( GtkAdjustment *adjust, wxWindow *win ) +static void gtk_window_vscroll_callback( GtkAdjustment *adjust, + SCROLLBAR_CBACK_ARG + wxWindowGTK *win ) { + DEBUG_MAIN_THREAD + if (g_isIdle) wxapp_install_idle_handler(); @@ -1472,14 +2037,7 @@ static void gtk_window_vscroll_callback( GtkAdjustment *adjust, wxWindow *win ) win->m_oldVerticalPos = adjust->value; - GtkScrolledWindow *scrolledWindow = GTK_SCROLLED_WINDOW(win->m_widget); - GtkRange *range = GTK_RANGE( scrolledWindow->vscrollbar ); - - wxEventType command = wxEVT_SCROLLWIN_THUMBTRACK; - if (range->scroll_type == GTK_SCROLL_STEP_BACKWARD) command = wxEVT_SCROLLWIN_LINEUP; - else if (range->scroll_type == GTK_SCROLL_STEP_FORWARD) command = wxEVT_SCROLLWIN_LINEDOWN; - else if (range->scroll_type == GTK_SCROLL_PAGE_BACKWARD) command = wxEVT_SCROLLWIN_PAGEUP; - else if (range->scroll_type == GTK_SCROLL_PAGE_FORWARD) command = wxEVT_SCROLLWIN_PAGEDOWN; + wxEventType command = GtkScrollWinTypeToWx(GET_SCROLL_TYPE(win->m_widget)); int value = (int)(adjust->value+0.5); @@ -1492,8 +2050,12 @@ static void gtk_window_vscroll_callback( GtkAdjustment *adjust, wxWindow *win ) // "value_changed" from m_hAdjust //----------------------------------------------------------------------------- -static void gtk_window_hscroll_callback( GtkAdjustment *adjust, wxWindow *win ) +static void gtk_window_hscroll_callback( GtkAdjustment *adjust, + SCROLLBAR_CBACK_ARG + wxWindowGTK *win ) { + DEBUG_MAIN_THREAD + if (g_isIdle) wxapp_install_idle_handler(); @@ -1503,16 +2065,9 @@ static void gtk_window_hscroll_callback( GtkAdjustment *adjust, wxWindow *win ) float diff = adjust->value - win->m_oldHorizontalPos; if (fabs(diff) < 0.2) return; - win->m_oldHorizontalPos = adjust->value; - - GtkScrolledWindow *scrolledWindow = GTK_SCROLLED_WINDOW(win->m_widget); - GtkRange *range = GTK_RANGE( scrolledWindow->hscrollbar ); + wxEventType command = GtkScrollWinTypeToWx(GET_SCROLL_TYPE(win->m_widget)); - wxEventType command = wxEVT_SCROLLWIN_THUMBTRACK; - if (range->scroll_type == GTK_SCROLL_STEP_BACKWARD) command = wxEVT_SCROLLWIN_LINEUP; - else if (range->scroll_type == GTK_SCROLL_STEP_FORWARD) command = wxEVT_SCROLLWIN_LINEDOWN; - else if (range->scroll_type == GTK_SCROLL_PAGE_BACKWARD) command = wxEVT_SCROLLWIN_PAGEUP; - else if (range->scroll_type == GTK_SCROLL_PAGE_FORWARD) command = wxEVT_SCROLLWIN_PAGEDOWN; + win->m_oldHorizontalPos = adjust->value; int value = (int)(adjust->value+0.5); @@ -1522,136 +2077,282 @@ static void gtk_window_hscroll_callback( GtkAdjustment *adjust, wxWindow *win ) } //----------------------------------------------------------------------------- -// "changed" from m_vAdjust +// "button_press_event" from scrollbar //----------------------------------------------------------------------------- -static void gtk_window_vscroll_change_callback( GtkWidget *WXUNUSED(widget), wxWindow *win ) +static gint gtk_scrollbar_button_press_callback( GtkRange *widget, + GdkEventButton *gdk_event, + wxWindowGTK *win) { + DEBUG_MAIN_THREAD + if (g_isIdle) wxapp_install_idle_handler(); - if (g_blockEventsOnDrag) return; - if (!win->m_hasVMT) return; - wxEventType command = wxEVT_SCROLLWIN_THUMBTRACK; - int value = (int)(win->m_vAdjust->value+0.5); + g_blockEventsOnScroll = TRUE; - wxScrollWinEvent event( command, value, wxVERTICAL ); - event.SetEventObject( win ); - win->GetEventHandler()->ProcessEvent( event ); + // FIXME: there is no 'slider' field in GTK+ 2.0 any more +#ifndef __WXGTK20__ + win->m_isScrolling = (gdk_event->window == widget->slider); +#endif + + return FALSE; } //----------------------------------------------------------------------------- -// "changed" from m_hAdjust +// "button_release_event" from scrollbar //----------------------------------------------------------------------------- -static void gtk_window_hscroll_change_callback( GtkWidget *WXUNUSED(widget), wxWindow *win ) +static gint gtk_scrollbar_button_release_callback( GtkRange *widget, + GdkEventButton *WXUNUSED(gdk_event), + wxWindowGTK *win) { - if (g_isIdle) - wxapp_install_idle_handler(); + DEBUG_MAIN_THREAD - if (g_blockEventsOnDrag) return; - if (!win->m_hasVMT) return; +// don't test here as we can release the mouse while being over +// a different window than the slider +// +// if (gdk_event->window != widget->slider) return FALSE; - wxEventType command = wxEVT_SCROLLWIN_THUMBTRACK; - int value = (int)(win->m_hAdjust->value+0.5); + g_blockEventsOnScroll = FALSE; - wxScrollWinEvent event( command, value, wxHORIZONTAL ); - event.SetEventObject( win ); - win->GetEventHandler()->ProcessEvent( event ); + if (win->m_isScrolling) + { + wxEventType command = wxEVT_SCROLLWIN_THUMBRELEASE; + int value = -1; + int dir = -1; + + GtkScrolledWindow *scrolledWindow = GTK_SCROLLED_WINDOW(win->m_widget); + if (widget == GTK_RANGE(scrolledWindow->hscrollbar)) + { + value = (int)(win->m_hAdjust->value+0.5); + dir = wxHORIZONTAL; + } + if (widget == GTK_RANGE(scrolledWindow->vscrollbar)) + { + value = (int)(win->m_vAdjust->value+0.5); + dir = wxVERTICAL; + } + + wxScrollWinEvent event( command, value, dir ); + event.SetEventObject( win ); + win->GetEventHandler()->ProcessEvent( event ); + } + + win->m_isScrolling = FALSE; + + 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() +{ + // the cast is necessary when we compile in wxUniversal mode + return (wxWindow *)g_focusWindow; } //----------------------------------------------------------------------------- -// "button_press_event" from scrollbar +// "realize" from m_widget //----------------------------------------------------------------------------- -static gint gtk_scrollbar_button_press_callback( GtkRange *WXUNUSED(widget), - GdkEventButton *WXUNUSED(gdk_event), - wxWindow *win ) +/* We cannot set colours and fonts before the widget has + been realized, so we do this directly after realization. */ + +static gint +gtk_window_realized_callback( GtkWidget *WXUNUSED(m_widget), wxWindow *win ) { + DEBUG_MAIN_THREAD + if (g_isIdle) wxapp_install_idle_handler(); -// don't test here as we can release the mouse while being over -// a different window than the slider -// -// if (gdk_event->window != widget->slider) return FALSE; + if (win->m_delayedBackgroundColour) + win->GtkSetBackgroundColour( win->GetBackgroundColour() ); + + if (win->m_delayedForegroundColour) + win->GtkSetForegroundColour( win->GetForegroundColour() ); - win->SetScrolling( TRUE ); + wxWindowCreateEvent event( win ); + event.SetEventObject( win ); + win->GetEventHandler()->ProcessEvent( event ); return FALSE; } //----------------------------------------------------------------------------- -// "button_release_event" from scrollbar +// "size_allocate" //----------------------------------------------------------------------------- -static gint gtk_scrollbar_button_release_callback( GtkRange *WXUNUSED(widget), - GdkEventButton *WXUNUSED(gdk_event), - wxWindow *win ) +static +void gtk_window_size_callback( GtkWidget *WXUNUSED(widget), + GtkAllocation *WXUNUSED(alloc), + wxWindow *win ) { + if (g_isIdle) + wxapp_install_idle_handler(); -// don't test here as we can release the mouse while being over -// a different window than the slider -// -// if (gdk_event->window != widget->slider) return FALSE; + if (!win->m_hasScrolling) return; - win->SetScrolling( FALSE ); + int client_width = 0; + int client_height = 0; + win->GetClientSize( &client_width, &client_height ); + if ((client_width == win->m_oldClientWidth) && (client_height == win->m_oldClientHeight)) + return; - return FALSE; + win->m_oldClientWidth = client_width; + win->m_oldClientHeight = client_height; + + if (!win->m_nativeSizeEvent) + { + wxSizeEvent event( win->GetSize(), win->GetId() ); + event.SetEventObject( win ); + win->GetEventHandler()->ProcessEvent( event ); + } } -// ---------------------------------------------------------------------------- -// this wxWindowBase function is implemented here (in platform-specific file) -// because it is static and so couldn't be made virtual -// ---------------------------------------------------------------------------- -wxWindow *wxWindowBase::FindFocus() +#ifdef HAVE_XIM + #define WXUNUSED_UNLESS_XIM(param) param +#else + #define WXUNUSED_UNLESS_XIM(param) WXUNUSED(param) +#endif + +/* Resize XIM window */ + +static +void gtk_wxwindow_size_callback( GtkWidget* WXUNUSED_UNLESS_XIM(widget), + GtkAllocation* WXUNUSED_UNLESS_XIM(alloc), + wxWindowGTK* WXUNUSED_UNLESS_XIM(win) ) { - return g_focusWindow; + if (g_isIdle) + wxapp_install_idle_handler(); + +#ifdef HAVE_XIM + if (!win->m_ic) + return; + + if (gdk_ic_get_style (win->m_ic) & GDK_IM_PREEDIT_POSITION) + { + gint width, height; + + gdk_window_get_size (widget->window, &width, &height); + win->m_icattr->preedit_area.width = width; + win->m_icattr->preedit_area.height = height; + gdk_ic_set_attr (win->m_ic, win->m_icattr, GDK_IC_PREEDIT_AREA); + } +#endif // HAVE_XIM } //----------------------------------------------------------------------------- -// "realize" from m_widget +// "realize" from m_wxwindow //----------------------------------------------------------------------------- -/* we cannot set colours and fonts before the widget has - been realized, so we do this directly after realization */ +/* Initialize XIM support */ static gint -gtk_window_realized_callback( GtkWidget * WXUNUSED(widget), wxWindow *win ) +gtk_wxwindow_realized_callback( GtkWidget * WXUNUSED_UNLESS_XIM(widget), + wxWindowGTK * WXUNUSED_UNLESS_XIM(win) ) { if (g_isIdle) wxapp_install_idle_handler(); - if (win->m_delayedFont) - win->SetFont( win->GetFont() ); +#ifdef HAVE_XIM + if (win->m_ic) return FALSE; + if (!widget) return FALSE; + if (!gdk_im_ready()) return FALSE; + + win->m_icattr = gdk_ic_attr_new(); + if (!win->m_icattr) return FALSE; + + gint width, height; + GdkEventMask mask; + GdkColormap *colormap; + GdkICAttr *attr = win->m_icattr; + unsigned attrmask = GDK_IC_ALL_REQ; + GdkIMStyle style; + GdkIMStyle supported_style = (GdkIMStyle) + (GDK_IM_PREEDIT_NONE | + GDK_IM_PREEDIT_NOTHING | + GDK_IM_PREEDIT_POSITION | + GDK_IM_STATUS_NONE | + GDK_IM_STATUS_NOTHING); + + if (widget->style && widget->style->font->type != GDK_FONT_FONTSET) + supported_style = (GdkIMStyle)(supported_style & ~GDK_IM_PREEDIT_POSITION); + + attr->style = style = gdk_im_decide_style (supported_style); + attr->client_window = widget->window; + + if ((colormap = gtk_widget_get_colormap (widget)) != + gtk_widget_get_default_colormap ()) + { + attrmask |= GDK_IC_PREEDIT_COLORMAP; + attr->preedit_colormap = colormap; + } + + attrmask |= GDK_IC_PREEDIT_FOREGROUND; + attrmask |= GDK_IC_PREEDIT_BACKGROUND; + attr->preedit_foreground = widget->style->fg[GTK_STATE_NORMAL]; + attr->preedit_background = widget->style->base[GTK_STATE_NORMAL]; - if (win->m_delayedBackgroundColour) - win->SetBackgroundColour( win->GetBackgroundColour() ); + switch (style & GDK_IM_PREEDIT_MASK) + { + case GDK_IM_PREEDIT_POSITION: + if (widget->style && widget->style->font->type != GDK_FONT_FONTSET) + { + g_warning ("over-the-spot style requires fontset"); + break; + } - if (win->m_delayedForegroundColour) - win->SetForegroundColour( win->GetForegroundColour() ); + gdk_window_get_size (widget->window, &width, &height); - wxWindowCreateEvent event( win ); - event.SetEventObject( win ); - win->GetEventHandler()->ProcessEvent( event ); + attrmask |= GDK_IC_PREEDIT_POSITION_REQ; + attr->spot_location.x = 0; + attr->spot_location.y = height; + attr->preedit_area.x = 0; + attr->preedit_area.y = 0; + attr->preedit_area.width = width; + attr->preedit_area.height = height; + attr->preedit_fontset = widget->style->font; + + break; + } + + win->m_ic = gdk_ic_new (attr, (GdkICAttributesType)attrmask); + + if (win->m_ic == NULL) + g_warning ("Can't create input context."); + else + { + mask = gdk_window_get_events (widget->window); + mask = (GdkEventMask)(mask | gdk_ic_get_events (win->m_ic)); + gdk_window_set_events (widget->window, mask); + + if (GTK_WIDGET_HAS_FOCUS(widget)) + gdk_im_begin (win->m_ic, widget->window); + } +#endif // HAVE_XIM return FALSE; } //----------------------------------------------------------------------------- -// InsertChild for wxWindow. +// InsertChild for wxWindowGTK. //----------------------------------------------------------------------------- -/* Callback for wxWindow. This very strange beast has to be used because +/* 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( wxWindow* parent, wxWindow* child ) +static void wxInsertChildInWindow( wxWindowGTK* parent, wxWindowGTK* child ) { /* the window might have been scrolled already, do we have to adapt the position */ @@ -1671,18 +2372,25 @@ static void wxInsertChildInWindow( wxWindow* parent, wxWindow* child ) // global functions //----------------------------------------------------------------------------- -wxWindow* wxGetActiveWindow() +wxWindow *wxGetActiveWindow() { - return g_focusWindow; + // the cast is necessary when we compile in wxUniversal mode + return (wxWindow *)g_focusWindow; } //----------------------------------------------------------------------------- -// wxWindow +// wxWindowGTK //----------------------------------------------------------------------------- -IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowBase) +// in wxUniv/MSW this class is abstract because it doesn't have DoPopupMenu() +// method +#ifdef __WXUNIVERSAL__ + IMPLEMENT_ABSTRACT_CLASS(wxWindowGTK, wxWindowBase) +#else // __WXGTK__ + IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowBase) +#endif // __WXUNIVERSAL__/__WXGTK__ -void wxWindow::Init() +void wxWindowGTK::Init() { // common init InitBase(); @@ -1690,6 +2398,7 @@ void wxWindow::Init() // GTK specific m_widget = (GtkWidget *) NULL; m_wxwindow = (GtkWidget *) NULL; + m_focusWidget = (GtkWidget *) NULL; // position/size m_x = 0; @@ -1720,54 +2429,65 @@ void wxWindow::Init() m_isStaticBox = FALSE; m_isRadioButton = FALSE; + m_isListBox = FALSE; m_isFrame = FALSE; m_acceptsFocus = FALSE; + m_clipPaintRegion = FALSE; + m_cursor = *wxSTANDARD_CURSOR; + + m_delayedForegroundColour = FALSE; + m_delayedBackgroundColour = FALSE; + +#ifdef HAVE_XIM + m_ic = (GdkIC*) NULL; + m_icattr = (GdkICAttr*) NULL; +#endif } -wxWindow::wxWindow() +wxWindowGTK::wxWindowGTK() { Init(); } -wxWindow::wxWindow( wxWindow *parent, wxWindowID id, - const wxPoint &pos, const wxSize &size, - long style, const wxString &name ) +wxWindowGTK::wxWindowGTK( wxWindow *parent, + wxWindowID id, + const wxPoint &pos, + const wxSize &size, + long style, + const wxString &name ) { Init(); Create( parent, id, pos, size, style, name ); } -bool wxWindow::Create( wxWindow *parent, wxWindowID id, - const wxPoint &pos, const wxSize &size, - long style, const wxString &name ) +bool wxWindowGTK::Create( wxWindow *parent, + wxWindowID id, + const wxPoint &pos, + const wxSize &size, + long style, + const wxString &name ) { if (!PreCreation( parent, pos, size ) || !CreateBase( parent, id, pos, size, style, wxDefaultValidator, name )) { - wxFAIL_MSG( wxT("wxWindow creation failed") ); + wxFAIL_MSG( wxT("wxWindowGTK creation failed") ); return FALSE; } 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 ); -#ifdef __WXDEBUG__ - debug_focus_in( m_widget, wxT("wxWindow::m_widget"), name ); -#endif - GtkScrolledWindow *scrolledWindow = GTK_SCROLLED_WINDOW(m_widget); -#ifdef __WXDEBUG__ - debug_focus_in( scrolledWindow->hscrollbar, wxT("wxWindow::hsrcollbar"), name ); - debug_focus_in( scrolledWindow->vscrollbar, wxT("wxWindow::vsrcollbar"), name ); -#endif - - GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT(m_widget)->klass ); + GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget) ); scroll_class->scrollbar_spacing = 0; gtk_scrolled_window_set_policy( scrolledWindow, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC ); @@ -1777,13 +2497,7 @@ bool wxWindow::Create( wxWindow *parent, wxWindowID id, m_wxwindow = gtk_pizza_new(); -#ifdef __WXDEBUG__ - debug_focus_in( m_wxwindow, wxT("wxWindow::m_wxwindow"), name ); -#endif - - gtk_container_add( GTK_CONTAINER(m_widget), m_wxwindow ); - -#if (GTK_MINOR_VERSION > 0) +#ifndef __WXUNIVERSAL__ GtkPizza *pizza = GTK_PIZZA(m_wxwindow); if (HasFlag(wxRAISED_BORDER)) @@ -1802,32 +2516,13 @@ bool wxWindow::Create( wxWindow *parent, wxWindowID id, { gtk_pizza_set_shadow_type( pizza, GTK_MYSHADOW_NONE ); } -#else // GTK_MINOR_VERSION == 0 - GtkViewport *viewport = GTK_VIEWPORT(scrolledWindow->viewport); +#endif // __WXUNIVERSAL__ - if (HasFlag(wxRAISED_BORDER)) - { - gtk_viewport_set_shadow_type( viewport, GTK_SHADOW_OUT ); - } - else if (HasFlag(wxSUNKEN_BORDER)) - { - gtk_viewport_set_shadow_type( viewport, GTK_SHADOW_IN ); - } - else - { - gtk_viewport_set_shadow_type( viewport, GTK_SHADOW_NONE ); - } -#endif // GTK_MINOR_VERSION + gtk_container_add( GTK_CONTAINER(m_widget), m_wxwindow ); GTK_WIDGET_SET_FLAGS( m_wxwindow, GTK_CAN_FOCUS ); m_acceptsFocus = TRUE; -#if (GTK_MINOR_VERSION == 0) - // shut the viewport up - gtk_viewport_set_hadjustment( viewport, (GtkAdjustment*) gtk_adjustment_new( 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) ); - gtk_viewport_set_vadjustment( viewport, (GtkAdjustment*) gtk_adjustment_new( 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) ); -#endif // GTK_MINOR_VERSION == 0 - // I _really_ don't want scrollbars in the beginning m_vAdjust->lower = 0.0; m_vAdjust->upper = 1.0; @@ -1869,16 +2564,13 @@ bool wxWindow::Create( wxWindow *parent, wxWindowID id, gtk_signal_connect( GTK_OBJECT(m_vAdjust), "value_changed", (GtkSignalFunc) gtk_window_vscroll_callback, (gpointer) this ); - gtk_signal_connect( GTK_OBJECT(m_hAdjust), "changed", - (GtkSignalFunc) gtk_window_hscroll_change_callback, (gpointer) this ); - gtk_signal_connect(GTK_OBJECT(m_vAdjust), "changed", - (GtkSignalFunc) gtk_window_vscroll_change_callback, (gpointer) this ); - gtk_widget_show( m_wxwindow ); if (m_parent) m_parent->DoAddChild( this ); + m_focusWidget = m_wxwindow; + PostCreation(); Show( TRUE ); @@ -1886,8 +2578,14 @@ bool wxWindow::Create( wxWindow *parent, wxWindowID id, return TRUE; } -wxWindow::~wxWindow() +wxWindowGTK::~wxWindowGTK() { + if (g_focusWindow == this) + g_focusWindow = NULL; + + if (g_activeFrame == this) + g_activeFrame = NULL; + m_isBeingDeleted = TRUE; m_hasVMT = FALSE; @@ -1899,9 +2597,20 @@ wxWindow::~wxWindow() if (m_parent) m_parent->RemoveChild( this ); +#ifdef HAVE_XIM + if (m_ic) + gdk_ic_destroy (m_ic); + if (m_icattr) + gdk_ic_attr_destroy (m_icattr); +#endif + if (m_widgetStyle) { - gtk_style_unref( m_widgetStyle ); +#if DISABLE_STYLE_IF_BROKEN_THEME + // don't delete if it's a pixmap theme style + if (!m_widgetStyle->engine_data) + gtk_style_unref( m_widgetStyle ); +#endif m_widgetStyle = (GtkStyle*) NULL; } @@ -1918,7 +2627,7 @@ wxWindow::~wxWindow() } } -bool wxWindow::PreCreation( wxWindow *parent, const wxPoint &pos, const wxSize &size ) +bool wxWindowGTK::PreCreation( wxWindowGTK *parent, const wxPoint &pos, const wxSize &size ) { wxCHECK_MSG( !m_needParent || parent, FALSE, wxT("Need complete parent.") ); @@ -1950,7 +2659,7 @@ bool wxWindow::PreCreation( wxWindow *parent, const wxPoint &pos, const wxSize return TRUE; } -void wxWindow::PostCreation() +void wxWindowGTK::PostCreation() { wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") ); @@ -1958,37 +2667,89 @@ void wxWindow::PostCreation() { if (!m_noExpose) { - /* these get reported to wxWindows -> wxPaintEvent */ + // these get reported to wxWindows -> wxPaintEvent + + gtk_pizza_set_external( GTK_PIZZA(m_wxwindow), TRUE ); + gtk_signal_connect( GTK_OBJECT(m_wxwindow), "expose_event", GTK_SIGNAL_FUNC(gtk_window_expose_callback), (gpointer)this ); +#ifndef __WXGTK20__ gtk_signal_connect( GTK_OBJECT(m_wxwindow), "draw", GTK_SIGNAL_FUNC(gtk_window_draw_callback), (gpointer)this ); + + if (HasFlag(wxNO_FULL_REPAINT_ON_RESIZE)) + { + gtk_signal_connect( GTK_OBJECT(m_wxwindow), "event", + GTK_SIGNAL_FUNC(gtk_window_event_event_callback), (gpointer)this ); + } +#else + gtk_widget_set_redraw_on_allocate( GTK_WIDGET(m_wxwindow), HasFlag( wxNO_FULL_REPAINT_ON_RESIZE ) ); +#endif } -#if (GTK_MINOR_VERSION > 0) - /* these are called when the "sunken" or "raised" borders are drawn */ + // these are called when the "sunken" or "raised" borders are drawn gtk_signal_connect( GTK_OBJECT(m_widget), "expose_event", GTK_SIGNAL_FUNC(gtk_window_own_expose_callback), (gpointer)this ); +#ifndef __WXGTK20__ gtk_signal_connect( GTK_OBJECT(m_widget), "draw", GTK_SIGNAL_FUNC(gtk_window_own_draw_callback), (gpointer)this ); #endif } + // 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 ); + + gtk_signal_connect( 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 + GtkWidget *connect_widget = GetConnectWidget(); ConnectWidget( connect_widget ); - /* we cannot set colours, fonts and cursors before the widget has + /* We cannot set colours, fonts and cursors before the widget has been realized, so we do this directly after realization */ gtk_signal_connect( GTK_OBJECT(connect_widget), "realize", GTK_SIGNAL_FUNC(gtk_window_realized_callback), (gpointer) this ); + if (m_wxwindow) + { + // Catch native resize events + gtk_signal_connect( GTK_OBJECT(m_wxwindow), "size_allocate", + GTK_SIGNAL_FUNC(gtk_window_size_callback), (gpointer)this ); + + // Initialize XIM support + gtk_signal_connect( GTK_OBJECT(m_wxwindow), "realize", + GTK_SIGNAL_FUNC(gtk_wxwindow_realized_callback), (gpointer) this ); + + // And resize XIM window + gtk_signal_connect( GTK_OBJECT(m_wxwindow), "size_allocate", + GTK_SIGNAL_FUNC(gtk_wxwindow_size_callback), (gpointer)this ); + } + + if (!GTK_IS_COMBO(m_widget)) + { + // This is needed if we want to add our windows into native + // GTK control, 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. + gtk_signal_connect( GTK_OBJECT(m_widget), "size_request", + GTK_SIGNAL_FUNC(gtk_window_size_request_callback), (gpointer) this ); + } + m_hasVMT = TRUE; } -void wxWindow::ConnectWidget( GtkWidget *widget ) +void wxWindowGTK::ConnectWidget( GtkWidget *widget ) { gtk_signal_connect( GTK_OBJECT(widget), "key_press_event", GTK_SIGNAL_FUNC(gtk_window_key_press_callback), (gpointer)this ); @@ -2005,12 +2766,6 @@ void wxWindow::ConnectWidget( GtkWidget *widget ) gtk_signal_connect( GTK_OBJECT(widget), "motion_notify_event", GTK_SIGNAL_FUNC(gtk_window_motion_notify_callback), (gpointer)this ); - gtk_signal_connect( GTK_OBJECT(widget), "focus_in_event", - GTK_SIGNAL_FUNC(gtk_window_focus_in_callback), (gpointer)this ); - - gtk_signal_connect( GTK_OBJECT(widget), "focus_out_event", - GTK_SIGNAL_FUNC(gtk_window_focus_out_callback), (gpointer)this ); - gtk_signal_connect( GTK_OBJECT(widget), "enter_notify_event", GTK_SIGNAL_FUNC(gtk_window_enter_callback), (gpointer)this ); @@ -2018,7 +2773,7 @@ void wxWindow::ConnectWidget( GtkWidget *widget ) GTK_SIGNAL_FUNC(gtk_window_leave_callback), (gpointer)this ); } -bool wxWindow::Destroy() +bool wxWindowGTK::Destroy() { wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") ); @@ -2027,14 +2782,31 @@ bool wxWindow::Destroy() return wxWindowBase::Destroy(); } -void wxWindow::DoSetSize( int x, int y, int width, int height, int sizeFlags ) +void wxWindowGTK::DoMoveWindow(int x, int y, int width, int height) +{ + gtk_pizza_set_size( GTK_PIZZA(m_parent->m_wxwindow), m_widget, x, y, width, height ); +} + +void wxWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags ) { wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") ); - wxASSERT_MSG( (m_parent != NULL), wxT("wxWindow::SetSize requires parent.\n") ); + wxASSERT_MSG( (m_parent != NULL), wxT("wxWindowGTK::SetSize requires parent.\n") ); + +/* + printf( "DoSetSize: name %s, x,y,w,h: %d,%d,%d,%d \n", GetName().c_str(), x,y,width,height ); +*/ if (m_resizing) return; /* I don't like recursions */ m_resizing = TRUE; + int currentX, currentY; + GetPosition(¤tX, ¤tY); + if (x == -1) + x = currentX; + if (y == -1) + y = currentY; + AdjustForParentClientOrigin(x, y, sizeFlags); + if (m_parent->m_wxwindow == NULL) /* i.e. wxNotebook */ { /* don't set the size for children of wxNotebook, just take the values. */ @@ -2046,7 +2818,6 @@ void wxWindow::DoSetSize( int x, int y, int width, int height, int sizeFlags ) else { GtkPizza *pizza = GTK_PIZZA(m_parent->m_wxwindow); - if ((sizeFlags & wxSIZE_ALLOW_MINUS_ONE) == 0) { if (x != -1) m_x = x + pizza->xoffset; @@ -2072,27 +2843,43 @@ void wxWindow::DoSetSize( int x, int y, int width, int height, int sizeFlags ) if (height == -1) m_height = 26; } - if ((m_minWidth != -1) && (m_width < m_minWidth)) m_width = m_minWidth; - if ((m_minHeight != -1) && (m_height < m_minHeight)) m_height = m_minHeight; - if ((m_maxWidth != -1) && (m_width > m_maxWidth)) m_width = m_maxWidth; - if ((m_maxHeight != -1) && (m_height > m_maxHeight)) m_height = m_maxHeight; + int minWidth = GetMinWidth(), + minHeight = GetMinHeight(), + maxWidth = GetMaxWidth(), + maxHeight = GetMaxHeight(); + + if ((minWidth != -1) && (m_width < minWidth)) m_width = minWidth; + if ((minHeight != -1) && (m_height < minHeight)) m_height = minHeight; + if ((maxWidth != -1) && (m_width > maxWidth)) m_width = maxWidth; + if ((maxHeight != -1) && (m_height > maxHeight)) m_height = maxHeight; int border = 0; int bottom_border = 0; +#ifndef __WXGTK20__ if (GTK_WIDGET_CAN_DEFAULT(m_widget)) { /* the default button has a border around it */ border = 6; bottom_border = 5; } +#endif + + DoMoveWindow( m_x-border, + m_y-border, + m_width+2*border, + m_height+border+bottom_border ); + } - gtk_pizza_set_size( GTK_PIZZA(m_parent->m_wxwindow), - m_widget, - m_x-border, - m_y-border, - m_width+2*border, - m_height+border+bottom_border ); + if (m_hasScrolling) + { + /* Sometimes the client area changes size without the + whole windows's size changing, but if the whole + windows's size doesn't change, no wxSizeEvent will + normally be sent. Here we add an extra test if + the client test has been changed and this will + be used then. */ + GetClientSize( &m_oldClientWidth, &m_oldClientHeight ); } /* @@ -2112,8 +2899,12 @@ void wxWindow::DoSetSize( int x, int y, int width, int height, int sizeFlags ) m_resizing = FALSE; } -void wxWindow::OnInternalIdle() +void wxWindowGTK::OnInternalIdle() { + // Update invalidated regions. + Update(); + + // Synthetize activate events. if ( g_sendActivateEvent != -1 ) { bool activate = g_sendActivateEvent != 0; @@ -2121,10 +2912,29 @@ void wxWindow::OnInternalIdle() // do it only once g_sendActivateEvent = -1; - wxActivateEvent event(wxEVT_ACTIVATE, activate, GetId()); - event.SetEventObject(this); + wxTheApp->SetActive(activate, (wxWindow *)g_focusWindowLast); + } - (void)GetEventHandler()->ProcessEvent(event); + 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; + } + + if (g_delayedFocus == this) + { + if (GTK_WIDGET_REALIZED(m_widget)) + { + gtk_widget_grab_focus( m_widget ); + g_delayedFocus = NULL; + } } wxCursor cursor = m_cursor; @@ -2164,7 +2974,7 @@ void wxWindow::OnInternalIdle() UpdateWindowUI(); } -void wxWindow::DoGetSize( int *width, int *height ) const +void wxWindowGTK::DoGetSize( int *width, int *height ) const { wxCHECK_RET( (m_widget != NULL), wxT("invalid window") ); @@ -2172,7 +2982,7 @@ void wxWindow::DoGetSize( int *width, int *height ) const if (height) (*height) = m_height; } -void wxWindow::DoSetClientSize( int width, int height ) +void wxWindowGTK::DoSetClientSize( int width, int height ) { wxCHECK_RET( (m_widget != NULL), wxT("invalid window") ); @@ -2185,24 +2995,7 @@ void wxWindow::DoSetClientSize( int width, int height ) int dw = 0; int dh = 0; -#if (GTK_MINOR_VERSION == 0) - if (HasFlag(wxRAISED_BORDER) || HasFlag(wxSUNKEN_BORDER)) - { - if (HasScrolling()) - { - GtkScrolledWindow *scroll_window = GTK_SCROLLED_WINDOW(m_widget); -#if 0 // unused - if this is ok, just remove this line (VZ) - GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT(m_widget)->klass ); -#endif // 0 - - GtkWidget *viewport = scroll_window->viewport; - GtkStyleClass *viewport_class = viewport->style->klass; - - dw += 2 * viewport_class->xthickness; - dh += 2 * viewport_class->ythickness; - } - } -#else +#ifndef __WXUNIVERSAL__ if (HasFlag(wxRAISED_BORDER) || HasFlag(wxSUNKEN_BORDER)) { /* when using GTK 1.2 we set the shadow border size to 2 */ @@ -2215,38 +3008,44 @@ void wxWindow::DoSetClientSize( int width, int height ) dw += 1 * 2; dh += 1 * 2; } -#endif +#endif // __WXUNIVERSAL__ - if (HasScrolling()) + if (m_hasScrolling) { -/* - GtkWidget *hscrollbar = scroll_window->hscrollbar; - GtkWidget *vscrollbar = scroll_window->vscrollbar; + GtkScrolledWindow *scroll_window = GTK_SCROLLED_WINDOW(m_widget); - we use this instead: range.slider_width = 11 + 2*2pts edge -*/ + GtkRequisition vscroll_req; + vscroll_req.width = 2; + vscroll_req.height = 2; + (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->vscrollbar) )->size_request ) + (scroll_window->vscrollbar, &vscroll_req ); - GtkScrolledWindow *scroll_window = GTK_SCROLLED_WINDOW(m_widget); - GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT(m_widget)->klass ); + GtkRequisition hscroll_req; + hscroll_req.width = 2; + hscroll_req.height = 2; + (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->hscrollbar) )->size_request ) + (scroll_window->hscrollbar, &hscroll_req ); + + GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget) ); if (scroll_window->vscrollbar_visible) { - dw += 15; /* dw += vscrollbar->allocation.width; */ + dw += vscroll_req.width; dw += scroll_class->scrollbar_spacing; } if (scroll_window->hscrollbar_visible) { - dh += 15; /* dh += hscrollbar->allocation.height; */ + dh += hscroll_req.height; dh += scroll_class->scrollbar_spacing; } - } + } SetSize( width+dw, height+dh ); } } -void wxWindow::DoGetClientSize( int *width, int *height ) const +void wxWindowGTK::DoGetClientSize( int *width, int *height ) const { wxCHECK_RET( (m_widget != NULL), wxT("invalid window") ); @@ -2260,24 +3059,7 @@ void wxWindow::DoGetClientSize( int *width, int *height ) const int dw = 0; int dh = 0; -#if (GTK_MINOR_VERSION == 0) - if (HasFlag(wxRAISED_BORDER) || HasFlag(wxSUNKEN_BORDER)) - { - if (HasScrolling()) - { - GtkScrolledWindow *scroll_window = GTK_SCROLLED_WINDOW(m_widget); -#if 0 // unused - if this is ok, just remove this line (VZ) - GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT(m_widget)->klass ); -#endif // 0 - - GtkWidget *viewport = scroll_window->viewport; - GtkStyleClass *viewport_class = viewport->style->klass; - - dw += 2 * viewport_class->xthickness; - dh += 2 * viewport_class->ythickness; - } - } -#else +#ifndef __WXUNIVERSAL__ if (HasFlag(wxRAISED_BORDER) || HasFlag(wxSUNKEN_BORDER)) { /* when using GTK 1.2 we set the shadow border size to 2 */ @@ -2290,28 +3072,35 @@ void wxWindow::DoGetClientSize( int *width, int *height ) const dw += 1 * 2; dh += 1 * 2; } -#endif - if (HasScrolling()) - { -/* - GtkWidget *hscrollbar = scroll_window->hscrollbar; - GtkWidget *vscrollbar = scroll_window->vscrollbar; - - we use this instead: range.slider_width = 11 + 2*2pts edge -*/ +#endif // __WXUNIVERSAL__ + if (m_hasScrolling) + { GtkScrolledWindow *scroll_window = GTK_SCROLLED_WINDOW(m_widget); - GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT(m_widget)->klass ); + + GtkRequisition vscroll_req; + vscroll_req.width = 2; + vscroll_req.height = 2; + (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->vscrollbar) )->size_request ) + (scroll_window->vscrollbar, &vscroll_req ); + + GtkRequisition hscroll_req; + hscroll_req.width = 2; + hscroll_req.height = 2; + (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->hscrollbar) )->size_request ) + (scroll_window->hscrollbar, &hscroll_req ); + + GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget) ); if (scroll_window->vscrollbar_visible) { - dw += 15; /* dw += vscrollbar->allocation.width; */ + dw += vscroll_req.width; dw += scroll_class->scrollbar_spacing; } if (scroll_window->hscrollbar_visible) { - dh += 15; /* dh += hscrollbar->allocation.height; */ + dh += hscroll_req.height; dh += scroll_class->scrollbar_spacing; } } @@ -2319,9 +3108,16 @@ void wxWindow::DoGetClientSize( int *width, int *height ) const if (width) (*width) = m_width - dw; if (height) (*height) = m_height - dh; } + +/* + printf( "GetClientSize, name %s ", GetName().c_str() ); + if (width) printf( " width = %d", (*width) ); + if (height) printf( " height = %d", (*height) ); + printf( "\n" ); +*/ } -void wxWindow::DoGetPosition( int *x, int *y ) const +void wxWindowGTK::DoGetPosition( int *x, int *y ) const { wxCHECK_RET( (m_widget != NULL), wxT("invalid window") ); @@ -2338,7 +3134,7 @@ void wxWindow::DoGetPosition( int *x, int *y ) const if (y) (*y) = m_y - dy; } -void wxWindow::DoClientToScreen( int *x, int *y ) const +void wxWindowGTK::DoClientToScreen( int *x, int *y ) const { wxCHECK_RET( (m_widget != NULL), wxT("invalid window") ); @@ -2367,7 +3163,7 @@ void wxWindow::DoClientToScreen( int *x, int *y ) const if (y) *y += org_y; } -void wxWindow::DoScreenToClient( int *x, int *y ) const +void wxWindowGTK::DoScreenToClient( int *x, int *y ) const { wxCHECK_RET( (m_widget != NULL), wxT("invalid window") ); @@ -2396,7 +3192,7 @@ void wxWindow::DoScreenToClient( int *x, int *y ) const if (y) *y -= org_y; } -bool wxWindow::Show( bool show ) +bool wxWindowGTK::Show( bool show ) { wxCHECK_MSG( (m_widget != NULL), FALSE, wxT("invalid window") ); @@ -2414,7 +3210,24 @@ bool wxWindow::Show( bool show ) return TRUE; } -bool wxWindow::Enable( bool enable ) +static void wxWindowNotifyEnable(wxWindowGTK* win, bool enable) +{ + win->OnParentEnable(enable); + + // Recurse, so that children have the opportunity to Do The Right Thing + // and reset colours that have been messed up by a parent's (really ancestor's) + // Enable call + for ( wxWindowList::Node *node = win->GetChildren().GetFirst(); + node; + node = node->GetNext() ) + { + wxWindow *child = node->GetData(); + if (!child->IsKindOf(CLASSINFO(wxDialog)) && !child->IsKindOf(CLASSINFO(wxFrame))) + wxWindowNotifyEnable(child, enable); + } +} + +bool wxWindowGTK::Enable( bool enable ) { wxCHECK_MSG( (m_widget != NULL), FALSE, wxT("invalid window") ); @@ -2428,10 +3241,12 @@ bool wxWindow::Enable( bool enable ) if ( m_wxwindow ) gtk_widget_set_sensitive( m_wxwindow, enable ); + wxWindowNotifyEnable(this, enable); + return TRUE; } -int wxWindow::GetCharHeight() const +int wxWindowGTK::GetCharHeight() const { wxCHECK_MSG( (m_widget != NULL), 12, wxT("invalid window") ); @@ -2442,7 +3257,7 @@ int wxWindow::GetCharHeight() const return font->ascent + font->descent; } -int wxWindow::GetCharWidth() const +int wxWindowGTK::GetCharWidth() const { wxCHECK_MSG( (m_widget != NULL), 8, wxT("invalid window") ); @@ -2453,7 +3268,7 @@ int wxWindow::GetCharWidth() const return gdk_string_width( font, "H" ); } -void wxWindow::GetTextExtent( const wxString& string, +void wxWindowGTK::GetTextExtent( const wxString& string, int *x, int *y, int *descent, @@ -2472,26 +3287,29 @@ void wxWindow::GetTextExtent( const wxString& string, if (externalLeading) (*externalLeading) = 0; // ?? } -void wxWindow::SetFocus() +void wxWindowGTK::SetFocus() { wxCHECK_RET( (m_widget != NULL), wxT("invalid window") ); - + if (m_wxwindow) { if (!GTK_WIDGET_HAS_FOCUS (m_wxwindow)) + { gtk_widget_grab_focus (m_wxwindow); - return; + } } - - if (m_widget) + else if (m_widget) { if (GTK_WIDGET_CAN_FOCUS(m_widget) && !GTK_WIDGET_HAS_FOCUS (m_widget) ) { - gtk_widget_grab_focus (m_widget); + if (!GTK_WIDGET_REALIZED(m_widget)) + g_delayedFocus = this; + else + gtk_widget_grab_focus (m_widget); } else if (GTK_IS_CONTAINER(m_widget)) { - gtk_container_focus( GTK_CONTAINER(m_widget), GTK_DIR_TAB_FORWARD ); + SET_CONTAINER_FOCUS( m_widget, GTK_DIR_TAB_FORWARD ); } else { @@ -2500,17 +3318,17 @@ void wxWindow::SetFocus() } } -bool wxWindow::AcceptsFocus() const +bool wxWindowGTK::AcceptsFocus() const { return m_acceptsFocus && wxWindowBase::AcceptsFocus(); } -bool wxWindow::Reparent( wxWindowBase *newParentBase ) +bool wxWindowGTK::Reparent( wxWindowBase *newParentBase ) { wxCHECK_MSG( (m_widget != NULL), FALSE, wxT("invalid window") ); - wxWindow *oldParent = m_parent, - *newParent = (wxWindow *)newParentBase; + wxWindowGTK *oldParent = m_parent, + *newParent = (wxWindowGTK *)newParentBase; wxASSERT( GTK_IS_WIDGET(m_widget) ); @@ -2541,7 +3359,7 @@ bool wxWindow::Reparent( wxWindowBase *newParentBase ) return TRUE; } -void wxWindow::DoAddChild(wxWindow *child) +void wxWindowGTK::DoAddChild(wxWindowGTK *child) { wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") ); @@ -2556,7 +3374,7 @@ void wxWindow::DoAddChild(wxWindow *child) (*m_insertCallback)(this, child); } -void wxWindow::Raise() +void wxWindowGTK::Raise() { wxCHECK_RET( (m_widget != NULL), wxT("invalid window") ); @@ -2565,7 +3383,7 @@ void wxWindow::Raise() gdk_window_raise( m_widget->window ); } -void wxWindow::Lower() +void wxWindowGTK::Lower() { wxCHECK_RET( (m_widget != NULL), wxT("invalid window") ); @@ -2574,19 +3392,28 @@ void wxWindow::Lower() gdk_window_lower( m_widget->window ); } -bool wxWindow::SetCursor( const wxCursor &cursor ) +bool wxWindowGTK::SetCursor( const wxCursor &cursor ) { wxCHECK_MSG( (m_widget != NULL), FALSE, wxT("invalid window") ); - return wxWindowBase::SetCursor( cursor ); + if (cursor == m_cursor) + return FALSE; + + if (g_isIdle) + wxapp_install_idle_handler(); + + if (cursor == wxNullCursor) + return wxWindowBase::SetCursor( *wxSTANDARD_CURSOR ); + else + return wxWindowBase::SetCursor( cursor ); } -void wxWindow::WarpPointer( int x, int y ) +void wxWindowGTK::WarpPointer( int x, int y ) { wxCHECK_RET( (m_widget != NULL), wxT("invalid window") ); - /* we provide this function ourselves as it is - missing in GDK (top of this file) */ + // We provide this function ourselves as it is + // missing in GDK (top of this file). GdkWindow *window = (GdkWindow*) NULL; if (m_wxwindow) @@ -2598,105 +3425,251 @@ void wxWindow::WarpPointer( int x, int y ) gdk_window_warp_pointer( window, x, y ); } -void wxWindow::Refresh( bool eraseBackground, const wxRect *rect ) +void wxWindowGTK::Refresh( bool eraseBackground, const wxRect *rect ) { - wxCHECK_RET( (m_widget != NULL), wxT("invalid window") ); - + if (!m_widget) return; if (!m_widget->window) return; +#ifndef __WXGTK20__ + if (g_isIdle) + wxapp_install_idle_handler(); + if (eraseBackground && m_wxwindow && m_wxwindow->window) { if (rect) { - gdk_window_clear_area( GTK_PIZZA(m_wxwindow)->bin_window, - rect->x, rect->y, - rect->width, rect->height ); + // Schedule for later Updating in ::Update() or ::OnInternalIdle(). + m_clearRegion.Union( rect->x, rect->y, rect->width, rect->height ); } else { - gdk_window_clear( GTK_PIZZA(m_wxwindow)->bin_window ); + // Schedule for later Updating in ::Update() or ::OnInternalIdle(). + m_clearRegion.Clear(); + m_clearRegion.Union( 0, 0, m_wxwindow->allocation.width, m_wxwindow->allocation.height ); } } - /* there is no GTK equivalent of "draw only, don't clear" so we - invent our own in the GtkPizza widget */ - - if (!rect) + if (rect) { if (m_wxwindow) { - GtkPizza *pizza = GTK_PIZZA(m_wxwindow); - gboolean old_clear = pizza->clear_on_draw; - gtk_pizza_set_clear( pizza, FALSE ); - - gtk_widget_draw( m_wxwindow, (GdkRectangle*) NULL ); - - gtk_pizza_set_clear( pizza, old_clear ); + // Schedule for later Updating in ::Update() or ::OnInternalIdle(). + m_updateRegion.Union( rect->x, rect->y, rect->width, rect->height ); } else - gtk_widget_draw( m_widget, (GdkRectangle*) NULL ); + { + GdkRectangle gdk_rect; + gdk_rect.x = rect->x; + gdk_rect.y = rect->y; + gdk_rect.width = rect->width; + gdk_rect.height = rect->height; + gtk_widget_draw( m_widget, &gdk_rect ); + } } else { - GdkRectangle gdk_rect; - gdk_rect.x = rect->x; - gdk_rect.y = rect->y; - gdk_rect.width = rect->width; - gdk_rect.height = rect->height; - if (m_wxwindow) { - GtkPizza *pizza = GTK_PIZZA(m_wxwindow); - gboolean old_clear = pizza->clear_on_draw; - gtk_pizza_set_clear( pizza, FALSE ); + // Schedule for later Updating in ::Update() or ::OnInternalIdle(). + m_updateRegion.Clear(); + m_updateRegion.Union( 0, 0, m_wxwindow->allocation.width, m_wxwindow->allocation.height ); + } + else + { + gtk_widget_draw( m_widget, (GdkRectangle*) NULL ); + } + } +#else + if (m_wxwindow) + { + 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 ); + } + else + { + gdk_window_invalidate_rect( GTK_PIZZA(m_wxwindow)->bin_window, NULL, TRUE ); + } + } +#endif +} + +void wxWindowGTK::Update() +{ +#ifdef __WXGTK20__ + if (m_wxwindow && GTK_PIZZA(m_wxwindow)->bin_window) + gdk_window_process_updates( GTK_PIZZA(m_wxwindow)->bin_window, FALSE ); +#endif + + if (!m_updateRegion.IsEmpty()) + GtkSendPaintEvents(); +} + +void wxWindowGTK::GtkSendPaintEvents() +{ + if (!m_wxwindow) + { + m_clearRegion.Clear(); + m_updateRegion.Clear(); + return; + } + + m_clipPaintRegion = TRUE; + + // if (!m_clearRegion.IsEmpty()) // always send an erase event + { + wxWindowDC dc( (wxWindow*)this ); + dc.SetClippingRegion( m_clearRegion ); + + wxEraseEvent erase_event( GetId(), &dc ); + erase_event.SetEventObject( this ); + + if (!GetEventHandler()->ProcessEvent(erase_event)) + { + if (!g_eraseGC) + { + g_eraseGC = gdk_gc_new( GTK_PIZZA(m_wxwindow)->bin_window ); + gdk_gc_set_fill( g_eraseGC, GDK_SOLID ); + } + gdk_gc_set_foreground( g_eraseGC, m_backgroundColour.GetColor() ); + + wxRegionIterator upd( m_clearRegion ); + while (upd) + { + gdk_draw_rectangle( GTK_PIZZA(m_wxwindow)->bin_window, g_eraseGC, 1, + upd.GetX(), upd.GetY(), upd.GetWidth(), upd.GetHeight() ); + upd ++; + } + } + m_clearRegion.Clear(); + } + + wxNcPaintEvent nc_paint_event( GetId() ); + nc_paint_event.SetEventObject( this ); + GetEventHandler()->ProcessEvent( nc_paint_event ); + + wxPaintEvent paint_event( GetId() ); + paint_event.SetEventObject( this ); + GetEventHandler()->ProcessEvent( paint_event ); + + m_clipPaintRegion = FALSE; + +#ifndef __WXUNIVERSAL__ +#ifndef __WXGTK20__ + // The following code will result in all window-less widgets + // being redrawn because the wxWindows class is allowed to + // paint over the window-less widgets. + + GtkPizza *pizza = GTK_PIZZA(m_wxwindow); + + GList *children = pizza->children; + while (children) + { + GtkPizzaChild *child = (GtkPizzaChild*) children->data; + children = children->next; + + if (GTK_WIDGET_NO_WINDOW (child->widget) && + GTK_WIDGET_DRAWABLE (child->widget)) + { + // Get intersection of widget area and update region + wxRegion region( m_updateRegion ); + + GdkEventExpose gdk_event; + gdk_event.type = GDK_EXPOSE; + gdk_event.window = pizza->bin_window; + gdk_event.count = 0; + + wxRegionIterator upd( m_updateRegion ); + while (upd) + { + GdkRectangle rect; + rect.x = upd.GetX(); + rect.y = upd.GetY(); + rect.width = upd.GetWidth(); + rect.height = upd.GetHeight(); - gtk_widget_draw( m_wxwindow, &gdk_rect ); + if (gtk_widget_intersect (child->widget, &rect, &gdk_event.area)) + { + gtk_widget_event (child->widget, (GdkEvent*) &gdk_event); + } - gtk_pizza_set_clear( pizza, old_clear ); + upd ++; + } } - else - gtk_widget_draw( m_widget, &gdk_rect ); } +#endif +#endif + + m_updateRegion.Clear(); } -void wxWindow::Clear() +void wxWindowGTK::Clear() { wxCHECK_RET( m_widget != NULL, wxT("invalid window") ); - if (!m_widget->window) return; - if (m_wxwindow && m_wxwindow->window) { - gdk_window_clear( m_wxwindow->window ); + m_clearRegion.Clear(); + wxSize size( GetClientSize() ); + m_clearRegion.Union( 0,0,size.x,size.y ); + + // Better do this in idle? + Update(); } } #if wxUSE_TOOLTIPS -void wxWindow::DoSetToolTip( wxToolTip *tip ) +void wxWindowGTK::DoSetToolTip( wxToolTip *tip ) { wxWindowBase::DoSetToolTip(tip); if (m_tooltip) - m_tooltip->Apply( this ); + m_tooltip->Apply( (wxWindow *)this ); } -void wxWindow::ApplyToolTip( GtkTooltips *tips, const wxChar *tip ) +void wxWindowGTK::ApplyToolTip( GtkTooltips *tips, const wxChar *tip ) { gtk_tooltips_set_tip( tips, GetConnectWidget(), wxConvCurrent->cWX2MB(tip), (gchar*) NULL ); } #endif // wxUSE_TOOLTIPS -bool wxWindow::SetBackgroundColour( const wxColour &colour ) +void wxWindowGTK::GtkSetBackgroundColour( const wxColour &colour ) { - wxCHECK_MSG( m_widget != NULL, FALSE, wxT("invalid window") ); + GdkWindow *window = (GdkWindow*) NULL; + if (m_wxwindow) + window = GTK_PIZZA(m_wxwindow)->bin_window; + else + window = GetConnectWidget()->window; - if (!wxWindowBase::SetBackgroundColour(colour)) + wxASSERT( window ); + + // We need the pixel value e.g. for background clearing. + m_backgroundColour.CalcPixel( gdk_window_get_colormap( window ) ); + + if ((m_wxwindow) && + (m_backgroundColour != wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE))) { - // don't leave if the GTK widget has just - // been realized - if (!m_delayedBackgroundColour) return FALSE; + /* wxMSW doesn't clear the window here. I don't do that either to + provide compatibility. call Clear() to do the job. */ + + gdk_window_set_background( window, m_backgroundColour.GetColor() ); } + ApplyWidgetStyle(); +} + +bool wxWindowGTK::SetBackgroundColour( const wxColour &colour ) +{ + wxCHECK_MSG( m_widget != NULL, FALSE, wxT("invalid window") ); + + if (!wxWindowBase::SetBackgroundColour(colour)) + return FALSE; + GdkWindow *window = (GdkWindow*) NULL; if (m_wxwindow) window = GTK_PIZZA(m_wxwindow)->bin_window; @@ -2709,36 +3682,30 @@ bool wxWindow::SetBackgroundColour( const wxColour &colour ) // but it couldn't get applied as the // widget hasn't been realized yet. m_delayedBackgroundColour = TRUE; - - // pretend we have done something return TRUE; } - - if (m_wxwindow) - { - /* wxMSW doesn't clear the window here. I don't do that either to - provide compatibility. call Clear() to do the job. */ - - m_backgroundColour.CalcPixel( gdk_window_get_colormap( window ) ); - gdk_window_set_background( window, m_backgroundColour.GetColor() ); - } - - wxColour sysbg = wxSystemSettings::GetSystemColour( wxSYS_COLOUR_BTNFACE ); - if (sysbg == m_backgroundColour) - { - m_backgroundColour = wxNullColour; - ApplyWidgetStyle(); - m_backgroundColour = sysbg; - } else { - ApplyWidgetStyle(); + GtkSetBackgroundColour( colour ); } return TRUE; } -bool wxWindow::SetForegroundColour( const wxColour &colour ) +void wxWindowGTK::GtkSetForegroundColour( const wxColour &colour ) +{ + GdkWindow *window = (GdkWindow*) NULL; + if (m_wxwindow) + window = GTK_PIZZA(m_wxwindow)->bin_window; + else + window = GetConnectWidget()->window; + + wxASSERT( window ); + + ApplyWidgetStyle(); +} + +bool wxWindowGTK::SetForegroundColour( const wxColour &colour ) { wxCHECK_MSG( m_widget != NULL, FALSE, wxT("invalid window") ); @@ -2761,65 +3728,136 @@ bool wxWindow::SetForegroundColour( const wxColour &colour ) // but it couldn't get applied as the // widget hasn't been realized yet. m_delayedForegroundColour = TRUE; - - // pretend we have done something - return TRUE; - } - - wxColour sysbg = wxSystemSettings::GetSystemColour( wxSYS_COLOUR_BTNFACE ); - if ( sysbg == m_backgroundColour ) - { - m_backgroundColour = wxNullColour; - ApplyWidgetStyle(); - m_backgroundColour = sysbg; } else { - ApplyWidgetStyle(); + GtkSetForegroundColour( colour ); } return TRUE; } -GtkStyle *wxWindow::GetWidgetStyle() +GtkStyle *wxWindowGTK::GetWidgetStyle() { - if (m_widgetStyle) gtk_style_unref( m_widgetStyle ); + if (m_widgetStyle) + { + GtkStyle *remake = gtk_style_copy( m_widgetStyle ); + + // FIXME: no more klass in 2.0 +#ifndef __WXGTK20__ + remake->klass = m_widgetStyle->klass; +#endif + + gtk_style_unref( m_widgetStyle ); + m_widgetStyle = remake; + } + else + { + GtkStyle *def = gtk_rc_get_style( m_widget ); + + if (!def) + def = gtk_widget_get_default_style(); - m_widgetStyle = gtk_style_copy( gtk_widget_get_style( m_widget ) ); + m_widgetStyle = gtk_style_copy( def ); + + // FIXME: no more klass in 2.0 +#ifndef __WXGTK20__ + m_widgetStyle->klass = def->klass; +#endif + } return m_widgetStyle; } -void wxWindow::SetWidgetStyle() +void wxWindowGTK::SetWidgetStyle() { +#if DISABLE_STYLE_IF_BROKEN_THEME + if (m_widget->style->engine_data) + { + static bool s_warningPrinted = FALSE; + if (!s_warningPrinted) + { + printf( "wxWindows warning: Widget styles disabled due to buggy GTK theme.\n" ); + s_warningPrinted = TRUE; + } + m_widgetStyle = m_widget->style; + return; + } +#endif + GtkStyle *style = GetWidgetStyle(); - gdk_font_unref( style->font ); - style->font = gdk_font_ref( m_font.GetInternalFont( 1.0 ) ); + if (m_font != wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT )) + { + SET_STYLE_FONT(style, m_font.GetInternalFont( 1.0 )); + } if (m_foregroundColour.Ok()) { m_foregroundColour.CalcPixel( gtk_widget_get_colormap( m_widget ) ); - style->fg[GTK_STATE_NORMAL] = *m_foregroundColour.GetColor(); - style->fg[GTK_STATE_PRELIGHT] = *m_foregroundColour.GetColor(); - style->fg[GTK_STATE_ACTIVE] = *m_foregroundColour.GetColor(); + if (m_foregroundColour != wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT)) + { + style->fg[GTK_STATE_NORMAL] = *m_foregroundColour.GetColor(); + style->fg[GTK_STATE_PRELIGHT] = *m_foregroundColour.GetColor(); + style->fg[GTK_STATE_ACTIVE] = *m_foregroundColour.GetColor(); + } + else + { + // Try to restore the gtk default style. This is still a little + // oversimplified for what is probably really needed here for controls + // other than buttons, but is better than not being able to (re)set a + // control's foreground colour to *wxBLACK -- RL + GtkStyle *def = gtk_rc_get_style( m_widget ); + + if (!def) + def = gtk_widget_get_default_style(); + + style->fg[GTK_STATE_NORMAL] = def->fg[GTK_STATE_NORMAL]; + style->fg[GTK_STATE_PRELIGHT] = def->fg[GTK_STATE_PRELIGHT]; + style->fg[GTK_STATE_ACTIVE] = def->fg[GTK_STATE_ACTIVE]; + } } 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(); - style->bg[GTK_STATE_PRELIGHT] = *m_backgroundColour.GetColor(); - style->base[GTK_STATE_PRELIGHT] = *m_backgroundColour.GetColor(); - style->bg[GTK_STATE_ACTIVE] = *m_backgroundColour.GetColor(); - style->base[GTK_STATE_ACTIVE] = *m_backgroundColour.GetColor(); - style->bg[GTK_STATE_INSENSITIVE] = *m_backgroundColour.GetColor(); - style->base[GTK_STATE_INSENSITIVE] = *m_backgroundColour.GetColor(); + if (m_backgroundColour != wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)) + { + style->bg[GTK_STATE_NORMAL] = *m_backgroundColour.GetColor(); + style->base[GTK_STATE_NORMAL] = *m_backgroundColour.GetColor(); + style->bg[GTK_STATE_PRELIGHT] = *m_backgroundColour.GetColor(); + style->base[GTK_STATE_PRELIGHT] = *m_backgroundColour.GetColor(); + style->bg[GTK_STATE_ACTIVE] = *m_backgroundColour.GetColor(); + style->base[GTK_STATE_ACTIVE] = *m_backgroundColour.GetColor(); + style->bg[GTK_STATE_INSENSITIVE] = *m_backgroundColour.GetColor(); + style->base[GTK_STATE_INSENSITIVE] = *m_backgroundColour.GetColor(); + } + else + { + // Try to restore the gtk default style. This is still a little + // oversimplified for what is probably really needed here for controls + // other than buttons, but is better than not being able to (re)set a + // control's background colour to default grey and means resetting a + // button to wxSYS_COLOUR_BTNFACE will restore its usual highlighting + // behavior -- RL + GtkStyle *def = gtk_rc_get_style( m_widget ); + + if (!def) + def = gtk_widget_get_default_style(); + + style->bg[GTK_STATE_NORMAL] = def->bg[GTK_STATE_NORMAL]; + style->base[GTK_STATE_NORMAL] = def->base[GTK_STATE_NORMAL]; + style->bg[GTK_STATE_PRELIGHT] = def->bg[GTK_STATE_PRELIGHT]; + style->base[GTK_STATE_PRELIGHT] = def->base[GTK_STATE_PRELIGHT]; + style->bg[GTK_STATE_ACTIVE] = def->bg[GTK_STATE_ACTIVE]; + style->base[GTK_STATE_ACTIVE] = def->base[GTK_STATE_ACTIVE]; + style->bg[GTK_STATE_INSENSITIVE] = def->bg[GTK_STATE_INSENSITIVE]; + style->base[GTK_STATE_INSENSITIVE] = def->base[GTK_STATE_INSENSITIVE]; + } } } -void wxWindow::ApplyWidgetStyle() +void wxWindowGTK::ApplyWidgetStyle() { } @@ -2827,12 +3865,15 @@ void wxWindow::ApplyWidgetStyle() // Pop-up menu stuff //----------------------------------------------------------------------------- -static void gtk_pop_hide_callback( GtkWidget *WXUNUSED(widget), bool* is_waiting ) +#if wxUSE_MENUS_NATIVE + +extern "C" +void gtk_pop_hide_callback( GtkWidget *WXUNUSED(widget), bool* is_waiting ) { *is_waiting = FALSE; } -static void SetInvokingWindow( wxMenu *menu, wxWindow *win ) +static void SetInvokingWindow( wxMenu *menu, wxWindowGTK *win ) { menu->SetInvokingWindow( win ); wxMenuItemList::Node *node = menu->GetMenuItems().GetFirst(); @@ -2848,19 +3889,35 @@ static void SetInvokingWindow( wxMenu *menu, wxWindow *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, - wxWindow *win ) +extern "C" void wxPopupMenuPositionCallback( GtkMenu *menu, + gint *x, gint *y, +#ifdef __WXGTK20__ + gboolean * WXUNUSED(whatever), +#endif + 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 wxWindow::DoPopupMenu( wxMenu *menu, int x, int y ) +bool wxWindowGTK::DoPopupMenu( wxMenu *menu, int x, int y ) { wxCHECK_MSG( m_widget != NULL, FALSE, wxT("invalid window") ); @@ -2872,20 +3929,23 @@ bool wxWindow::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; - gtk_signal_connect( GTK_OBJECT(menu->m_menu), "hide", - GTK_SIGNAL_FUNC(gtk_pop_hide_callback), (gpointer)&is_waiting ); + gtk_signal_connect( GTK_OBJECT(menu->m_menu), + "hide", + GTK_SIGNAL_FUNC(gtk_pop_hide_callback), + (gpointer)&is_waiting ); 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) @@ -2897,9 +3957,11 @@ bool wxWindow::DoPopupMenu( wxMenu *menu, int x, int y ) return TRUE; } +#endif // wxUSE_MENUS_NATIVE + #if wxUSE_DRAG_AND_DROP -void wxWindow::SetDropTarget( wxDropTarget *dropTarget ) +void wxWindowGTK::SetDropTarget( wxDropTarget *dropTarget ) { wxCHECK_RET( m_widget != NULL, wxT("invalid window") ); @@ -2915,7 +3977,7 @@ void wxWindow::SetDropTarget( wxDropTarget *dropTarget ) #endif // wxUSE_DRAG_AND_DROP -GtkWidget* wxWindow::GetConnectWidget() +GtkWidget* wxWindowGTK::GetConnectWidget() { GtkWidget *connect_widget = m_widget; if (m_wxwindow) connect_widget = m_wxwindow; @@ -2923,7 +3985,7 @@ GtkWidget* wxWindow::GetConnectWidget() return connect_widget; } -bool wxWindow::IsOwnGtkWindow( GdkWindow *window ) +bool wxWindowGTK::IsOwnGtkWindow( GdkWindow *window ) { if (m_wxwindow) return (window == GTK_PIZZA(m_wxwindow)->bin_window); @@ -2931,7 +3993,7 @@ bool wxWindow::IsOwnGtkWindow( GdkWindow *window ) return (window == m_widget->window); } -bool wxWindow::SetFont( const wxFont &font ) +bool wxWindowGTK::SetFont( const wxFont &font ) { wxCHECK_MSG( m_widget != NULL, FALSE, wxT("invalid window") ); @@ -2940,7 +4002,7 @@ bool wxWindow::SetFont( const wxFont &font ) return FALSE; } - wxColour sysbg = wxSystemSettings::GetSystemColour( wxSYS_COLOUR_BTNFACE ); + wxColour sysbg = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ); if ( sysbg == m_backgroundColour ) { m_backgroundColour = wxNullColour; @@ -2955,19 +4017,21 @@ bool wxWindow::SetFont( const wxFont &font ) return TRUE; } -void wxWindow::CaptureMouse() +void wxWindowGTK::DoCaptureMouse() { wxCHECK_RET( m_widget != NULL, wxT("invalid window") ); - wxCHECK_RET( g_captureWindow == NULL, wxT("CaptureMouse called twice") ); - GdkWindow *window = (GdkWindow*) NULL; if (m_wxwindow) window = GTK_PIZZA(m_wxwindow)->bin_window; else window = GetConnectWidget()->window; - if (!window) return; + wxCHECK_RET( window, _T("CaptureMouse() failed") ); + + wxCursor* cursor = & m_cursor; + if (!cursor->Ok()) + cursor = wxSTANDARD_CURSOR; gdk_pointer_grab( window, FALSE, (GdkEventMask) @@ -2976,16 +4040,19 @@ void wxWindow::CaptureMouse() GDK_POINTER_MOTION_HINT_MASK | GDK_POINTER_MOTION_MASK), (GdkWindow *) NULL, - m_cursor.GetCursor(), + cursor->GetCursor(), (guint32)GDK_CURRENT_TIME ); g_captureWindow = this; + g_captureWindowHasMouse = TRUE; } -void wxWindow::ReleaseMouse() +void wxWindowGTK::DoReleaseMouse() { wxCHECK_RET( m_widget != NULL, wxT("invalid window") ); - wxCHECK_RET( g_captureWindow, wxT("ReleaseMouse called twice") ); + wxCHECK_RET( g_captureWindow, wxT("can't release mouse - not captured") ); + + g_captureWindow = (wxWindowGTK*) NULL; GdkWindow *window = (GdkWindow*) NULL; if (m_wxwindow) @@ -2997,15 +4064,20 @@ void wxWindow::ReleaseMouse() return; gdk_pointer_ungrab ( (guint32)GDK_CURRENT_TIME ); - g_captureWindow = (wxWindow*) NULL; } -bool wxWindow::IsRetained() const +/* static */ +wxWindow *wxWindowBase::GetCapture() +{ + return (wxWindow *)g_captureWindow; +} + +bool wxWindowGTK::IsRetained() const { return FALSE; } -void wxWindow::SetScrollbar( int orient, int pos, int thumbVisible, +void wxWindowGTK::SetScrollbar( int orient, int pos, int thumbVisible, int range, bool refresh ) { wxCHECK_RET( m_widget != NULL, wxT("invalid window") ); @@ -3069,7 +4141,7 @@ void wxWindow::SetScrollbar( int orient, int pos, int thumbVisible, gtk_signal_emit_by_name( GTK_OBJECT(m_vAdjust), "changed" ); } -void wxWindow::SetScrollPos( int orient, int pos, bool WXUNUSED(refresh) ) +void wxWindowGTK::SetScrollPos( int orient, int pos, bool WXUNUSED(refresh) ) { wxCHECK_RET( m_widget != NULL, wxT("invalid window") ); @@ -3096,23 +4168,32 @@ void wxWindow::SetScrollPos( int orient, int pos, bool WXUNUSED(refresh) ) m_vAdjust->value = fpos; } -/* - if (!m_isScrolling) + if (m_wxwindow->window) { -*/ - if (m_wxwindow->window) + if (orient == wxHORIZONTAL) { - if (orient == wxHORIZONTAL) - gtk_signal_emit_by_name( GTK_OBJECT(m_hAdjust), "value_changed" ); - else - gtk_signal_emit_by_name( GTK_OBJECT(m_vAdjust), "value_changed" ); + gtk_signal_disconnect_by_func( GTK_OBJECT(m_hAdjust), + (GtkSignalFunc) gtk_window_hscroll_callback, (gpointer) this ); + + gtk_signal_emit_by_name( GTK_OBJECT(m_hAdjust), "value_changed" ); + + gtk_signal_connect( GTK_OBJECT(m_hAdjust), "value_changed", + (GtkSignalFunc) gtk_window_hscroll_callback, (gpointer) this ); + } + else + { + gtk_signal_disconnect_by_func( GTK_OBJECT(m_vAdjust), + (GtkSignalFunc) gtk_window_vscroll_callback, (gpointer) this ); + + gtk_signal_emit_by_name( GTK_OBJECT(m_vAdjust), "value_changed" ); + + gtk_signal_connect( GTK_OBJECT(m_vAdjust), "value_changed", + (GtkSignalFunc) gtk_window_vscroll_callback, (gpointer) this ); } -/* } -*/ } -int wxWindow::GetScrollThumb( int orient ) const +int wxWindowGTK::GetScrollThumb( int orient ) const { wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid window") ); @@ -3124,7 +4205,7 @@ int wxWindow::GetScrollThumb( int orient ) const return (int)(m_vAdjust->page_size+0.5); } -int wxWindow::GetScrollPos( int orient ) const +int wxWindowGTK::GetScrollPos( int orient ) const { wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid window") ); @@ -3136,7 +4217,7 @@ int wxWindow::GetScrollPos( int orient ) const return (int)(m_vAdjust->value+0.5); } -int wxWindow::GetScrollRange( int orient ) const +int wxWindowGTK::GetScrollRange( int orient ) const { wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid window") ); @@ -3148,68 +4229,121 @@ int wxWindow::GetScrollRange( int orient ) const return (int)(m_vAdjust->upper+0.5); } -void wxWindow::ScrollWindow( int dx, int dy, const wxRect* WXUNUSED(rect) ) +void wxWindowGTK::ScrollWindow( int dx, int dy, const wxRect* WXUNUSED(rect) ) { wxCHECK_RET( m_widget != NULL, wxT("invalid window") ); wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") ); - gtk_pizza_scroll( GTK_PIZZA(m_wxwindow), -dx, -dy ); + // No scrolling requested. + if ((dx == 0) && (dy == 0)) return; -/* - if (!m_scrollGC) +#ifndef __WXGTK20__ + if (!m_updateRegion.IsEmpty()) { - m_scrollGC = gdk_gc_new( m_wxwindow->window ); - gdk_gc_set_exposures( m_scrollGC, TRUE ); + m_updateRegion.Offset( dx, dy ); + + int cw = 0; + int ch = 0; + GetClientSize( &cw, &ch ); + m_updateRegion.Intersect( 0, 0, cw, ch ); } - wxNode *node = m_children.First(); - while (node) + if (!m_clearRegion.IsEmpty()) { - wxWindow *child = (wxWindow*) node->Data(); - int sx = 0; - int sy = 0; - child->GetSize( &sx, &sy ); - child->SetSize( child->m_x + dx, child->m_y + dy, sx, sy, wxSIZE_ALLOW_MINUS_ONE ); - node = node->Next(); + m_clearRegion.Offset( dx, dy ); + + int cw = 0; + int ch = 0; + GetClientSize( &cw, &ch ); + m_clearRegion.Intersect( 0, 0, cw, ch ); } - int cw = 0; - int ch = 0; - GetClientSize( &cw, &ch ); - int w = cw - abs(dx); - int h = ch - abs(dy); + m_clipPaintRegion = TRUE; - if ((h < 0) || (w < 0)) - { - Refresh(); - } - else - { - int s_x = 0; - int s_y = 0; - if (dx < 0) s_x = -dx; - if (dy < 0) s_y = -dy; - int d_x = 0; - int d_y = 0; - if (dx > 0) d_x = dx; - if (dy > 0) d_y = dy; + gtk_pizza_scroll( GTK_PIZZA(m_wxwindow), -dx, -dy ); - gdk_window_copy_area( m_wxwindow->window, m_scrollGC, d_x, d_y, - m_wxwindow->window, s_x, s_y, w, h ); + m_clipPaintRegion = FALSE; +#else - wxRect rect; - if (dx < 0) rect.x = cw+dx; else rect.x = 0; - if (dy < 0) rect.y = ch+dy; else rect.y = 0; - if (dy != 0) rect.width = cw; else rect.width = abs(dx); - if (dx != 0) rect.height = ch; else rect.height = abs(dy); + gdk_window_scroll( GTK_PIZZA(m_wxwindow)->bin_window, dx, dy ); - Refresh( TRUE, &rect ); - } -*/ + GTK_PIZZA(m_wxwindow)->xoffset += dx; + GTK_PIZZA(m_wxwindow)->yoffset += dy; + +#endif + +} + + +// Find the wxWindow at the current mouse position, also returning the mouse +// position. +wxWindow* wxFindWindowAtPointer(wxPoint& pt) +{ + pt = wxGetMousePosition(); + wxWindow* found = wxFindWindowAtPoint(pt); + return found; +} + +// Get the current mouse position. +wxPoint wxGetMousePosition() +{ + /* This crashes when used within wxHelpContext, + so we have to use the X-specific implementation below. + gint x, y; + GdkModifierType *mask; + (void) gdk_window_get_pointer(NULL, &x, &y, mask); + + return wxPoint(x, y); + */ + + int x, y; + GdkWindow* windowAtPtr = gdk_window_at_pointer(& x, & y); + if (!windowAtPtr) + return wxPoint(-999, -999); + + Display *display = GDK_WINDOW_XDISPLAY(windowAtPtr); + Window rootWindow = RootWindowOfScreen (DefaultScreenOfDisplay(display)); + Window rootReturn, childReturn; + int rootX, rootY, winX, winY; + unsigned int maskReturn; + + XQueryPointer (display, + rootWindow, + &rootReturn, + &childReturn, + &rootX, &rootY, &winX, &winY, &maskReturn); + return wxPoint(rootX, rootY); + +} + +// ---------------------------------------------------------------------------- +// wxDCModule +// ---------------------------------------------------------------------------- + +class wxWinModule : public wxModule +{ +public: + bool OnInit(); + void OnExit(); + +private: + DECLARE_DYNAMIC_CLASS(wxWinModule) +}; + +IMPLEMENT_DYNAMIC_CLASS(wxWinModule, wxModule) + +bool wxWinModule::OnInit() +{ + // g_eraseGC = gdk_gc_new( GDK_ROOT_PARENT() ); + // gdk_gc_set_fill( g_eraseGC, GDK_SOLID ); + + return TRUE; } -void wxWindow::SetScrolling(bool scroll) +void wxWinModule::OnExit() { - m_isScrolling = g_blockEventsOnScroll = scroll; + if (g_eraseGC) + gdk_gc_unref( g_eraseGC ); } +