#include "wx/fontutil.h"
#include "wx/sysopt.h"
-#ifdef __WXDEBUG__
- #include "wx/thread.h"
-#endif
-
#include <ctype.h>
#include "wx/gtk/private.h"
-#include "wx/gtk/win_gtk.h"
+#include "wx/gtk/private/win_gtk.h"
#include <gdk/gdkkeysyms.h>
#include <gdk/gdkx.h>
static wxWindowGTK *g_captureWindow = (wxWindowGTK*) NULL;
static bool g_captureWindowHasMouse = false;
-wxWindowGTK *g_focusWindow = (wxWindowGTK*) NULL;
+// The window that currently has focus or is scheduled to get it in the next
+// event loop iteration
+static wxWindowGTK *gs_focusWindow = 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
-wxWindowGTK *g_focusWindowLast = (wxWindowGTK*) NULL;
-
-// If a window get the focus set but has not been realized
-// yet, defer setting the focus to idle time.
-wxWindowGTK *g_delayedFocus = (wxWindowGTK*) NULL;
+// the window that has deferred focus-out event pending, if any (see
+// GTKAddDeferredFocusOut() for details)
+static wxWindowGTK *gs_deferredFocusOut = NULL;
// global variables because GTK+ DnD want to have the
// mouse event that caused it
GdkEvent *g_lastMouseEvent = (GdkEvent*) NULL;
int g_lastButtonNumber = 0;
-extern bool g_mainThreadLocked;
-
//-----------------------------------------------------------------------------
// debug
//-----------------------------------------------------------------------------
-#ifdef __WXDEBUG__
-
-#if wxUSE_THREADS
-# define DEBUG_MAIN_THREAD if (wxThread::IsMain() && g_mainThreadLocked) printf("gui reentrance");
-#else
-# define DEBUG_MAIN_THREAD
-#endif
-#else
-#define DEBUG_MAIN_THREAD
-#endif // Debug
-
// the trace mask used for the focus debugging messages
#define TRACE_FOCUS _T("focus")
}
}
-//-----------------------------------------------------------------------------
-// local code (see below)
-//-----------------------------------------------------------------------------
-
-// returns the child of win which currently has focus or NULL if not found
-//
-// Note: can't be static, needed by textctrl.cpp.
-wxWindow *wxFindFocusedChild(wxWindowGTK *win)
-{
- wxWindow *winFocus = wxWindowGTK::FindFocus();
- if ( !winFocus )
- return (wxWindow *)NULL;
-
- if ( winFocus == win )
- return (wxWindow *)win;
-
- for ( wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst();
- node;
- node = node->GetNext() )
- {
- wxWindow *child = wxFindFocusedChild(node->GetData());
- if ( child )
- return child;
- }
-
- return (wxWindow *)NULL;
-}
-
-static void GetScrollbarWidth(GtkWidget* widget, int& w, int& h)
-{
- GtkScrolledWindow* scroll_window = GTK_SCROLLED_WINDOW(widget);
- GtkScrolledWindowClass* scroll_class = GTK_SCROLLED_WINDOW_CLASS(GTK_OBJECT_GET_CLASS(scroll_window));
- GtkRequisition scroll_req;
-
- w = 0;
- if (scroll_window->vscrollbar_visible)
- {
- scroll_req.width = 2;
- scroll_req.height = 2;
- (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->vscrollbar) )->size_request )
- (scroll_window->vscrollbar, &scroll_req );
- w = scroll_req.width +
- scroll_class->scrollbar_spacing;
- }
-
- h = 0;
- if (scroll_window->hscrollbar_visible)
- {
- scroll_req.width = 2;
- scroll_req.height = 2;
- (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->hscrollbar) )->size_request )
- (scroll_window->hscrollbar, &scroll_req );
- h = scroll_req.height +
- scroll_class->scrollbar_spacing;
- }
-}
//-----------------------------------------------------------------------------
// "size_request" of m_widget
GdkEventExpose *gdk_event,
wxWindow *win )
{
- DEBUG_MAIN_THREAD
-
#if 0
if (win->GetName())
{
GdkEventKey *gdk_event,
wxWindow *win )
{
- DEBUG_MAIN_THREAD
-
if (!win->m_hasVMT)
return FALSE;
if (g_blockEventsOnDrag)
GdkEventKey *gdk_event,
wxWindowGTK *win )
{
- DEBUG_MAIN_THREAD
-
if (!win->m_hasVMT)
return FALSE;
int wxWindowGTK::GTKCallbackCommonPrologue(GdkEventAny *event) const
{
- DEBUG_MAIN_THREAD
-
if (!m_hasVMT)
return FALSE;
if (g_blockEventsOnDrag)
if ( rc != -1 ) \
return rc
-// 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->HandleWindowEvent(eventChildFocus);
-
- wxFocusEvent eventFocus(wxEVT_SET_FOCUS, win->GetId());
- eventFocus.SetEventObject(win);
-
- return win->HandleWindowEvent(eventFocus);
-}
-
// all event handlers must have C linkage as they're called from GTK+ C code
extern "C"
{
return TRUE;
if ((event_type == wxEVT_LEFT_DOWN) && !win->IsOfStandardClass() &&
- (g_focusWindow != win) /* && win->IsFocusable() */)
+ (gs_focusWindow != win) /* && win->IsFocusable() */)
{
win->SetFocus();
}
static gboolean
window_scroll_event(GtkWidget*, GdkEventScroll* gdk_event, wxWindow* win)
{
- DEBUG_MAIN_THREAD
-
if (gdk_event->direction != GDK_SCROLL_UP &&
gdk_event->direction != GDK_SCROLL_DOWN)
{
wxMouseEvent event(wxEVT_MOUSEWHEEL);
InitMouseEvent(win, event, gdk_event);
+
+ // FIXME: Get these values from GTK or GDK
event.m_linesPerAction = 3;
event.m_wheelDelta = 120;
if (gdk_event->direction == GDK_SCROLL_UP)
static gboolean
gtk_window_focus_in_callback( GtkWidget * WXUNUSED(widget),
GdkEventFocus *WXUNUSED(event),
- wxWindow *win )
+ wxWindowGTK *win )
{
- DEBUG_MAIN_THREAD
-
- if (win->m_imData)
- gtk_im_context_focus_in(win->m_imData->context);
-
- g_focusWindowLast =
- g_focusWindow = win;
-
- wxLogTrace(TRACE_FOCUS,
- _T("%s: focus in"), win->GetName().c_str());
-
-#if wxUSE_CARET
- // caret needs to be informed about focus change
- wxCaret *caret = win->GetCaret();
- if ( caret )
- {
- caret->OnSetFocus();
- }
-#endif // wxUSE_CARET
-
- gboolean ret = FALSE;
-
- // does the window itself think that it has the focus?
- if ( !win->m_hasFocus )
- {
- // not yet, notify it
- win->m_hasFocus = true;
-
- (void)DoSendFocusEvents(win);
-
- ret = TRUE;
- }
-
- // Disable default focus handling for custom windows
- // since the default GTK+ handler issues a repaint
- if (win->m_wxwindow)
- return ret;
-
- return FALSE;
+ return win->GTKHandleFocusIn();
}
//-----------------------------------------------------------------------------
GdkEventFocus * WXUNUSED(gdk_event),
wxWindowGTK *win )
{
- DEBUG_MAIN_THREAD
-
- if (win->m_imData)
- gtk_im_context_focus_out(win->m_imData->context);
-
- wxLogTrace( TRACE_FOCUS,
- _T("%s: focus out"), win->GetName().c_str() );
-
-
- wxWindowGTK *winFocus = wxFindFocusedChild(win);
- if ( winFocus )
- win = winFocus;
-
- g_focusWindow = (wxWindowGTK *)NULL;
-
-#if wxUSE_CARET
- // caret needs to be informed about focus change
- wxCaret *caret = win->GetCaret();
- if ( caret )
- {
- caret->OnKillFocus();
- }
-#endif // wxUSE_CARET
-
- // don't send the window a kill focus event if it thinks that it doesn't
- // have focus already
- if ( win->m_hasFocus )
- {
- // the event handler might delete the window when it loses focus, so
- // check whether this is a custom window before calling it
- const bool has_wxwindow = win->m_wxwindow != NULL;
-
- win->m_hasFocus = false;
-
- wxFocusEvent event( wxEVT_KILL_FOCUS, win->GetId() );
- event.SetEventObject( win );
-
- (void)win->GTKProcessEvent( event );
-
- // Disable default focus handling for custom windows
- // since the default GTK+ handler issues a repaint
- if ( has_wxwindow )
- return TRUE;
- }
-
- // continue with normal processing
- return FALSE;
+ return win->GTKHandleFocusOut();
}
+//-----------------------------------------------------------------------------
+// "focus"
+//-----------------------------------------------------------------------------
+
static gboolean
wx_window_focus_callback(GtkWidget *widget,
GtkDirectionType WXUNUSED(direction),
static gboolean
gtk_scrollbar_button_press_event(GtkRange*, GdkEventButton*, wxWindow* win)
{
- DEBUG_MAIN_THREAD
-
g_blockEventsOnScroll = true;
win->m_mouseButtonDown = true;
static gboolean
gtk_scrollbar_button_release_event(GtkRange* range, GdkEventButton*, wxWindow* win)
{
- DEBUG_MAIN_THREAD
-
g_blockEventsOnScroll = false;
win->m_mouseButtonDown = false;
// If thumb tracking
static void
gtk_window_realized_callback(GtkWidget* widget, wxWindow* win)
{
- DEBUG_MAIN_THREAD
-
if (win->m_imData)
{
gtk_im_context_set_client_window( win->m_imData->context,
wxWindow *wxWindowBase::DoFindFocus()
{
// the cast is necessary when we compile in wxUniversal mode
- return (wxWindow *)g_focusWindow;
+ return wx_static_cast(wxWindow*, gs_focusWindow);
}
//-----------------------------------------------------------------------------
m_noExpose = false;
m_nativeSizeEvent = false;
- m_hasScrolling = false;
m_isScrolling = false;
m_mouseButtonDown = false;
m_insertCallback = wxInsertChildInWindow;
- m_hasFocus = false;
-
m_clipPaintRegion = false;
m_needsStyleChange = false;
{
SendDestroyEvent();
- if (g_focusWindow == this)
- g_focusWindow = NULL;
+ if (gs_focusWindow == this)
+ gs_focusWindow = NULL;
- if ( g_delayedFocus == this )
- g_delayedFocus = NULL;
+ if ( gs_deferredFocusOut == this )
+ gs_deferredFocusOut = NULL;
m_isBeingDeleted = true;
m_hasVMT = false;
void wxWindowGTK::OnInternalIdle()
{
+ if ( gs_deferredFocusOut )
+ GTKHandleDeferredFocusOut();
+
// Check if we have to show window now
if (GtkShowFromOnIdle()) return;
}
}
- if (wxUpdateUIEvent::CanUpdate(this) && IsShown())
+ if (wxUpdateUIEvent::CanUpdate(this) && IsShownOnScreen())
UpdateWindowUI(wxUPDATE_UI_FROMIDLE);
}
if (m_wxwindow)
{
- int dw = 0;
- int dh = 0;
-
- if (m_hasScrolling)
- GetScrollbarWidth(m_widget, dw, dh);
+ // if window is scrollable, account for scrollbars
+ for (int i = 0; i < 2 && m_scrollBar[i]; i++)
+ {
+ GtkRequisition req;
+ GtkAdjustment* adj = gtk_range_get_adjustment(m_scrollBar[i]);
+ // if scrollbar enabled
+ if (adj->upper > adj->page_size)
+ {
+ gtk_widget_size_request(GTK_WIDGET(m_scrollBar[i]), &req);
+ if (i == ScrollDir_Horz)
+ h -= req.height;
+ else
+ w -= req.width;
+ }
+ }
int border_x, border_y;
WX_PIZZA(m_wxwindow)->get_border_widths(border_x, border_y);
- dw += 2 * border_x;
- dh += 2 * border_y;
+ w -= 2 * border_x;
+ h -= 2 * border_y;
- w -= dw;
- h -= dh;
if (w < 0)
w = 0;
if (h < 0)
g_object_unref (layout);
}
-bool wxWindowGTK::GTKSetDelayedFocusIfNeeded()
+
+bool wxWindowGTK::GTKHandleFocusIn()
{
- if ( g_delayedFocus == this )
+ // Disable default focus handling for custom windows since the default GTK+
+ // handler issues a repaint
+ const bool retval = m_wxwindow ? true : false;
+
+
+ // NB: if there's still unprocessed deferred focus-out event (see
+ // GTKHandleFocusOut() for explanation), we need to process it first so
+ // that the order of focus events -- focus-out first, then focus-in
+ // elsewhere -- is preserved
+ if ( gs_deferredFocusOut )
{
- if ( GTK_WIDGET_REALIZED(m_widget) )
+ if ( GTKNeedsToFilterSameWindowFocus() &&
+ gs_deferredFocusOut == this )
{
- gtk_widget_grab_focus(m_widget);
- g_delayedFocus = NULL;
-
- return true;
+ // GTK+ focus changed from this wxWindow back to itself, so don't
+ // emit any events at all
+ wxLogTrace(TRACE_FOCUS,
+ "filtered out spurious focus change within %s(%p, %s)",
+ GetClassInfo()->GetClassName(), this, GetLabel());
+ gs_deferredFocusOut = NULL;
+ return retval;
}
+
+ // otherwise we need to send focus-out first
+ wxASSERT_MSG ( gs_deferredFocusOut != this,
+ "GTKHandleFocusIn(GTKFocus_Normal) called even though focus changed back to itself - derived class should handle this" );
+ GTKHandleDeferredFocusOut();
}
- return false;
+
+ wxLogTrace(TRACE_FOCUS,
+ "handling focus_in event for %s(%p, %s)",
+ GetClassInfo()->GetClassName(), this, GetLabel());
+
+ if (m_imData)
+ gtk_im_context_focus_in(m_imData->context);
+
+ // NB: SetFocus() does this assignment too, but not all focus changes
+ // originate from SetFocus() call
+ gs_focusWindow = this;
+
+#if wxUSE_CARET
+ // caret needs to be informed about focus change
+ wxCaret *caret = GetCaret();
+ if ( caret )
+ {
+ caret->OnSetFocus();
+ }
+#endif // wxUSE_CARET
+
+ // Notify the parent keeping track of focus for the kbd navigation
+ // purposes that we got it.
+ wxChildFocusEvent eventChildFocus(this);
+ GTKProcessEvent(eventChildFocus);
+
+ wxFocusEvent eventFocus(wxEVT_SET_FOCUS, GetId());
+ eventFocus.SetEventObject(this);
+ GTKProcessEvent(eventFocus);
+
+ return retval;
}
-void wxWindowGTK::SetFocus()
+bool wxWindowGTK::GTKHandleFocusOut()
{
- wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
- if ( m_hasFocus )
+ // Disable default focus handling for custom windows since the default GTK+
+ // handler issues a repaint
+ const bool retval = m_wxwindow ? true : false;
+
+
+ // NB: If a control is composed of several GtkWidgets and when focus
+ // changes from one of them to another within the same wxWindow, we get
+ // a focus-out event followed by focus-in for another GtkWidget owned
+ // by the same wx control. We don't want to generate two spurious
+ // wxEVT_SET_FOCUS events in this case, so we defer sending wx events
+ // from GTKHandleFocusOut() until we know for sure it's not coming back
+ // (i.e. in GTKHandleFocusIn() or at idle time).
+ if ( GTKNeedsToFilterSameWindowFocus() )
{
- // don't do anything if we already have focus
- return;
+ wxASSERT_MSG( gs_deferredFocusOut == NULL,
+ "deferred focus out event already pending" );
+ wxLogTrace(TRACE_FOCUS,
+ "deferring focus_out event for %s(%p, %s)",
+ GetClassInfo()->GetClassName(), this, GetLabel());
+ gs_deferredFocusOut = this;
+ return retval;
}
- if (m_wxwindow)
+ GTKHandleFocusOutNoDeferring();
+
+ return retval;
+}
+
+void wxWindowGTK::GTKHandleFocusOutNoDeferring()
+{
+ wxLogTrace(TRACE_FOCUS,
+ "handling focus_out event for %s(%p, %s)",
+ GetClassInfo()->GetClassName(), this, GetLabel());
+
+ if (m_imData)
+ gtk_im_context_focus_out(m_imData->context);
+
+ if ( gs_focusWindow != this )
{
- // wxWindow::SetFocus() should really set the focus to
- // this control, whatever the flags are
- if (!GTK_WIDGET_CAN_FOCUS(m_wxwindow))
- GTK_WIDGET_SET_FLAGS(m_wxwindow, GTK_CAN_FOCUS);
+ // Something is terribly wrong, gs_focusWindow is out of sync with the
+ // real focus. We will reset it to NULL anyway, because after this
+ // focus-out event is handled, one of the following with happen:
+ //
+ // * either focus will go out of the app altogether, in which case
+ // gs_focusWindow _should_ be NULL
+ //
+ // * or it goes to another control, in which case focus-in event will
+ // follow immediately and it will set gs_focusWindow to the right
+ // value
+ wxLogDebug("window %s(%p, %s) lost focus even though it didn't have it",
+ GetClassInfo()->GetClassName(), this, GetLabel());
+ }
+ gs_focusWindow = NULL;
- if (!GTK_WIDGET_HAS_FOCUS (m_wxwindow))
- {
- gtk_widget_grab_focus (m_wxwindow);
- }
+#if wxUSE_CARET
+ // caret needs to be informed about focus change
+ wxCaret *caret = GetCaret();
+ if ( caret )
+ {
+ caret->OnKillFocus();
}
- else
+#endif // wxUSE_CARET
+
+ wxFocusEvent event( wxEVT_KILL_FOCUS, GetId() );
+ event.SetEventObject( this );
+ GTKProcessEvent( event );
+}
+
+/*static*/
+void wxWindowGTK::GTKHandleDeferredFocusOut()
+{
+ // NB: See GTKHandleFocusOut() for explanation. This function is called
+ // from either GTKHandleFocusIn() or OnInternalIdle() to process
+ // deferred event.
+ if ( gs_deferredFocusOut )
{
- // wxWindow::SetFocus() should really set the focus to
- // this control, whatever the flags are
- if (!GTK_WIDGET_CAN_FOCUS(m_widget))
- GTK_WIDGET_SET_FLAGS(m_widget, GTK_CAN_FOCUS);
+ wxWindowGTK *win = gs_deferredFocusOut;
+ gs_deferredFocusOut = NULL;
- if (GTK_IS_CONTAINER(m_widget))
- {
- if (GTK_IS_RADIO_BUTTON(m_widget))
- {
- gtk_widget_grab_focus (m_widget);
- return;
- }
+ wxLogTrace(TRACE_FOCUS,
+ "processing deferred focus_out event for %s(%p, %s)",
+ win->GetClassInfo()->GetClassName(), win, win->GetLabel());
- gtk_widget_child_focus( m_widget, GTK_DIR_TAB_FORWARD );
- }
- else
- if (GTK_WIDGET_CAN_FOCUS(m_widget) && !GTK_WIDGET_HAS_FOCUS (m_widget) )
- {
+ win->GTKHandleFocusOutNoDeferring();
+ }
+}
- 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());
+void wxWindowGTK::SetFocus()
+{
+ wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
- gtk_widget_grab_focus (m_widget);
- }
- }
- else
- {
- wxLogTrace(TRACE_FOCUS,
- _T("Can't set focus to %s(%s)"),
- GetClassInfo()->GetClassName(), GetLabel().c_str());
- }
+ // Setting "physical" focus is not immediate in GTK+ and while
+ // gtk_widget_is_focus ("determines if the widget is the focus widget
+ // within its toplevel", i.e. returns true for one widget per TLW, not
+ // globally) returns true immediately after grabbing focus,
+ // GTK_WIDGET_HAS_FOCUS (which returns true only for the one widget that
+ // has focus at the moment) takes affect only after the window is shown
+ // (if it was hidden at the moment of the call) or at the next event loop
+ // iteration.
+ //
+ // Because we want to FindFocus() call immediately following
+ // foo->SetFocus() to return foo, we have to keep track of "pending" focus
+ // ourselves.
+ gs_focusWindow = this;
+
+ GtkWidget *widget = m_wxwindow ? m_wxwindow : m_focusWidget;
+
+ if ( GTK_IS_CONTAINER(widget) &&
+ !GTK_WIDGET_CAN_FOCUS(widget) )
+ {
+ wxLogTrace(TRACE_FOCUS,
+ _T("Setting focus to a child of %s(%p, %s)"),
+ GetClassInfo()->GetClassName(), this, GetLabel().c_str());
+ gtk_widget_child_focus(widget, GTK_DIR_TAB_FORWARD);
+ }
+ else
+ {
+ wxLogTrace(TRACE_FOCUS,
+ _T("Setting focus to %s(%p, %s)"),
+ GetClassInfo()->GetClassName(), this, GetLabel().c_str());
+ gtk_widget_grab_focus(widget);
}
}
GtkRange* const sb = m_scrollBar[dir];
wxCHECK_RET( sb, _T("this window is not scrollable") );
- if (range > 0)
- {
- m_hasScrolling = true;
- }
- else
+ if (range <= 0)
{
// GtkRange requires upper > lower
range =
wxEventType wxWindowGTK::GetScrollEventType(GtkRange* range)
{
- DEBUG_MAIN_THREAD
-
wxASSERT(range == m_scrollBar[0] || range == m_scrollBar[1]);
const int barIndex = range == m_scrollBar[1];
window = m_wxwindow->window;
return window;
}
+
+// ----------------------------------------------------------------------------
+// freeze/thaw
+// ----------------------------------------------------------------------------
+
+void wxWindowGTK::GTKFreezeWidget(GtkWidget *w)
+{
+ if ( w && !GTK_WIDGET_NO_WINDOW(w) )
+ gdk_window_freeze_updates(w->window);
+}
+
+void wxWindowGTK::GTKThawWidget(GtkWidget *w)
+{
+ if ( w && !GTK_WIDGET_NO_WINDOW(w) )
+ gdk_window_thaw_updates(w->window);
+}
+
+void wxWindowGTK::DoFreeze()
+{
+ GTKFreezeWidget(m_widget);
+ if ( m_wxwindow && m_widget != m_wxwindow )
+ GTKFreezeWidget(m_wxwindow);
+}
+
+void wxWindowGTK::DoThaw()
+{
+ GTKThawWidget(m_widget);
+ if ( m_wxwindow && m_widget != m_wxwindow )
+ GTKThawWidget(m_wxwindow);
+}