X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/a752b8c4e028fb42f20625d9017b8685ed863924..53e8c532c641ea8285b436990ff6e29ddd8d0443:/src/gtk/window.cpp diff --git a/src/gtk/window.cpp b/src/gtk/window.cpp index 41a21c2a14..2ffbc1a024 100644 --- a/src/gtk/window.cpp +++ b/src/gtk/window.cpp @@ -28,6 +28,7 @@ #include "wx/dialog.h" #include "wx/msgdlg.h" #include "wx/module.h" +#include "wx/combobox.h" #if wxUSE_DRAG_AND_DROP #include "wx/dnd.h" @@ -56,7 +57,7 @@ #include "wx/thread.h" #endif -#include +#include "wx/math.h" #include #include "wx/gtk/private.h" @@ -230,12 +231,12 @@ static GdkGC *g_eraseGC = NULL; static wxWindowGTK *g_captureWindow = (wxWindowGTK*) NULL; static bool g_captureWindowHasMouse = FALSE; -/* extern */ wxWindowGTK *g_focusWindow = (wxWindowGTK*) NULL; +wxWindowGTK *g_focusWindow = (wxWindowGTK*) NULL; // the last window which had the focus - this is normally never NULL (except // if we never had focus at all) as even when g_focusWindow is NULL it still // keeps its previous value -static wxWindowGTK *g_focusWindowLast = (wxWindowGTK*) NULL; +wxWindowGTK *g_focusWindowLast = (wxWindowGTK*) NULL; // If a window get the focus set but has not been realized // yet, defer setting the focus to idle time. @@ -476,6 +477,34 @@ void wxgtk_window_size_request_callback(GtkWidget *widget, requisition->width = w; } + +extern "C" +void wxgtk_combo_size_request_callback(GtkWidget *widget, + GtkRequisition *requisition, + wxComboBox *win) +{ + // This callback is actually hooked into the text entry + // of the combo box, not the GtkHBox. + + int w, h; + win->GetSize( &w, &h ); + if (w < 2) + w = 2; + if (h < 2) + h = 2; + + GtkCombo *gcombo = GTK_COMBO(win->m_widget); + + GtkRequisition entry_req; + entry_req.width = 2; + entry_req.height = 2; + (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(gcombo->button) )->size_request ) + (gcombo->button, &entry_req ); + + requisition->width = w - entry_req.width; + requisition->height = entry_req.height+4; // TODO: why +4? +} + //----------------------------------------------------------------------------- // "expose_event" of m_wxwindow //----------------------------------------------------------------------------- @@ -960,6 +989,7 @@ static void wxFillOtherKeyEventFields(wxKeyEvent& event, gdk_window_get_pointer(gdk_event->window, &x, &y, &state); event.SetTimestamp( gdk_event->time ); + event.SetId(win->GetId()); event.m_shiftDown = (gdk_event->state & GDK_SHIFT_MASK) != 0; event.m_controlDown = (gdk_event->state & GDK_CONTROL_MASK) != 0; event.m_altDown = (gdk_event->state & GDK_MOD1_MASK) != 0; @@ -967,6 +997,9 @@ static void wxFillOtherKeyEventFields(wxKeyEvent& event, event.m_scanCode = gdk_event->keyval; event.m_rawCode = (wxUint32) gdk_event->keyval; event.m_rawFlags = 0; +#if wxUSE_UNICODE + event.m_uniChar = gdk_keyval_to_unicode(gdk_event->keyval); +#endif event.m_x = x; event.m_y = y; event.SetEventObject( win ); @@ -1107,48 +1140,113 @@ static gint gtk_window_key_press_callback( GtkWidget *widget, return FALSE; if (g_blockEventsOnDrag) return FALSE; - -#ifdef __WXGTK20__ - // We have to pass key press events through GTK+'s Input Method context - // object in order to get correct characters. By doing so, we loose the - // ability to let other GTK+'s handlers (namely, widgets' default signal - // handlers) handle the signal by returning false from this callback. - // Because GTK+ sends the events to parent widgets as well, we can't - // afford loosing it, otherwise native widgets inserted into wxPanel - // would break in subtle ways (e.g. spacebar would no longer toggle - // wxCheckButton's state). Therefore, we only pass the event to IM if it - // originated in this window's widget, which we detect by checking if we've - // seen the same event before (no events from children are lost this way, - // because gtk_window_key_press_callback is installed for native controls - // as well and the wxKeyEvent it creates propagates upwards). - static GdkEventKey s_lastEvent; - - bool useIM = (win->m_imData != NULL) && - memcmp(gdk_event, &s_lastEvent, sizeof(GdkEventKey)) != 0; - - s_lastEvent = *gdk_event; -#endif - + + wxKeyEvent event( wxEVT_KEY_DOWN ); - if ( !wxTranslateGTKKeyEventToWx(event, win, gdk_event) ) + bool ret = false; + bool return_after_IM = false; + + if( wxTranslateGTKKeyEventToWx(event, win, gdk_event) ) + { + // Emit KEY_DOWN event + ret = win->GetEventHandler()->ProcessEvent( event ); + } + else { - // unknown key pressed, ignore (the event would be useless anyhow) + // Return after IM processing as we cannot do + // anything with it anyhow. + return_after_IM = true; + } + #ifdef __WXGTK20__ - if ( useIM ) + // 2005.01.26 modified by Hong Jen Yee (hzysoft@sina.com.tw): + // When we get a key_press event here, it could be originate + // from the current widget or its child widgets. However, only the widget + // with the INPUT FOCUS can generate the INITIAL key_press event. That is, + // if the CURRENT widget doesn't have the FOCUS at all, this event definitely + // originated from its child widgets and shouldn't be passed to IM context. + // In fact, what a GTK+ IM should do is filtering keyEvents and convert them + // into text input ONLY WHEN THE WIDGET HAS INPUT FOCUS. Besides, when current + // widgets has both IM context and input focus, the event should be filtered + // by gtk_im_context_filter_keypress(). + // Then, we should, according to GTK+ 2.0 API doc, return whatever it returns. + if ((!ret) && (win->m_imData != NULL) && ( wxWindow::FindFocus() == win )) + { + // We should let GTK+ IM filter key event first. According to GTK+ 2.0 API + // docs, if IM filter returns true, no further processing should be done. + // we should send the key_down event anyway. + bool intercepted_by_IM = gtk_im_context_filter_keypress(win->m_imData->context, gdk_event); + win->m_imData->lastKeyEvent = NULL; + if (intercepted_by_IM) { - // it may be useful for the input method, though: - win->m_imData->lastKeyEvent = gdk_event; - bool ret = gtk_im_context_filter_keypress(win->m_imData->context, - gdk_event); - win->m_imData->lastKeyEvent = NULL; - return ret; + wxLogTrace(TRACE_KEYS, _T("Key event intercepted by IM")); + return true; } -#endif - return FALSE; } +#endif + if (return_after_IM) + return true; - // Emit KEY_DOWN event - bool ret = win->GetEventHandler()->ProcessEvent( event ); +#ifndef __WXGTK20__ + // This is for GTK+ 1.2 only. The char event generatation for GTK+ 2.0 is done + // in the "commit" handler. + + // 2005.02.02 modified by Hong Jen Yee (hzysoft@sina.com.tw). + // In GTK+ 1.2, strings sent by IMs are also regarded as key_press events whose + // keyCodes cannot be recognized by wxWidgets. These MBCS strings, however, are + // composed of more than one character, which means gdk_event->length will always + // greater than one. When gtk_event->length == 1, this may be an ASCII character + // and can be translated by wx. However, when MBCS characters are sent by IM, + // gdk_event->length will >= 2. So neither should we pass it to accelerator table, + // nor should we pass it to controls. The following explanation was excerpted + // from GDK documentation. + // gint length : the length of string. + // gchar *string : a null-terminated multi-byte string containing the composed + // characters resulting from the key press. When text is being input, in a GtkEntry + // for example, it is these characters which should be added to the input buffer. + // When using Input Methods to support internationalized text input, the composed + // characters appear here after the pre-editing has been completed. + + if ( (!ret) && (gdk_event->length > 1) ) // If this event contains a pre-edited string from IM. + { + // We should translate this key event into wxEVT_CHAR not wxEVT_KEY_DOWN. + #if wxUSE_UNICODE // GTK+ 1.2 is not UTF-8 based. + const wxWCharBuffer string = wxConvLocal.cMB2WC( gdk_event->string ); + if( !string ) + return false; + #else + const char* string = gdk_event->string; + #endif + + // Implement OnCharHook by checking ancesteror top level windows + wxWindow *parent = win; + while (parent && !parent->IsTopLevel()) + parent = parent->GetParent(); + + for( const wxChar* pstr = string; *pstr; pstr++ ) + { + #if wxUSE_UNICODE + event.m_uniChar = *pstr; + // Backward compatible for ISO-8859-1 + event.m_keyCode = *pstr < 256 ? event.m_uniChar : 0; + #else + event.m_keyCode = *pstr; + #endif + if (parent) + { + event.SetEventType( wxEVT_CHAR_HOOK ); + ret = parent->GetEventHandler()->ProcessEvent( event ); + } + if (!ret) + { + event.SetEventType(wxEVT_CHAR); + win->GetEventHandler()->ProcessEvent( event ); + } + } + return true; + } + +#endif // #ifndef __WXGTK20__ #if wxUSE_ACCEL if (!ret) @@ -1174,41 +1272,22 @@ static gint gtk_window_key_press_callback( GtkWidget *widget, // will only be sent if it is not in an accelerator table. if (!ret) { -#ifdef __WXGTK20__ - if (useIM) - { - // 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. - win->m_imData->lastKeyEvent = gdk_event; - if ( gtk_im_context_filter_keypress(win->m_imData->context, - gdk_event) ) - { - win->m_imData->lastKeyEvent = NULL; - wxLogTrace(TRACE_KEYS, _T("Key event intercepted by IM")); - return TRUE; - } - else - win->m_imData->lastKeyEvent = NULL; - } -#endif - long key_code; KeySym keysym = gdk_event->keyval; // Find key code for EVT_CHAR and EVT_CHAR_HOOK events 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) ) + if ( wxIsAsciiKeysym(keysym) ) { // ASCII key key_code = (unsigned char)keysym; } + // gdk_event->string is actually deprecated + else if ( gdk_event->length == 1 ) + { + key_code = (unsigned char)gdk_event->string[0]; + } } if ( key_code ) @@ -1235,6 +1314,10 @@ static gint gtk_window_key_press_callback( GtkWidget *widget, } } + + + + // win is a control: tab can be propagated up if ( !ret && ((gdk_event->keyval == GDK_Tab) || (gdk_event->keyval == GDK_ISO_Left_Tab)) && @@ -1340,7 +1423,7 @@ static void gtk_wxwindow_commit_cb (GtkIMContext *context, { #if wxUSE_UNICODE event.m_uniChar = *pstr; - // Backward compatible for ISO-8859 + // Backward compatible for ISO-8859-1 event.m_keyCode = *pstr < 256 ? event.m_uniChar : 0; wxLogTrace(TRACE_KEYS, _T("IM sent character '%c'"), event.m_uniChar); #else @@ -1688,21 +1771,6 @@ static gint gtk_window_button_press_callback( GtkWidget *widget, // a chance to correct this win->FixUpMouseEvent(widget, event.m_x, event.m_y); - if ( event_type == wxEVT_RIGHT_DOWN ) - { - // generate a "context menu" event: this is similar to right mouse - // click under many GUIs except that it is generated differently - // (right up under MSW, ctrl-click under Mac, right down here) and - // - // (a) it's a command event and so is propagated to the parent - // (b) under 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); - } - // find the correct window to send the event too: it may be a different one // from the one which got it at GTK+ level because some control don't have // their own X window and thus cannot get any events. @@ -1731,6 +1799,23 @@ static gint gtk_window_button_press_callback( GtkWidget *widget, return TRUE; } + if (event_type == wxEVT_RIGHT_DOWN) + { + // generate a "context menu" event: this is similar to right mouse + // click under many GUIs except that it is generated differently + // (right up under MSW, ctrl-click under Mac, right down here) and + // + // (a) it's a command event and so is propagated to the parent + // (b) under some ports it can be generated from kbd too + // (c) it uses screen coords (because of (a)) + wxContextMenuEvent evtCtx( + wxEVT_CONTEXT_MENU, + win->GetId(), + win->ClientToScreen(event.GetPosition())); + evtCtx.SetEventObject(win); + return win->GetEventHandler()->ProcessEvent(evtCtx); + } + return FALSE; } @@ -1923,7 +2008,20 @@ static gint gtk_window_wheel_callback (GtkWidget * widget, return FALSE; } -#endif + +//----------------------------------------------------------------------------- +// "popup-menu" +//----------------------------------------------------------------------------- +static gboolean wxgtk_window_popup_menu_callback(GtkWidget*, wxWindowGTK* win) +{ + wxContextMenuEvent event( + wxEVT_CONTEXT_MENU, + win->GetId(), + wxPoint(-1, -1)); + event.SetEventObject(win); + return win->GetEventHandler()->ProcessEvent(event); +} +#endif // __WXGTK20__ //----------------------------------------------------------------------------- // "focus_in_event" @@ -2830,7 +2928,7 @@ void wxWindowGTK::PostCreation() 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_connect_after( GTK_OBJECT(m_focusWidget), "focus_out_event", GTK_SIGNAL_FUNC(gtk_window_focus_out_callback), (gpointer)this ); } @@ -2860,13 +2958,20 @@ void wxWindowGTK::PostCreation() GTK_SIGNAL_FUNC(gtk_wxwindow_size_callback), (gpointer)this ); } - if ( !GTK_IS_COMBO(m_widget)) + if (GTK_IS_COMBO(m_widget)) + { + GtkCombo *gcombo = GTK_COMBO(m_widget); + + gtk_signal_connect( GTK_OBJECT(gcombo->entry), "size_request", + GTK_SIGNAL_FUNC(wxgtk_combo_size_request_callback), + (gpointer) this ); + } + else { // This is needed if we want to add our windows into native - // GTK control, such as the toolbar. With this callback, the + // GTK controls, such as the toolbar. With this callback, the // toolbar gets to know the correct size (the one set by the - // programmer). Sadly, it misbehaves for wxComboBox. FIXME - // when moving to GTK 2.0. + // programmer). Sadly, it misbehaves for wxComboBox. gtk_signal_connect( GTK_OBJECT(m_widget), "size_request", GTK_SIGNAL_FUNC(wxgtk_window_size_request_callback), (gpointer) this ); @@ -2902,6 +3007,8 @@ void wxWindowGTK::ConnectWidget( GtkWidget *widget ) #ifdef __WXGTK20__ gtk_signal_connect( GTK_OBJECT(widget), "scroll_event", GTK_SIGNAL_FUNC(gtk_window_wheel_callback), (gpointer)this ); + g_signal_connect(widget, "popup_menu", + G_CALLBACK(wxgtk_window_popup_menu_callback), this); #endif gtk_signal_connect( GTK_OBJECT(widget), "enter_notify_event", @@ -3328,7 +3435,7 @@ bool wxWindowGTK::Show( bool show ) gtk_widget_hide( m_widget ); wxShowEvent eventShow(GetId(), show); - eventShow.m_eventObject = this; + eventShow.SetEventObject(this); GetEventHandler()->ProcessEvent(eventShow); @@ -3687,18 +3794,28 @@ void wxWindowGTK::Raise() { wxCHECK_RET( (m_widget != NULL), wxT("invalid window") ); - if (!m_widget->window) return; - - gdk_window_raise( m_widget->window ); + if (m_wxwindow && m_wxwindow->window) + { + gdk_window_raise( m_wxwindow->window ); + } + else if (m_widget->window) + { + gdk_window_raise( m_widget->window ); + } } void wxWindowGTK::Lower() { wxCHECK_RET( (m_widget != NULL), wxT("invalid window") ); - if (!m_widget->window) return; - - gdk_window_lower( m_widget->window ); + if (m_wxwindow && m_wxwindow->window) + { + gdk_window_lower( m_wxwindow->window ); + } + else if (m_widget->window) + { + gdk_window_lower( m_widget->window ); + } } bool wxWindowGTK::SetCursor( const wxCursor &cursor ) @@ -3863,25 +3980,28 @@ void wxWindowGTK::GtkSendPaintEvents() if (!parent) parent = (wxWindow*)this; - wxRegionIterator upd( m_updateRegion ); - while (upd) + if (GTK_WIDGET_MAPPED(parent->m_widget)) { - GdkRectangle rect; - rect.x = upd.GetX(); - rect.y = upd.GetY(); - rect.width = upd.GetWidth(); - rect.height = upd.GetHeight(); - - gtk_paint_flat_box( parent->m_widget->style, - pizza->bin_window, - (GtkStateType)GTK_WIDGET_STATE(m_wxwindow), - GTK_SHADOW_NONE, - &rect, - parent->m_widget, - (char *)"base", - 0, 0, -1, -1 ); - - upd ++; + wxRegionIterator upd( m_updateRegion ); + while (upd) + { + GdkRectangle rect; + rect.x = upd.GetX(); + rect.y = upd.GetY(); + rect.width = upd.GetWidth(); + rect.height = upd.GetHeight(); + + gtk_paint_flat_box( parent->m_widget->style, + pizza->bin_window, + (GtkStateType)GTK_WIDGET_STATE(m_wxwindow), + GTK_SHADOW_NONE, + &rect, + parent->m_widget, + (char *)"base", + 0, 0, -1, -1 ); + + upd ++; + } } } else @@ -4212,9 +4332,10 @@ void gtk_pop_hide_callback( GtkWidget *WXUNUSED(widget), bool* is_waiting ) *is_waiting = FALSE; } -static void SetInvokingWindow( wxMenu *menu, wxWindowGTK *win ) +void SetInvokingWindow( wxMenu *menu, wxWindow* win ) { menu->SetInvokingWindow( win ); + wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst(); while (node) { @@ -4255,6 +4376,10 @@ bool wxWindowGTK::DoPopupMenu( wxMenu *menu, int x, int y ) wxCHECK_MSG( menu != NULL, false, wxT("invalid popup-menu") ); + // NOTE: if you change this code, you need to update + // the same code in taskbar.cpp as well. This + // is ugly code duplication, I know, + SetInvokingWindow( menu, this ); menu->UpdateUI();