1{ $Id$}
2{
3 *****************************************************************************
4 *                             Gtk2WSControls.pp                             *
5 *                             -----------------                             *
6 *                                                                           *
7 *                                                                           *
8 *****************************************************************************
9
10 *****************************************************************************
11  This file is part of the Lazarus Component Library (LCL)
12
13  See the file COPYING.modifiedLGPL.txt, included in this distribution,
14  for details about the license.
15 *****************************************************************************
16}
17unit Gtk2WSControls;
18
19{$mode objfpc}{$H+}
20{$I gtk2defines.inc}
21
22interface
23
24uses
25  // RTL
26  Classes, SysUtils,
27  Gtk2, Gdk2, Glib2, gdk2pixbuf,
28  // LCL
29////////////////////////////////////////////////////
30// I M P O R T A N T
31////////////////////////////////////////////////////
32// To get as little as posible circles,
33// uncomment only when needed for registration
34////////////////////////////////////////////////////
35  Controls, ComCtrls,
36////////////////////////////////////////////////////
37  Graphics,
38
39  Gtk2Globals, Gtk2Def,
40  Gtk2Proc, LCLType, LCLProc,
41  WSControls, WSProc, Gtk2WinapiWindow;
42
43
44type
45
46  { TGtk2WSDragImageListResolution }
47
48  TGtk2WSDragImageListResolution = class(TWSDragImageListResolution)
49  published
50    class function BeginDrag(const ADragImageList: TDragImageListResolution; {%H-}Window: HWND; AIndex, X, Y: Integer): Boolean; override;
51    class function DragMove(const {%H-}ADragImageList: TDragImageListResolution; X, Y: Integer): Boolean; override;
52    class procedure EndDrag(const {%H-}ADragImageList: TDragImageListResolution); override;
53    class function HideDragImage(const {%H-}ADragImageList: TDragImageListResolution;
54      {%H-}ALockedWindow: HWND; {%H-}DoUnLock: Boolean): Boolean; override;
55    class function ShowDragImage(const {%H-}ADragImageList: TDragImageListResolution;
56      {%H-}ALockedWindow: HWND; X, Y: Integer; {%H-}DoLock: Boolean): Boolean; override;
57  end;
58
59  { TGtkWSControl }
60
61  TGtk2WSControl = class(TWSControl)
62  published
63  end;
64
65
66  { TGtk2WSWinControl }
67
68  TGtk2WSWinControl = class(TWSWinControl)
69  private
70  protected
71  public
72    // Internal public
73    class procedure SetCallbacks(const AGTKObject: PGTKObject; const AComponent: TComponent);
74  published
75    class function CreateHandle(const AWinControl: TWinControl; const AParams: TCreateParams): HWND; override;
76
77    class procedure AddControl(const AControl: TControl); override;
78    class function  CanFocus(const AWinControl: TWinControl): Boolean; override;
79    class procedure ConstraintsChange(const AWinControl: TWinControl); override;
80    class procedure DestroyHandle(const AWinControl: TWinControl); override;
81    class procedure Invalidate(const AWinControl: TWinControl); override;
82
83    class function  GetText(const AWinControl: TWinControl; var AText: String): Boolean; override;
84
85    class procedure SetBorderStyle(const AWinControl: TWinControl; const ABorderStyle: TBorderStyle); override;
86    class procedure SetBounds(const AWinControl: TWinControl; const ALeft, ATop, AWidth, AHeight: Integer); override;
87    class procedure SetChildZPosition(const AWinControl, AChild: TWinControl; const {%H-}AOldPos, ANewPos: Integer; const AChildren: TFPList); override;
88    class procedure SetColor(const AWinControl: TWinControl); override;
89    class procedure SetCursor(const AWinControl: TWinControl; const ACursor: HCursor); override;
90    class procedure SetFont(const AWinControl: TWinControl; const AFont: TFont); override;
91    class procedure SetSize(const AWinControl: TWinControl; const AWidth, AHeight: Integer); override;
92    class procedure SetPos(const AWinControl: TWinControl; const ALeft, ATop: Integer); override;
93    class procedure SetText(const AWinControl: TWinControl; const AText: string); override;
94    class procedure SetShape(const AWinControl: TWinControl; const AShape: HBITMAP); override;
95    class procedure SetBiDiMode(const AWinControl: TWinControl; UseRightToLeftAlign, {%H-}UseRightToLeftReading, {%H-}UseRightToLeftScrollBar : Boolean); override;
96
97    class procedure PaintTo(const AWinControl: TWinControl; ADC: HDC; X, Y: Integer); override;
98    class procedure Repaint(const AWinControl: TWinControl); override;
99    class procedure ShowHide(const AWinControl: TWinControl); override;
100    class procedure ScrollBy(const AWinControl: TWinControl; DeltaX, DeltaY: integer); override;
101  end;
102
103  { TGtk2WSGraphicControl }
104
105  TGtk2WSGraphicControl = class(TWSGraphicControl)
106  published
107  end;
108
109  { TGtk2WSCustomControl }
110
111  TGtk2WSCustomControl = class(TWSCustomControl)
112  published
113  end;
114
115  { TGtk2WSImageList }
116
117  TGtk2WSImageList = class(TWSImageList)
118  published
119  end;
120
121  { TGtkWSBaseScrollingWinControl }
122  {
123    TGtkWSBaseScrollingWinControl is a shared gtk only base implementation of
124    all scrolling widgets, like TListView, TScrollingWinControl etc.
125    It only creates a scrolling widget and handles the LM_HSCROLL and LM_VSCROLL
126    messages
127  }
128  PBaseScrollingWinControlData = ^TBaseScrollingWinControlData;
129  TBaseScrollingWinControlData = record
130    HValue: Integer;
131    HScroll: PGTKWidget;
132    VValue: Integer;
133    VScroll: PGTKWidget;
134  end;
135
136  { TGtk2WSBaseScrollingWinControl }
137
138  TGtk2WSBaseScrollingWinControl = class(TWSWinControl)
139  public
140    class procedure SetCallbacks(const AWidget: PGtkWidget; const AWidgetInfo: PWidgetInfo); virtual;
141  published
142    class function  CreateHandle(const AWinControl: TWinControl; const AParams: TCreateParams): HWND; override;
143  end;
144
145function GetWidgetHAdjustment(AWidget: PGTKWidget): PGTKAdjustment;
146function GetWidgetVAdjustment(AWidget: PGTKWidget): PGTKAdjustment;
147
148var
149  HasWarnedLibOverlay: boolean = false;
150
151implementation
152
153uses
154  Gtk2Int, LMessages, Gtk2WSPrivate, Forms;
155
156{ TGtk2WSWinControl }
157
158
159class function TGtk2WSWinControl.CreateHandle(const AWinControl: TWinControl;
160  const AParams: TCreateParams): HWND;
161var
162  Widget: PGtkWidget;
163  WidgetInfo: PWidgetInfo;
164  Allocation: TGTKAllocation;
165  ScrollBar: PGtkWidget;
166  Adjustment: PGtkAdjustment;
167begin
168  Widget := GTK2WidgetSet.CreateAPIWidget(AWinControl);
169  {$IFDEF DebugLCLComponents}
170  DebugGtkWidgets.MarkCreated(Widget, dbgsName(AWinControl));
171  {$ENDIF}
172
173  Result := THandle({%H-}PtrUInt(Widget));
174  if Result = 0 then Exit;
175
176  WidgetInfo := GetWidgetInfo(Widget); // Widget info already created in CreateAPIWidget
177  WidgetInfo^.Style := AParams.Style;
178  WidgetInfo^.ExStyle := AParams.ExStyle;
179  WidgetInfo^.WndProc := {%H-}PtrUInt(AParams.WindowClass.lpfnWndProc);
180
181  // set allocation
182  Allocation.X := AParams.X;
183  Allocation.Y := AParams.Y;
184  Allocation.Width := AParams.Width;
185  Allocation.Height := AParams.Height;
186  gtk_widget_size_allocate(Widget, @Allocation);
187
188  Set_RC_Name(AWinControl, Widget);
189
190  TGtk2WSWinControl.SetCallbacks(GTK_OBJECT(Widget), AWinControl);
191
192  // scrollbars
193  if (GetWidgetClassName(GTK_SCROLLED_WINDOW(Widget)^.vscrollbar)='OsScrollbar')
194  or (GetWidgetClassName(GTK_SCROLLED_WINDOW(Widget)^.hscrollbar)='OsScrollbar')
195  then begin
196    // ubuntu liboverlay scrollbar is active
197    if not HasWarnedLibOverlay then begin
198      HasWarnedLibOverlay:=true;
199      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.']);
200    end;
201  end;
202
203  ScrollBar:=GTK_SCROLLED_WINDOW(Widget)^.hscrollbar;
204  if (GetWidgetClassName(ScrollBar)='OsScrollbar')
205  then begin
206    // the ubuntu scroll bar eats the change-value signal => use value-changed
207    Adjustment:=gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(Widget));
208    g_signal_connect_after(Adjustment, 'value-changed',TGCallback(@Gtk2RangeUbuntuScrollCB), WidgetInfo);
209  end else begin
210    g_signal_connect_after(ScrollBar, 'change-value',
211      TGCallback(@Gtk2RangeScrollCB), WidgetInfo);
212  end;
213
214  ScrollBar:=GTK_SCROLLED_WINDOW(Widget)^.vscrollbar;
215  if (GetWidgetClassName(ScrollBar)='OsScrollbar')
216  then begin
217    // the ubuntu scroll bar eats the change-value signal => use value-changed
218    Adjustment:=gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(Widget));
219    g_signal_connect_after(Adjustment, 'value-changed',TGCallback(@Gtk2RangeUbuntuScrollCB), WidgetInfo);
220  end else begin
221    g_signal_connect_after(ScrollBar, 'change-value',
222      TGCallback(@Gtk2RangeScrollCB), WidgetInfo);
223  end;
224
225  g_signal_connect(GTK_SCROLLED_WINDOW(Widget)^.hscrollbar, 'button-press-event',
226    TGCallback(@Gtk2RangeScrollPressCB), WidgetInfo);
227  g_signal_connect(GTK_SCROLLED_WINDOW(Widget)^.hscrollbar, 'button-release-event',
228    TGCallback(@Gtk2RangeScrollReleaseCB), WidgetInfo);
229    g_signal_connect(GTK_SCROLLED_WINDOW(Widget)^.vscrollbar, 'button-press-event',
230    TGCallback(@Gtk2RangeScrollPressCB), WidgetInfo);
231  g_signal_connect(GTK_SCROLLED_WINDOW(Widget)^.vscrollbar, 'button-release-event',
232    TGCallback(@Gtk2RangeScrollReleaseCB), WidgetInfo);
233
234  g_signal_connect(Widget, 'scroll-event', TGCallback(@Gtk2ScrolledWindowScrollCB), WidgetInfo);
235end;
236
237class procedure TGtk2WSWinControl.SetBiDiMode(const AWinControl : TWinControl;
238  UseRightToLeftAlign, UseRightToLeftReading, UseRightToLeftScrollBar : Boolean
239  );
240const
241  WidgetDirection : array[boolean] of longint = (GTK_TEXT_DIR_LTR, GTK_TEXT_DIR_RTL);
242var
243  Info: PWidgetInfo;
244begin
245  if not WSCheckHandleAllocated(AWinControl, 'SetBiDiMode') then
246    Exit;
247  gtk_widget_set_direction({%H-}PGtkWidget(AWinControl.Handle),
248    WidgetDirection[UseRightToLeftAlign]);
249  Info := GetWidgetInfo({%H-}PGtkWidget(AWinControl.Handle));
250  if Info <> nil then
251  begin
252    if Info^.CoreWidget <> nil then
253      gtk_widget_set_direction(Info^.CoreWidget,
254        WidgetDirection[UseRightToLeftAlign]);
255    if Info^.ClientWidget <> nil then
256      gtk_widget_set_direction(Info^.ClientWidget,
257        WidgetDirection[UseRightToLeftAlign]);
258  end;
259end;
260
261function Gtk1GetText(const AWinControl: TWinControl; var AText: String): Boolean;
262var
263  CS: PChar;
264  Handle: HWND;
265begin
266  if not WSCheckHandleAllocated(AWinControl, 'GetText')
267  then Exit(False);
268  Result := true;
269  Handle := AWinControl.Handle;
270  case AWinControl.fCompStyle of
271    csComboBox:
272      begin
273        AText := StrPas(gtk_entry_get_text(PGtkEntry({%H-}PGtkCombo(Handle)^.entry)));
274      end;
275    csEdit: AText:= StrPas(gtk_entry_get_text({%H-}PgtkEntry(Handle)));
276    csSpinEdit: AText:= StrPas(gtk_entry_get_text(@{%H-}PGtkSpinButton(Handle)^.entry));
277    csMemo:
278      begin
279        CS := gtk_editable_get_chars(PGtkEditable(
280          GetOrCreateWidgetInfo({%H-}Pointer(Handle))^.CoreWidget), 0, -1);
281        AText := StrPas(CS);
282        g_free(CS);
283      end;
284  else
285    Result := false;
286  end;
287end;
288
289
290class function TGtk2WSWinControl.GetText(const AWinControl: TWinControl;
291  var AText: String): Boolean;
292var
293  TextBuf: PGtkTextBuffer;
294  StartIter,
295  EndIter: TGtkTextIter;
296  CS: PChar;
297  Handle: HWND;
298begin
299  Result := true;
300  Handle := AWinControl.Handle;
301  case AWinControl.fCompStyle of
302    csMemo:
303      begin
304        TextBuf := gtk_text_view_get_buffer(PGtkTextView(GetOrCreateWidgetInfo({%H-}Pointer(Handle))^.CoreWidget));
305        gtk_text_buffer_get_start_iter(TextBuf, @StartIter);
306        gtk_text_buffer_get_end_iter(TextBuf, @EndIter);
307        CS := gtk_text_buffer_get_text(TextBuf, @StartIter, @EndIter, False);
308        AText := StrPas(CS);
309        g_free(CS);
310      end;
311    else
312      Result:=Gtk1GetText(AWinControl, AText);
313  end;
314end;
315
316
317procedure Gtk1SetBorderStyle(const AWinControl: TWinControl; const ABorderStyle: TBorderStyle);
318var
319  Widget: PGtkWidget;
320begin
321  if not WSCheckHandleAllocated(AWinControl, 'SetBorderStyle')
322  then Exit;
323
324  Widget := {%H-}PGtkWidget(AWinControl.Handle);
325  if GtkWidgetIsA(Widget, GTKAPIWidget_GetType) then
326    GTKAPIWidget_SetShadowType(PGTKAPIWidget(Widget), BorderStyleShadowMap[ABorderStyle])
327  else
328  if GTK_IS_FRAME(Widget) then
329    gtk_frame_set_shadow_type(PGtkFrame(Widget), BorderStyleShadowMap[ABorderStyle])
330  else
331  if GTK_IS_VIEWPORT(Widget) then
332    gtk_viewport_set_shadow_type(PGtkViewport(Widget), BorderStyleShadowMap[ABorderStyle]);
333end;
334
335class procedure TGtk2WSWinControl.SetBorderStyle(
336  const AWinControl: TWinControl; const ABorderStyle: TBorderStyle);
337var
338  Widget: PGtkWidget;
339begin
340  if not WSCheckHandleAllocated(AWinControl, 'SetBorderStyle')
341  then Exit;
342
343  Widget := {%H-}PGtkWidget(AWinControl.Handle);
344  if GTK_IS_SCROLLED_WINDOW(Widget) then
345    gtk_scrolled_window_set_shadow_type(PGtkScrolledWindow(Widget), BorderStyleShadowMap[ABorderStyle])
346  else
347  if GTK_IS_ENTRY(Widget) then
348    gtk_entry_set_has_frame(PGtkEntry(Widget), ABorderStyle <> bsNone)
349  else
350    Gtk1SetBorderStyle(AWinControl, ABorderStyle);
351end;
352
353function GetWidgetHAdjustment(AWidget: PGTKWidget): PGTKAdjustment;
354begin
355  if GtkWidgetIsA(AWidget,GTK_TYPE_SCROLLED_WINDOW) then
356    Result:=gtk_scrolled_window_get_hadjustment(PGTKScrolledWindow(AWidget))
357  else if GtkWidgetIsA(AWidget,GTK_TYPE_TREE_VIEW) then
358    Result:=gtk_tree_view_get_hadjustment(PGtkTreeView(AWidget))
359  else
360    Result:=nil;
361end;
362
363function GetWidgetVAdjustment(AWidget: PGTKWidget): PGTKAdjustment;
364begin
365  if GtkWidgetIsA(AWidget,GTK_TYPE_SCROLLED_WINDOW) then
366    Result:=gtk_scrolled_window_get_vadjustment(PGTKScrolledWindow(AWidget))
367  else if GtkWidgetIsA(AWidget,GTK_TYPE_TREE_VIEW) then
368    Result:=gtk_tree_view_get_vadjustment(PGtkTreeView(AWidget))
369  else
370    Result:=nil;
371end;
372
373{ TGtk2WSDragImageListResolution }
374
375class function TGtk2WSDragImageListResolution.BeginDrag(
376  const ADragImageList: TDragImageListResolution; Window: HWND; AIndex, X, Y: Integer
377  ): Boolean;
378var
379  ABitmap: TBitmap;
380  GDIObject: PGDIObject;
381  Pixmap: PGdkPixmap;
382  Mask: PGdkBitmap;
383begin
384  ABitmap := TBitmap.Create;
385  ADragImageList.GetBitmap(AIndex, ABitmap);
386
387  if (ABitmap.Handle = 0) or (ABitmap.Width = 0) or (ABitmap.Height = 0) then
388  begin
389    Result := False;
390    Exit;
391  end;
392
393  GDIObject := {%H-}PGDIObject(ABitmap.Handle);
394
395  Pixmap := nil;
396  Mask := nil;
397  case GDIObject^.GDIBitmapType of
398    gbBitmap:
399      begin
400        Pixmap := GDIObject^.GDIBitmapObject;
401        gdk_bitmap_ref(Pixmap);
402        Mask := nil;
403      end;
404    gbPixmap:
405      begin
406        Pixmap := GDIObject^.GDIPixmapObject.Image;
407        Mask := GDIObject^.GDIPixmapObject.Mask;
408        gdk_pixmap_ref(Pixmap);
409        gdk_bitmap_ref(Mask);
410      end;
411    gbPixbuf:
412      begin
413        Pixmap := nil;
414        Mask := nil;
415        // todo: TEST
416        //gdk_pixbuf_render_pixmap_and_mask(GDIObject^.GDIPixbufObject, Pixmap, Mask, $80);
417        gdk_pixbuf_render_pixmap_and_mask(GDIObject^.GDIPixbufObject, Pixmap, Mask, $80);
418      end;
419  end;
420
421  Result := Gtk2Widgetset.DragImageList_BeginDrag(Pixmap, Mask, ADragImageList.DragHotSpot);
422  if Result then
423    Gtk2Widgetset.DragImageList_DragMove(X, Y);
424  gdk_pixmap_unref(Pixmap);
425  gdk_bitmap_unref(Mask);
426  ABitmap.Free;
427end;
428
429class function TGtk2WSDragImageListResolution.DragMove(
430  const ADragImageList: TDragImageListResolution; X, Y: Integer): Boolean;
431begin
432  Result := Gtk2Widgetset.DragImageList_DragMove(X, Y);
433end;
434
435class procedure TGtk2WSDragImageListResolution.EndDrag(
436  const ADragImageList: TDragImageListResolution);
437begin
438  Gtk2Widgetset.DragImageList_EndDrag;
439end;
440
441class function TGtk2WSDragImageListResolution.HideDragImage(
442  const ADragImageList: TDragImageListResolution; ALockedWindow: HWND; DoUnLock: Boolean
443  ): Boolean;
444begin
445  Result := Gtk2Widgetset.DragImageList_SetVisible(False);
446end;
447
448class function TGtk2WSDragImageListResolution.ShowDragImage(
449  const ADragImageList: TDragImageListResolution; ALockedWindow: HWND; X, Y: Integer;
450  DoLock: Boolean): Boolean;
451begin
452  Result := Gtk2Widgetset.DragImageList_DragMove(X, Y) and Gtk2Widgetset.DragImageList_SetVisible(True);
453end;
454
455
456{ TGtk2WSWinControl }
457
458type
459  TWinControlHack = class(TWinControl)
460  end;
461
462function Gtk2TreeViewEditorEvent(widget: PGtkWidget; event: PGdkEvent; data: GPointer): gboolean; cdecl;
463var
464  R: TRect;
465  Alloc: TGtkAllocation;
466  w: PGtkWidget;
467  AOrientation: TGtkOrientation;
468begin
469  Result := CallBackDefaultReturn;
470  case event^._type of
471    GDK_FOCUS_CHANGE:
472    begin
473      // cheat GtkTreeView container , so we are visible and ready for input.
474      if event^.focus_change._in = 1 then
475      begin
476        R := TWinControl(Data).BoundsRect;
477        Alloc.x := R.Left;
478        Alloc.y := R.Top;
479        Alloc.width := R.Right - R.Left;
480        Alloc.height := R.Bottom - R.Top;
481        gtk_widget_size_allocate(Widget, @Alloc);
482      end else
483      begin
484        w := gtk_widget_get_parent(Widget);
485        if Assigned(w) and GTK_IS_ICON_VIEW(w) then
486        begin
487          //gtk2 does not layout items correctly when iconArrangement is iaTop.
488          //so we force it to do so.
489          AOrientation := gtk_icon_view_get_orientation(PGtkIconView(w));
490          if AOrientation = GTK_ORIENTATION_HORIZONTAL then
491            gtk_icon_view_set_orientation(PGtkIconView(w), GTK_ORIENTATION_VERTICAL)
492          else
493            gtk_icon_view_set_orientation(PGtkIconView(w), GTK_ORIENTATION_HORIZONTAL);
494          gtk_icon_view_set_orientation(PGtkIconView(w), AOrientation)
495        end;
496      end;
497    end;
498  end;
499end;
500
501class procedure TGtk2WSWinControl.AddControl(const AControl: TControl);
502var
503  AParent: TWinControl;
504  ParentWidget: PGTKWidget;
505  ChildWidget: PGTKWidget;
506  pFixed: PGTKWidget;
507begin
508  AParent := TWinControl(AControl).Parent;
509  // DebugLn('LM_AddChild: ',dbgsName(AControl),' ',dbgs(AParent<>nil));
510  if not Assigned(AParent) then
511    Assert(true, Format('Trace: [TGtkWSWinControl.AddControl] %s --> Parent is not assigned', [AControl.ClassName]))
512  else
513  begin
514    // DebugLn(Format('Trace:  [TGtkWSWinControl.AddControl] %s --> Calling Add Child: %s', [AParent.ClassName, AControl.ClassName]));
515
516    ParentWidget := {%H-}PGtkwidget(AParent.Handle);
517    pFixed := GetFixedWidget(ParentWidget);
518
519    // gtk2 is pretty tricky about adding editor into control
520    if (AParent.FCompStyle = csListView) and
521      (TWinControl(AControl).FCompStyle = csEdit) then
522    begin
523      ChildWidget := {%H-}PGtkWidget(TWinControl(AControl).Handle);
524      ParentWidget := gtk_bin_get_child(PGtkBin(PFixed)); // treeview
525      // MUST allocate some size before adding it to container !
526      gtk_widget_set_size_request(ChildWidget, 80, 25);
527      gtk_widget_set_parent(ChildWidget, ParentWidget);
528      // now we connect our GtkEntry directly to event filter
529      g_signal_connect(PGtkObject(ChildWidget), 'event',
530        gtk_signal_func(@Gtk2TreeViewEditorEvent), AControl);
531    end else
532    begin
533      if pFixed <> ParentWidget then
534      begin
535        // parent changed for child
536        ChildWidget := {%H-}PGtkWidget(TWinControl(AControl).Handle);
537        FixedPutControl(pFixed, ChildWidget, AControl.Left, AControl.Top);
538        RegroupAccelerator(ChildWidget);
539      end;
540    end;
541  end;
542end;
543
544class function TGtk2WSWinControl.CanFocus(const AWinControl: TWinControl): Boolean;
545var
546  Widget, FocusWidget: PGtkWidget;
547begin
548  if AWinControl.HandleAllocated then
549  begin
550    Widget := {%H-}PGtkWidget(AWinControl.Handle);
551    FocusWidget := FindFocusWidget(Widget);
552    Result := (FocusWidget <> nil) and GTK_WIDGET_CAN_FOCUS(FocusWidget);
553  end else
554    Result := False;
555end;
556
557class procedure TGtk2WSWinControl.ConstraintsChange(const AWinControl: TWinControl);
558var
559  Widget: PGtkWidget;
560  Geometry: TGdkGeometry;
561begin
562  Widget := {%H-}PGtkWidget(AWinControl.Handle);
563  if (Widget <> nil) and (GtkWidgetIsA(Widget, gtk_window_get_type)) then
564  begin
565    with Geometry do
566    begin
567      if AWinControl.Constraints.MinWidth > 0 then
568        min_width := AWinControl.Constraints.MinWidth
569      else
570        min_width := 1;
571      if AWinControl.Constraints.MaxWidth > 0 then
572        max_width := AWinControl.Constraints.MaxWidth
573      else
574        max_width := 32767;
575      if AWinControl.Constraints.MinHeight > 0 then
576        min_height := AWinControl.Constraints.MinHeight
577      else
578        min_height := 1;
579      if AWinControl.Constraints.MaxHeight > 0 then
580        max_height := AWinControl.Constraints.MaxHeight
581      else
582        max_height := 32767;
583
584      base_width := AWinControl.Width;
585      base_height := AWinControl.Height;
586      width_inc := 1;
587      height_inc := 1;
588      min_aspect := 0;
589      max_aspect := 1;
590      win_gravity := gtk_window_get_gravity(PGtkWindow(Widget));
591    end;
592    //debugln('TGtk2WSWinControl.ConstraintsChange A ',GetWidgetDebugReport(Widget),' max=',dbgs(Geometry.max_width),'x',dbgs(Geometry.max_height));
593    gtk_window_set_geometry_hints(PGtkWindow(Widget), nil, @Geometry,
594      GDK_HINT_POS or GDK_HINT_MIN_SIZE or GDK_HINT_MAX_SIZE);
595  end;
596end;
597
598class procedure TGtk2WSWinControl.DestroyHandle(const AWinControl: TWinControl);
599begin
600  //DebugLn('TGtk2WSWinControl.DestroyHandle ',DbgSName(AWinControl));
601  Gtk2WidgetSet.DestroyLCLComponent(AWinControl);
602end;
603
604class procedure TGtk2WSWinControl.Invalidate(const AWinControl: TWinControl);
605begin
606  if not WSCheckHandleAllocated(AWinControl, 'Invalidate')
607  then Exit;
608
609  //DebugLn('Trace:Trying to invalidate window... !!!');
610  gtk_widget_queue_draw({%H-}PGtkWidget(AWinControl.Handle));
611end;
612
613class procedure TGtk2WSWinControl.ShowHide(const AWinControl: TWinControl);
614begin
615  // other methods use ShowHide also, can't move code
616  Gtk2WidgetSet.SetVisible(AWinControl, AWinControl.HandleObjectShouldBeVisible);
617  InvalidateLastWFPResult(AWinControl, AWinControl.BoundsRect);
618end;
619
620class procedure TGtk2WSWinControl.ScrollBy(const AWinControl: TWinControl;
621  DeltaX, DeltaY: integer);
622var
623  Scrolled: PGtkScrolledWindow;
624  Adjustment: PGtkAdjustment;
625  h, v: Double;
626  NewPos: Double;
627begin
628  if not AWinControl.HandleAllocated then exit;
629  Scrolled := GTK_SCROLLED_WINDOW({%H-}Pointer(AWinControl.Handle));
630  if not GTK_IS_SCROLLED_WINDOW(Scrolled) then
631    exit;
632  Adjustment := gtk_scrolled_window_get_hadjustment(Scrolled);
633  if Adjustment <> nil then
634  begin
635    h := gtk_adjustment_get_value(Adjustment);
636    NewPos := Adjustment^.upper - Adjustment^.page_size;
637    if h - DeltaX <= NewPos then
638      NewPos := h - DeltaX;
639    gtk_adjustment_set_value(Adjustment, NewPos);
640  end;
641  Adjustment := gtk_scrolled_window_get_vadjustment(Scrolled);
642  if Adjustment <> nil then
643  begin
644    v := gtk_adjustment_get_value(Adjustment);
645    NewPos := Adjustment^.upper - Adjustment^.page_size;
646    if v - DeltaY <= NewPos then
647      NewPos := v - DeltaY;
648    gtk_adjustment_set_value(Adjustment, NewPos);
649  end;
650  AWinControl.Invalidate;
651end;
652
653class procedure TGtk2WSWinControl.SetBounds(const AWinControl: TWinControl;
654  const ALeft, ATop, AWidth, AHeight: Integer);
655var
656  AForm: TCustomForm;
657  Geometry: TGdkGeometry;
658  AHints: TGdkWindowHints;
659  AFixedWidthHeight: Boolean;
660begin
661  if not WSCheckHandleAllocated(AWinControl, 'SetBounds')
662  then Exit;
663  ResizeHandle(AWinControl);
664  InvalidateLastWFPResult(AWinControl, Rect(ALeft, ATop, AWidth, AHeight));
665  if not AWinControl.Visible then // Gtk2WSForms.ShowHide will correct visibility
666    exit;
667  if (not (AWinControl is TCustomForm)) or (AWinControl.Parent<>nil)
668  or (AWinControl.ParentWindow<>0) then
669    exit;
670  AForm := TCustomForm(AWinControl);
671  if not (csDesigning in AForm.ComponentState) and
672    AForm.HandleObjectShouldBeVisible then
673  begin
674    // we must set fixed size, gtk_window_set_resizable does not work
675    // as expected for some reason.issue #20741.
676    // Constraints fix issue #29563
677    AFixedWidthHeight := AForm.BorderStyle in [bsDialog, bsSingle, bsToolWindow];
678    FillChar(Geometry{%H-}, SizeOf(TGdkGeometry), 0);
679    with Geometry do
680    begin
681      if not AFixedWidthHeight and (AForm.Constraints.MinWidth > 0) then
682        min_width := AForm.Constraints.MinWidth
683      else
684      if AFixedWidthHeight then
685        min_width := AForm.Width;
686      if not AFixedWidthHeight and (AForm.Constraints.MaxWidth > 0) then
687        max_width := AForm.Constraints.MaxWidth
688      else
689      if AFixedWidthHeight then
690        max_width := AForm.Width;
691      if not AFixedWidthHeight and (AForm.Constraints.MinHeight > 0) then
692        min_height := AForm.Constraints.MinHeight
693      else
694        if AFixedWidthHeight then
695          min_height := AForm.Height;
696      if not AFixedWidthHeight and (AForm.Constraints.MaxHeight > 0) then
697        max_height := AForm.Constraints.MaxHeight
698      else
699        if AFixedWidthHeight then
700          max_height := AForm.Height;
701
702      if not AFixedWidthHeight and (AForm.Constraints.MaxHeight = 0) then
703        max_height := 32767;
704      if not AFixedWidthHeight and (AForm.Constraints.MaxWidth = 0) then
705        max_width := 32767;
706
707      base_width := AForm.Width;
708      base_height := AForm.Height;
709      width_inc := 1;
710      height_inc := 1;
711      min_aspect := 0;
712      max_aspect := 1;
713      win_gravity := gtk_window_get_gravity({%H-}PGtkWindow(AForm.Handle));
714    end;
715    //debugln('TGtk2WSWinControl.SetBounds A maxw=',dbgs(Geometry.max_width),' maxh=',dbgs(Geometry.max_height),
716    //' base w=',dbgs(Geometry.base_width),' h=',dbgs(Geometry.base_height));
717    if AFixedWidthHeight then
718      gtk_window_set_geometry_hints({%H-}PGtkWindow(AForm.Handle), nil, @Geometry,
719        GDK_HINT_POS or GDK_HINT_MIN_SIZE or GDK_HINT_MAX_SIZE)
720    else
721    begin
722      if AForm.BorderStyle <> bsNone then
723      begin
724        AHints := GDK_HINT_POS or GDK_HINT_BASE_SIZE;
725        if (AForm.Constraints.MinHeight > 0) or (AForm.Constraints.MinWidth > 0) then
726          AHints := AHints or GDK_HINT_MIN_SIZE;
727        if (AForm.Constraints.MaxHeight > 0) or (AForm.Constraints.MaxWidth > 0) then begin
728          AHints := AHints or GDK_HINT_MAX_SIZE;
729          { Work around for only one maximum specified; see TGtk2WSWinControl.ConstraintsChange }
730          if AForm.Constraints.MaxHeight = 0 then
731            Geometry.max_height := 32767;
732          if AForm.Constraints.MaxWidth = 0 then
733            Geometry.max_width := 32767;
734        end;
735        {$IFDEF HASX}
736        if (AHints and GDK_HINT_MIN_SIZE = 0) and (AHints and GDK_HINT_MAX_SIZE = 0) and
737          (Gtk2WidgetSet.GetWindowManager = 'openbox') then
738        else
739        {$ENDIF}
740          gtk_window_set_geometry_hints({%H-}PGtkWindow(AForm.Handle), nil, @Geometry,
741            AHints);
742      end;
743      gtk_window_resize({%H-}PGtkWindow(AForm.Handle), AForm.Width, AForm.Height);
744    end;
745  end;
746end;
747
748
749class procedure TGtk2WSWinControl.SetCallbacks(const AGTKObject: PGTKObject;
750  const AComponent: TComponent);
751begin
752  Gtk2WidgetSet.SetCommonCallbacks(AGtkObject, AComponent);
753end;
754
755class procedure TGtk2WSWinControl.SetChildZPosition(
756  const AWinControl, AChild: TWinControl;
757  const AOldPos, ANewPos: Integer; const AChildren: TFPList);
758var
759  n: Integer;
760  child: TWinControlHack;
761begin
762  if not WSCheckHandleAllocated(AWincontrol, 'SetChildZPosition')
763  then Exit;
764
765  if not WSCheckHandleAllocated(AChild, 'SetChildZPosition (child)') then
766    Exit;
767
768  if (ANewPos <= 0) or (ANewPos >= AChildren.Count - 1) then
769  begin
770    // simple
771    Child := TWinControlHack(AChild);
772    if ANewPos <= 0 then // bottom
773      TGtkPrivateWidgetClass(
774          Child.WidgetSetClass.WSPrivate).SetZPosition(Child, wszpBack)
775    else
776      TGtkPrivateWidgetClass(
777          Child.WidgetSetClass.WSPrivate).SetZPosition(Child, wszpFront);
778  end else
779  begin
780    for n := 1 to AChildren.Count - 1 do
781    begin
782      Child := TWinControlHack(AChildren[n]);
783      if Child.HandleAllocated then
784        TGtkPrivateWidgetClass(
785          Child.WidgetSetClass.WSPrivate).SetZPosition(Child, wszpBack);
786    end;
787  end;
788end;
789
790class procedure TGtk2WSWinControl.SetCursor(const AWinControl: TWinControl; const ACursor: HCursor);
791var
792  WidgetInfo: PWidgetInfo;
793begin
794  if not WSCheckHandleAllocated(AWinControl, 'SetCursor')
795  then Exit;
796
797  WidgetInfo := GetWidgetInfo({%H-}Pointer(AWinControl.Handle));
798  if WidgetInfo^.ControlCursor <> ACursor then
799  begin
800    WidgetInfo^.ControlCursor := ACursor;
801    TGtkPrivateWidgetClass(AWinControl.WidgetSetClass.WSPrivate).UpdateCursor(WidgetInfo);
802  end;
803end;
804
805class procedure TGtk2WSWinControl.SetFont(const AWinControl: TWinControl;
806  const AFont: TFont);
807var
808  Widget: PGtkWidget;
809begin
810  if not WSCheckHandleAllocated(AWinControl, 'SetFont')
811  then Exit;
812
813  Widget := {%H-}PGtkWidget(AWinControl.Handle);
814  if GtkWidgetIsA(Widget, GTKAPIWidget_GetType) then
815    exit;
816
817  //DebugLn('TGtk2WSWinControl.SetFont ',DbgSName(AWinControl));
818  Gtk2WidgetSet.SetWidgetFont(Widget, AFont);
819  Gtk2WidgetSet.SetWidgetColor(Widget, AFont.Color, clNone,
820                              [GTK_STATE_NORMAL,GTK_STATE_ACTIVE,
821                               GTK_STATE_PRELIGHT,GTK_STATE_SELECTED,
822                               GTK_STYLE_TEXT]);
823end;
824
825class procedure TGtk2WSWinControl.SetPos(const AWinControl: TWinControl;
826  const ALeft, ATop: Integer);
827var
828  Widget: PGtkWidget;
829  Allocation: TGTKAllocation;
830begin
831  if not WSCheckHandleAllocated(AWinControl, 'SetPos')
832  then Exit;
833  {$IFDEF VerboseSizeMsg}
834  DebugLn(['TGtk2WSWinControl.SetPos ',DbgSName(AWinControl),' ',ALeft,',',ATop]);
835  {$ENDIF}
836
837  Widget := {%H-}PGtkWidget(AWinControl.Handle);
838  Allocation.X := gint16(ALeft);
839  Allocation.Y := gint16(ATop);
840  Allocation.Width := guint16(Widget^.Allocation.Width);
841  Allocation.Height := guint16(Widget^.Allocation.Height);
842  gtk_widget_size_allocate(Widget, @Allocation);// Beware: this triggers callbacks
843end;
844
845class procedure TGtk2WSWinControl.SetSize(const AWinControl: TWinControl;
846  const AWidth, AHeight: Integer);
847var
848  Widget: PGtkWidget;
849  Allocation: TGTKAllocation;
850begin
851  if not WSCheckHandleAllocated(AWinControl, 'SetSize')
852  then Exit;
853  {$IFDEF VerboseSizeMsg}
854  DebugLn(['TGtk2WSWinControl.SetSize ',DbgSName(AWinControl),' ',AWidth,',',AHeight]);
855  {$ENDIF}
856
857  Widget := {%H-}PGtkWidget(AWinControl.Handle);
858  Allocation.X := Widget^.Allocation.X;
859  Allocation.Y := Widget^.Allocation.Y;
860  Allocation.Width := guint16(AWidth);
861  Allocation.Height := guint16(AHeight);
862  gtk_widget_size_allocate(Widget, @Allocation);// Beware: this triggers callbacks
863end;
864
865class procedure TGtk2WSWinControl.SetColor(const AWinControl: TWinControl);
866begin
867  if not WSCheckHandleAllocated(AWinControl, 'SetColor')
868  then Exit;
869
870  // do not change color of scrollbar. issue #22996
871  if (AWinControl.FCompStyle = csScrollBar) then
872    exit;
873
874  if ((csOpaque in AWinControl.ControlStyle) and
875      GtkWidgetIsA({%H-}pGtkWidget(AWinControl.handle),GTKAPIWidget_GetType)) then
876    Exit;
877
878  //DebugLn('TGtk2WSWinControl.SetColor ',DbgSName(AWinControl));
879  Gtk2WidgetSet.SetWidgetColor({%H-}PGtkWidget(AWinControl.Handle),
880                              AWinControl.Font.Color, AWinControl.Color,
881                              [GTK_STATE_NORMAL, GTK_STATE_ACTIVE,
882                               GTK_STATE_PRELIGHT, GTK_STATE_SELECTED]);
883//    GtkWidgetSet.setWidgetFont(pGtkWidget(AWinControl.handle),aWinControl.font);
884  UpdateWidgetStyleOfControl(AWinControl);
885end;
886
887class procedure TGtk2WSWinControl.SetText(const AWinControl: TWinControl;
888  const AText: string);
889
890  procedure SetNotebookPageTabLabel;
891  var
892    NoteBookWidget: PGtkWidget; // the notebook
893    PageWidget: PGtkWidget;     // the page (content widget)
894    TabWidget: PGtkWidget;      // the tab (hbox containing a pixmap, a label
895                                //          and a close button)
896    TabLabelWidget: PGtkWidget; // the label in the tab
897    MenuWidget: PGtkWidget;     // the popup menu (hbox containing a pixmap and
898                                // a label)
899    MenuLabelWidget: PGtkWidget; // the label in the popup menu item
900    NewText: PChar;
901  begin
902    if (AWinControl.Parent <> nil) and (AWinControl.Parent is TTabControl) then
903      exit;
904
905    // dig through the hierachy to get the labels
906    NoteBookWidget:={%H-}PGtkWidget((AWinControl.Parent).Handle);
907    PageWidget:={%H-}PGtkWidget(AWinControl.Handle);
908    TabWidget:=gtk_notebook_get_tab_label(PGtkNoteBook(NotebookWidget),
909                                          PageWidget);
910    if TabWidget<>nil then
911      TabLabelWidget:=g_object_get_data(PGObject(TabWidget), 'TabLabel')
912    else
913      TabLabelWidget:=nil;
914    MenuWidget:=gtk_notebook_get_menu_label(PGtkNoteBook(NotebookWidget),
915                                            PageWidget);
916    if MenuWidget<>nil then
917      MenuLabelWidget:=g_object_get_data(PGObject(MenuWidget), 'TabMenuLabel')
918    else
919      MenuLabelWidget:=nil;
920    // set new text
921    NewText:=PChar(AText);
922    if TabLabelWidget<>nil then
923      gtk_label_set_text(pGtkLabel(TabLabelWidget), NewText);
924    if MenuLabelWidget<>nil then
925      gtk_label_set_text(pGtkLabel(MenuLabelWidget), NewText);
926  end;
927
928var
929  P : Pointer;
930  aLabel, pLabel: pchar;
931begin
932  if not WSCheckHandleAllocated(AWinControl, 'SetText')
933  then Exit;
934
935  //TODO: create classprocedures for this in the corresponding classes
936
937  P := {%H-}Pointer(AWinControl.Handle);
938  Assert(p <> nil, 'Trace:WARNING: [TGtkWidgetSet.SetLabel] --> got nil pointer');
939  //DebugLn('Trace:Setting Str1 in SetLabel');
940  pLabel := pchar(AText);
941
942  case AWinControl.fCompStyle of
943    csBitBtn,
944    csButton: DebugLn('[WARNING] Obsolete call to TGTKOBject.SetLabel for ', AWinControl.ClassName);
945
946    csForm,
947    csFileDialog, csOpenFileDialog, csSaveFileDialog, csSelectDirectoryDialog,
948    csPreviewFileDialog,
949    csColorDialog,
950    csFontDialog:
951      if GtkWidgetIsA(p,gtk_window_get_type) then
952        gtk_window_set_title(pGtkWindow(p),PLabel);
953
954    csCheckBox,
955    csToggleBox,
956    csRadioButton:
957      begin
958        aLabel := Ampersands2Underscore(PLabel);
959        try
960          gtk_label_set_text(
961            pGtkLabel(gtk_bin_get_child(PGtkBin(@PGTKToggleButton(p)^.Button))),
962                      aLabel);
963          gtk_label_parse_uline(
964            pGtkLabel(gtk_bin_get_child(PGtkBin(@PGTKToggleButton(p)^.Button))),
965                      aLabel);
966        finally
967          StrDispose(aLabel);
968        end;
969      end;
970
971    csEdit:
972      begin
973        LockOnChange(PGtkObject(p),+1);
974        gtk_entry_set_text(pGtkEntry(P), pLabel);
975        LockOnChange(PGtkObject(p),-1);
976      end;
977
978    csSpinEdit:
979      begin
980        LockOnChange(PGtkObject(p),+1);
981        gtk_entry_set_text(pGtkEntry(P), pLabel);
982        gtk_spin_button_update(PGtkSpinButton(p));
983        LockOnChange(PGtkObject(p),-1);
984      end;
985
986    csMemo:
987      begin
988        P:= GetOrCreateWidgetInfo(P)^.CoreWidget;
989        //debugln('TGtk2WSWinControl.SetText A ',dbgs(gtk_text_get_length(PGtkText(P))),' AText="',AText,'"');
990        gtk_text_freeze(PGtkText(P));
991        gtk_text_set_point(PGtkText(P), 0);
992        gtk_text_forward_delete(PGtkText(P), gtk_text_get_length(PGtkText(P)));
993        gtk_text_insert(PGtkText(P), nil, nil, nil, pLabel, -1);
994        gtk_text_thaw(PGtkText(P));
995        //debugln('TGtk2WSWinControl.SetText B ',dbgs(gtk_text_get_length(PGtkText(P))));
996      end;
997
998    csPage:
999      SetNotebookPageTabLabel;
1000
1001      // else
1002      // DebugLn('WARNING: [TGtkWidgetSet.SetLabel] --> not handled for class ',Sender.ClassName);
1003  end;
1004  //DebugLn(Format('trace:  [TGtkWidgetSet.SetLabel] %s --> END', [AWinControl.ClassName]));
1005end;
1006
1007class procedure TGtk2WSWinControl.SetShape(const AWinControl: TWinControl;
1008  const AShape: HBITMAP);
1009var
1010  GtkWidget, FixedWidget: PGtkWidget;
1011  GdkBitmap: PGDKBitmap;
1012begin
1013  if not WSCheckHandleAllocated(AWinControl, 'SetShape') then
1014    Exit;
1015
1016  GtkWidget := {%H-}PGtkWidget(AWinControl.Handle);
1017  FixedWidget := GetFixedWidget(GtkWidget);
1018
1019  if AShape <> 0 then
1020  begin
1021    if Gtk2Widgetset.IsValidGDIObjectType(AShape, gdiBitmap) then
1022      GdkBitmap := {%H-}PGdiObject(AShape)^.GDIBitmapObject
1023    else
1024      GdkBitmap := nil;
1025  end
1026  else
1027    GdkBitmap := nil;
1028
1029  gtk_widget_shape_combine_mask(GtkWidget, GdkBitmap, 0, 0);
1030  if FixedWidget <> GtkWidget then
1031    gtk_widget_shape_combine_mask(FixedWidget, GdkBitmap, 0, 0);
1032end;
1033
1034{
1035  Paint control to X, Y point of device context.
1036}
1037class procedure TGtk2WSWinControl.PaintTo(const AWinControl: TWinControl;
1038  ADC: HDC; X, Y: Integer);
1039var
1040  DC: TGtkDeviceContext absolute ADC;
1041
1042  procedure PaintGtkForm(AWindow: PGdkWindow);
1043  var
1044    W, H: gint;
1045    Pixbuf: PGdkPixbuf;
1046    MenuPixBuf: PGdkPixBuf;
1047    AMenuBar: PGtkWidget;
1048    OffsetY: Integer;
1049  begin
1050    OffsetY := 0;
1051    MenuPixBuf := nil;
1052
1053    if Assigned(TCustomForm(AWinControl).Menu) then
1054    begin
1055      AMenuBar := {%H-}PGtkWidget(TCustomForm(AWinControl).Menu.Handle);
1056      if GTK_IS_MENU_BAR(AMenuBar) and GTK_WIDGET_VISIBLE(AMenuBar) then
1057      begin
1058        OffsetY := AMenuBar^.allocation.height;
1059        MenuPixbuf := gdk_pixbuf_get_from_drawable(nil, AMenuBar^.Window, nil,
1060          0, 0, 0, 0, AMenuBar^.allocation.Width, AMenuBar^.Allocation.Height);
1061
1062        gdk_pixbuf_render_to_drawable(MenuPixbuf, DC.Drawable, DC.GC, 0, 0, X, Y + OffsetY,
1063          AMenuBar^.allocation.Width, AMenuBar^.Allocation.Height, GDK_RGB_DITHER_NONE, 0, 0);
1064
1065        gdk_pixbuf_unref(MenuPixbuf);
1066      end;
1067    end;
1068    gdk_window_get_size(AWindow, @W, @H);
1069
1070    Pixbuf := gdk_pixbuf_get_from_drawable(nil, AWindow, nil,
1071      0, 0, 0, 0, W, H);
1072
1073    // put menubar into form screenshoot too
1074    if OffsetY <> 0 then
1075    begin
1076      MenuPixBuf := gdk_pixbuf_scale_simple(PixBuf, W, H - OffsetY,GDK_INTERP_NEAREST);
1077      gdk_pixbuf_render_to_drawable(MenuPixbuf, DC.Drawable, DC.GC, 0, 0, X, Y + (OffsetY * 2),
1078        -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
1079      if MenuPixBuf <> nil then
1080        gdk_pixbuf_unref(MenuPixBuf);
1081    end else
1082      gdk_pixbuf_render_to_drawable(Pixbuf, DC.Drawable, DC.GC, 0, 0, X, Y,
1083        -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
1084
1085    gdk_pixbuf_unref(Pixbuf);
1086  end;
1087
1088  procedure PaintWindow(AWindow: PGdkWindow);
1089  var
1090    W, H: gint;
1091    Pixbuf: PGdkPixbuf;
1092  begin
1093    gdk_window_get_size(AWindow, @W, @H);
1094    // for some reason gdk_window_copy_area does not work
1095    Pixbuf := gdk_pixbuf_get_from_drawable(nil, AWindow, nil,
1096      0, 0, 0, 0, W, H);
1097    gdk_pixbuf_render_to_drawable(Pixbuf, DC.Drawable, DC.GC, 0, 0, X, Y,
1098      -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
1099    gdk_pixbuf_unref(Pixbuf);
1100  end;
1101
1102  procedure PaintWidget(AWidget: PGtkWidget);
1103  var
1104    AWindow: PGdkWindow;
1105  begin
1106    if (AWinControl.FCompStyle = csForm) then
1107      PaintGtkForm(AWidget^.window)
1108    else
1109    begin
1110      AWindow := GetControlWindow(AWidget);
1111      if AWindow <> nil then
1112        PaintWindow(AWindow);
1113    end;
1114  end;
1115
1116begin
1117  if not WSCheckHandleAllocated(AWinControl, 'PaintTo') then
1118    Exit;
1119  PaintWidget(GetFixedWidget({%H-}PGtkWidget(AWinControl.Handle)));
1120end;
1121
1122class procedure TGtk2WSWinControl.Repaint(const AWinControl: TWinControl);
1123begin
1124  if not WSCheckHandleAllocated(AWinControl, 'Repaint')
1125  then Exit;
1126  gtk_widget_queue_draw({%H-}PGtkWidget(AWinControl.Handle));
1127end;
1128
1129{ TGtk2WSBaseScrollingWinControl }
1130
1131function Gtk2WSBaseScrollingWinControl_HValueChanged(AAdjustment: PGTKAdjustment; AInfo: PWidgetInfo): GBoolean; cdecl;
1132var
1133  ScrollingData: PBaseScrollingWinControlData;
1134  Msg: TLMHScroll;
1135  OldValue, V, U, L, StepI, PageI: Integer;
1136  X, Y: GInt;
1137  Mask: TGdkModifierType;
1138begin
1139  Result := CallBackDefaultReturn;
1140  if AInfo^.ChangeLock > 0 then Exit;
1141
1142  ScrollingData := AInfo^.UserData;
1143
1144  // round values
1145  V := Round(AAdjustment^.Value);
1146  U := Round(AAdjustment^.Upper);
1147  L := Round(AAdjustment^.Lower);
1148  StepI := Round(AAdjustment^.Step_Increment);
1149  PageI := Round(AAdjustment^.Page_Increment);
1150
1151  OldValue := ScrollingData^.HValue;
1152  ScrollingData^.HValue := V;
1153
1154  // get keystates
1155  Mask := 0;
1156  if ScrollingData^.HScroll <> nil then
1157  begin
1158    {$IFDEF UseGDKErrorTrap}
1159    BeginGDKErrorTrap;
1160    {$ENDIF}
1161    gdk_window_get_pointer(GetControlWindow(ScrollingData^.HScroll), @X, @Y, @Mask);
1162    {$IFDEF UseGDKErrorTrap}
1163    EndGDKErrorTrap;
1164    {$ENDIF}
1165  end;
1166
1167  Msg.msg := LM_HSCROLL;
1168  // get scrollcode
1169  if ssLeft in GTKEventStateToShiftState(Word(Mask))
1170  then Msg.ScrollCode := SB_THUMBTRACK
1171  else if V <= L
1172  then Msg.ScrollCode := SB_TOP
1173  else if V >= U
1174  then Msg.ScrollCode := SB_BOTTOM
1175  else if V - OldValue = StepI
1176  then Msg.ScrollCode := SB_LINERIGHT
1177  else if OldValue - V = StepI
1178  then Msg.ScrollCode := SB_LINELEFT
1179  else if V - OldValue = PageI
1180  then Msg.ScrollCode := SB_PAGERIGHT
1181  else if OldValue - V = PageI
1182  then Msg.ScrollCode := SB_PAGELEFT
1183  else Msg.ScrollCode := SB_THUMBPOSITION;
1184  Msg.Pos := V;
1185  if V < High(Msg.SmallPos)
1186  then Msg.SmallPos := V
1187  else Msg.SmallPos := High(Msg.SmallPos);
1188  Msg.ScrollBar := HWND({%H-}PtrUInt(ScrollingData^.HScroll));
1189
1190  Result := (DeliverMessage(AInfo^.LCLObject, Msg) <> 0) xor CallBackDefaultReturn;
1191end;
1192
1193function Gtk2WSBaseScrollingWinControl_VValueChanged(AAdjustment: PGTKAdjustment; AInfo: PWidgetInfo): GBoolean; cdecl;
1194var
1195  ScrollingData: PBaseScrollingWinControlData;
1196  Msg: TLMHScroll;
1197  OldValue, V, U, L, StepI, PageI: Integer;
1198  X, Y: GInt;
1199  Mask: TGdkModifierType;
1200begin
1201  Result := CallBackDefaultReturn;
1202  if AInfo^.ChangeLock > 0 then Exit;
1203
1204  ScrollingData := AInfo^.UserData;
1205
1206  // round values
1207  V := Round(AAdjustment^.Value);
1208  U := Round(AAdjustment^.Upper);
1209  L := Round(AAdjustment^.Lower);
1210  StepI := Round(AAdjustment^.Step_Increment);
1211  PageI := Round(AAdjustment^.Page_Increment);
1212
1213  OldValue := ScrollingData^.VValue;
1214  ScrollingData^.VValue := V;
1215
1216  // get keystates
1217  Mask := 0;
1218  if ScrollingData^.VScroll <> nil then
1219  begin
1220    {$IFDEF UseGDKErrorTrap}
1221    BeginGDKErrorTrap;
1222    {$ENDIF}
1223    gdk_window_get_pointer(GetControlWindow(ScrollingData^.VScroll), @X, @Y, @Mask);
1224    {$IFDEF UseGDKErrorTrap}
1225    EndGDKErrorTrap;
1226    {$ENDIF}
1227  end;
1228
1229  Msg.msg := LM_VSCROLL;
1230  // Get scrollcode
1231  if ssLeft in GTKEventStateToShiftState(Word(Mask))
1232  then Msg.ScrollCode := SB_THUMBTRACK
1233  else if V <= L
1234  then Msg.ScrollCode := SB_TOP
1235  else if V >= U
1236  then Msg.ScrollCode := SB_BOTTOM
1237  else if V - OldValue = StepI
1238  then Msg.ScrollCode := SB_LINEDOWN
1239  else if OldValue - V = StepI
1240  then Msg.ScrollCode := SB_LINEUP
1241  else if V - OldValue = PageI
1242  then Msg.ScrollCode := SB_PAGEDOWN
1243  else if OldValue - V = PageI
1244  then Msg.ScrollCode := SB_PAGEUP
1245  else Msg.ScrollCode := SB_THUMBPOSITION;
1246  Msg.Pos := V;
1247  if V < High(Msg.SmallPos)
1248  then Msg.SmallPos := V
1249  else Msg.SmallPos := High(Msg.SmallPos);
1250  Msg.ScrollBar := HWND({%H-}PtrUInt(ScrollingData^.HScroll));
1251
1252  Result := (DeliverMessage(AInfo^.LCLObject, Msg) <> 0) xor CallBackDefaultReturn;
1253end;
1254
1255class function TGtk2WSBaseScrollingWinControl.CreateHandle(
1256  const AWinControl: TWinControl; const AParams: TCreateParams): HWND;
1257var
1258  Widget: PGtkWidget;
1259  WidgetInfo: PWidgetInfo;
1260  ScrollingData: PBaseScrollingWinControlData;
1261  Allocation: TGTKAllocation;
1262begin
1263  Widget := gtk_scrolled_window_new(nil, nil);
1264  {$IFDEF DebugLCLComponents}
1265  DebugGtkWidgets.MarkCreated(Widget,dbgsName(AWinControl));
1266  {$ENDIF}
1267
1268  Result := THandle({%H-}PtrUInt(Widget));
1269  if Result = 0 then Exit;
1270
1271  gtk_widget_show(Widget);
1272
1273  WidgetInfo := CreateWidgetInfo(Widget, AWinControl, AParams);
1274  New(ScrollingData);
1275  ScrollingData^.HValue := 0;
1276  ScrollingData^.VValue := 0;
1277  ScrollingData^.HScroll := PGtkScrolledWindow(Widget)^.HScrollbar;
1278  ScrollingData^.VScroll := PGtkScrolledWindow(Widget)^.VScrollbar;
1279  WidgetInfo^.UserData := ScrollingData;
1280  WidgetInfo^.DataOwner := True;
1281
1282  // set allocation
1283  Allocation.X := AParams.X;
1284  Allocation.Y := AParams.Y;
1285  Allocation.Width := AParams.Width;
1286  Allocation.Height := AParams.Height;
1287  gtk_widget_size_allocate(Widget, @Allocation);
1288  Set_RC_Name(AWinControl, Widget);
1289
1290  // SetCallbacks isn't called here, it should be done in the 'derived' class
1291end;
1292
1293class procedure TGtk2WSBaseScrollingWinControl.SetCallbacks(
1294  const AWidget: PGtkWidget; const AWidgetInfo: PWidgetInfo);
1295begin
1296  TGtk2WSWinControl.SetCallbacks(PGtkObject(AWidget),
1297                                TComponent(AWidgetInfo^.LCLObject));
1298  SignalConnect(
1299    PGtkWidget(GetWidgetHAdjustment(AWidget)),
1300    'value-changed',
1301    @Gtk2WSBaseScrollingWinControl_HValueChanged,
1302    AWidgetInfo
1303  );
1304  SignalConnect(
1305    PGtkWidget(GetWidgetVAdjustment(AWidget)),
1306    'value-changed',
1307    @Gtk2WSBaseScrollingWinControl_VValueChanged,
1308    AWidgetInfo
1309  );
1310end;
1311
1312
1313end.
1314