diff --git a/include/wx/gtk/window.h b/include/wx/gtk/window.h index 6c36ce99c1..2d37388a06 100644 --- a/include/wx/gtk/window.h +++ b/include/wx/gtk/window.h @@ -330,9 +330,7 @@ public: // find the direction of the given scrollbar (must be one of ours) ScrollDir ScrollDirFromRange(GtkRange *range) const; - // set the current cursor for all GdkWindows making part of this widget - // (see GTKGetWindow) - void GTKUpdateCursor(bool update_self = true, bool recurse = true); + void GTKUpdateCursor(bool isBusyOrGlobalCursor = false, bool isRealize = false); // extra (wxGTK-specific) flags bool m_noExpose:1; // wxGLCanvas has its own redrawing diff --git a/src/gtk/cursor.cpp b/src/gtk/cursor.cpp index c84da466c4..7b5436e5f2 100644 --- a/src/gtk/cursor.cpp +++ b/src/gtk/cursor.cpp @@ -283,7 +283,10 @@ void wxCursor::InitFromImage( const wxImage & image ) GdkCursor *wxCursor::GetCursor() const { - return M_CURSORDATA->m_cursor; + GdkCursor* cursor = NULL; + if (m_refData) + cursor = M_CURSORDATA->m_cursor; + return cursor; } wxGDIRefData *wxCursor::CreateGDIRefData() const @@ -308,119 +311,43 @@ wxCursor::CloneGDIRefData(const wxGDIRefData * WXUNUSED(data)) const // busy cursor routines //----------------------------------------------------------------------------- +wxCursor g_globalCursor; +wxCursor g_busyCursor; +static wxCursor gs_storedCursor; static int gs_busyCount = 0; -const wxCursor &wxBusyCursor::GetStoredCursor() +const wxCursor& wxBusyCursor::GetStoredCursor() { - static wxCursor s_storedCursor; - return s_storedCursor; + return gs_storedCursor; } const wxCursor wxBusyCursor::GetBusyCursor() { - return wxCursor(wxCURSOR_WATCH); + return g_busyCursor; } -void wxBeginBusyCursor(const wxCursor* cursor) +static void UpdateCursors(wxWindow* win, bool isBusyOrGlobalCursor) { - if (gs_busyCount++ == 0) - wxSetCursor(*cursor); + win->GTKUpdateCursor(isBusyOrGlobalCursor); + const wxWindowList& children = win->GetChildren(); + wxWindowList::const_iterator i = children.begin(); + for (size_t n = children.size(); n--; ++i) + UpdateCursors(*i, isBusyOrGlobalCursor); } -void wxEndBusyCursor() -{ - if (gs_busyCount && --gs_busyCount == 0) - wxSetCursor(wxCursor()); -} - -bool wxIsBusy() -{ - return gs_busyCount > 0; -} - -// Table holding non-default cursors to be restored when global cursor is unset -WX_DECLARE_HASH_MAP(GdkWindow*, GdkCursor*, wxPointerHash, wxPointerEqual, wxGdkWindowGdkCursorMapBase); -class wxGdkWindowGdkCursorMap: public wxGdkWindowGdkCursorMapBase -{ -public: - ~wxGdkWindowGdkCursorMap(); -}; -static wxGdkWindowGdkCursorMap* gs_windowCursorMap; - -wxGdkWindowGdkCursorMap::~wxGdkWindowGdkCursorMap() -{ - const_iterator i = begin(); - for (size_t n = size(); n--; ++i) - { -#ifdef __WXGTK3__ - g_object_unref(i->second); -#else - gdk_cursor_unref(i->second); -#endif - } -} - -// Set cursor to default for window and all its children, -// recording non-default cursors in gs_windowCursorMap -static void clearCursors(GdkWindow* window) -{ - GdkCursor* cursor = gdk_window_get_cursor(window); - if (cursor) - { -#ifdef __WXGTK3__ - g_object_ref(cursor); -#else - gdk_cursor_ref(cursor); -#endif - (*gs_windowCursorMap)[window] = cursor; - gdk_window_set_cursor(window, NULL); - } - for (const GList* p = gdk_window_peek_children(window); p; p = p->next) - clearCursors(static_cast(p->data)); -} - -// Restore non-default cursors -static void restoreCursors(GdkWindow* window) -{ - wxGdkWindowGdkCursorMap::const_iterator i = gs_windowCursorMap->find(window); - if (i != gs_windowCursorMap->end()) - gdk_window_set_cursor(window, i->second); - for (const GList* p = gdk_window_peek_children(window); p; p = p->next) - restoreCursors(static_cast(p->data)); -} - -void wxSetCursor( const wxCursor& cursor ) +static void SetGlobalCursor(const wxCursor& cursor) { + GdkCursor* gdk_cursor = cursor.GetCursor(); GdkDisplay* display = NULL; wxWindowList::const_iterator i = wxTopLevelWindows.begin(); for (size_t n = wxTopLevelWindows.size(); n--; ++i) { - GtkWidget* widget = (*i)->m_widget; + wxWindow* win = *i; GdkWindow* window; - if (widget && (window = gtk_widget_get_window(widget))) + if (win->m_widget && (window = gtk_widget_get_window(win->m_widget))) { - // if setting global cursor - if (cursor.IsOk()) - { - delete gs_windowCursorMap; - gs_windowCursorMap = new wxGdkWindowGdkCursorMap; - // clear all cursors, saving non-default ones for later - clearCursors(window); - // set global cursor - gdk_window_set_cursor(window, cursor.GetCursor()); - } - else - { - // remove global cursor - gdk_window_set_cursor(window, NULL); - if (gs_windowCursorMap) - { - // restore non-default cursors - restoreCursors(window); - delete gs_windowCursorMap; - gs_windowCursorMap = NULL; - } - } + gdk_window_set_cursor(window, gdk_cursor); + UpdateCursors(win, gdk_cursor != NULL); if (display == NULL) display = gdk_window_get_display(window); } @@ -428,3 +355,35 @@ void wxSetCursor( const wxCursor& cursor ) if (display) gdk_display_flush(display); } + +void wxBeginBusyCursor(const wxCursor* cursor) +{ + if (gs_busyCount++ == 0) + { + g_busyCursor = *cursor; + gs_storedCursor = g_globalCursor; + SetGlobalCursor(*cursor); + } +} + +void wxEndBusyCursor() +{ + if (gs_busyCount && --gs_busyCount == 0) + { + g_globalCursor = gs_storedCursor; + gs_storedCursor = + g_busyCursor = wxCursor(); + SetGlobalCursor(g_globalCursor); + } +} + +bool wxIsBusy() +{ + return gs_busyCount > 0; +} + +void wxSetCursor( const wxCursor& cursor ) +{ + g_globalCursor = cursor; + SetGlobalCursor(cursor); +} diff --git a/src/gtk/dialog.cpp b/src/gtk/dialog.cpp index 6ad78a8c0e..2cdf18098f 100644 --- a/src/gtk/dialog.cpp +++ b/src/gtk/dialog.cpp @@ -160,15 +160,15 @@ int wxDialog::ShowModal() } #endif + // NOTE: this will cause a gtk_grab_add() during Show() + gtk_window_set_modal(GTK_WINDOW(m_widget), true); + Show( true ); m_modalShowing = true; wxOpenModalDialogLocker modalLock; - // NOTE: gtk_window_set_modal internally calls gtk_grab_add() ! - gtk_window_set_modal(GTK_WINDOW(m_widget), TRUE); - // Run modal dialog event loop. { wxGUIEventLoopTiedPtr modal(&m_modalLoop, new wxGUIEventLoop()); diff --git a/src/gtk/toplevel.cpp b/src/gtk/toplevel.cpp index 19af5b8cf9..78cec5d3ce 100644 --- a/src/gtk/toplevel.cpp +++ b/src/gtk/toplevel.cpp @@ -74,6 +74,9 @@ static wxTopLevelWindowGTK *g_lastActiveFrame = NULL; // send any activate events at all static int g_sendActivateEvent = -1; +extern wxCursor g_globalCursor; +extern wxCursor g_busyCursor; + #ifdef GDK_WINDOWING_X11 // Whether _NET_REQUEST_FRAME_EXTENTS support is working static enum { @@ -351,15 +354,22 @@ void wxTopLevelWindowGTK::GTKHandleRealized() { wxNonOwnedWindow::GTKHandleRealized(); - gdk_window_set_decorations(gtk_widget_get_window(m_widget), - (GdkWMDecoration)m_gdkDecor); - gdk_window_set_functions(gtk_widget_get_window(m_widget), - (GdkWMFunction)m_gdkFunc); + GdkWindow* window = gtk_widget_get_window(m_widget); + + gdk_window_set_decorations(window, (GdkWMDecoration)m_gdkDecor); + gdk_window_set_functions(window, (GdkWMFunction)m_gdkFunc); const wxIconBundle& icons = GetIcons(); if (icons.GetIconCount()) SetIcons(icons); + GdkCursor* cursor = g_globalCursor.GetCursor(); + if (wxIsBusy() && !gtk_window_get_modal(GTK_WINDOW(m_widget))) + cursor = g_busyCursor.GetCursor(); + + if (cursor) + gdk_window_set_cursor(window, cursor); + #ifdef __WXGTK3__ if (gtk_window_get_has_resize_grip(GTK_WINDOW(m_widget))) { diff --git a/src/gtk/window.cpp b/src/gtk/window.cpp index 9045ef5f6e..cbefad8985 100644 --- a/src/gtk/window.cpp +++ b/src/gtk/window.cpp @@ -197,6 +197,7 @@ typedef guint KeySym; bool g_blockEventsOnDrag; // Don't allow mouse event propagation during scroll bool g_blockEventsOnScroll; +extern wxCursor g_globalCursor; // mouse capture state: the window which has it and if the mouse is currently // inside it @@ -2067,7 +2068,7 @@ void wxWindowGTK::GTKHandleRealized() event.SetEventObject( this ); GTKProcessEvent( event ); - GTKUpdateCursor(true, false); + GTKUpdateCursor(false, true); if (m_wxwindow && IsTopLevel()) { @@ -3663,35 +3664,50 @@ bool wxWindowGTK::SetCursor( const wxCursor &cursor ) if (!wxWindowBase::SetCursor(cursor)) return false; - if (m_cursor.IsOk()) - GTKUpdateCursor(); + GTKUpdateCursor(); return true; } -void wxWindowGTK::GTKUpdateCursor(bool, bool) +void wxWindowGTK::GTKUpdateCursor(bool isBusyOrGlobalCursor, bool isRealize) { if (m_widget == NULL || !gtk_widget_get_realized(m_widget)) return; - GdkCursor* cursor = NULL; - if (m_cursor.IsOk()) - cursor = m_cursor.GetCursor(); - - wxArrayGdkWindows windows; - GdkWindow* window = GTKGetWindow(windows); - if (window) - gdk_window_set_cursor(window, cursor); - else + // if we don't already know there is a busy/global cursor, we have to check for one + if (!isBusyOrGlobalCursor) { - for (size_t i = windows.size(); i--;) + if (g_globalCursor.IsOk()) + isBusyOrGlobalCursor = true; + else if (wxIsBusy()) { - window = windows[i]; - if (window) - gdk_window_set_cursor(window, cursor); + wxWindow* win = wxGetTopLevelParent(this); + if (win && win->m_widget && !gtk_window_get_modal(GTK_WINDOW(win->m_widget))) + isBusyOrGlobalCursor = true; } } - if (window && cursor == NULL && m_wxwindow == NULL) + GdkCursor* cursor = NULL; + if (!isBusyOrGlobalCursor) + cursor = m_cursor.GetCursor(); + + GdkWindow* window = NULL; + if (cursor || isBusyOrGlobalCursor || !isRealize) + { + wxArrayGdkWindows windows; + window = GTKGetWindow(windows); + if (window) + gdk_window_set_cursor(window, cursor); + else + { + for (size_t i = windows.size(); i--;) + { + window = windows[i]; + if (window) + gdk_window_set_cursor(window, cursor); + } + } + } + if (window && cursor == NULL && m_wxwindow == NULL && !isBusyOrGlobalCursor && !isRealize) { void* data; gdk_window_get_user_data(window, &data);