X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/1ec3a9848a6843aca1b3590f636a149dd274efdc..c91a2f7e67fae4278e2f931209aa386b5d67ae9d:/src/gtk/window.cpp diff --git a/src/gtk/window.cpp b/src/gtk/window.cpp index 5835152aa8..af1bddc332 100644 --- a/src/gtk/window.cpp +++ b/src/gtk/window.cpp @@ -48,12 +48,14 @@ #include "wx/intl.h" #include "wx/settings.h" #include "wx/log.h" +#include "wx/fontutil.h" #ifdef __WXDEBUG__ #include "wx/thread.h" #endif #include +#include #include "wx/gtk/private.h" #include @@ -65,6 +67,10 @@ #include "wx/gtk/win_gtk.h" +#ifdef __WXGTK20__ +#include +#endif + #ifdef __WXGTK20__ #define SET_CONTAINER_FOCUS(w, d) gtk_widget_child_focus((w), (d)) #else @@ -260,7 +266,7 @@ extern bool g_mainThreadLocked; //----------------------------------------------------------------------------- #ifndef __WXGTK20__ -#define DISABLE_STYLE_IF_BROKEN_THEME 1 +#define DISABLE_STYLE_IF_BROKEN_THEME 0 #endif #ifdef __WXDEBUG__ @@ -495,6 +501,14 @@ static int gtk_window_expose_callback( GtkWidget *widget, if (g_isIdle) wxapp_install_idle_handler(); +#ifdef __WXGTK20__ + // This callback gets called in drawing-idle time under + // GTK 2.0, so we don't need to defer anything to idle + // time anymore. + + GtkPizza *pizza = GTK_PIZZA( widget ); + if (gdk_event->window != pizza->bin_window) return FALSE; + #if 0 if (win->GetName()) { @@ -508,6 +522,18 @@ static int gtk_window_expose_callback( GtkWidget *widget, } #endif + win->GetUpdateRegion() = wxRegion( gdk_event->region ); + + win->GtkSendPaintEvents(); + + + // Let parent window draw window less widgets + (* GTK_WIDGET_CLASS (pizza_parent_class)->expose_event) (widget, gdk_event); +#else + // This gets called immediately after an expose event + // under GTK 1.2 so we collect the calls and wait for + // the idle handler to pick things up. + win->GetUpdateRegion().Union( gdk_event->area.x, gdk_event->area.y, gdk_event->area.width, @@ -518,15 +544,10 @@ static int gtk_window_expose_callback( GtkWidget *widget, gdk_event->area.height ); // Actual redrawing takes place in idle time. - win->GtkUpdate(); - -#ifdef __WXGTK20__ - - (* GTK_WIDGET_CLASS (pizza_parent_class)->expose_event) (widget, gdk_event); - + // win->GtkUpdate(); #endif - return TRUE; + return FALSE; } //----------------------------------------------------------------------------- @@ -611,20 +632,12 @@ static void gtk_window_draw_callback( GtkWidget *widget, (char *)"base", 0, 0, -1, -1); } - - - 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->m_clearRegion.Union( rect->x, rect->y, rect->width, rect->height ); win->GetUpdateRegion().Union( rect->x, rect->y, rect->width, rect->height ); - // Actual redrawing takes place in idle time. - + // Update immediately, not in idle time. win->GtkUpdate(); #ifndef __WXUNIVERSAL__ @@ -953,7 +966,7 @@ wxTranslateGTKKeyEventToWx(wxKeyEvent& event, KeySym keysym = gdk_event->keyval; - wxLogTrace(TRACE_KEYS, _T("Key %s event: keysym = %d"), + wxLogTrace(TRACE_KEYS, _T("Key %s event: keysym = %ld"), event.GetEventType() == wxEVT_KEY_UP ? _T("release") : _T("press"), keysym); @@ -1023,7 +1036,7 @@ wxTranslateGTKKeyEventToWx(wxKeyEvent& event, } } - wxLogTrace(TRACE_KEYS, _T("\t-> wxKeyCode %d"), key_code); + wxLogTrace(TRACE_KEYS, _T("\t-> wxKeyCode %ld"), key_code); // sending unknown key events doesn't really make sense if ( !key_code ) @@ -1052,6 +1065,7 @@ wxTranslateGTKKeyEventToWx(wxKeyEvent& event, return TRUE; } + static gint gtk_window_key_press_callback( GtkWidget *widget, GdkEventKey *gdk_event, wxWindow *win ) @@ -1101,42 +1115,58 @@ static gint gtk_window_key_press_callback( GtkWidget *widget, // will only be sent if it is not in an accelerator table. if (!ret) { - // Find key code for EVT_CHAR and EVT_CHAR_HOOK events + long key_code; KeySym keysym = gdk_event->keyval; - long key_code = wxTranslateKeySymToWXKey(keysym, TRUE /* isChar */); - if ( !key_code ) +#ifdef __WXGTK20__ + // In GTK 2.0, we need to hand over the key event to an input method + // and the IM will emit a "commit" event containing the actual utf8 + // character. In that case the EVT_CHAR events will be sent from + // there. But only do it this way for non-KeySym keys. + key_code = wxTranslateKeySymToWXKey(gdk_event->keyval, FALSE /* isChar */); + if ( !key_code && win->m_imContext ) { - if ( gdk_event->length == 1 ) - { - key_code = (unsigned char)gdk_event->string[0]; - } - else if ( wxIsAsciiKeysym(keysym) ) - { - // ASCII key - key_code = (unsigned char)keysym; - } + gtk_im_context_filter_keypress ( (GtkIMContext*) win->m_imContext, gdk_event ); + ret = TRUE; } - - if ( key_code ) + else +#endif { - wxLogTrace(TRACE_KEYS, _T("Char event: %ld"), key_code); - - event.m_keyCode = key_code; - - // Implement OnCharHook by checking ancesteror top level windows - wxWindow *parent = win; - while (parent && !parent->IsTopLevel()) - parent = parent->GetParent(); - if (parent) + // Find key code for EVT_CHAR and EVT_CHAR_HOOK events + key_code = wxTranslateKeySymToWXKey(keysym, TRUE /* isChar */); + if ( !key_code ) { - event.SetEventType( wxEVT_CHAR_HOOK ); - ret = parent->GetEventHandler()->ProcessEvent( event ); + 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) + if ( key_code ) { - event.SetEventType(wxEVT_CHAR); - ret = win->GetEventHandler()->ProcessEvent( event ); + wxLogTrace(TRACE_KEYS, _T("Char event: %ld"), key_code); + + event.m_keyCode = key_code; + + // Implement OnCharHook by checking ancesteror top level windows + wxWindow *parent = win; + while (parent && !parent->IsTopLevel()) + parent = parent->GetParent(); + if (parent) + { + event.SetEventType( wxEVT_CHAR_HOOK ); + ret = parent->GetEventHandler()->ProcessEvent( event ); + } + + if (!ret) + { + event.SetEventType(wxEVT_CHAR); + ret = win->GetEventHandler()->ProcessEvent( event ); + } } } } @@ -1166,50 +1196,95 @@ static gint gtk_window_key_press_callback( GtkWidget *widget, if ( !ret && (gdk_event->keyval == GDK_Escape) ) { - wxCommandEvent new_event(wxEVT_COMMAND_BUTTON_CLICKED,wxID_CANCEL); - new_event.SetEventObject( win ); - ret = win->GetEventHandler()->ProcessEvent( new_event ); - } - - // Doesn't work. -#if 0 - // Pressing F10 will activate the menu bar of the top frame - if ( (!ret) && - (gdk_event->keyval == GDK_F10) ) - { - wxWindowGTK *ancestor = win; - while (ancestor) + // however only do it if we have a Cancel button in the dialog, + // otherwise the user code may get confused by the events from a + // non-existing button and, worse, a wxButton might get button event + // from another button which is not really expected + wxWindow *winForCancel = win, + *btnCancel = NULL; + while ( winForCancel ) { - if (wxIsKindOf(ancestor,wxFrame)) + btnCancel = winForCancel->FindWindow(wxID_CANCEL); + if ( btnCancel ) { - wxFrame *frame = (wxFrame*) ancestor; - wxMenuBar *menubar = frame->GetMenuBar(); - if (menubar) - { - wxNode *node = menubar->GetMenus().First(); - if (node) - { - wxMenu *firstMenu = (wxMenu*) node->Data(); - gtk_menu_item_select( GTK_MENU_ITEM(firstMenu->m_owner) ); - ret = TRUE; - break; - } - } + // found a cancel button + break; } - ancestor = ancestor->GetParent(); + + if ( winForCancel->IsTopLevel() ) + { + // no need to look further + break; + } + + // maybe our parent has a cancel button? + winForCancel = winForCancel->GetParent(); + } + + if ( btnCancel ) + { + wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, wxID_CANCEL); + event.SetEventObject(btnCancel); + ret = btnCancel->GetEventHandler()->ProcessEvent(event); } } -#endif // 0 if (ret) { gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "key_press_event" ); return TRUE; } - + return FALSE; } +#ifdef __WXGTK20__ +static void gtk_wxwindow_commit_cb (GtkIMContext *context, + const gchar *str, + wxWindow *window) +{ + bool ret = FALSE; + + wxKeyEvent event( wxEVT_KEY_DOWN ); + +#if wxUSE_UNICODE + event.m_uniChar = g_utf8_get_char( str ); + + // Backward compatible for ISO-8859 + if (event.m_uniChar < 256) + event.m_keyCode = event.m_uniChar; +#else + gunichar uniChar = g_utf8_get_char( str ); + // We cannot handle Unicode in non-Unicode mode + if (uniChar > 255) return; + + event.m_keyCode = uniChar; +#endif + + + // TODO: We still need to set all the extra attributes of the + // event, modifiers and such... + + + // Implement OnCharHook by checking ancestor top level windows + wxWindow *parent = window; + while (parent && !parent->IsTopLevel()) + parent = parent->GetParent(); + if (parent) + { + event.SetEventType( wxEVT_CHAR_HOOK ); + ret = parent->GetEventHandler()->ProcessEvent( event ); + } + + if (!ret) + { + event.SetEventType(wxEVT_CHAR); + ret = window->GetEventHandler()->ProcessEvent( event ); + } +} +#endif + + //----------------------------------------------------------------------------- // "key_release_event" from any window //----------------------------------------------------------------------------- @@ -1252,21 +1327,37 @@ static gint gtk_window_key_release_callback( GtkWidget *widget, // ---------------------------------------------------------------------------- // 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; \ - } +// +// NB: this has to be a macro as gdk_event type is different for different +// events we're used with +#define InitMouseEvent(/* wxWindowGTK * */ win, \ + /* wxMouseEvent& */ event, \ + /* GdkEventXXX * */ 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); \ + if (event.GetEventType()==wxEVT_MOUSEWHEEL) \ + { \ + if (((GdkEventButton*)gdk_event)->button == 4) \ + event.m_wheelRotation = 120; \ + else if (((GdkEventButton*)gdk_event)->button == 5) \ + event.m_wheelRotation = -120; \ + } \ + \ + wxPoint pt = win->GetClientAreaOrigin(); \ + event.m_x = (wxCoord)gdk_event->x - pt.x; \ + event.m_y = (wxCoord)gdk_event->y - pt.y; \ + \ + event.SetEventObject( win ); \ + event.SetId( win->GetId() ); \ + event.SetTimestamp( gdk_event->time ); \ +} \ static void AdjustEventButtonState(wxMouseEvent& event) { @@ -1314,12 +1405,12 @@ wxWindowGTK *FindWindowForMouseEvent(wxWindowGTK *win, wxCoord& x, wxCoord& y) yy += pizza->yoffset; } - wxNode *node = win->GetChildren().First(); + wxWindowList::Node *node = win->GetChildren().GetFirst(); while (node) { - wxWindowGTK *child = (wxWindowGTK*)node->Data(); + wxWindowGTK *child = node->GetData(); - node = node->Next(); + node = node->GetNext(); if (!child->IsShown()) continue; @@ -1402,6 +1493,26 @@ static gint gtk_window_button_press_callback( GtkWidget *widget, */ } + // GDK sends surplus button down event + // before a double click event. We + // need to filter these out. + if (gdk_event->type == GDK_BUTTON_PRESS) + { + GdkEvent *peek_event = gdk_event_peek(); + if (peek_event) + { + if (peek_event->type == GDK_2BUTTON_PRESS) + { + gdk_event_free( peek_event ); + return TRUE; + } + else + { + gdk_event_free( peek_event ); + } + } + } + wxEventType event_type = wxEVT_NULL; if (gdk_event->button == 1) @@ -1431,6 +1542,22 @@ static gint gtk_window_button_press_callback( GtkWidget *widget, default: break; } } + else if (gdk_event->button == 4) + { + switch (gdk_event->type) + { + case GDK_BUTTON_PRESS: event_type = wxEVT_MOUSEWHEEL; break; + default: break; + } + } + else if (gdk_event->button == 5) + { + switch (gdk_event->type) + { + case GDK_BUTTON_PRESS: event_type = wxEVT_MOUSEWHEEL; break; + default: break; + } + } if ( event_type == wxEVT_NULL ) { @@ -1453,8 +1580,6 @@ static gint gtk_window_button_press_callback( GtkWidget *widget, if ( !g_captureWindow ) win = FindWindowForMouseEvent(win, event.m_x, event.m_y); - event.SetEventObject( win ); - gs_timeLastClick = gdk_event->time; /* @@ -1464,6 +1589,20 @@ static gint gtk_window_button_press_callback( GtkWidget *widget, wxPrintf( wxT(".\n") ); */ +#ifndef __WXGTK20__ + if (event_type == wxEVT_LEFT_DCLICK) + { + // GTK 1.2 crashes when intercepting double + // click events from both wxSpinButton and + // wxSpinCtrl + if (GTK_IS_SPIN_BUTTON(win->m_widget)) + { + // Just disable this event for now. + return FALSE; + } + } +#endif + if (win->GetEventHandler()->ProcessEvent( event )) { gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "button_press_event" ); @@ -1477,7 +1616,9 @@ static gint gtk_window_button_press_callback( GtkWidget *widget, // "button_release_event" //----------------------------------------------------------------------------- -static gint gtk_window_button_release_callback( GtkWidget *widget, GdkEventButton *gdk_event, wxWindowGTK *win ) +static gint gtk_window_button_release_callback( GtkWidget *widget, + GdkEventButton *gdk_event, + wxWindowGTK *win ) { DEBUG_MAIN_THREAD @@ -1490,21 +1631,25 @@ static gint gtk_window_button_release_callback( GtkWidget *widget, GdkEventButto if (!win->IsOwnGtkWindow( gdk_event->window )) return FALSE; -/* - printf( "OnButtonRelease from " ); - if (win->GetClassInfo() && win->GetClassInfo()->GetClassName()) - printf( win->GetClassInfo()->GetClassName() ); - printf( ".\n" ); -*/ - wxEventType event_type = wxEVT_NULL; switch (gdk_event->button) { - 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; + 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: + // unknwon button, don't process + return FALSE; } wxMouseEvent event( event_type ); @@ -1515,11 +1660,23 @@ static gint gtk_window_button_release_callback( GtkWidget *widget, GdkEventButto // same wxListBox hack as above win->FixUpMouseEvent(widget, event.m_x, event.m_y); + if ( event_type == wxEVT_RIGHT_UP ) + { + // generate a "context menu" event: this is similar to wxEVT_RIGHT_UP + // except that: + // + // (a) it's a command event and so is propagated to the parent + // (b) under MSW it can be generated from kbd too + // (c) it uses screen coords (because of (a)) + wxContextMenuEvent evtCtx(wxEVT_CONTEXT_MENU, + win->GetId(), + win->ClientToScreen(event.GetPosition())); + (void)win->GetEventHandler()->ProcessEvent(evtCtx); + } + if ( !g_captureWindow ) win = FindWindowForMouseEvent(win, event.m_x, event.m_y); - event.SetEventObject( win ); - if (win->GetEventHandler()->ProcessEvent( event )) { gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "button_release_event" ); @@ -1572,6 +1729,10 @@ static gint gtk_window_motion_notify_callback( GtkWidget *widget, { // synthetize a mouse enter or leave event if needed GdkWindow *winUnderMouse = gdk_window_at_pointer(NULL, NULL); + // This seems to be necessary and actually been added to + // GDK itself in version 2.0.X + gdk_flush(); + bool hasMouse = winUnderMouse == gdk_event->window; if ( hasMouse != g_captureWindowHasMouse ) { @@ -1590,8 +1751,6 @@ static gint gtk_window_motion_notify_callback( GtkWidget *widget, win = FindWindowForMouseEvent(win, event.m_x, event.m_y); } - event.SetEventObject( win ); - if (win->GetEventHandler()->ProcessEvent( event )) { gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "motion_notify_event" ); @@ -1667,7 +1826,7 @@ static gint gtk_window_focus_in_callback( GtkWidget *widget, #endif // wxUSE_CARET g_activeFrameLostFocus = FALSE; - + wxWindowGTK *active = wxGetTopLevelParent(win); if ( active != g_activeFrame ) { @@ -1684,7 +1843,7 @@ static gint gtk_window_focus_in_callback( GtkWidget *widget, wxActivateEvent event(wxEVT_ACTIVATE, TRUE, g_activeFrame->GetId()); event.SetEventObject(g_activeFrame); g_activeFrame->GetEventHandler()->ProcessEvent(event); - + // Don't send focus events in addition to activate // if (win == g_activeFrame) // return TRUE; @@ -1730,7 +1889,7 @@ static gint gtk_window_focus_out_callback( GtkWidget *widget, GdkEventFocus *gdk // always) and makes using Mahogany quite annoying #if 0 wxASSERT_MSG( wxGetTopLevelParent(win) == g_activeFrame, - wxT("unfocusing window that hasn't gained focus properly") ) + wxT("unfocusing window that hasn't gained focus properly") ); #endif // 0 g_activeFrameLostFocus = TRUE; @@ -1784,7 +1943,10 @@ static gint gtk_window_focus_out_callback( GtkWidget *widget, GdkEventFocus *gdk // "enter_notify_event" //----------------------------------------------------------------------------- -static gint gtk_window_enter_callback( GtkWidget *widget, GdkEventCrossing *gdk_event, wxWindowGTK *win ) +static +gint gtk_window_enter_callback( GtkWidget *widget, + GdkEventCrossing *gdk_event, + wxWindowGTK *win ) { DEBUG_MAIN_THREAD @@ -1794,11 +1956,10 @@ static gint gtk_window_enter_callback( GtkWidget *widget, GdkEventCrossing *gdk_ if (!win->m_hasVMT) return FALSE; if (g_blockEventsOnDrag) return FALSE; - if (!win->IsOwnGtkWindow( gdk_event->window )) return FALSE; + // Event was emitted after a grab + if (gdk_event->mode != GDK_CROSSING_NORMAL) return FALSE; - wxMouseEvent event( wxEVT_ENTER_WINDOW ); - event.SetTimestamp( gdk_event->time ); - event.SetEventObject( win ); + if (!win->IsOwnGtkWindow( gdk_event->window )) return FALSE; int x = 0; int y = 0; @@ -1806,6 +1967,7 @@ static gint gtk_window_enter_callback( GtkWidget *widget, GdkEventCrossing *gdk_ gdk_window_get_pointer( widget->window, &x, &y, &state ); + wxMouseEvent event( wxEVT_ENTER_WINDOW ); InitMouseEvent(win, event, gdk_event); wxPoint pt = win->GetClientAreaOrigin(); event.m_x = x + pt.x; @@ -1834,6 +1996,9 @@ static gint gtk_window_leave_callback( GtkWidget *widget, GdkEventCrossing *gdk_ if (!win->m_hasVMT) return FALSE; if (g_blockEventsOnDrag) return FALSE; + // Event was emitted after an ungrab + if (gdk_event->mode != GDK_CROSSING_NORMAL) return FALSE; + if (!win->IsOwnGtkWindow( gdk_event->window )) return FALSE; wxMouseEvent event( wxEVT_LEAVE_WINDOW ); @@ -1889,7 +2054,9 @@ static void gtk_window_vscroll_callback( GtkAdjustment *adjust, win->m_oldVerticalPos = adjust->value; +#ifndef __WXGTK20__ GtkScrolledWindow *sw = GTK_SCROLLED_WINDOW(win->m_widget); +#endif wxEventType command = GtkScrollWinTypeToWx(GET_SCROLL_TYPE(sw->vscrollbar)); int value = (int)(adjust->value+0.5); @@ -1918,7 +2085,9 @@ static void gtk_window_hscroll_callback( GtkAdjustment *adjust, float diff = adjust->value - win->m_oldHorizontalPos; if (fabs(diff) < 0.2) return; +#ifndef __WXGTK20__ GtkScrolledWindow *sw = GTK_SCROLLED_WINDOW(win->m_widget); +#endif wxEventType command = GtkScrollWinTypeToWx(GET_SCROLL_TYPE(sw->hscrollbar)); win->m_oldHorizontalPos = adjust->value; @@ -2010,15 +2179,6 @@ wxWindow *wxWindowBase::FindFocus() return (wxWindow *)g_focusWindow; } -//----------------------------------------------------------------------------- -// "destroy" event -//----------------------------------------------------------------------------- - -static void gtk_window_destroy_callback( GtkWidget* widget, wxWindow *win ) -{ - wxWindowDestroyEvent event(win); - win->GetEventHandler()->ProcessEvent(event); -} //----------------------------------------------------------------------------- // "realize" from m_widget @@ -2028,7 +2188,7 @@ static void gtk_window_destroy_callback( GtkWidget* widget, wxWindow *win ) been realized, so we do this directly after realization. */ static gint -gtk_window_realized_callback( GtkWidget *WXUNUSED(m_widget), wxWindow *win ) +gtk_window_realized_callback( GtkWidget *m_widget, wxWindow *win ) { DEBUG_MAIN_THREAD @@ -2041,6 +2201,14 @@ gtk_window_realized_callback( GtkWidget *WXUNUSED(m_widget), wxWindow *win ) if (win->m_delayedForegroundColour) win->GtkSetForegroundColour( win->GetForegroundColour() ); +#ifdef __WXGTK20__ + if (win->m_imContext) + { + GtkPizza *pizza = GTK_PIZZA( m_widget ); + gtk_im_context_set_client_window( (GtkIMContext*) win->m_imContext, pizza->bin_window ); + } +#endif + wxWindowCreateEvent event( win ); event.SetEventObject( win ); win->GetEventHandler()->ProcessEvent( event ); @@ -2300,10 +2468,15 @@ void wxWindowGTK::Init() m_delayedForegroundColour = FALSE; m_delayedBackgroundColour = FALSE; +#ifdef __WXGTK20__ + m_imContext = NULL; + m_x11Context = NULL; +#else #ifdef HAVE_XIM m_ic = (GdkIC*) NULL; m_icattr = (GdkICAttr*) NULL; #endif +#endif } wxWindowGTK::wxWindowGTK() @@ -2338,9 +2511,9 @@ bool wxWindowGTK::Create( wxWindow *parent, } m_insertCallback = wxInsertChildInWindow; - + // always needed for background clearing - m_delayedBackgroundColour = TRUE; + m_delayedBackgroundColour = TRUE; m_widget = gtk_scrolled_window_new( (GtkAdjustment *) NULL, (GtkAdjustment *) NULL ); GTK_WIDGET_UNSET_FLAGS( m_widget, GTK_CAN_FOCUS ); @@ -2424,6 +2597,17 @@ bool wxWindowGTK::Create( wxWindow *parent, gtk_signal_connect( GTK_OBJECT(m_vAdjust), "value_changed", (GtkSignalFunc) gtk_window_vscroll_callback, (gpointer) this ); +#ifdef __WXGTK20__ + // Create input method handler + m_imContext = (GtkIMMulticontext*) gtk_im_multicontext_new (); + + // Cannot handle drawing preedited text yet + gtk_im_context_set_use_preedit( (GtkIMContext*) m_imContext, FALSE ); + + g_signal_connect (G_OBJECT (m_imContext), "commit", + G_CALLBACK (gtk_wxwindow_commit_cb), this); +#endif + gtk_widget_show( m_wxwindow ); if (m_parent) @@ -2440,6 +2624,8 @@ bool wxWindowGTK::Create( wxWindow *parent, wxWindowGTK::~wxWindowGTK() { + SendDestroyEvent(); + if (g_focusWindow == this) g_focusWindow = NULL; @@ -2494,17 +2680,17 @@ bool wxWindowGTK::PreCreation( wxWindowGTK *parent, const wxPoint &pos, const w { wxCHECK_MSG( !m_needParent || parent, FALSE, wxT("Need complete parent.") ); - /* this turns -1 into 20 so that a minimal window is - visible even although -1,-1 has been given as the - size of the window. the same trick is used in other - ports and should make debugging easier */ - m_width = WidthDefault(size.x); + // This turns -1 into 30 so that a minimal window is + // visible even although -1,-1 has been given as the + // size of the window. the same trick is used in other + // ports and should make debugging easier. + m_width = WidthDefault(size.x) ; m_height = HeightDefault(size.y); m_x = (int)pos.x; m_y = (int)pos.y; - /* some reasonable defaults */ + // some reasonable defaults if (!parent) { if (m_x == -1) @@ -2547,7 +2733,18 @@ void wxWindowGTK::PostCreation() 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 ) ); + // gtk_widget_set_redraw_on_allocate( GTK_WIDGET(m_wxwindow), HasFlag( wxNO_FULL_REPAINT_ON_RESIZE ) ); +#endif + +#ifdef __WXGTK20__ + // Create input method handler + m_imContext = (GtkIMMulticontext*) gtk_im_multicontext_new (); + + // Cannot handle drawing preedited text yet + gtk_im_context_set_use_preedit( (GtkIMContext*) m_imContext, FALSE ); + + g_signal_connect (G_OBJECT (m_imContext), "commit", + G_CALLBACK (gtk_wxwindow_commit_cb), this); #endif } @@ -2634,11 +2831,6 @@ void wxWindowGTK::ConnectWidget( GtkWidget *widget ) gtk_signal_connect( GTK_OBJECT(widget), "leave_notify_event", GTK_SIGNAL_FUNC(gtk_window_leave_callback), (gpointer)this ); - - // This keeps crashing on me. RR. - // - // gtk_signal_connect( GTK_OBJECT(widget), "destroy", - // GTK_SIGNAL_FUNC(gtk_window_destroy_callback), (gpointer)this ); } bool wxWindowGTK::Destroy() @@ -2690,16 +2882,14 @@ void wxWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags { if (x != -1) m_x = x + pizza->xoffset; if (y != -1) m_y = y + pizza->yoffset; - if (width != -1) m_width = width; - if (height != -1) m_height = height; } else { m_x = x + pizza->xoffset; m_y = y + pizza->yoffset; - m_width = width; - m_height = height; } + if (width != -1) m_width = width; + if (height != -1) m_height = height; if ((sizeFlags & wxSIZE_AUTO_WIDTH) == wxSIZE_AUTO_WIDTH) { @@ -2795,7 +2985,7 @@ void wxWindowGTK::OnInternalIdle() } g_activeFrameLostFocus = FALSE; } - + wxCursor cursor = m_cursor; if (g_globalCursor.Ok()) cursor = g_globalCursor; @@ -3066,6 +3256,11 @@ bool wxWindowGTK::Show( bool show ) else gtk_widget_hide( m_widget ); + wxShowEvent eventShow(GetId(), show); + eventShow.m_eventObject = this; + + GetEventHandler()->ProcessEvent(eventShow); + return TRUE; } @@ -3111,9 +3306,31 @@ int wxWindowGTK::GetCharHeight() const wxCHECK_MSG( m_font.Ok(), 12, wxT("invalid font") ); +#ifdef __WXGTK20__ + PangoContext *context = NULL; + if (m_widget) + context = gtk_widget_get_pango_context( m_widget ); + + if (!context) + return 0; + + PangoFontDescription *desc = m_font.GetNativeFontInfo()->description; + PangoLayout *layout = pango_layout_new(context); + pango_layout_set_font_description(layout, desc); + pango_layout_set_text(layout, "H", 1); + PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data; + + PangoRectangle rect; + pango_layout_line_get_extents(line, NULL, &rect); + + g_object_unref( G_OBJECT( layout ) ); + + return (int) (rect.height / PANGO_SCALE); +#else GdkFont *font = m_font.GetInternalFont( 1.0 ); return font->ascent + font->descent; +#endif } int wxWindowGTK::GetCharWidth() const @@ -3122,9 +3339,31 @@ int wxWindowGTK::GetCharWidth() const wxCHECK_MSG( m_font.Ok(), 8, wxT("invalid font") ); +#ifdef __WXGTK20__ + PangoContext *context = NULL; + if (m_widget) + context = gtk_widget_get_pango_context( m_widget ); + + if (!context) + return 0; + + PangoFontDescription *desc = m_font.GetNativeFontInfo()->description; + PangoLayout *layout = pango_layout_new(context); + pango_layout_set_font_description(layout, desc); + pango_layout_set_text(layout, "H", 1); + PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data; + + PangoRectangle rect; + pango_layout_line_get_extents(line, NULL, &rect); + + g_object_unref( G_OBJECT( layout ) ); + + return (int) (rect.width / PANGO_SCALE); +#else GdkFont *font = m_font.GetInternalFont( 1.0 ); return gdk_string_width( font, "H" ); +#endif } void wxWindowGTK::GetTextExtent( const wxString& string, @@ -3139,11 +3378,60 @@ void wxWindowGTK::GetTextExtent( const wxString& string, wxCHECK_RET( fontToUse.Ok(), wxT("invalid font") ); + if (string.IsEmpty()) + { + if (x) (*x) = 0; + if (y) (*y) = 0; + return; + } + +#ifdef __WXGTK20__ + PangoContext *context = NULL; + if (m_widget) + context = gtk_widget_get_pango_context( m_widget ); + + if (!context) + { + if (x) (*x) = 0; + if (y) (*y) = 0; + return; + } + + PangoFontDescription *desc = fontToUse.GetNativeFontInfo()->description; + PangoLayout *layout = pango_layout_new(context); + pango_layout_set_font_description(layout, desc); + { +#if wxUSE_UNICODE + const wxCharBuffer data = wxConvUTF8.cWC2MB( string ); + pango_layout_set_text(layout, (const char*) data, strlen( (const char*) data )); +#else + const wxWCharBuffer wdata = wxConvLocal.cMB2WC( string ); + const wxCharBuffer data = wxConvUTF8.cWC2MB( wdata ); + pango_layout_set_text(layout, (const char*) data, strlen( (const char*) data )); +#endif + } + PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data; + + PangoRectangle rect; + pango_layout_line_get_extents(line, NULL, &rect); + + if (x) (*x) = (wxCoord) (rect.width / PANGO_SCALE); + if (y) (*y) = (wxCoord) (rect.height / PANGO_SCALE); + if (descent) + { + // Do something about metrics here + (*descent) = 0; + } + if (externalLeading) (*externalLeading) = 0; // ?? + + g_object_unref( G_OBJECT( layout ) ); +#else GdkFont *font = fontToUse.GetInternalFont( 1.0 ); - if (x) (*x) = gdk_string_width( font, string.mbc_str() ); + if (x) (*x) = gdk_string_width( font, wxGTK_CONV( string ) ); if (y) (*y) = font->ascent + font->descent; if (descent) (*descent) = font->descent; if (externalLeading) (*externalLeading) = 0; // ?? +#endif } void wxWindowGTK::SetFocus() @@ -3307,6 +3595,7 @@ void wxWindowGTK::WarpPointer( int x, int y ) gdk_window_warp_pointer( window, x, y ); } + void wxWindowGTK::Refresh( bool eraseBackground, const wxRect *rect ) { if (!m_widget) return; @@ -3315,7 +3604,7 @@ void wxWindowGTK::Refresh( bool eraseBackground, const wxRect *rect ) #ifndef __WXGTK20__ if (g_isIdle) wxapp_install_idle_handler(); - + if (eraseBackground && m_wxwindow && m_wxwindow->window) { if (rect) @@ -3391,27 +3680,31 @@ void wxWindowGTK::GtkUpdate() #ifdef __WXGTK20__ if (m_wxwindow && GTK_PIZZA(m_wxwindow)->bin_window) gdk_window_process_updates( GTK_PIZZA(m_wxwindow)->bin_window, FALSE ); -#endif - +#else if (!m_updateRegion.IsEmpty()) GtkSendPaintEvents(); +#endif } void wxWindowGTK::GtkSendPaintEvents() { if (!m_wxwindow) { +#ifndef __WXGTK20__ m_clearRegion.Clear(); +#endif m_updateRegion.Clear(); return; } - // widget to draw on - GtkPizza *pizza = GTK_PIZZA (m_wxwindow); - // Clip to paint region in wxClientDC m_clipPaintRegion = TRUE; - + +#ifndef __WXGTK20__ + // widget to draw on + GtkPizza *pizza = GTK_PIZZA (m_wxwindow); + + // later for GTK 2.0, too. if (GetThemeEnabled()) { // find ancestor from which to steal background @@ -3420,7 +3713,7 @@ void wxWindowGTK::GtkSendPaintEvents() parent = parent->GetParent(); if (!parent) parent = (wxWindow*)this; - + wxRegionIterator upd( m_updateRegion ); while (upd) { @@ -3429,7 +3722,7 @@ void wxWindowGTK::GtkSendPaintEvents() rect.y = upd.GetY(); rect.width = upd.GetWidth(); rect.height = upd.GetHeight(); - + gtk_paint_flat_box( parent->m_widget->style, pizza->bin_window, GTK_STATE_NORMAL, @@ -3438,15 +3731,31 @@ void wxWindowGTK::GtkSendPaintEvents() parent->m_widget, (char *)"base", 0, 0, -1, -1 ); - + upd ++; } } else - // if (!m_clearRegion.IsEmpty()) // always send an erase event +#endif + +#ifdef __WXGTK20__ { wxWindowDC dc( (wxWindow*)this ); - dc.SetClippingRegion( m_clearRegion ); + dc.SetClippingRegion( m_updateRegion ); + + wxEraseEvent erase_event( GetId(), &dc ); + erase_event.SetEventObject( this ); + + GetEventHandler()->ProcessEvent(erase_event); + } +#else + // if (!m_clearRegion.IsEmpty()) // Always send an erase event under GTK 1.2 + { + wxWindowDC dc( (wxWindow*)this ); + if (m_clearRegion.IsEmpty()) + dc.SetClippingRegion( m_updateRegion ); + else + dc.SetClippingRegion( m_clearRegion ); wxEraseEvent erase_event( GetId(), &dc ); erase_event.SetEventObject( this ); @@ -3470,6 +3779,7 @@ void wxWindowGTK::GtkSendPaintEvents() } m_clearRegion.Clear(); } +#endif wxNcPaintEvent nc_paint_event( GetId() ); nc_paint_event.SetEventObject( this ); @@ -3492,7 +3802,7 @@ void wxWindowGTK::GtkSendPaintEvents() { GtkPizzaChild *child = (GtkPizzaChild*) children->data; children = children->next; - + if (GTK_WIDGET_NO_WINDOW (child->widget) && GTK_WIDGET_DRAWABLE (child->widget)) { @@ -3532,15 +3842,17 @@ void wxWindowGTK::Clear() { wxCHECK_RET( m_widget != NULL, wxT("invalid window") ); +#ifndef __WXGTK20__ if (m_wxwindow && m_wxwindow->window) { m_clearRegion.Clear(); wxSize size( GetClientSize() ); m_clearRegion.Union( 0,0,size.x,size.y ); - + // Better do this in idle? GtkUpdate(); } +#endif } #if wxUSE_TOOLTIPS @@ -3567,10 +3879,10 @@ void wxWindowGTK::GtkSetBackgroundColour( const wxColour &colour ) window = GetConnectWidget()->window; wxASSERT( window ); - + // We need the pixel value e.g. for background clearing. m_backgroundColour.CalcPixel( gdk_window_get_colormap( window ) ); - + if (m_wxwindow) { // wxMSW doesn't clear the window here, either. @@ -3654,6 +3966,21 @@ bool wxWindowGTK::SetForegroundColour( const wxColour &colour ) return TRUE; } +#ifdef __WXGTK20__ +PangoContext *wxWindowGTK::GtkGetPangoDefaultContext() +{ + return gtk_widget_get_pango_context( m_widget ); +} + +PangoContext *wxWindowGTK::GtkGetPangoX11Context() +{ + if (!m_x11Context) + m_x11Context = pango_x_get_context( gdk_display ); + + return m_x11Context; +} +#endif + GtkStyle *wxWindowGTK::GetWidgetStyle() { if (m_widgetStyle) @@ -3706,7 +4033,13 @@ void wxWindowGTK::SetWidgetStyle() if (m_font != wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT )) { - SET_STYLE_FONT(style, m_font.GetInternalFont( 1.0 )); +#ifdef __WXGTK20__ + pango_font_description_free( style->font_desc ); + pango_font_description_copy( m_font.GetNativeFontInfo()->description ); +#else + gdk_font_unref( style->font ); + style->font = gdk_font_ref( m_font.GetInternalFont( 1.0 ) ); +#endif } if (m_foregroundColour.Ok()) @@ -4151,7 +4484,7 @@ 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") ); - + // No scrolling requested. if ((dx == 0) && (dy == 0)) return; @@ -4175,20 +4508,13 @@ void wxWindowGTK::ScrollWindow( int dx, int dy, const wxRect* WXUNUSED(rect) ) GetClientSize( &cw, &ch ); m_clearRegion.Intersect( 0, 0, cw, ch ); } +#endif + m_clipPaintRegion = TRUE; gtk_pizza_scroll( GTK_PIZZA(m_wxwindow), -dx, -dy ); m_clipPaintRegion = FALSE; -#else - - gdk_window_scroll( GTK_PIZZA(m_wxwindow)->bin_window, dx, dy ); - - GTK_PIZZA(m_wxwindow)->xoffset += dx; - GTK_PIZZA(m_wxwindow)->yoffset += dy; - -#endif - } @@ -4215,10 +4541,8 @@ wxPoint wxGetMousePosition() int x, y; GdkWindow* windowAtPtr = gdk_window_at_pointer(& x, & y); - if (!windowAtPtr) - return wxPoint(-999, -999); - Display *display = GDK_WINDOW_XDISPLAY(windowAtPtr); + Display *display = windowAtPtr ? GDK_WINDOW_XDISPLAY(windowAtPtr) : GDK_DISPLAY(); Window rootWindow = RootWindowOfScreen (DefaultScreenOfDisplay(display)); Window rootReturn, childReturn; int rootX, rootY, winX, winY;