#include "wx/intl.h"
#include "wx/settings.h"
#include "wx/log.h"
+#include "wx/fontutil.h"
#ifdef __WXDEBUG__
#include "wx/thread.h"
#endif
#include <math.h>
+#include <ctype.h>
#include "wx/gtk/private.h"
#include <gdk/gdkprivate.h>
#include "wx/gtk/win_gtk.h"
+#ifdef __WXGTK20__
+#include <pango/pangox.h>
+#endif
+
#ifdef __WXGTK20__
#define SET_CONTAINER_FOCUS(w, d) gtk_widget_child_focus((w), (d))
#else
// 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;
+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 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
// OnIdle() call and it is reset to -1: this value means that we shouldn't
//-----------------------------------------------------------------------------
#ifndef __WXGTK20__
-#define DISABLE_STYLE_IF_BROKEN_THEME 1
+#define DISABLE_STYLE_IF_BROKEN_THEME 0
#endif
#ifdef __WXDEBUG__
#else
# define DEBUG_MAIN_THREAD
#endif
-
-static gint gtk_debug_focus_in_callback( GtkWidget *WXUNUSED(widget),
- GdkEvent *WXUNUSED(event),
- const wxChar *WXUNUSED(name) )
-{
-/*
- static bool s_done = FALSE;
- if ( !s_done )
- {
- wxLog::AddTraceMask("focus");
- s_done = TRUE;
- }
- wxLogTrace(wxT("FOCUS NOW AT: %s"), name);
-*/
-
- return FALSE;
-}
-
-void debug_focus_in( GtkWidget* widget, const wxChar* name, const wxChar *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];
-
- wxStrcpy( s, tmp );
-
- 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
+// the trace mask used for the focus debugging messages
+#define TRACE_FOCUS _T("focus")
+
//-----------------------------------------------------------------------------
// missing gdk functions
//-----------------------------------------------------------------------------
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
draw_frame( widget, win );
}
-#endif
-
-//-----------------------------------------------------------------------------
-// key code mapping routines
-//-----------------------------------------------------------------------------
-
-static long map_to_unmodified_wx_keysym( GdkEventKey *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 resue it as last resort
- //
- // NB: should be MT-neutral as always called from main thread only
- static struct
- {
- KeySym keysym;
- long keycode;
- } s_lastKeyPress = { 0, 0 };
-
- KeySym keysym = event->keyval;
- long key_code;
-
- 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:
- {
- // do we have the translation?
- if ( event->length == 1 )
- {
- keysym = (KeySym)event->string[0];
- }
- else if ( (keysym & 0xFF) != keysym )
- {
- // non ASCII key, what to do?
-
- if ( event->type == GDK_KEY_RELEASE )
- {
- // reuse the one from the last keypress if any
- if ( keysym == s_lastKeyPress.keysym )
- {
- key_code = s_lastKeyPress.keycode;
-
- // skip "return 0"
- break;
- }
- }
-
- // ignore this one, we don't know it
- return 0;
- }
- //else: ASCII key, ok
-
- guint upper = gdk_keyval_to_upper( (guint)keysym );
- key_code = upper ? upper : keysym;
-
- if ( event->type == GDK_KEY_PRESS )
- {
- // remember it to be reused below later
- s_lastKeyPress.keysym = keysym;
- s_lastKeyPress.keycode = key_code;
- }
- }
- }
-
- return key_code;
-}
-
-static long map_to_wx_keysym( GdkEventKey *event )
-{
- KeySym keysym = event->keyval;
- guint key_code = 0;
-
- switch (keysym)
- {
- 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 (event->length == 1)
- {
- key_code = (unsigned char)*event->string;
- }
- else if ((keysym & 0xFF) == keysym)
- {
- key_code = (guint)keysym;
- }
- }
-
- return key_code;
-}
+#endif // GTK+ < 2.0
//-----------------------------------------------------------------------------
// "size_request" of m_widget
if (g_isIdle)
wxapp_install_idle_handler();
-/*
- if (win->GetName() == wxT("panel"))
+#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())
{
wxPrintf( wxT("OnExpose from ") );
if (win->GetClassInfo() && win->GetClassInfo()->GetClassName())
(int)gdk_event->area.width,
(int)gdk_event->area.height );
}
-*/
+#endif
-#ifndef __WXUNIVERSAL__
- GtkPizza *pizza = GTK_PIZZA (widget);
+ win->GetUpdateRegion() = wxRegion( gdk_event->region );
- if (win->GetThemeEnabled())
- {
- wxWindow *parent = win->GetParent();
- while (parent && !parent->IsTopLevel())
- parent = parent->GetParent();
- if (!parent)
- parent = win;
+ win->GtkSendPaintEvents();
- 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
+
+ // 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,
gdk_event->area.height );
+ win->m_clearRegion.Union( gdk_event->area.x,
+ gdk_event->area.y,
+ gdk_event->area.width,
+ gdk_event->area.height );
// Actual redrawing takes place in idle time.
- win->Update();
-
-#ifdef __WXGTK20__
-
- (* GTK_WIDGET_CLASS (pizza_parent_class)->expose_event) (widget, gdk_event);
-
+ // win->GtkUpdate();
#endif
- return TRUE;
+ return FALSE;
}
//-----------------------------------------------------------------------------
return;
}
-/*
- if (win->GetName() == wxT("panel"))
+#if 0
+ if (win->GetName())
{
wxPrintf( wxT("OnDraw from ") );
if (win->GetClassInfo() && win->GetClassInfo()->GetClassName())
(int)rect->width,
(int)rect->height );
}
-*/
+#endif
#ifndef __WXUNIVERSAL__
GtkPizza *pizza = GTK_PIZZA (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.
-
- win->Update();
+ // Update immediately, not in idle time.
+ win->GtkUpdate();
#ifndef __WXUNIVERSAL__
// Redraw child widgets
// "key_press_event" from any window
//-----------------------------------------------------------------------------
-// turn on to see the key event codes on the console
-#undef DEBUG_KEY_EVENTS
+// set WXTRACE to this to see the key event codes on the console
+#define TRACE_KEYS _T("keyevent")
-static gint gtk_window_key_press_callback( GtkWidget *widget,
- GdkEventKey *gdk_event,
- wxWindow *win )
+// 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)
{
- DEBUG_MAIN_THREAD
+ long key_code;
- if (g_isIdle)
- wxapp_install_idle_handler();
+ 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;
- if (!win->m_hasVMT) return FALSE;
- if (g_blockEventsOnDrag) return FALSE;
+ // 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;
- int x = 0;
- int y = 0;
- GdkModifierType state;
- if (gdk_event->window)
- gdk_window_get_pointer(gdk_event->window, &x, &y, &state);
+ case GDK_Num_Lock:
+ key_code = isChar ? 0 : WXK_NUMLOCK;
+ break;
- bool ret = FALSE;
- long key_code = map_to_unmodified_wx_keysym( gdk_event );
+ // various other special keys
+ case GDK_Menu:
+ key_code = WXK_MENU;
+ break;
-#ifdef DEBUG_KEY_EVENTS
- wxPrintf(_T("Key press event: %d => %ld\n"), gdk_event->keyval, key_code);
-#endif // DEBUG_KEY_EVENTS
+ case GDK_Help:
+ key_code = WXK_HELP;
+ break;
- /* sending unknown key events doesn't really make sense */
- if (key_code == 0)
- return FALSE;
+ case GDK_BackSpace:
+ key_code = WXK_BACK;
+ break;
- 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_keyCode = key_code;
- event.m_scanCode = gdk_event->keyval;
- event.m_x = x;
- event.m_y = y;
- event.SetEventObject( win );
- ret = win->GetEventHandler()->ProcessEvent( event );
+ case GDK_ISO_Left_Tab:
+ case GDK_Tab:
+ key_code = WXK_TAB;
+ break;
-#if wxUSE_ACCEL
- if (!ret)
- {
- wxWindowGTK *ancestor = win;
- while (ancestor)
- {
- int command = ancestor->GetAcceleratorTable()->GetCommand( event );
- if (command != -1)
- {
- wxCommandEvent command_event( wxEVT_COMMAND_MENU_SELECTED, command );
- ret = ancestor->GetEventHandler()->ProcessEvent( command_event );
- break;
- }
- if (ancestor->IsTopLevel())
- break;
- ancestor = ancestor->GetParent();
- }
- }
-#endif // wxUSE_ACCEL
+ case GDK_Linefeed:
+ case GDK_Return:
+ key_code = WXK_RETURN;
+ break;
- /* Only send wxEVT_CHAR event if not processed yet. Thus, ALT-x
- will only be sent if it is not in an accelerator table. */
- if ( !ret )
- {
- key_code = map_to_wx_keysym( gdk_event );
+ case GDK_Clear:
+ key_code = WXK_CLEAR;
+ break;
- if ( key_code )
- {
-#ifdef DEBUG_KEY_EVENTS
- wxPrintf(_T("Char event: %ld\n"), key_code);
-#endif // DEBUG_KEY_EVENTS
+ case GDK_Pause:
+ key_code = WXK_PAUSE;
+ break;
- // reuse the ame 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;
+ case GDK_Select:
+ key_code = WXK_SELECT;
+ break;
- ret = win->GetEventHandler()->ProcessEvent( event );
- }
- }
+ case GDK_Print:
+ key_code = WXK_PRINT;
+ break;
- /* win is a control: tab can be propagated up */
- if ( !ret &&
- ((gdk_event->keyval == GDK_Tab) || (gdk_event->keyval == GDK_ISO_Left_Tab)) &&
-// 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->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->GetParent()->GetEventHandler()->ProcessEvent( new_event );
- }
+ case GDK_Execute:
+ key_code = WXK_EXECUTE;
+ break;
- /* generate wxID_CANCEL if <esc> has been pressed (typically in dialogs) */
- 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 );
- }
+ case GDK_Escape:
+ key_code = WXK_ESCAPE;
+ break;
- /* 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) )
- {
- wxWindowGTK *ancestor = win;
- while (ancestor)
- {
- if (wxIsKindOf(ancestor,wxFrame))
- {
- 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;
- }
- }
- }
- ancestor = ancestor->GetParent();
- }
- }
-#endif // 0
+ // cursor and other extended keyboard keys
+ case GDK_Delete:
+ key_code = WXK_DELETE;
+ break;
- if (ret)
- {
- gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "key_press_event" );
- return TRUE;
- }
+ case GDK_Home:
+ key_code = WXK_HOME;
+ break;
- return FALSE;
-}
+ case GDK_Left:
+ key_code = WXK_LEFT;
+ break;
-//-----------------------------------------------------------------------------
-// "key_release_event" from any window
-//-----------------------------------------------------------------------------
+ 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;
-static gint gtk_window_key_release_callback( GtkWidget *widget, GdkEventKey *gdk_event, wxWindowGTK *win )
+ 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)
{
- DEBUG_MAIN_THREAD
+ return ks < 256;
+}
- if (g_isIdle)
- wxapp_install_idle_handler();
+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 };
- if (!win->m_hasVMT) return FALSE;
- if (g_blockEventsOnDrag) return FALSE;
+ KeySym keysym = gdk_event->keyval;
+
+ wxLogTrace(TRACE_KEYS, _T("Key %s event: keysym = %ld"),
+ 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;
+ }
+ }
+ }
- long key_code = map_to_unmodified_wx_keysym( gdk_event );
+ 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;
+ }
+ }
-#ifdef DEBUG_KEY_EVENTS
- wxPrintf(_T("Key release event: %d => %ld\n"), gdk_event->keyval, key_code);
-#endif // DEBUG_KEY_EVENTS
+ wxLogTrace(TRACE_KEYS, _T("\t-> wxKeyCode %ld"), key_code);
- /* sending unknown key events doesn't really make sense */
- if (key_code == 0) return FALSE;
+ // 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);
- 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_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 );
- if (win->GetEventHandler()->ProcessEvent( event ))
+ return TRUE;
+}
+
+#ifndef __WXGTK20__
+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;
+ }
+
+ // Emit KEY_DOWN event
+ bool ret = win->GetEventHandler()->ProcessEvent( event );
+
+#if wxUSE_ACCEL
+ if (!ret)
{
- gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "key_release_event" );
+ wxWindowGTK *ancestor = win;
+ while (ancestor)
+ {
+ int command = ancestor->GetAcceleratorTable()->GetCommand( event );
+ if (command != -1)
+ {
+ wxCommandEvent command_event( wxEVT_COMMAND_MENU_SELECTED, command );
+ ret = ancestor->GetEventHandler()->ProcessEvent( command_event );
+ break;
+ }
+ if (ancestor->IsTopLevel())
+ break;
+ ancestor = ancestor->GetParent();
+ }
+ }
+#endif // wxUSE_ACCEL
+
+ // Only send wxEVT_CHAR event if not processed yet. Thus, ALT-x
+ // 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
+ 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 ( key_code )
+ {
+ 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 );
+ }
+ }
+ }
+
+ // win is a control: tab can be propagated up
+ if ( !ret &&
+ ((gdk_event->keyval == GDK_Tab) || (gdk_event->keyval == GDK_ISO_Left_Tab)) &&
+// 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->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->GetParent()->GetEventHandler()->ProcessEvent( new_event );
+ }
+
+ // generate wxID_CANCEL if <esc> has been pressed (typically in dialogs)
+ if ( !ret &&
+ (gdk_event->keyval == GDK_Escape) )
+ {
+ // 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 )
+ {
+ btnCancel = winForCancel->FindWindow(wxID_CANCEL);
+ if ( btnCancel )
+ {
+ // found a cancel button
+ break;
+ }
+
+ 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);
+ }
+ }
+
+ if (ret)
+ {
+ gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "key_press_event" );
return TRUE;
}
return FALSE;
}
+#endif
+
+#ifdef __WXGTK20__
+static gint gtk_window_key_press_callback( GtkWidget *widget,
+ GdkEventKey *gdk_event,
+ wxWindow *win )
+{
+ if (g_isIdle)
+ wxapp_install_idle_handler();
+
+ if (!win->m_hasVMT)
+ return FALSE;
+ if (g_blockEventsOnDrag)
+ return FALSE;
+
+ bool ret = FALSE;
+ bool dont_use_IM = FALSE;
+
+ wxKeyEvent event( wxEVT_KEY_DOWN );
+ long keycode = wxTranslateKeySymToWXKey( gdk_event->keyval, FALSE );
+ if (keycode)
+ {
+ // We were able to decode the key press without
+ // any input method, so don't use it.
+ dont_use_IM = TRUE;
+
+ // now fill all the other fields
+ int x = 0;
+ int y = 0;
+ GdkModifierType state;
+ if (gdk_event->window)
+ gdk_window_get_pointer(gdk_event->window, &x, &y, &state);
+
+ event.SetTimestamp( gdk_event->time );
+ event.m_shiftDown = (gdk_event->state & GDK_SHIFT_MASK) != 0;
+ event.m_controlDown = (gdk_event->state & GDK_CONTROL_MASK) != 0;
+ event.m_altDown = (gdk_event->state & GDK_MOD1_MASK) != 0;
+ event.m_metaDown = (gdk_event->state & GDK_MOD2_MASK) != 0;
+ event.m_keyCode = keycode;
+ 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 );
+
+ // send key down event
+ ret = win->GetEventHandler()->ProcessEvent( event );
+
+ if (!ret)
+ {
+ // Implement OnCharHook by checking ancesteror top level windows
+ wxWindow *parent = win;
+ while (parent && !parent->IsTopLevel())
+ parent = parent->GetParent();
+ if (parent)
+ {
+ event.SetEventType( wxEVT_CHAR_HOOK );
+ ret = parent->GetEventHandler()->ProcessEvent( event );
+ }
+
+ if (!ret)
+ {
+ event.SetEventType(wxEVT_CHAR);
+ ret = win->GetEventHandler()->ProcessEvent( event );
+ }
+ }
+
+ // win is a control: tab can be propagated up
+ if ( !ret &&
+ ((gdk_event->keyval == GDK_Tab) || (gdk_event->keyval == GDK_ISO_Left_Tab)) &&
+ win->GetParent() && (win->GetParent()->HasFlag( wxTAB_TRAVERSAL)) )
+ {
+ wxNavigationKeyEvent new_event;
+ 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->GetParent()->GetEventHandler()->ProcessEvent( new_event );
+ }
+
+ if ( !ret &&
+ (gdk_event->keyval == GDK_Escape) )
+ {
+ wxWindow *winForCancel = win, *btnCancel = NULL;
+ while ( winForCancel )
+ {
+ btnCancel = winForCancel->FindWindow(wxID_CANCEL);
+ if ( btnCancel ) break;
+
+ if ( winForCancel->IsTopLevel() ) break;
+
+ winForCancel = winForCancel->GetParent();
+ }
+
+ if ( btnCancel )
+ {
+ wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, wxID_CANCEL);
+ event.SetEventObject(btnCancel);
+ ret = btnCancel->GetEventHandler()->ProcessEvent(event);
+ }
+ }
+ }
+
+ if (ret)
+ {
+ gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "key_press_event" );
+ return TRUE;
+ }
+
+ if (!dont_use_IM && win->m_imContext)
+ {
+ // 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.
+ gtk_im_context_filter_keypress ( (GtkIMContext*) win->m_imContext, gdk_event );
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+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
+
+ if (!ret)
+ {
+ // Implement OnCharHook by checking ancesteror 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
+//-----------------------------------------------------------------------------
+
+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;
+
+ wxKeyEvent event( wxEVT_KEY_UP );
+ if ( !wxTranslateGTKKeyEventToWx(event, win, gdk_event) )
+ {
+ // unknown key pressed, ignore (the event would be useless anyhow
+ return FALSE;
+ }
+
+ if ( !win->GetEventHandler()->ProcessEvent( event ) )
+ return FALSE;
+
+ gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "key_release_event" );
+ return TRUE;
+}
// ============================================================================
// 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; \
- }
-
// ----------------------------------------------------------------------------
-// mouse event processing helper
+// mouse event processing helpers
// ----------------------------------------------------------------------------
+// init wxMouseEvent with the info from gdk_event
+//
+// 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)
{
// GDK reports the old state of the button for a button press event, but
}
}
+// find the window to send the mouse event too
+static
+wxWindowGTK *FindWindowForMouseEvent(wxWindowGTK *win, wxCoord& x, wxCoord& y)
+{
+ wxCoord xx = x;
+ wxCoord yy = y;
+
+ if (win->m_wxwindow)
+ {
+ GtkPizza *pizza = GTK_PIZZA(win->m_wxwindow);
+ xx += pizza->xoffset;
+ yy += pizza->yoffset;
+ }
+
+ wxWindowList::Node *node = win->GetChildren().GetFirst();
+ while (node)
+ {
+ wxWindowGTK *child = node->GetData();
+
+ node = node->GetNext();
+ if (!child->IsShown())
+ continue;
+
+ if (child->IsTransparentForMouse())
+ {
+ // wxStaticBox is transparent in the box itself
+ int xx1 = child->m_x;
+ int yy1 = child->m_y;
+ int xx2 = child->m_x + child->m_width;
+ int yy2 = child->m_x + child->m_height;
+
+ // left
+ if (((xx >= xx1) && (xx <= xx1+10) && (yy >= yy1) && (yy <= yy2)) ||
+ // right
+ ((xx >= xx2-10) && (xx <= xx2) && (yy >= yy1) && (yy <= yy2)) ||
+ // top
+ ((xx >= xx1) && (xx <= xx2) && (yy >= yy1) && (yy <= yy1+10)) ||
+ // bottom
+ ((xx >= xx1) && (xx <= xx2) && (yy >= yy2-1) && (yy <= yy2)))
+ {
+ win = child;
+ x -= child->m_x;
+ y -= child->m_y;
+ break;
+ }
+
+ }
+ else
+ {
+ if ((child->m_wxwindow == (GtkWidget*) NULL) &&
+ (child->m_x <= xx) &&
+ (child->m_y <= yy) &&
+ (child->m_x+child->m_width >= xx) &&
+ (child->m_y+child->m_height >= yy))
+ {
+ win = child;
+ x -= child->m_x;
+ y -= child->m_y;
+ break;
+ }
+ }
+ }
+
+ return win;
+}
+
//-----------------------------------------------------------------------------
// "button_press_event"
//-----------------------------------------------------------------------------
-static gint gtk_window_button_press_callback( GtkWidget *widget, GdkEventButton *gdk_event, wxWindowGTK *win )
+static gint gtk_window_button_press_callback( GtkWidget *widget,
+ GdkEventButton *gdk_event,
+ wxWindowGTK *win )
{
DEBUG_MAIN_THREAD
*/
}
+ // 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)
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 )
{
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.
-
- if (!g_captureWindow)
- {
- wxCoord x = event.m_x;
- wxCoord y = event.m_y;
- if (win->m_wxwindow)
- {
- GtkPizza *pizza = GTK_PIZZA(win->m_wxwindow);
- x += pizza->xoffset;
- y += pizza->yoffset;
- }
-
- wxNode *node = win->GetChildren().First();
- while (node)
- {
- wxWindowGTK *child = (wxWindowGTK*)node->Data();
-
- node = node->Next();
- if (!child->IsShown())
- continue;
-
- if (child->m_isStaticBox)
- {
- // wxStaticBox is transparent in the box itself
- int xx1 = child->m_x;
- int yy1 = child->m_y;
- int xx2 = child->m_x + child->m_width;
- int yy2 = child->m_x + child->m_height;
-
- // left
- if (((x >= xx1) && (x <= xx1+10) && (y >= yy1) && (y <= yy2)) ||
- // right
- ((x >= xx2-10) && (x <= xx2) && (y >= yy1) && (y <= yy2)) ||
- // top
- ((x >= xx1) && (x <= xx2) && (y >= yy1) && (y <= yy1+10)) ||
- // bottom
- ((x >= xx1) && (x <= xx2) && (y >= yy2-1) && (y <= yy2)))
- {
- win = child;
- event.m_x -= child->m_x;
- event.m_y -= child->m_y;
- break;
- }
-
- }
- else
- {
- if ((child->m_wxwindow == (GtkWidget*) NULL) &&
- (child->m_x <= x) &&
- (child->m_y <= y) &&
- (child->m_x+child->m_width >= x) &&
- (child->m_y+child->m_height >= y))
- {
- win = child;
- event.m_x -= child->m_x;
- event.m_y -= child->m_y;
- break;
- }
- }
- }
- }
+ // wxListBox actually get mouse events from the item, so we need to give it
+ // a chance to correct this
+ win->FixUpMouseEvent(widget, event.m_x, event.m_y);
- event.SetEventObject( win );
+ // 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.
+ if ( !g_captureWindow )
+ win = FindWindowForMouseEvent(win, event.m_x, event.m_y);
gs_timeLastClick = gdk_event->time;
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" );
// "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
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;
- }
-
- wxMouseEvent event( event_type );
- InitMouseEvent( win, event, gdk_event );
+ case 1:
+ event_type = wxEVT_LEFT_UP;
+ break;
- AdjustEventButtonState(event);
+ case 2:
+ event_type = wxEVT_MIDDLE_UP;
+ break;
- // wxListBox actually get mouse events from the item
+ case 3:
+ event_type = wxEVT_RIGHT_UP;
+ break;
- if (win->m_isListBox)
- {
- event.m_x += widget->allocation.x;
- event.m_y += widget->allocation.y;
+ default:
+ // unknwon button, don't process
+ return FALSE;
}
- // Some control don't have their own X window and thus cannot get
- // any events.
-
- if (!g_captureWindow)
- {
- wxCoord x = event.m_x;
- wxCoord y = event.m_y;
- if (win->m_wxwindow)
- {
- GtkPizza *pizza = GTK_PIZZA(win->m_wxwindow);
- x += pizza->xoffset;
- y += pizza->yoffset;
- }
-
- wxNode *node = win->GetChildren().First();
- while (node)
- {
- wxWindowGTK *child = (wxWindowGTK*)node->Data();
+ wxMouseEvent event( event_type );
+ InitMouseEvent( win, event, gdk_event );
- node = node->Next();
- if (!child->IsShown())
- continue;
+ AdjustEventButtonState(event);
- if (child->m_isStaticBox)
- {
- // wxStaticBox is transparent in the box itself
- int xx1 = child->m_x;
- int yy1 = child->m_y;
- int xx2 = child->m_x + child->m_width;
- int yy2 = child->m_x + child->m_height;
-
- // left
- if (((x >= xx1) && (x <= xx1+10) && (y >= yy1) && (y <= yy2)) ||
- // right
- ((x >= xx2-10) && (x <= xx2) && (y >= yy1) && (y <= yy2)) ||
- // top
- ((x >= xx1) && (x <= xx2) && (y >= yy1) && (y <= yy1+10)) ||
- // bottom
- ((x >= xx1) && (x <= xx2) && (y >= yy2-1) && (y <= yy2)))
- {
- win = child;
- event.m_x -= child->m_x;
- event.m_y -= child->m_y;
- break;
- }
+ // same wxListBox hack as above
+ win->FixUpMouseEvent(widget, event.m_x, event.m_y);
- }
- else
- {
- if ((child->m_wxwindow == (GtkWidget*) NULL) &&
- (child->m_x <= x) &&
- (child->m_y <= y) &&
- (child->m_x+child->m_width >= x) &&
- (child->m_y+child->m_height >= y))
- {
- win = child;
- event.m_x -= child->m_x;
- event.m_y -= child->m_y;
- break;
- }
- }
- }
+ 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);
}
- event.SetEventObject( win );
+ if ( !g_captureWindow )
+ win = FindWindowForMouseEvent(win, event.m_x, event.m_y);
if (win->GetEventHandler()->ProcessEvent( event ))
{
wxMouseEvent event( wxEVT_MOTION );
InitMouseEvent(win, event, gdk_event);
- if ( g_captureWindow )
+ if ( !g_captureWindow )
{
// synthetize a mouse enter or leave event if needed
GdkWindow *winUnderMouse = gdk_window_at_pointer(NULL, NULL);
}
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)
- {
- GtkPizza *pizza = GTK_PIZZA(win->m_wxwindow);
- x += pizza->xoffset;
- y += pizza->yoffset;
- }
-
- wxNode *node = win->GetChildren().First();
- while (node)
- {
- wxWindowGTK *child = (wxWindowGTK*)node->Data();
-
- node = node->Next();
- if (!child->IsShown())
- continue;
-
- if (child->m_isStaticBox)
- {
- // wxStaticBox is transparent in the box itself
- int xx1 = child->m_x;
- int yy1 = child->m_y;
- int xx2 = child->m_x + child->m_width;
- int yy2 = child->m_x + child->m_height;
-
- // left
- if (((x >= xx1) && (x <= xx1+10) && (y >= yy1) && (y <= yy2)) ||
- // right
- ((x >= xx2-10) && (x <= xx2) && (y >= yy1) && (y <= yy2)) ||
- // top
- ((x >= xx1) && (x <= xx2) && (y >= yy1) && (y <= yy1+10)) ||
- // bottom
- ((x >= xx1) && (x <= xx2) && (y >= yy2-1) && (y <= yy2)))
- {
- win = child;
- event.m_x -= child->m_x;
- event.m_y -= child->m_y;
- break;
- }
-
- }
- else
- {
- if ((child->m_wxwindow == (GtkWidget*) NULL) &&
- (child->m_x <= x) &&
- (child->m_y <= y) &&
- (child->m_x+child->m_width >= x) &&
- (child->m_y+child->m_height >= y))
- {
- win = child;
- event.m_x -= child->m_x;
- event.m_y -= child->m_y;
- break;
- }
- }
- }
+ 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" );
// "focus_in_event"
//-----------------------------------------------------------------------------
+// send the wxChildFocusEvent and wxFocusEvent, common code of
+// gtk_window_focus_in_callback() and SetFocus()
+static bool DoSendFocusEvents(wxWindow *win)
+{
+ // Notify the parent keeping track of focus for the kbd navigation
+ // purposes that we got it.
+ wxChildFocusEvent eventChildFocus(win);
+ (void)win->GetEventHandler()->ProcessEvent(eventChildFocus);
+
+ wxFocusEvent eventFocus(wxEVT_SET_FOCUS, win->GetId());
+ eventFocus.SetEventObject(win);
+
+ return win->GetEventHandler()->ProcessEvent(eventFocus);
+}
+
static gint gtk_window_focus_in_callback( GtkWidget *widget,
GdkEvent *WXUNUSED(event),
wxWindow *win )
g_focusWindowLast =
g_focusWindow = win;
-#if 0
- wxLogDebug( wxT("OnSetFocus 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);
+ wxLogTrace(TRACE_FOCUS,
+ _T("%s: focus in"), win->GetName().c_str());
#ifdef HAVE_XIM
if (win->m_ic)
}
#endif // wxUSE_CARET
+ g_activeFrameLostFocus = FALSE;
+
wxWindowGTK *active = wxGetTopLevelParent(win);
if ( active != g_activeFrame )
{
wxActivateEvent event(wxEVT_ACTIVATE, TRUE, g_activeFrame->GetId());
event.SetEventObject(g_activeFrame);
g_activeFrame->GetEventHandler()->ProcessEvent(event);
- }
- g_activeFrameLostFocus = FALSE;
+ // 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 ))
+ // does the window itself think that it has the focus?
+ if ( !win->m_hasFocus )
{
- gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "focus_in_event" );
- return TRUE;
- }
+ // not yet, notify it
+ win->m_hasFocus = TRUE;
+ if ( DoSendFocusEvents(win) )
+ {
+ gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "focus_in_event" );
+ return TRUE;
+ }
+ }
return FALSE;
}
if (!win->m_hasVMT) return FALSE;
if (g_blockEventsOnDrag) return FALSE;
-#if 0
- wxLogDebug( wxT("OnKillFocus from %s"), win->GetName().c_str() );
-#endif
+ wxLogTrace( TRACE_FOCUS,
+ _T("%s: focus out"), win->GetName().c_str() );
if ( !g_activeFrameLostFocus && g_activeFrame )
{
// 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;
}
#endif // wxUSE_CARET
- wxFocusEvent event( wxEVT_KILL_FOCUS, win->GetId() );
- event.SetEventObject( win );
-
- if (win->GetEventHandler()->ProcessEvent( event ))
+ // don't send the window a kill focus event if it thinks that it doesn't
+ // have focus already
+ if ( win->m_hasFocus )
{
- gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "focus_out_event" );
- return TRUE;
+ win->m_hasFocus = FALSE;
+
+ wxFocusEvent event( wxEVT_KILL_FOCUS, win->GetId() );
+ event.SetEventObject( win );
+
+ if (win->GetEventHandler()->ProcessEvent( event ))
+ {
+ gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "focus_out_event" );
+ return TRUE;
+ }
}
return FALSE;
// "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
if (!win->IsOwnGtkWindow( gdk_event->window )) return FALSE;
- wxMouseEvent event( wxEVT_ENTER_WINDOW );
- event.SetTimestamp( gdk_event->time );
- event.SetEventObject( win );
-
int x = 0;
int y = 0;
GdkModifierType state = (GdkModifierType)0;
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;
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;
wxPoint pt = win->GetClientAreaOrigin();
event.m_x = x + pt.x;
win->m_oldVerticalPos = adjust->value;
- wxEventType command = GtkScrollWinTypeToWx(GET_SCROLL_TYPE(win->m_widget));
+#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);
float diff = adjust->value - win->m_oldHorizontalPos;
if (fabs(diff) < 0.2) return;
- wxEventType command = GtkScrollWinTypeToWx(GET_SCROLL_TYPE(win->m_widget));
+#ifndef __WXGTK20__
+ GtkScrolledWindow *sw = GTK_SCROLLED_WINDOW(win->m_widget);
+#endif
+ wxEventType command = GtkScrollWinTypeToWx(GET_SCROLL_TYPE(sw->hscrollbar));
win->m_oldHorizontalPos = adjust->value;
return (wxWindow *)g_focusWindow;
}
+
//-----------------------------------------------------------------------------
// "realize" from m_widget
//-----------------------------------------------------------------------------
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
wxapp_install_idle_handler();
if (win->m_delayedBackgroundColour)
- win->SetBackgroundColour( win->GetBackgroundColour() );
+ win->GtkSetBackgroundColour( win->GetBackgroundColour() );
if (win->m_delayedForegroundColour)
- win->SetForegroundColour( win->GetForegroundColour() );
+ 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 );
wxWindow *wxGetActiveWindow()
{
- // the cast is necessary when we compile in wxUniversal mode
- return (wxWindow *)g_focusWindow;
+ return wxWindow::FindFocus();
}
//-----------------------------------------------------------------------------
m_insertCallback = (wxInsertChildFunction) NULL;
- m_isStaticBox = FALSE;
- m_isRadioButton = FALSE;
- m_isListBox = FALSE;
- m_isFrame = FALSE;
m_acceptsFocus = FALSE;
+ m_hasFocus = FALSE;
m_clipPaintRegion = FALSE;
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()
}
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 );
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)
wxWindowGTK::~wxWindowGTK()
{
+ SendDestroyEvent();
+
if (g_focusWindow == this)
g_focusWindow = NULL;
if (g_activeFrame == this)
g_activeFrame = NULL;
+ if ( g_delayedFocus == this )
+ g_delayedFocus = NULL;
+
m_isBeingDeleted = TRUE;
m_hasVMT = FALSE;
{
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)
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
}
{
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)
{
void wxWindowGTK::OnInternalIdle()
{
// Update invalidated regions.
- Update();
+ GtkUpdate();
// Synthetize activate events.
if ( g_sendActivateEvent != -1 )
else
gtk_widget_hide( m_widget );
+ wxShowEvent eventShow(GetId(), show);
+ eventShow.m_eventObject = this;
+
+ GetEventHandler()->ProcessEvent(eventShow);
+
return TRUE;
}
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
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,
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()
{
- wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
+ wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
-#if 0
- wxPrintf( "SetFocus from " );
- if (GetClassInfo() && GetClassInfo()->GetClassName())
- wxPrintf( GetClassInfo()->GetClassName() );
- wxPrintf( ".\n" );
-#endif
+ if ( m_hasFocus )
+ {
+ // don't do anything if we already have focus
+ return;
+ }
if (m_wxwindow)
{
{
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))
+ {
+ // we can't set the focus to the widget now so we remember that
+ // it should be focused and will do it later, during the idle
+ // time, as soon as we can
+ wxLogTrace(TRACE_FOCUS,
+ _T("Delaying setting focus to %s(%s)"),
+ GetClassInfo()->GetClassName(), GetLabel().c_str());
+
+ g_delayedFocus = this;
+ }
+ else
+ {
+ wxLogTrace(TRACE_FOCUS,
+ _T("Setting focus to %s(%s)"),
+ GetClassInfo()->GetClassName(), GetLabel().c_str());
+
+ gtk_widget_grab_focus (m_widget);
+ }
}
else if (GTK_IS_CONTAINER(m_widget))
{
}
else
{
- // ?
+ wxLogTrace(TRACE_FOCUS,
+ _T("Can't set focus to %s(%s)"),
+ GetClassInfo()->GetClassName(), GetLabel().c_str());
}
}
}
gdk_window_warp_pointer( window, x, y );
}
+
void wxWindowGTK::Refresh( bool eraseBackground, const wxRect *rect )
{
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)
}
void wxWindowGTK::Update()
+{
+ GtkUpdate();
+}
+
+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;
}
+ // Clip to paint region in wxClientDC
m_clipPaintRegion = TRUE;
- // if (!m_clearRegion.IsEmpty()) // always send an erase event
+#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
+ wxWindow *parent = GetParent();
+ while (parent && !parent->IsTopLevel())
+ parent = parent->GetParent();
+ if (!parent)
+ parent = (wxWindow*)this;
+
+ 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,
+ GTK_STATE_NORMAL,
+ GTK_SHADOW_NONE,
+ &rect,
+ parent->m_widget,
+ (char *)"base",
+ 0, 0, -1, -1 );
+
+ upd ++;
+ }
+ }
+ else
+#endif
+
+#ifdef __WXGTK20__
+ {
+ wxWindowDC dc( (wxWindow*)this );
+ 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 );
- dc.SetClippingRegion( m_clearRegion );
+ if (m_clearRegion.IsEmpty())
+ dc.SetClippingRegion( m_updateRegion );
+ else
+ dc.SetClippingRegion( m_clearRegion );
wxEraseEvent erase_event( GetId(), &dc );
erase_event.SetEventObject( this );
{
if (!g_eraseGC)
{
- g_eraseGC = gdk_gc_new( GTK_PIZZA(m_wxwindow)->bin_window );
+ g_eraseGC = gdk_gc_new( pizza->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() );
+ gdk_draw_rectangle( pizza->bin_window, g_eraseGC, 1,
+ upd.GetX(), upd.GetY(), upd.GetWidth(), upd.GetHeight() );
upd ++;
}
}
m_clearRegion.Clear();
}
+#endif
wxNcPaintEvent nc_paint_event( GetId() );
nc_paint_event.SetEventObject( this );
// 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)
{
{
wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
- if (!m_widget->window) return;
-
+#ifndef __WXGTK20__
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?
+ GtkUpdate();
}
+#endif
}
#if wxUSE_TOOLTIPS
}
#endif // wxUSE_TOOLTIPS
+void wxWindowGTK::GtkSetBackgroundColour( const wxColour &colour )
+{
+ GdkWindow *window = (GdkWindow*) NULL;
+ if (m_wxwindow)
+ window = GTK_PIZZA(m_wxwindow)->bin_window;
+ else
+ window = GetConnectWidget()->window;
+
+ wxASSERT( window );
+
+ // 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.
+ 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))
- {
- // don't leave if the GTK widget has just
- // been realized
- if (!m_delayedBackgroundColour) return FALSE;
- }
+ return FALSE;
GdkWindow *window = (GdkWindow*) NULL;
if (m_wxwindow)
// but it couldn't get applied as the
// widget hasn't been realized yet.
m_delayedBackgroundColour = TRUE;
+ return TRUE;
}
-
- if (window)
+ else
{
- // We need the pixel value e.g. for background clearing.
- m_backgroundColour.CalcPixel( gdk_window_get_colormap( window ) );
+ GtkSetBackgroundColour( colour );
}
-
- if ((m_wxwindow) &&
- (m_wxwindow->window) &&
- (m_backgroundColour != wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)))
- {
- /* 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() );
- }
+ return TRUE;
+}
- ApplyWidgetStyle();
+void wxWindowGTK::GtkSetForegroundColour( const wxColour &colour )
+{
+ GdkWindow *window = (GdkWindow*) NULL;
+ if (m_wxwindow)
+ window = GTK_PIZZA(m_wxwindow)->bin_window;
+ else
+ window = GetConnectWidget()->window;
- return TRUE;
+ wxASSERT( window );
+
+ ApplyWidgetStyle();
}
bool wxWindowGTK::SetForegroundColour( const wxColour &colour )
// widget hasn't been realized yet.
m_delayedForegroundColour = TRUE;
}
-
- ApplyWidgetStyle();
+ else
+ {
+ GtkSetForegroundColour( 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)
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())
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
-
}
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;
gdk_gc_unref( g_eraseGC );
}
+// vi:sts=4:sw=4:et