diff --git a/lcl/interfaces/gtk2/gtk2def.pp b/lcl/interfaces/gtk2/gtk2def.pp index 1553460215..33bdb3a6b8 100644 --- a/lcl/interfaces/gtk2/gtk2def.pp +++ b/lcl/interfaces/gtk2/gtk2def.pp @@ -472,7 +472,6 @@ type DoubleBuffer: PGdkPixmap; CursorPos: integer; // needed for delayed SetSelStart ControlCursor: HCursor; // current widget cursor - DefaultCursor: HCursor; // default widget cursor Flags: TWidgetInfoFlags; ChangeLock: Integer; // lock events PaintDepth: integer; // increased/decreased by Begin/EndPaint diff --git a/lcl/interfaces/gtk2/gtk2extra.inc b/lcl/interfaces/gtk2/gtk2extra.inc index 61d5bdb309..bce6cfad0f 100644 --- a/lcl/interfaces/gtk2/gtk2extra.inc +++ b/lcl/interfaces/gtk2/gtk2extra.inc @@ -56,6 +56,7 @@ end; var gtkhandle: tlibhandle; glibhandle: tlibhandle; + gdklibhandle: tlibhandle; libIter: Integer; initialization @@ -77,9 +78,14 @@ initialization break; end; end; + gdklibhandle := LoadLibrary(gdklib); + if gdklibhandle <> 0 + then pointer(gdk_window_get_cursor):=GetProcAddress(gdklibhandle,'gdk_window_get_cursor'); finalization if gtkhandle <> 0 then FreeLibrary(gtkhandle); if glibhandle <> 0 then FreeLibrary(glibhandle); + if gdklibhandle <> 0 then + FreeLibrary(gdklibhandle); diff --git a/lcl/interfaces/gtk2/gtk2extrah.inc b/lcl/interfaces/gtk2/gtk2extrah.inc index 36977756dd..9eb5e757a5 100644 --- a/lcl/interfaces/gtk2/gtk2extrah.inc +++ b/lcl/interfaces/gtk2/gtk2extrah.inc @@ -228,6 +228,7 @@ function gdk_screen_is_composited(screen: PGdkScreen): gboolean; cdecl; external var gtk_window_set_opacity: procedure(window: PGtkWindow; opacity: gdouble); cdecl; g_object_ref_sink: function(anObject: PGObject): gpointer; cdecl; + gdk_window_get_cursor: function(window: PGdkWindow): PGdkCursor; cdecl; {$ifdef ver2_2} {$ifdef darwin} diff --git a/lcl/interfaces/gtk2/gtk2privatewidget.inc b/lcl/interfaces/gtk2/gtk2privatewidget.inc index e0ca1e8f34..07d2908a8d 100644 --- a/lcl/interfaces/gtk2/gtk2privatewidget.inc +++ b/lcl/interfaces/gtk2/gtk2privatewidget.inc @@ -29,14 +29,9 @@ begin // always recurse windows which do not accept controls. // this way we will catch all widgets with double windows if not (csAcceptsControls in TControl(AInfo^.LCLObject).ControlStyle) then - SetWindowCursor(Window, AInfo^.ControlCursor, True) + SetWindowCursor(Window, AInfo^.ControlCursor, False, True) else - SetCursorForWindowsWithInfo(Window, AInfo); -end; - -class procedure TGtkPrivateWidget.SetDefaultCursor(AInfo: PWidgetInfo); -begin - AInfo^.DefaultCursor := Screen.Cursors[crDefault]; + SetCursorForWindowsWithInfo(Window, AInfo, True); end; class procedure TGtkPrivateWidget.SetZPosition(const AWinControl: TWinControl; const APosition: TWSZPosition); @@ -70,17 +65,12 @@ begin Widget := AInfo^.CoreWidget; Window := PGTkPaned(Widget)^.handle; if Window = nil then Exit; - SetWindowCursor(Window, AInfo^.ControlCursor, False); + SetWindowCursor(Window, AInfo^.ControlCursor, False, True); end; { TGtkPrivateEntry } -class procedure TGtkPrivateEntry.SetDefaultCursor(AInfo: PWidgetInfo); -begin - AInfo^.DefaultCursor := Screen.Cursors[crIBeam]; -end; - class procedure TGtk2PrivateButton.UpdateCursor(AInfo: PWidgetInfo); var Widget: PGtkWidget; @@ -90,7 +80,7 @@ begin if (Widget = nil) or not GTK_IS_BUTTON(Widget) then Exit; Window := PGtkButton(Widget)^.event_window; if Window = nil then Exit; - SetWindowCursor(Window, AInfo^.ControlCursor, False); + SetWindowCursor(Window, AInfo^.ControlCursor, False, True); end; class procedure TGtk2PrivateNotebook.UpdateCursor(AInfo: PWidgetInfo); @@ -110,9 +100,9 @@ var // always recurse windows which do not accept controls. // this way we will catch all widgets with double windows if not (csAcceptsControls in TControl(AInfo^.LCLObject).ControlStyle) then - SetWindowCursor(Window, AInfo^.ControlCursor, True) + SetWindowCursor(Window, AInfo^.ControlCursor, True, True) else - SetCursorForWindowsWithInfo(Window, AInfo); + SetCursorForWindowsWithInfo(Window, AInfo, True); end; begin @@ -124,7 +114,7 @@ begin Widget := AInfo^.CoreWidget; Window := PGTkNotebook(Widget)^.event_window; if Window <> nil then - SetWindowCursor(Window, AInfo^.ControlCursor, False); + SetWindowCursor(Window, AInfo^.ControlCursor, False, True); // do not know how to set cursor under tabs end; diff --git a/lcl/interfaces/gtk2/gtk2proc.inc b/lcl/interfaces/gtk2/gtk2proc.inc index 716b2eef57..f4ca6e4348 100644 --- a/lcl/interfaces/gtk2/gtk2proc.inc +++ b/lcl/interfaces/gtk2/gtk2proc.inc @@ -3918,7 +3918,6 @@ begin New(Result); FillChar(Result^, SizeOf(Result^), 0); gtk_object_set_data(AWidget, 'widgetinfo', Result); - Result^.DefaultCursor := HCursor(-1); end; end; diff --git a/lcl/interfaces/gtk2/gtk2winapi.inc b/lcl/interfaces/gtk2/gtk2winapi.inc index b153c35a53..0ab9fcc40a 100644 --- a/lcl/interfaces/gtk2/gtk2winapi.inc +++ b/lcl/interfaces/gtk2/gtk2winapi.inc @@ -7932,97 +7932,13 @@ end; Returns : current cursor ------------------------------------------------------------------------------} function TGtk2WidgetSet.SetCursor(ACursor: HCURSOR): HCURSOR; -var - DefaultCursor: HCursor; - - - procedure SetGlobalCursor; - var - TopList, List: PGList; - begin - TopList := gdk_window_get_toplevels; - List := TopList; - while List <> nil do - begin - if (List^.Data <> nil) then - SetWindowCursor(PGDKWindow(List^.Data), ACursor, True); - list := g_list_next(list); - end; - - if TopList <> nil then - g_list_free(TopList); - end; - - procedure ResetGlobalCursor; - procedure SetToWindow(AWindow: PGDKWindow); - var - data: gpointer; - Widget: PGTKWidget absolute data; - WidgetInfo: PWidgetInfo; - WSPrivate: TWSPrivateClass; - begin - gdk_window_get_user_data(AWindow, @data); - - if GtkWidgetIsA(Widget, gtk_widget_get_type) - then begin - WidgetInfo := GetWidgetInfo(Widget); - if (WidgetInfo <> nil) - and (WidgetInfo^.LCLObject <> nil) - and (WidgetInfo^.LCLObject is TWinControl) - then begin - WSPrivate := TWinControl(WidgetInfo^.LCLObject).WidgetSetClass.WSPrivate; - TGtkPrivateWidgetClass(WSPrivate).UpdateCursor(WidgetInfo); - Exit; - end; - end; - - // no lcl cursor, so reset to default - //gdk_window_set_cursor(AWindow, PGdkCursor(DefaultCursor)); - SetWindowCursor(AWindow, DefaultCursor, True); - end; - - procedure Traverse(AWindow: PGDKWindow); - var - ChildWindows, ListEntry: PGList; - begin - SetToWindow(AWindow); - - ChildWindows := gdk_window_get_children(AWindow); - - ListEntry := ChildWindows; - while ListEntry <> nil do - begin - Traverse(PGdkWindow(ListEntry^.Data)); - ListEntry := ListEntry^.Next; - end; - g_list_free(ChildWindows); - end; - var - TopList, List: PGList; - begin - TopList := gdk_window_get_toplevels; - List := TopList; - while List <> nil do - begin - if (List^.Data <> nil) then - Traverse(PGDKWindow(List^.Data)); - list := g_list_next(list); - end; - - if TopList <> nil then - g_list_free(TopList); - end; - - begin // set global gtk cursor Result := FGlobalCursor; if ACursor = FGlobalCursor then Exit; - - DefaultCursor := Screen.Cursors[crDefault]; - if ACursor <> DefaultCursor - then SetGlobalCursor - else ResetGlobalCursor; + if ACursor = Screen.Cursors[crDefault] + then SetGlobalCursor(0) + else SetGlobalCursor(ACursor); FGlobalCursor := ACursor; end; diff --git a/lcl/interfaces/gtk2/gtk2wscontrols.pp b/lcl/interfaces/gtk2/gtk2wscontrols.pp index d16d4fdd0a..6249095d1d 100644 --- a/lcl/interfaces/gtk2/gtk2wscontrols.pp +++ b/lcl/interfaces/gtk2/gtk2wscontrols.pp @@ -592,22 +592,21 @@ end; class procedure TGtk2WSWinControl.SetCursor(const AWinControl: TWinControl; const ACursor: HCursor); var WidgetInfo: PWidgetInfo; + NewCursor: HCURSOR; begin if not WSCheckHandleAllocated(AWinControl, 'SetCursor') then Exit; - WidgetInfo := GetWidgetInfo(Pointer(AWinControl.Handle)); - if (WidgetInfo^.ControlCursor = ACursor) and - (WidgetInfo^.DefaultCursor <> HCursor(-1)) then Exit; if ACursor <> Screen.Cursors[crDefault] then - WidgetInfo^.ControlCursor := ACursor + NewCursor := ACursor else + NewCursor := 0; + WidgetInfo := GetWidgetInfo(Pointer(AWinControl.Handle)); + if WidgetInfo^.ControlCursor <> NewCursor then begin - if WidgetInfo^.DefaultCursor = HCursor(-1) then - TGtkPrivateWidgetClass(AWinControl.WidgetSetClass.WSPrivate).SetDefaultCursor(WidgetInfo); - WidgetInfo^.ControlCursor := WidgetInfo^.DefaultCursor; + WidgetInfo^.ControlCursor := NewCursor; + TGtkPrivateWidgetClass(AWinControl.WidgetSetClass.WSPrivate).UpdateCursor(WidgetInfo); end; - TGtkPrivateWidgetClass(AWinControl.WidgetSetClass.WSPrivate).UpdateCursor(WidgetInfo); end; class procedure TGtk2WSWinControl.SetFont(const AWinControl: TWinControl; diff --git a/lcl/interfaces/gtk2/gtk2wscustomlistview.inc b/lcl/interfaces/gtk2/gtk2wscustomlistview.inc index d8c259a860..20a961443f 100644 --- a/lcl/interfaces/gtk2/gtk2wscustomlistview.inc +++ b/lcl/interfaces/gtk2/gtk2wscustomlistview.inc @@ -1591,7 +1591,6 @@ begin // already created in TGtkWSBaseScrollingWinControl // Replace the ScrollingInfo with our info WidgetInfo := GetWidgetInfo(ScrollWidget); - WidgetInfo^.DefaultCursor:=0; OrigScrollingData := WidgetInfo^.UserData; Widgets^.ScrollingData := OrigScrollingData^; diff --git a/lcl/interfaces/gtk2/gtk2wsprivate.pp b/lcl/interfaces/gtk2/gtk2wsprivate.pp index 503cf1718a..0d456513c1 100644 --- a/lcl/interfaces/gtk2/gtk2wsprivate.pp +++ b/lcl/interfaces/gtk2/gtk2wsprivate.pp @@ -60,7 +60,6 @@ type public class procedure SetZPosition(const AWinControl: TWinControl; const APosition: TWSZPosition); virtual; class procedure UpdateCursor(AInfo: PWidgetInfo); virtual; - class procedure SetDefaultCursor(AInfo: PWidgetInfo); virtual; end; TGtkPrivateWidgetClass = class of TGtkPrivateWidget; @@ -71,7 +70,6 @@ type private protected public - class procedure SetDefaultCursor(AInfo: PWidgetInfo); override; end; @@ -259,11 +257,17 @@ type function GetWidgetWithWindow(const AHandle: THandle): PGtkWidget; -procedure SetWindowCursor(AWindow: PGdkWindow; ACursor: HCursor; ARecursive: Boolean); -procedure SetCursorForWindowsWithInfo(AWindow: PGdkWindow; AInfo: PWidgetInfo); +procedure SetWindowCursor(AWindow: PGdkWindow; ACursor: HCursor; + ARecursive: Boolean; ASetDefault: Boolean); +procedure SetCursorForWindowsWithInfo(AWindow: PGdkWindow; AInfo: PWidgetInfo; + ASetDefault: Boolean); +procedure SetGlobalCursor(Cursor: HCURSOR); implementation +uses + Gtk2Extra; + {$I Gtk2PrivateWidget.inc} {$I Gtk2PrivateList.inc} @@ -369,6 +373,68 @@ begin end; end; +{------------------------------------------------------------------------------ + procedure: SetWindowCursor + Params: AWindow : PGDkWindow, ACursor: PGdkCursor, ASetDefault: Boolean + Returns: Nothing + + Sets the cursor for a window. + Tries to avoid messing with the cursors of implicitly created + child windows (e.g. headers in TListView) with the following logic: + - If Cursor <> nil, saves the old cursor (if not already done or ASetDefault = true) + before setting the new one. + - If Cursor = nil, restores the old cursor (if not already done). + + Unfortunately gdk_window_get_cursor is only available from + version 2.18, so it needs to be retrieved dynamically. + If gdk_window_get_cursor is not available, the cursor is set + according to LCL widget data. + ------------------------------------------------------------------------------} +procedure SetWindowCursor(AWindow: PGdkWindow; Cursor: PGdkCursor; ASetDefault: Boolean); +var + OldCursor: PGdkCursor; + Data: gpointer; + Info: PWidgetInfo; +begin + Info := nil; + gdk_window_get_user_data(AWindow, @Data); + if (Data <> nil) and GTK_IS_WIDGET(Data) then + begin + Info := GetWidgetInfo(PGtkWidget(Data), False); + end; + if not Assigned(gdk_window_get_cursor) and (Info = nil) + then Exit; + if ASetDefault then //and ((Cursor <> nil) or ( <> nil)) then + begin + // Override any old default cursor + g_object_steal_data(PGObject(AWindow), 'havesavedcursor'); // OK? + g_object_steal_data(PGObject(AWindow), 'savedcursor'); + gdk_window_set_cursor(AWindow, Cursor); + Exit; + end; + if Cursor <> nil then + begin + if Assigned(gdk_window_get_cursor) + then OldCursor := gdk_window_get_cursor(AWindow) + else OldCursor := PGdkCursor(Info^.ControlCursor); + // As OldCursor can be nil, use a separate key to indicate whether it + // is stored. + if ASetDefault or (g_object_get_data(PGObject(AWindow), 'havesavedcursor') = nil) then + begin + g_object_set_data(PGObject(AWindow), 'havesavedcursor', gpointer(1)); + g_object_set_data(PGObject(AWindow), 'savedcursor', gpointer(OldCursor)); + end; + gdk_window_set_cursor(AWindow, Cursor); + end else + begin + if g_object_steal_data(PGObject(AWindow), 'havesavedcursor') <> nil then + begin + Cursor := g_object_steal_data(PGObject(AWindow), 'savedcursor'); + gdk_window_set_cursor(AWindow, Cursor); + end; + end; +end; + {------------------------------------------------------------------------------ procedure: SetWindowCursor Params: AWindow : PGDkWindow, ACursor: HCursor, ARecursive: Boolean @@ -376,7 +442,8 @@ end; Sets the cursor for a window (or recursively for window with children) ------------------------------------------------------------------------------} -procedure SetWindowCursor(AWindow: PGdkWindow; ACursor: HCursor; ARecursive: Boolean); +procedure SetWindowCursor(AWindow: PGdkWindow; ACursor: HCursor; + ARecursive: Boolean; ASetDefault: Boolean); var Cursor: PGdkCursor; @@ -384,7 +451,7 @@ var var ChildWindows, ListEntry: PGList; begin - gdk_window_set_cursor(AWindow, Cursor); + SetWindowCursor(AWindow, Cursor, ASetDefault); ChildWindows := gdk_window_get_children(AWindow); @@ -398,10 +465,9 @@ var end; begin Cursor := PGdkCursor(ACursor); - if Cursor = nil then Exit; if ARecursive then SetCursorRecursive(AWindow) - else gdk_window_set_cursor(AWindow, Cursor); + else SetWindowCursor(AWindow, Cursor, ASetDefault); end; // Helper functions @@ -421,7 +487,8 @@ begin end; end; -procedure SetCursorForWindowsWithInfo(AWindow: PGdkWindow; AInfo: PWidgetInfo); +procedure SetCursorForWindowsWithInfo(AWindow: PGdkWindow; AInfo: PWidgetInfo; + ASetDefault: Boolean); var Cursor: PGdkCursor; Data: gpointer; @@ -436,7 +503,7 @@ var begin Info := GetWidgetInfo(PGtkWidget(Data), False); if Info = AInfo then - gdk_window_set_cursor(AWindow, Cursor); + SetWindowCursor(AWindow, Cursor, ASetDefault); end; ChildWindows := gdk_window_get_children(AWindow); @@ -452,9 +519,35 @@ var begin if AInfo = nil then Exit; Cursor := PGdkCursor(AInfo^.ControlCursor); - if Cursor = nil then Exit; SetCursorRecursive(AWindow); end; +{------------------------------------------------------------------------------ + procedure: SetGlobalCursor + Params: ACursor: HCursor + Returns: Nothing + + Sets the cursor for all toplevel windows. Also sets the cursor for all child + windows recursively provided gdk_get_window_cursor is available. + ------------------------------------------------------------------------------} +procedure SetGlobalCursor(Cursor: HCURSOR); +var + TopList, List: PGList; +begin + TopList := gdk_window_get_toplevels; + List := TopList; + while List <> nil do + begin + if (List^.Data <> nil) then + SetWindowCursor(PGDKWindow(List^.Data), Cursor, + Assigned(gdk_window_get_cursor), False); + list := g_list_next(list); + end; + + if TopList <> nil then + g_list_free(TopList); +end; + + end. diff --git a/lcl/interfaces/gtk2/gtk2wsspin.pp b/lcl/interfaces/gtk2/gtk2wsspin.pp index f667086101..a4c5a83575 100644 --- a/lcl/interfaces/gtk2/gtk2wsspin.pp +++ b/lcl/interfaces/gtk2/gtk2wsspin.pp @@ -202,7 +202,6 @@ begin Result := TLCLIntfHandle(PtrUInt(Widget)); WidgetInfo := CreateWidgetInfo(Widget, AWinControl, AParams); - WidgetInfo^.DefaultCursor:=0; Set_RC_Name(AWinControl, Widget); SetCallbacks(Widget, WidgetInfo); end;