{ $Id$} { ***************************************************************************** * Gtk2WSControls.pp * * ----------------- * * * * * ***************************************************************************** ***************************************************************************** This file is part of the Lazarus Component Library (LCL) See the file COPYING.modifiedLGPL.txt, included in this distribution, for details about the license. ***************************************************************************** } unit Gtk2WSControls; {$mode objfpc}{$H+} {$I gtk2defines.inc} interface uses // RTL Classes, SysUtils, Gtk2, Gdk2, Glib2, gdk2pixbuf, // LCL //////////////////////////////////////////////////// // I M P O R T A N T //////////////////////////////////////////////////// // To get as little as posible circles, // uncomment only when needed for registration //////////////////////////////////////////////////// Controls, ComCtrls, //////////////////////////////////////////////////// Graphics, Gtk2Globals, Gtk2Def, Gtk2Proc, LCLType, LCLProc, WSControls, WSProc, Gtk2WinapiWindow; type { TGtk2WSDragImageList } TGtk2WSDragImageList = class(TWSDragImageList) published class function BeginDrag(const ADragImageList: TDragImageList; {%H-}Window: HWND; AIndex, X, Y: Integer): Boolean; override; class function DragMove(const {%H-}ADragImageList: TDragImageList; X, Y: Integer): Boolean; override; class procedure EndDrag(const {%H-}ADragImageList: TDragImageList); override; class function HideDragImage(const {%H-}ADragImageList: TDragImageList; {%H-}ALockedWindow: HWND; {%H-}DoUnLock: Boolean): Boolean; override; class function ShowDragImage(const {%H-}ADragImageList: TDragImageList; {%H-}ALockedWindow: HWND; X, Y: Integer; {%H-}DoLock: Boolean): Boolean; override; end; { TGtkWSControl } TGtk2WSControl = class(TWSControl) published end; { TGtk2WSWinControl } TGtk2WSWinControl = class(TWSWinControl) private protected public // Internal public class procedure SetCallbacks(const AGTKObject: PGTKObject; const AComponent: TComponent); published class function CreateHandle(const AWinControl: TWinControl; const AParams: TCreateParams): HWND; override; class procedure AddControl(const AControl: TControl); override; class function CanFocus(const AWinControl: TWinControl): Boolean; override; class procedure ConstraintsChange(const AWinControl: TWinControl); override; class procedure DestroyHandle(const AWinControl: TWinControl); override; class procedure Invalidate(const AWinControl: TWinControl); override; class function GetText(const AWinControl: TWinControl; var AText: String): Boolean; override; class procedure SetBorderStyle(const AWinControl: TWinControl; const ABorderStyle: TBorderStyle); override; class procedure SetBounds(const AWinControl: TWinControl; const ALeft, ATop, AWidth, AHeight: Integer); override; class procedure SetChildZPosition(const AWinControl, AChild: TWinControl; const {%H-}AOldPos, ANewPos: Integer; const AChildren: TFPList); override; class procedure SetColor(const AWinControl: TWinControl); override; class procedure SetCursor(const AWinControl: TWinControl; const ACursor: HCursor); override; class procedure SetFont(const AWinControl: TWinControl; const AFont: TFont); override; class procedure SetSize(const AWinControl: TWinControl; const AWidth, AHeight: Integer); override; class procedure SetPos(const AWinControl: TWinControl; const ALeft, ATop: Integer); override; class procedure SetText(const AWinControl: TWinControl; const AText: string); override; class procedure SetShape(const AWinControl: TWinControl; const AShape: HBITMAP); override; class procedure SetBiDiMode(const AWinControl: TWinControl; UseRightToLeftAlign, {%H-}UseRightToLeftReading, {%H-}UseRightToLeftScrollBar : Boolean); override; class procedure PaintTo(const AWinControl: TWinControl; ADC: HDC; X, Y: Integer); override; class procedure ShowHide(const AWinControl: TWinControl); override; class procedure ScrollBy(const AWinControl: TWinControl; DeltaX, DeltaY: integer); override; end; { TGtk2WSGraphicControl } TGtk2WSGraphicControl = class(TWSGraphicControl) published end; { TGtk2WSCustomControl } TGtk2WSCustomControl = class(TWSCustomControl) published end; { TGtk2WSImageList } TGtk2WSImageList = class(TWSImageList) published end; { TGtkWSBaseScrollingWinControl } { TGtkWSBaseScrollingWinControl is a shared gtk only base implementation of all scrolling widgets, like TListView, TScrollingWinControl etc. It only creates a scrolling widget and handles the LM_HSCROLL and LM_VSCROLL messages } PBaseScrollingWinControlData = ^TBaseScrollingWinControlData; TBaseScrollingWinControlData = record HValue: Integer; HScroll: PGTKWidget; VValue: Integer; VScroll: PGTKWidget; end; { TGtk2WSBaseScrollingWinControl } TGtk2WSBaseScrollingWinControl = class(TWSWinControl) public class procedure SetCallbacks(const AWidget: PGtkWidget; const AWidgetInfo: PWidgetInfo); virtual; published class function CreateHandle(const AWinControl: TWinControl; const AParams: TCreateParams): HWND; override; end; function GetWidgetHAdjustment(AWidget: PGTKWidget): PGTKAdjustment; function GetWidgetVAdjustment(AWidget: PGTKWidget): PGTKAdjustment; var HasWarnedLibOverlay: boolean = false; implementation uses Gtk2Int, LMessages, Gtk2WSPrivate, Forms; { TGtk2WSWinControl } class function TGtk2WSWinControl.CreateHandle(const AWinControl: TWinControl; const AParams: TCreateParams): HWND; var Widget: PGtkWidget; WidgetInfo: PWidgetInfo; Allocation: TGTKAllocation; ScrollBar: PGtkWidget; Adjustment: PGtkAdjustment; begin Widget := GTK2WidgetSet.CreateAPIWidget(AWinControl); {$IFDEF DebugLCLComponents} DebugGtkWidgets.MarkCreated(Widget, dbgsName(AWinControl)); {$ENDIF} Result := THandle({%H-}PtrUInt(Widget)); if Result = 0 then Exit; WidgetInfo := GetWidgetInfo(Widget); // Widget info already created in CreateAPIWidget WidgetInfo^.Style := AParams.Style; WidgetInfo^.ExStyle := AParams.ExStyle; WidgetInfo^.WndProc := {%H-}PtrUInt(AParams.WindowClass.lpfnWndProc); // set allocation Allocation.X := AParams.X; Allocation.Y := AParams.Y; Allocation.Width := AParams.Width; Allocation.Height := AParams.Height; gtk_widget_size_allocate(Widget, @Allocation); Set_RC_Name(AWinControl, Widget); TGtk2WSWinControl.SetCallbacks(GTK_OBJECT(Widget), AWinControl); // scrollbars if (GetWidgetClassName(GTK_SCROLLED_WINDOW(Widget)^.vscrollbar)='OsScrollbar') or (GetWidgetClassName(GTK_SCROLLED_WINDOW(Widget)^.hscrollbar)='OsScrollbar') then begin // ubuntu liboverlay scrollbar is active if not HasWarnedLibOverlay then begin HasWarnedLibOverlay:=true; debugln(['WARNING: liboverlay_scrollbar is active for control=',AWinControl,'. Set environment option LIBOVERLAY_SCROLLBAR=0 before starting this application, otherwise scrollbars will not work properly.']); end; end; ScrollBar:=GTK_SCROLLED_WINDOW(Widget)^.hscrollbar; if (GetWidgetClassName(ScrollBar)='OsScrollbar') then begin // the ubuntu scroll bar eats the change-value signal => use value-changed Adjustment:=gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(Widget)); g_signal_connect_after(Adjustment, 'value-changed',TGCallback(@Gtk2RangeUbuntuScrollCB), WidgetInfo); end else begin g_signal_connect_after(ScrollBar, 'change-value', TGCallback(@Gtk2RangeScrollCB), WidgetInfo); end; ScrollBar:=GTK_SCROLLED_WINDOW(Widget)^.vscrollbar; if (GetWidgetClassName(ScrollBar)='OsScrollbar') then begin // the ubuntu scroll bar eats the change-value signal => use value-changed Adjustment:=gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(Widget)); g_signal_connect_after(Adjustment, 'value-changed',TGCallback(@Gtk2RangeUbuntuScrollCB), WidgetInfo); end else begin g_signal_connect_after(ScrollBar, 'change-value', TGCallback(@Gtk2RangeScrollCB), WidgetInfo); end; g_signal_connect(GTK_SCROLLED_WINDOW(Widget)^.hscrollbar, 'button-press-event', TGCallback(@Gtk2RangeScrollPressCB), WidgetInfo); g_signal_connect(GTK_SCROLLED_WINDOW(Widget)^.hscrollbar, 'button-release-event', TGCallback(@Gtk2RangeScrollReleaseCB), WidgetInfo); g_signal_connect(GTK_SCROLLED_WINDOW(Widget)^.vscrollbar, 'button-press-event', TGCallback(@Gtk2RangeScrollPressCB), WidgetInfo); g_signal_connect(GTK_SCROLLED_WINDOW(Widget)^.vscrollbar, 'button-release-event', TGCallback(@Gtk2RangeScrollReleaseCB), WidgetInfo); g_signal_connect(Widget, 'scroll-event', TGCallback(@Gtk2ScrolledWindowScrollCB), WidgetInfo); end; class procedure TGtk2WSWinControl.SetBiDiMode(const AWinControl : TWinControl; UseRightToLeftAlign, UseRightToLeftReading, UseRightToLeftScrollBar : Boolean ); const WidgetDirection : array[boolean] of longint = (GTK_TEXT_DIR_LTR, GTK_TEXT_DIR_RTL); var Info: PWidgetInfo; begin if not WSCheckHandleAllocated(AWinControl, 'SetBiDiMode') then Exit; gtk_widget_set_direction({%H-}PGtkWidget(AWinControl.Handle), WidgetDirection[UseRightToLeftAlign]); Info := GetWidgetInfo({%H-}PGtkWidget(AWinControl.Handle)); if Info <> nil then begin if Info^.CoreWidget <> nil then gtk_widget_set_direction(Info^.CoreWidget, WidgetDirection[UseRightToLeftAlign]); if Info^.ClientWidget <> nil then gtk_widget_set_direction(Info^.ClientWidget, WidgetDirection[UseRightToLeftAlign]); end; end; function Gtk1GetText(const AWinControl: TWinControl; var AText: String): Boolean; var CS: PChar; Handle: HWND; begin Result := False; if not WSCheckHandleAllocated(AWinControl, 'GetText') then Exit; Result := true; Handle := AWinControl.Handle; case AWinControl.fCompStyle of csComboBox: begin AText := StrPas(gtk_entry_get_text(PGtkEntry({%H-}PGtkCombo(Handle)^.entry))); end; csEdit: AText:= StrPas(gtk_entry_get_text({%H-}PgtkEntry(Handle))); csSpinEdit: AText:= StrPas(gtk_entry_get_text(@{%H-}PGtkSpinButton(Handle)^.entry)); csMemo: begin CS := gtk_editable_get_chars(PGtkEditable( GetWidgetInfo({%H-}Pointer(Handle), True)^.CoreWidget), 0, -1); AText := StrPas(CS); g_free(CS); end; else Result := false; end; end; class function TGtk2WSWinControl.GetText(const AWinControl: TWinControl; var AText: String): Boolean; var TextBuf: PGtkTextBuffer; StartIter, EndIter: TGtkTextIter; CS: PChar; Handle: HWND; begin Result := true; Handle := AWinControl.Handle; case AWinControl.fCompStyle of csMemo: begin TextBuf := gtk_text_view_get_buffer(PGtkTextView(GetWidgetInfo({%H-}Pointer(Handle), True)^.CoreWidget)); gtk_text_buffer_get_start_iter(TextBuf, @StartIter); gtk_text_buffer_get_end_iter(TextBuf, @EndIter); CS := gtk_text_buffer_get_text(TextBuf, @StartIter, @EndIter, False); AText := StrPas(CS); g_free(CS); end; else Result:=Gtk1GetText(AWinControl, AText); end; end; procedure Gtk1SetBorderStyle(const AWinControl: TWinControl; const ABorderStyle: TBorderStyle); var Widget: PGtkWidget; begin if not WSCheckHandleAllocated(AWinControl, 'SetBorderStyle') then Exit; Widget := {%H-}PGtkWidget(AWinControl.Handle); if GtkWidgetIsA(Widget, GTKAPIWidget_GetType) then GTKAPIWidget_SetShadowType(PGTKAPIWidget(Widget), BorderStyleShadowMap[ABorderStyle]) else if GTK_IS_FRAME(Widget) then gtk_frame_set_shadow_type(PGtkFrame(Widget), BorderStyleShadowMap[ABorderStyle]) else if GTK_IS_VIEWPORT(Widget) then gtk_viewport_set_shadow_type(PGtkViewport(Widget), BorderStyleShadowMap[ABorderStyle]); end; class procedure TGtk2WSWinControl.SetBorderStyle( const AWinControl: TWinControl; const ABorderStyle: TBorderStyle); var Widget: PGtkWidget; begin if not WSCheckHandleAllocated(AWinControl, 'SetBorderStyle') then Exit; Widget := {%H-}PGtkWidget(AWinControl.Handle); if GTK_IS_SCROLLED_WINDOW(Widget) then gtk_scrolled_window_set_shadow_type(PGtkScrolledWindow(Widget), BorderStyleShadowMap[ABorderStyle]) else if GTK_IS_ENTRY(Widget) then gtk_entry_set_has_frame(PGtkEntry(Widget), ABorderStyle <> bsNone) else Gtk1SetBorderStyle(AWinControl, ABorderStyle); end; function GetWidgetHAdjustment(AWidget: PGTKWidget): PGTKAdjustment; begin if GtkWidgetIsA(AWidget,GTK_TYPE_SCROLLED_WINDOW) then Result:=gtk_scrolled_window_get_hadjustment(PGTKScrolledWindow(AWidget)) else if GtkWidgetIsA(AWidget,GTK_TYPE_TREE_VIEW) then Result:=gtk_tree_view_get_hadjustment(PGtkTreeView(AWidget)) else Result:=nil; end; function GetWidgetVAdjustment(AWidget: PGTKWidget): PGTKAdjustment; begin if GtkWidgetIsA(AWidget,GTK_TYPE_SCROLLED_WINDOW) then Result:=gtk_scrolled_window_get_vadjustment(PGTKScrolledWindow(AWidget)) else if GtkWidgetIsA(AWidget,GTK_TYPE_TREE_VIEW) then Result:=gtk_tree_view_get_vadjustment(PGtkTreeView(AWidget)) else Result:=nil; end; { TGtk2WSDragImageList } class function TGtk2WSDragImageList.BeginDrag( const ADragImageList: TDragImageList; Window: HWND; AIndex, X, Y: Integer ): Boolean; var ABitmap: TBitmap; GDIObject: PGDIObject; Pixmap: PGdkPixmap; Mask: PGdkBitmap; begin ABitmap := TBitmap.Create; ADragImageList.GetBitmap(AIndex, ABitmap); if (ABitmap.Handle = 0) or (ABitmap.Width = 0) or (ABitmap.Height = 0) then begin Result := False; Exit; end; GDIObject := {%H-}PGDIObject(ABitmap.Handle); Pixmap := nil; Mask := nil; case GDIObject^.GDIBitmapType of gbBitmap: begin Pixmap := GDIObject^.GDIBitmapObject; gdk_bitmap_ref(Pixmap); Mask := nil; end; gbPixmap: begin Pixmap := GDIObject^.GDIPixmapObject.Image; Mask := GDIObject^.GDIPixmapObject.Mask; gdk_pixmap_ref(Pixmap); gdk_bitmap_ref(Mask); end; gbPixbuf: begin Pixmap := nil; Mask := nil; // todo: TEST //gdk_pixbuf_render_pixmap_and_mask(GDIObject^.GDIPixbufObject, Pixmap, Mask, $80); gdk_pixbuf_render_pixmap_and_mask(GDIObject^.GDIPixbufObject, Pixmap, Mask, $80); end; end; Result := Gtk2Widgetset.DragImageList_BeginDrag(Pixmap, Mask, ADragImageList.DragHotSpot); if Result then Gtk2Widgetset.DragImageList_DragMove(X, Y); gdk_pixmap_unref(Pixmap); gdk_bitmap_unref(Mask); ABitmap.Free; end; class function TGtk2WSDragImageList.DragMove( const ADragImageList: TDragImageList; X, Y: Integer): Boolean; begin Result := Gtk2Widgetset.DragImageList_DragMove(X, Y); end; class procedure TGtk2WSDragImageList.EndDrag( const ADragImageList: TDragImageList); begin Gtk2Widgetset.DragImageList_EndDrag; end; class function TGtk2WSDragImageList.HideDragImage( const ADragImageList: TDragImageList; ALockedWindow: HWND; DoUnLock: Boolean ): Boolean; begin Result := Gtk2Widgetset.DragImageList_SetVisible(False); end; class function TGtk2WSDragImageList.ShowDragImage( const ADragImageList: TDragImageList; ALockedWindow: HWND; X, Y: Integer; DoLock: Boolean): Boolean; begin Result := Gtk2Widgetset.DragImageList_DragMove(X, Y) and Gtk2Widgetset.DragImageList_SetVisible(True); end; { TGtk2WSWinControl } type TWinControlHack = class(TWinControl) end; function Gtk2TreeViewEditorEvent(widget: PGtkWidget; event: PGdkEvent; data: GPointer): gboolean; cdecl; var R: TRect; Alloc: TGtkAllocation; w: PGtkWidget; AOrientation: TGtkOrientation; begin Result := CallBackDefaultReturn; case event^._type of GDK_FOCUS_CHANGE: begin // cheat GtkTreeView container , so we are visible and ready for input. if event^.focus_change._in = 1 then begin R := TWinControl(Data).BoundsRect; Alloc.x := R.Left; Alloc.y := R.Top; Alloc.width := R.Right - R.Left; Alloc.height := R.Bottom - R.Top; gtk_widget_size_allocate(Widget, @Alloc); end else begin w := gtk_widget_get_parent(Widget); if Assigned(w) and GTK_IS_ICON_VIEW(w) then begin //gtk2 does not layout items correctly when iconArrangement is iaTop. //so we force it to do so. AOrientation := gtk_icon_view_get_orientation(PGtkIconView(w)); if AOrientation = GTK_ORIENTATION_HORIZONTAL then gtk_icon_view_set_orientation(PGtkIconView(w), GTK_ORIENTATION_VERTICAL) else gtk_icon_view_set_orientation(PGtkIconView(w), GTK_ORIENTATION_HORIZONTAL); gtk_icon_view_set_orientation(PGtkIconView(w), AOrientation) end; end; end; end; end; class procedure TGtk2WSWinControl.AddControl(const AControl: TControl); var AParent: TWinControl; ParentWidget: PGTKWidget; ChildWidget: PGTKWidget; pFixed: PGTKWidget; begin {$IFDEF OldToolBar} if (AControl.Parent is TToolbar) then exit; {$ENDIF} AParent := TWinControl(AControl).Parent; // DebugLn('LM_AddChild: ',dbgsName(AControl),' ',dbgs(AParent<>nil)); if not Assigned(AParent) then Assert(true, Format('Trace: [TGtkWSWinControl.AddControl] %s --> Parent is not assigned', [AControl.ClassName])) else begin // DebugLn(Format('Trace: [TGtkWSWinControl.AddControl] %s --> Calling Add Child: %s', [AParent.ClassName, AControl.ClassName])); ParentWidget := {%H-}PGtkwidget(AParent.Handle); pFixed := GetFixedWidget(ParentWidget); // gtk2 is pretty tricky about adding editor into control if (AParent.FCompStyle = csListView) and (TWinControl(AControl).FCompStyle = csEdit) then begin ChildWidget := {%H-}PGtkWidget(TWinControl(AControl).Handle); ParentWidget := gtk_bin_get_child(PGtkBin(PFixed)); // treeview // MUST allocate some size before adding it to container ! gtk_widget_set_size_request(ChildWidget, 80, 25); gtk_widget_set_parent(ChildWidget, ParentWidget); // now we connect our GtkEntry directly to event filter g_signal_connect(PGtkObject(ChildWidget), 'event', gtk_signal_func(@Gtk2TreeViewEditorEvent), AControl); end else begin if pFixed <> ParentWidget then begin // parent changed for child ChildWidget := {%H-}PGtkWidget(TWinControl(AControl).Handle); FixedPutControl(pFixed, ChildWidget, AControl.Left, AControl.Top); RegroupAccelerator(ChildWidget); end; end; end; end; class function TGtk2WSWinControl.CanFocus(const AWinControl: TWinControl): Boolean; var Widget, FocusWidget: PGtkWidget; begin if AWinControl.HandleAllocated then begin Widget := {%H-}PGtkWidget(AWinControl.Handle); FocusWidget := FindFocusWidget(Widget); Result := (FocusWidget <> nil) and GTK_WIDGET_CAN_FOCUS(FocusWidget); end else Result := False; end; class procedure TGtk2WSWinControl.ConstraintsChange(const AWinControl: TWinControl); var Widget: PGtkWidget; Geometry: TGdkGeometry; begin Widget := {%H-}PGtkWidget(AWinControl.Handle); if (Widget <> nil) and (GtkWidgetIsA(Widget, gtk_window_get_type)) then begin with Geometry do begin if AWinControl.Constraints.MinWidth > 0 then min_width := AWinControl.Constraints.MinWidth else min_width := 1; if AWinControl.Constraints.MaxWidth > 0 then max_width := AWinControl.Constraints.MaxWidth else max_width := 32767; if AWinControl.Constraints.MinHeight > 0 then min_height := AWinControl.Constraints.MinHeight else min_height := 1; if AWinControl.Constraints.MaxHeight > 0 then max_height := AWinControl.Constraints.MaxHeight else max_height := 32767; base_width := AWinControl.Width; base_height := AWinControl.Height; width_inc := 1; height_inc := 1; min_aspect := 0; max_aspect := 1; win_gravity := gtk_window_get_gravity(PGtkWindow(Widget)); end; //debugln('TGtk2WSWinControl.ConstraintsChange A ',GetWidgetDebugReport(Widget),' max=',dbgs(Geometry.max_width),'x',dbgs(Geometry.max_height)); gtk_window_set_geometry_hints(PGtkWindow(Widget), nil, @Geometry, GDK_HINT_POS or GDK_HINT_MIN_SIZE or GDK_HINT_MAX_SIZE); end; end; class procedure TGtk2WSWinControl.DestroyHandle(const AWinControl: TWinControl); begin //DebugLn('TGtk2WSWinControl.DestroyHandle ',DbgSName(AWinControl)); Gtk2WidgetSet.DestroyLCLComponent(AWinControl); end; class procedure TGtk2WSWinControl.Invalidate(const AWinControl: TWinControl); begin if not WSCheckHandleAllocated(AWinControl, 'Invalidate') then Exit; //DebugLn('Trace:Trying to invalidate window... !!!'); gtk_widget_queue_draw({%H-}PGtkWidget(AWinControl.Handle)); end; class procedure TGtk2WSWinControl.ShowHide(const AWinControl: TWinControl); begin // other methods use ShowHide also, can't move code Gtk2WidgetSet.SetVisible(AWinControl, AWinControl.HandleObjectShouldBeVisible); InvalidateLastWFPResult(AWinControl, AWinControl.BoundsRect); end; class procedure TGtk2WSWinControl.ScrollBy(const AWinControl: TWinControl; DeltaX, DeltaY: integer); var Scrolled: PGtkScrolledWindow; Adjustment: PGtkAdjustment; h, v: Double; NewPos: Double; begin if not AWinControl.HandleAllocated then exit; Scrolled := GTK_SCROLLED_WINDOW({%H-}Pointer(AWinControl.Handle)); if not GTK_IS_SCROLLED_WINDOW(Scrolled) then exit; Adjustment := gtk_scrolled_window_get_hadjustment(Scrolled); if Adjustment <> nil then begin h := gtk_adjustment_get_value(Adjustment); NewPos := Adjustment^.upper - Adjustment^.page_size; if h - DeltaX <= NewPos then NewPos := h - DeltaX; gtk_adjustment_set_value(Adjustment, NewPos); end; Adjustment := gtk_scrolled_window_get_vadjustment(Scrolled); if Adjustment <> nil then begin v := gtk_adjustment_get_value(Adjustment); NewPos := Adjustment^.upper - Adjustment^.page_size; if v - DeltaY <= NewPos then NewPos := v - DeltaY; gtk_adjustment_set_value(Adjustment, NewPos); end; AWinControl.Invalidate; end; class procedure TGtk2WSWinControl.SetBounds(const AWinControl: TWinControl; const ALeft, ATop, AWidth, AHeight: Integer); var AForm: TCustomForm; Geometry: TGdkGeometry; AHints: TGdkWindowHints; AFixedWidthHeight: Boolean; begin if not WSCheckHandleAllocated(AWinControl, 'SetBounds') then Exit; ResizeHandle(AWinControl); InvalidateLastWFPResult(AWinControl, Rect(ALeft, ATop, AWidth, AHeight)); if not AWinControl.Visible then // Gtk2WSForms.ShowHide will correct visibility exit; if (not (AWinControl is TCustomForm)) or (AWinControl.Parent<>nil) or (AWinControl.ParentWindow<>0) then exit; AForm := TCustomForm(AWinControl); if not (csDesigning in AForm.ComponentState) and AForm.HandleObjectShouldBeVisible then begin // we must set fixed size, gtk_window_set_resizable does not work // as expected for some reason.issue #20741. // Constraints fix issue #29563 AFixedWidthHeight := AForm.BorderStyle in [bsDialog, bsSingle, bsToolWindow]; with Geometry do begin if not AFixedWidthHeight and (AForm.Constraints.MinWidth > 0) then min_width := AForm.Constraints.MinWidth else min_width := AForm.Width; if not AFixedWidthHeight and (AForm.Constraints.MaxWidth > 0) then max_width := AForm.Constraints.MaxWidth else max_width := AForm.Width; if not AFixedWidthHeight and (AForm.Constraints.MinHeight > 0) then min_height := AForm.Constraints.MinHeight else min_height := AForm.Height; if not AFixedWidthHeight and (AForm.Constraints.MaxHeight > 0) then max_height := AForm.Constraints.MaxHeight else max_height := AForm.Height; base_width := AForm.Width; base_height := AForm.Height; width_inc := 1; height_inc := 1; min_aspect := 0; max_aspect := 1; win_gravity := gtk_window_get_gravity({%H-}PGtkWindow(AForm.Handle)); end; //debugln('TGtk2WSWinControl.ConstraintsChange A ',GetWidgetDebugReport(Widget),' max=',dbgs(Geometry.max_width),'x',dbgs(Geometry.max_height)); if AFixedWidthHeight then gtk_window_set_geometry_hints({%H-}PGtkWindow(AForm.Handle), nil, @Geometry, GDK_HINT_POS or GDK_HINT_MIN_SIZE or GDK_HINT_MAX_SIZE) else begin if AForm.BorderStyle <> bsNone then begin AHints := GDK_HINT_POS or GDK_HINT_BASE_SIZE; if (AForm.Constraints.MinHeight > 0) or (AForm.Constraints.MinWidth > 0) then AHints := AHints or GDK_HINT_MIN_SIZE; if (AForm.Constraints.MaxHeight > 0) or (AForm.Constraints.MaxWidth > 0) then AHints := AHints or GDK_HINT_MAX_SIZE; {$IFDEF HASX} if (AHints and GDK_HINT_MIN_SIZE = 0) and (AHints and GDK_HINT_MAX_SIZE = 0) and (Gtk2WidgetSet.GetWindowManager = 'openbox') then else {$ENDIF} gtk_window_set_geometry_hints({%H-}PGtkWindow(AForm.Handle), nil, @Geometry, AHints); end; gtk_window_resize({%H-}PGtkWindow(AForm.Handle), AForm.Width, AForm.Height); end; end; end; class procedure TGtk2WSWinControl.SetCallbacks(const AGTKObject: PGTKObject; const AComponent: TComponent); begin Gtk2WidgetSet.SetCommonCallbacks(AGtkObject, AComponent); end; class procedure TGtk2WSWinControl.SetChildZPosition( const AWinControl, AChild: TWinControl; const AOldPos, ANewPos: Integer; const AChildren: TFPList); var n: Integer; child: TWinControlHack; begin if not WSCheckHandleAllocated(AWincontrol, 'SetChildZPosition') then Exit; if not WSCheckHandleAllocated(AChild, 'SetChildZPosition (child)') then Exit; if (ANewPos <= 0) or (ANewPos >= AChildren.Count - 1) then begin // simple Child := TWinControlHack(AChild); if ANewPos <= 0 then // bottom TGtkPrivateWidgetClass( Child.WidgetSetClass.WSPrivate).SetZPosition(Child, wszpBack) else TGtkPrivateWidgetClass( Child.WidgetSetClass.WSPrivate).SetZPosition(Child, wszpFront); end else begin for n := 1 to AChildren.Count - 1 do begin Child := TWinControlHack(AChildren[n]); if Child.HandleAllocated then TGtkPrivateWidgetClass( Child.WidgetSetClass.WSPrivate).SetZPosition(Child, wszpBack); end; end; end; class procedure TGtk2WSWinControl.SetCursor(const AWinControl: TWinControl; const ACursor: HCursor); var WidgetInfo: PWidgetInfo; begin if not WSCheckHandleAllocated(AWinControl, 'SetCursor') then Exit; WidgetInfo := GetWidgetInfo({%H-}Pointer(AWinControl.Handle)); if WidgetInfo^.ControlCursor <> ACursor then begin WidgetInfo^.ControlCursor := ACursor; TGtkPrivateWidgetClass(AWinControl.WidgetSetClass.WSPrivate).UpdateCursor(WidgetInfo); end; end; class procedure TGtk2WSWinControl.SetFont(const AWinControl: TWinControl; const AFont: TFont); var Widget: PGtkWidget; begin if not WSCheckHandleAllocated(AWinControl, 'SetFont') then Exit; Widget := {%H-}PGtkWidget(AWinControl.Handle); if GtkWidgetIsA(Widget, GTKAPIWidget_GetType) then exit; //DebugLn('TGtk2WSWinControl.SetFont ',DbgSName(AWinControl)); Gtk2WidgetSet.SetWidgetFont(Widget, AFont); Gtk2WidgetSet.SetWidgetColor(Widget, AFont.Color, clNone, [GTK_STATE_NORMAL,GTK_STATE_ACTIVE, GTK_STATE_PRELIGHT,GTK_STATE_SELECTED, GTK_STYLE_TEXT]); end; class procedure TGtk2WSWinControl.SetPos(const AWinControl: TWinControl; const ALeft, ATop: Integer); var Widget: PGtkWidget; Allocation: TGTKAllocation; begin if not WSCheckHandleAllocated(AWinControl, 'SetPos') then Exit; {$IFDEF VerboseSizeMsg} DebugLn(['TGtk2WSWinControl.SetPos ',DbgSName(AWinControl),' ',ALeft,',',ATop]); {$ENDIF} Widget := {%H-}PGtkWidget(AWinControl.Handle); Allocation.X := gint16(ALeft); Allocation.Y := gint16(ATop); Allocation.Width := guint16(Widget^.Allocation.Width); Allocation.Height := guint16(Widget^.Allocation.Height); gtk_widget_size_allocate(Widget, @Allocation);// Beware: this triggers callbacks end; class procedure TGtk2WSWinControl.SetSize(const AWinControl: TWinControl; const AWidth, AHeight: Integer); var Widget: PGtkWidget; Allocation: TGTKAllocation; begin if not WSCheckHandleAllocated(AWinControl, 'SetSize') then Exit; {$IFDEF VerboseSizeMsg} DebugLn(['TGtk2WSWinControl.SetSize ',DbgSName(AWinControl),' ',AWidth,',',AHeight]); {$ENDIF} Widget := {%H-}PGtkWidget(AWinControl.Handle); Allocation.X := Widget^.Allocation.X; Allocation.Y := Widget^.Allocation.Y; Allocation.Width := guint16(AWidth); Allocation.Height := guint16(AHeight); gtk_widget_size_allocate(Widget, @Allocation);// Beware: this triggers callbacks end; class procedure TGtk2WSWinControl.SetColor(const AWinControl: TWinControl); begin if not WSCheckHandleAllocated(AWinControl, 'SetColor') then Exit; // do not change color of scrollbar. issue #22996 if (AWinControl.FCompStyle = csScrollBar) then exit; if ((csOpaque in AWinControl.ControlStyle) and GtkWidgetIsA({%H-}pGtkWidget(AWinControl.handle),GTKAPIWidget_GetType)) then Exit; //DebugLn('TGtk2WSWinControl.SetColor ',DbgSName(AWinControl)); Gtk2WidgetSet.SetWidgetColor({%H-}PGtkWidget(AWinControl.Handle), AWinControl.Font.Color, AWinControl.Color, [GTK_STATE_NORMAL, GTK_STATE_ACTIVE, GTK_STATE_PRELIGHT, GTK_STATE_SELECTED]); // GtkWidgetSet.setWidgetFont(pGtkWidget(AWinControl.handle),aWinControl.font); UpdateWidgetStyleOfControl(AWinControl); end; class procedure TGtk2WSWinControl.SetText(const AWinControl: TWinControl; const AText: string); procedure SetNotebookPageTabLabel; var NoteBookWidget: PGtkWidget; // the notebook PageWidget: PGtkWidget; // the page (content widget) TabWidget: PGtkWidget; // the tab (hbox containing a pixmap, a label // and a close button) TabLabelWidget: PGtkWidget; // the label in the tab MenuWidget: PGtkWidget; // the popup menu (hbox containing a pixmap and // a label) MenuLabelWidget: PGtkWidget; // the label in the popup menu item NewText: PChar; begin if (AWinControl.Parent <> nil) and (AWinControl.Parent is TTabControl) then exit; // dig through the hierachy to get the labels NoteBookWidget:={%H-}PGtkWidget((AWinControl.Parent).Handle); PageWidget:={%H-}PGtkWidget(AWinControl.Handle); TabWidget:=gtk_notebook_get_tab_label(PGtkNoteBook(NotebookWidget), PageWidget); if TabWidget<>nil then TabLabelWidget:=g_object_get_data(PGObject(TabWidget), 'TabLabel') else TabLabelWidget:=nil; MenuWidget:=gtk_notebook_get_menu_label(PGtkNoteBook(NotebookWidget), PageWidget); if MenuWidget<>nil then MenuLabelWidget:=g_object_get_data(PGObject(MenuWidget), 'TabMenuLabel') else MenuLabelWidget:=nil; // set new text NewText:=PChar(AText); if TabLabelWidget<>nil then gtk_label_set_text(pGtkLabel(TabLabelWidget), NewText); if MenuLabelWidget<>nil then gtk_label_set_text(pGtkLabel(MenuLabelWidget), NewText); end; var P : Pointer; aLabel, pLabel: pchar; begin if not WSCheckHandleAllocated(AWinControl, 'SetText') then Exit; //TODO: create classprocedures for this in the corresponding classes P := {%H-}Pointer(AWinControl.Handle); Assert(p <> nil, 'Trace:WARNING: [TGtkWidgetSet.SetLabel] --> got nil pointer'); //DebugLn('Trace:Setting Str1 in SetLabel'); pLabel := pchar(AText); case AWinControl.fCompStyle of csBitBtn, csButton: DebugLn('[WARNING] Obsolete call to TGTKOBject.SetLabel for ', AWinControl.ClassName); {$IFDEF OldToolBar} csToolButton: with PgtkButton(P)^ do begin //aLabel := StrAlloc(Length(AnsiString(PLabel)) + 1); aLabel := Ampersands2Underscore(PLabel); Try //StrPCopy(aLabel, AnsiString(PLabel)); //Accel := Ampersands2Underscore(aLabel); if gtk_bin_get_child(P) = nil then begin //DebugLn(Format('trace: [TGtkWidgetSet.SetLabel] %s has no child label', [AWinControl.ClassName])); gtk_container_add(P, gtk_label_new(aLabel)); end else begin //DebugLn(Format('trace: [TGtkWidgetSet.SetLabel] %s has child label', [AWinControl.ClassName])); gtk_label_set_text(pgtkLabel( gtk_bin_get_child(P)), aLabel); end; //If Accel <> -1 then AccelKey:=gtk_label_parse_uline(PGtkLabel( gtk_bin_get_child(P)), aLabel); Accelerate(AWinControl,PGtkWidget(P),AccelKey,0,'clicked'); finally StrDispose(aLabel); end; end; {$ENDIF OldToolBar} csForm, csFileDialog, csOpenFileDialog, csSaveFileDialog, csSelectDirectoryDialog, csPreviewFileDialog, csColorDialog, csFontDialog: if GtkWidgetIsA(p,gtk_window_get_type) then gtk_window_set_title(pGtkWindow(p),PLabel); csCheckBox, csToggleBox, csRadioButton: begin aLabel := Ampersands2Underscore(PLabel); try gtk_label_set_text( pGtkLabel(gtk_bin_get_child(PGtkBin(@PGTKToggleButton(p)^.Button))), aLabel); gtk_label_parse_uline( pGtkLabel(gtk_bin_get_child(PGtkBin(@PGTKToggleButton(p)^.Button))), aLabel); finally StrDispose(aLabel); end; end; csEdit: begin LockOnChange(PGtkObject(p),+1); gtk_entry_set_text(pGtkEntry(P), pLabel); LockOnChange(PGtkObject(p),-1); end; csSpinEdit: begin LockOnChange(PGtkObject(p),+1); gtk_entry_set_text(pGtkEntry(P), pLabel); gtk_spin_button_update(PGtkSpinButton(p)); LockOnChange(PGtkObject(p),-1); end; csMemo: begin P:= GetWidgetInfo(P, True)^.CoreWidget; //debugln('TGtk2WSWinControl.SetText A ',dbgs(gtk_text_get_length(PGtkText(P))),' AText="',AText,'"'); gtk_text_freeze(PGtkText(P)); gtk_text_set_point(PGtkText(P), 0); gtk_text_forward_delete(PGtkText(P), gtk_text_get_length(PGtkText(P))); gtk_text_insert(PGtkText(P), nil, nil, nil, pLabel, -1); gtk_text_thaw(PGtkText(P)); //debugln('TGtk2WSWinControl.SetText B ',dbgs(gtk_text_get_length(PGtkText(P)))); end; csPage: SetNotebookPageTabLabel; // else // DebugLn('WARNING: [TGtkWidgetSet.SetLabel] --> not handled for class ',Sender.ClassName); end; //DebugLn(Format('trace: [TGtkWidgetSet.SetLabel] %s --> END', [AWinControl.ClassName])); end; class procedure TGtk2WSWinControl.SetShape(const AWinControl: TWinControl; const AShape: HBITMAP); var GtkWidget, FixedWidget: PGtkWidget; GdkBitmap: PGDKBitmap; begin if not WSCheckHandleAllocated(AWinControl, 'SetShape') then Exit; GtkWidget := {%H-}PGtkWidget(AWinControl.Handle); FixedWidget := GetFixedWidget(GtkWidget); if AShape <> 0 then begin if Gtk2Widgetset.IsValidGDIObjectType(AShape, gdiBitmap) then GdkBitmap := {%H-}PGdiObject(AShape)^.GDIBitmapObject else GdkBitmap := nil; end else GdkBitmap := nil; gtk_widget_shape_combine_mask(GtkWidget, GdkBitmap, 0, 0); if FixedWidget <> GtkWidget then gtk_widget_shape_combine_mask(FixedWidget, GdkBitmap, 0, 0); end; { Paint control to X, Y point of device context. } class procedure TGtk2WSWinControl.PaintTo(const AWinControl: TWinControl; ADC: HDC; X, Y: Integer); var DC: TGtkDeviceContext absolute ADC; procedure PaintGtkForm(AWindow: PGdkWindow); var W, H: gint; Pixbuf: PGdkPixbuf; MenuPixBuf: PGdkPixBuf; AMenuBar: PGtkWidget; OffsetY: Integer; begin OffsetY := 0; MenuPixBuf := nil; if Assigned(TCustomForm(AWinControl).Menu) then begin AMenuBar := {%H-}PGtkWidget(TCustomForm(AWinControl).Menu.Handle); if GTK_IS_MENU_BAR(AMenuBar) and GTK_WIDGET_VISIBLE(AMenuBar) then begin OffsetY := AMenuBar^.allocation.height; MenuPixbuf := gdk_pixbuf_get_from_drawable(nil, AMenuBar^.Window, nil, 0, 0, 0, 0, AMenuBar^.allocation.Width, AMenuBar^.Allocation.Height); gdk_pixbuf_render_to_drawable(MenuPixbuf, DC.Drawable, DC.GC, 0, 0, X, Y + OffsetY, AMenuBar^.allocation.Width, AMenuBar^.Allocation.Height, GDK_RGB_DITHER_NONE, 0, 0); gdk_pixbuf_unref(MenuPixbuf); end; end; gdk_window_get_size(AWindow, @W, @H); Pixbuf := gdk_pixbuf_get_from_drawable(nil, AWindow, nil, 0, 0, 0, 0, W, H); // put menubar into form screenshoot too if OffsetY <> 0 then begin MenuPixBuf := gdk_pixbuf_scale_simple(PixBuf, W, H - OffsetY,GDK_INTERP_NEAREST); gdk_pixbuf_render_to_drawable(MenuPixbuf, DC.Drawable, DC.GC, 0, 0, X, Y + (OffsetY * 2), -1, -1, GDK_RGB_DITHER_NONE, 0, 0); if MenuPixBuf <> nil then gdk_pixbuf_unref(MenuPixBuf); end else gdk_pixbuf_render_to_drawable(Pixbuf, DC.Drawable, DC.GC, 0, 0, X, Y, -1, -1, GDK_RGB_DITHER_NONE, 0, 0); gdk_pixbuf_unref(Pixbuf); end; procedure PaintWindow(AWindow: PGdkWindow); var W, H: gint; Pixbuf: PGdkPixbuf; begin gdk_window_get_size(AWindow, @W, @H); // for some reason gdk_window_copy_area does not work Pixbuf := gdk_pixbuf_get_from_drawable(nil, AWindow, nil, 0, 0, 0, 0, W, H); gdk_pixbuf_render_to_drawable(Pixbuf, DC.Drawable, DC.GC, 0, 0, X, Y, -1, -1, GDK_RGB_DITHER_NONE, 0, 0); gdk_pixbuf_unref(Pixbuf); end; procedure PaintWidget(AWidget: PGtkWidget); var AWindow: PGdkWindow; begin if (AWinControl.FCompStyle = csForm) then PaintGtkForm(AWidget^.window) else begin AWindow := GetControlWindow(AWidget); if AWindow <> nil then PaintWindow(AWindow); end; end; begin if not WSCheckHandleAllocated(AWinControl, 'PaintTo') then Exit; PaintWidget(GetFixedWidget({%H-}PGtkWidget(AWinControl.Handle))); end; { TGtk2WSBaseScrollingWinControl } function Gtk2WSBaseScrollingWinControl_HValueChanged(AAdjustment: PGTKAdjustment; AInfo: PWidgetInfo): GBoolean; cdecl; var ScrollingData: PBaseScrollingWinControlData; Msg: TLMHScroll; OldValue, V, U, L, StepI, PageI: Integer; X, Y: GInt; Mask: TGdkModifierType; begin Result := CallBackDefaultReturn; if AInfo^.ChangeLock > 0 then Exit; ScrollingData := AInfo^.UserData; // round values V := Round(AAdjustment^.Value); U := Round(AAdjustment^.Upper); L := Round(AAdjustment^.Lower); StepI := Round(AAdjustment^.Step_Increment); PageI := Round(AAdjustment^.Page_Increment); OldValue := ScrollingData^.HValue; ScrollingData^.HValue := V; // get keystates Mask := 0; if ScrollingData^.HScroll <> nil then begin {$IFDEF UseGDKErrorTrap} BeginGDKErrorTrap; {$ENDIF} gdk_window_get_pointer(GetControlWindow(ScrollingData^.HScroll), @X, @Y, @Mask); {$IFDEF UseGDKErrorTrap} EndGDKErrorTrap; {$ENDIF} end; Msg.msg := LM_HSCROLL; // get scrollcode if ssLeft in GTKEventStateToShiftState(Word(Mask)) then Msg.ScrollCode := SB_THUMBTRACK else if V <= L then Msg.ScrollCode := SB_TOP else if V >= U then Msg.ScrollCode := SB_BOTTOM else if V - OldValue = StepI then Msg.ScrollCode := SB_LINERIGHT else if OldValue - V = StepI then Msg.ScrollCode := SB_LINELEFT else if V - OldValue = PageI then Msg.ScrollCode := SB_PAGERIGHT else if OldValue - V = PageI then Msg.ScrollCode := SB_PAGELEFT else Msg.ScrollCode := SB_THUMBPOSITION; Msg.Pos := V; if V < High(Msg.SmallPos) then Msg.SmallPos := V else Msg.SmallPos := High(Msg.SmallPos); Msg.ScrollBar := HWND({%H-}PtrUInt(ScrollingData^.HScroll)); Result := (DeliverMessage(AInfo^.LCLObject, Msg) <> 0) xor CallBackDefaultReturn; end; function Gtk2WSBaseScrollingWinControl_VValueChanged(AAdjustment: PGTKAdjustment; AInfo: PWidgetInfo): GBoolean; cdecl; var ScrollingData: PBaseScrollingWinControlData; Msg: TLMHScroll; OldValue, V, U, L, StepI, PageI: Integer; X, Y: GInt; Mask: TGdkModifierType; begin Result := CallBackDefaultReturn; if AInfo^.ChangeLock > 0 then Exit; ScrollingData := AInfo^.UserData; // round values V := Round(AAdjustment^.Value); U := Round(AAdjustment^.Upper); L := Round(AAdjustment^.Lower); StepI := Round(AAdjustment^.Step_Increment); PageI := Round(AAdjustment^.Page_Increment); OldValue := ScrollingData^.VValue; ScrollingData^.VValue := V; // get keystates Mask := 0; if ScrollingData^.VScroll <> nil then begin {$IFDEF UseGDKErrorTrap} BeginGDKErrorTrap; {$ENDIF} gdk_window_get_pointer(GetControlWindow(ScrollingData^.VScroll), @X, @Y, @Mask); {$IFDEF UseGDKErrorTrap} EndGDKErrorTrap; {$ENDIF} end; Msg.msg := LM_VSCROLL; // Get scrollcode if ssLeft in GTKEventStateToShiftState(Word(Mask)) then Msg.ScrollCode := SB_THUMBTRACK else if V <= L then Msg.ScrollCode := SB_TOP else if V >= U then Msg.ScrollCode := SB_BOTTOM else if V - OldValue = StepI then Msg.ScrollCode := SB_LINEDOWN else if OldValue - V = StepI then Msg.ScrollCode := SB_LINEUP else if V - OldValue = PageI then Msg.ScrollCode := SB_PAGEDOWN else if OldValue - V = PageI then Msg.ScrollCode := SB_PAGEUP else Msg.ScrollCode := SB_THUMBPOSITION; Msg.Pos := V; if V < High(Msg.SmallPos) then Msg.SmallPos := V else Msg.SmallPos := High(Msg.SmallPos); Msg.ScrollBar := HWND({%H-}PtrUInt(ScrollingData^.HScroll)); Result := (DeliverMessage(AInfo^.LCLObject, Msg) <> 0) xor CallBackDefaultReturn; end; class function TGtk2WSBaseScrollingWinControl.CreateHandle( const AWinControl: TWinControl; const AParams: TCreateParams): HWND; var Widget: PGtkWidget; WidgetInfo: PWidgetInfo; ScrollingData: PBaseScrollingWinControlData; Allocation: TGTKAllocation; begin Widget := gtk_scrolled_window_new(nil, nil); {$IFDEF DebugLCLComponents} DebugGtkWidgets.MarkCreated(Widget,dbgsName(AWinControl)); {$ENDIF} Result := THandle({%H-}PtrUInt(Widget)); if Result = 0 then Exit; gtk_widget_show(Widget); WidgetInfo := CreateWidgetInfo(Widget, AWinControl, AParams); New(ScrollingData); ScrollingData^.HValue := 0; ScrollingData^.VValue := 0; ScrollingData^.HScroll := PGtkScrolledWindow(Widget)^.HScrollbar; ScrollingData^.VScroll := PGtkScrolledWindow(Widget)^.VScrollbar; WidgetInfo^.UserData := ScrollingData; WidgetInfo^.DataOwner := True; // set allocation Allocation.X := AParams.X; Allocation.Y := AParams.Y; Allocation.Width := AParams.Width; Allocation.Height := AParams.Height; gtk_widget_size_allocate(Widget, @Allocation); Set_RC_Name(AWinControl, Widget); // SetCallbacks isn't called here, it should be done in the 'derived' class end; class procedure TGtk2WSBaseScrollingWinControl.SetCallbacks( const AWidget: PGtkWidget; const AWidgetInfo: PWidgetInfo); begin TGtk2WSWinControl.SetCallbacks(PGtkObject(AWidget), TComponent(AWidgetInfo^.LCLObject)); SignalConnect( PGtkWidget(GetWidgetHAdjustment(AWidget)), 'value-changed', @Gtk2WSBaseScrollingWinControl_HValueChanged, AWidgetInfo ); SignalConnect( PGtkWidget(GetWidgetVAdjustment(AWidget)), 'value-changed', @Gtk2WSBaseScrollingWinControl_VValueChanged, AWidgetInfo ); end; end.