1 {
2  *****************************************************************************
3  *                               gtk3widgets.pas                             *
4  *                               -------------                               *
5  *                                                                           *
6  *                                                                           *
7  *****************************************************************************
8 
9  *****************************************************************************
10   This file is part of the Lazarus Component Library (LCL)
11 
12   See the file COPYING.modifiedLGPL.txt, included in this distribution,
13   for details about the license.
14  *****************************************************************************
15 }
16 
17 unit gtk3widgets;
18 {$i gtk3defines.inc}
19 {$mode objfpc}
20 {$H+}
21 
22 interface
23 
24 uses
25   Classes, SysUtils, types, math,
26   // LCL
27   Controls, StdCtrls, ExtCtrls, ComCtrls, Graphics, Dialogs, Forms, Menus, ExtDlgs,
28   Spin, CheckLst, PairSplitter, LCLType, LCLProc, LMessages, LCLMessageGlue, LCLIntf,
29   // GTK3
30   LazGtk3, LazGdk3, LazGObject2, LazGLib2, LazCairo1, LazPango1, LazGdkPixbuf2,
31   gtk3objects, gtk3procs, gtk3private, Gtk3CellRenderer;
32 
33 type
34   TByteSet = set of byte;
35 
36   // records
37   TPaintData = record
38     PaintWidget: PGtkWidget;
39     ClipRect: PRect;
40     ClipRegion: Pcairo_region_t;
41   end;
42 
43   TDefaultRGBA = record
44     R: Double;
45     G: Double;
46     B: Double;
47     Alpha: Double;
48   end;
49 
50   TGtk3WidgetType = (wtWidget, wtStaticText, wtProgressBar, wtLayout,
51     wtContainer, wtMenuBar, wtMenu, wtMenuItem, wtEntry, wtSpinEdit,
52     wtNotebook, wtTabControl, wtComboBox,
53     wtGroupBox, wtCalendar, wtTrackBar, wtScrollBar,
54     wtScrollingWin, wtListBox, wtListView, wtCheckListBox, wtMemo, wtTreeModel,
55     wtCustomControl, wtScrollingWinControl,
56     wtWindow, wtDialog, wtHintWindow);
57   TGtk3WidgetTypes = set of TGtk3WidgetType;
58 
59   { TGtk3Widget }
60 
61   TGtk3Widget = class(TGtk3Object, IUnknown)
62   private
63     FFocusableByMouse: Boolean; {shell we call SetFocus on mouse down. Default = False}
64     FEnterLeaveTime: Cardinal;
65     FHasPaint: Boolean;
66     FKeysToEat: TByteSet;
67     FPaintData: TPaintData;
68     FContext: HDC;
69     FCairoContext: Pcairo_t;
70     FWidgetType: TGtk3WidgetTypes;
71     FParams: TCreateParams;
72     FOwnWidget: Boolean;
73     FOwner: PGtkWidget;
74     FCentralWidget: PGtkWidget;
75     FWidget: PGtkWidget;
76     FProps: TStringList;
77     FWidgetRGBA: array [0{GTK_STATE_NORMAL}..4{GTK_STATE_INSENSITIVE}] of TDefaultRGBA;
78     FCentralWidgetRGBA: array [0{GTK_STATE_NORMAL}..4{GTK_STATE_INSENSITIVE}] of TDefaultRGBA;
79 
CanSendLCLMessagenull80     function CanSendLCLMessage: Boolean;
GetCairoContextnull81     function GetCairoContext: Pcairo_t;
GetEnablednull82     function GetEnabled: Boolean;
GetFontnull83     function GetFont: PPangoFontDescription;
GetStyleContextnull84     function GetStyleContext: PGtkStyleContext;
GetVisiblenull85     function GetVisible: Boolean;
86     procedure SetEnabled(AValue: Boolean);
87     procedure SetFont(AValue: PPangoFontDescription);
88     procedure SetVisible(AValue: Boolean);
89     procedure SetStyleContext(AValue: PGtkStyleContext);
90   protected
91     // IUnknown implementation
QueryInterfacenull92     function QueryInterface(constref iid: TGuid; out obj): LongInt; {$IFDEF WINDOWS}stdcall{$ELSE}cdecl{$ENDIF};
_AddRefnull93     function _AddRef: LongInt; {$IFDEF WINDOWS}stdcall{$ELSE}cdecl{$ENDIF};
_Releasenull94     function _Release: LongInt; {$IFDEF WINDOWS}stdcall{$ELSE}cdecl{$ENDIF};
EatArrowKeysnull95     function EatArrowKeys(const AKey: Word): Boolean; virtual;
getTextnull96     function getText: String; virtual;
97     procedure setText(AValue: String); virtual;
GetContextnull98     function GetContext: HDC; virtual;
CreateWidgetnull99     function CreateWidget(const Params: TCreateParams):PGtkWidget; virtual;
100     procedure DestroyWidget; virtual;
101     procedure DoBeforeLCLPaint; virtual;
102 
GetColornull103     function GetColor: TColor; virtual;
104     procedure SetColor(AValue: TColor); virtual;
GetFontColornull105     function GetFontColor: TColor; virtual;
106     procedure SetFontColor(AValue: TColor); virtual;
107   public
108     LCLObject: TWinControl;
109   public
110     constructor Create(const AWinControl: TWinControl; const AParams: TCreateParams); virtual; overload;
111     constructor CreateFrom(const AWinControl: TWinControl; AWidget: PGtkWidget); virtual;
112 
113     procedure InitializeWidget; virtual;
114     procedure DeInitializeWidget;
115     procedure RecreateWidget;
116     procedure DestroyNotify(AWidget: PGtkWidget); virtual;
117     destructor Destroy; override;
118 
CanFocusnull119     function CanFocus: Boolean; virtual;
GetFocusableByMousenull120     function GetFocusableByMouse: Boolean;
getClientOffsetnull121     function getClientOffset: TPoint; virtual;
getWidgetPosnull122     function getWidgetPos: TPoint; virtual;
123 
124     procedure OffsetMousePos(APoint: PPoint); virtual;
125 
DeliverMessagenull126     function DeliverMessage(var Msg; const AIsInputEvent: Boolean = False): LRESULT; virtual;
GtkEventMouseEnterLeavenull127     function GtkEventMouseEnterLeave(Sender: PGtkWidget; Event: PGdkEvent): Boolean; virtual; cdecl;
GtkEventKeynull128     function GtkEventKey(Sender: PGtkWidget; Event: PGdkEvent; AKeyPress: Boolean): Boolean; virtual; cdecl;
GtkEventMousenull129     function GtkEventMouse(Sender: PGtkWidget; Event: PGdkEvent): Boolean; virtual; cdecl;
GtkEventMouseMovenull130     function GtkEventMouseMove(Sender: PGtkWidget; Event: PGdkEvent): Boolean; virtual; cdecl;
GtkEventPaintnull131     function GtkEventPaint(Sender: PGtkWidget; AContext: Pcairo_t): Boolean; virtual; cdecl;
GtkEventResizenull132     function GtkEventResize(Sender: PGtkWidget; Event: PGdkEvent): Boolean; virtual; cdecl;
133     procedure GtkEventFocus(Sender: PGtkWidget; Event: PGdkEvent); cdecl;
134     procedure GtkEventDestroy; cdecl;
GtkEventMouseWheelnull135     function GtkEventMouseWheel(Sender: PGtkWidget; Event: PGdkEvent): Boolean; virtual; cdecl;
136 
IsValidHandlenull137     function IsValidHandle: Boolean;
IsWidgetOknull138     function IsWidgetOk: Boolean; virtual;
IsIconicnull139     function IsIconic: Boolean; virtual;
140 
getTypenull141     function getType: TGType;
getTypeNamenull142     function getTypeName: PgChar;
143 
144     procedure lowerWidget; virtual;
145     procedure raiseWidget; virtual;
146     procedure stackUnder(AWidget: PGtkWidget); virtual;
147 
GetCapturenull148     function GetCapture: TGtk3Widget; virtual;
SetCapturenull149     function SetCapture: HWND; virtual;
150 
getClientRectnull151     function getClientRect: TRect; virtual;
getClientBoundsnull152     function getClientBounds: TRect; virtual;
GetContainerWidgetnull153     function GetContainerWidget: PGtkWidget; virtual;
GetPositionnull154     function GetPosition(out APoint: TPoint): Boolean; virtual;
155     procedure Release; override;
156     procedure Hide; virtual;
getParentnull157     function getParent: TGtk3Widget;
GetWindownull158     function GetWindow: PGdkWindow; virtual;
159     procedure Move(ALeft, ATop: Integer);
160     procedure Activate; virtual;
161     procedure preferredSize(var PreferredWidth, PreferredHeight: integer; WithThemeSpace: Boolean); virtual;
162     procedure SetCursor(ACursor: HCURSOR);
163     procedure SetFocus; virtual;
164     procedure SetParent(AParent: TGtk3Widget; const ALeft, ATop: Integer); virtual;
165     procedure Show; virtual;
166     procedure ShowAll; virtual;
167     procedure Update(ARect: PRect);
168     property CairoContext: Pcairo_t read GetCairoContext;
169     property Color: TColor read GetColor write SetColor;
170     property Context: HDC read GetContext;
171     property Enabled: Boolean read GetEnabled write SetEnabled;
172     property Font: PPangoFontDescription read GetFont write SetFont;
173     property FontColor: TColor read GetFontColor write SetFontColor;
174     property KeysToEat: TByteSet read FKeysToEat write FKeysToEat;
175     property PaintData: TPaintData read FPaintData write FPaintData;
176     property StyleContext: PGtkStyleContext read GetStyleContext write SetStyleContext;
177     property Text: String read getText write setText;
178     property Visible: Boolean read GetVisible write SetVisible;
179     property Widget: PGtkWidget read FWidget;
180     property WidgetType: TGtk3WidgetTypes read FWidgetType;
181   end;
182 
183   { TGtk3Editable }
184 
185   TGtk3Editable = class(TGtk3Widget)
186   private
GetReadOnlynull187     function GetReadOnly: Boolean;
188     procedure SetReadOnly(AValue: Boolean);
189   protected
190     PrivateCursorPos: Integer; // used only for delayed selStart and selLength
191     PrivateSelection: Integer;
getCaretPosnull192     function getCaretPos: TPoint; virtual;
193     procedure SetCaretPos(AValue: TPoint); virtual;
194   public
getSelStartnull195     function getSelStart: Integer; virtual;
getSelLengthnull196     function getSelLength: Integer; virtual;
197     procedure setSelStart(AValue: Integer); virtual;
198     procedure setSelLength(AValue: Integer); virtual;
199     property CaretPos: TPoint read GetCaretPos write SetCaretPos;
200     property ReadOnly: Boolean read GetReadOnly write SetReadOnly;
201   end;
202 
203   { TGtk3Entry }
204 
205   TGtk3Entry = class(TGtk3Editable)
206   private
GetAlignmentnull207     function GetAlignment: TAlignment;
208     procedure SetAlignment(AValue: TAlignment);
209   protected
EatArrowKeysnull210     function EatArrowKeys(const AKey: Word): Boolean; override;
getTextnull211     function getText: String; override;
212     procedure setText(AValue: String); override;
CreateWidgetnull213     function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
214   public
215     procedure InitializeWidget; override;
216     procedure SetEchoMode(AVisible: Boolean);
217     procedure SetMaxLength(AMaxLength: Integer);
218     procedure SetPasswordChar(APasswordChar: Char);
IsWidgetOknull219     function IsWidgetOk: Boolean; override;
220     property Alignment: TAlignment read GetAlignment write SetAlignment;
221   end;
222 
223   { TGtk3SpinEdit }
224 
225   TGtk3SpinEdit = class(TGtk3Entry)
226   private
GetMaximumnull227     function GetMaximum: Double;
GetMinimumnull228     function GetMinimum: Double;
GetNumDigitsnull229     function GetNumDigits: Integer;
GetNumericnull230     function GetNumeric: Boolean;
GetStepnull231     function GetStep: Double;
GetValuenull232     function GetValue: Double;
233     procedure SetNumDigits(AValue: Integer);
234     procedure SetNumeric(AValue: Boolean);
235     procedure SetStep(AValue: Double);
236     procedure SetValue(AValue: Double);
237   protected
CreateWidgetnull238     function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
EatArrowKeysnull239     function EatArrowKeys(const AKey: Word): Boolean; override;
240   public
IsWidgetOknull241     function IsWidgetOk: Boolean; override;
242     procedure SetRange(AMin, AMax: Double);
243     property Minimum: Double read GetMinimum;
244     property Maximum: Double read GetMaximum;
245     property Numeric: Boolean read GetNumeric write SetNumeric;
246     property NumDigits: Integer read GetNumDigits write SetNumDigits;
247     property Step: Double read GetStep write SetStep;
248     property Value: Double read GetValue write SetValue;
249   end;
250 
251   { TGtk3Range }
252 
253   TGtk3Range = class(TGtk3Widget)
254   private
GetPositionnull255     function GetPosition: Integer;
GetRangenull256     function GetRange: TPoint;
257     procedure SetPosition(AValue: Integer);
258     procedure SetRange(AValue: TPoint);
259   public
260     procedure InitializeWidget; override;
261     procedure SetStep(AStep: Integer; APageSize: Integer);
262     property Range: TPoint read GetRange write SetRange;
263     property Position: Integer read GetPosition write SetPosition;
264   end;
265 
266   { TGtk3TrackBar }
267 
268   TGtk3TrackBar = class(TGtk3Range)
269   private
270     FOrientation: TTrackBarOrientation;
GetReversednull271     function GetReversed: Boolean;
272     procedure SetReversed(AValue: Boolean);
273   protected
CreateWidgetnull274     function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
275   public
GetTrackBarOrientationnull276     function GetTrackBarOrientation: TTrackBarOrientation;
277     procedure SetScalePos(AValue: TTrackBarScalePos);
278     procedure SetTickMarks(AValue: TTickMark; ATickStyle: TTickStyle);
279     property Reversed: Boolean read GetReversed write SetReversed;
280   end;
281 
282   { TGtk3ScrollBar }
283 
284   TGtk3ScrollBar = class(TGtk3Range)
285   protected
CreateWidgetnull286     function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
287   public
288     procedure SetParams;
289   end;
290 
291   { TGtk3ProgressBar }
292 
293   TGtk3ProgressBar = class(TGtk3Widget)
294   private
GetOrientationnull295     function GetOrientation: TProgressBarOrientation;
GetPositionnull296     function GetPosition: Integer;
GetShowTextnull297     function GetShowText: Boolean;
GetStylenull298     function GetStyle: TProgressBarStyle;
299     procedure SetOrientation(AValue: TProgressBarOrientation);
300     procedure SetPosition(AValue: Integer);
301     procedure SetShowText(AValue: Boolean);
302     procedure SetStyle(AValue: TProgressBarStyle);
303   protected
CreateWidgetnull304     function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
305   public
306     procedure InitializeWidget; override;
307     property Orientation: TProgressBarOrientation read GetOrientation write SetOrientation;
308     property Position: Integer read GetPosition write SetPosition;
309     property ShowText: Boolean read GetShowText write SetShowText;
310     property Style: TProgressBarStyle read GetStyle write SetStyle;
311   end;
312 
313   { TGtk3Calendar }
314 
315   TGtk3Calendar = class(TGtk3Widget)
316   protected
CreateWidgetnull317     function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
318   public
319     procedure GetDate(out AYear, AMonth, ADay: Word);
320     procedure SetDate(const AYear, AMonth, ADay: Word);
321     procedure SetDisplayOptions(const ADisplayOptions: TGtkCalendarDisplayOptions);
322   end;
323 
324   { TGtk3StaticText }
325 
326   TGtk3StaticText = class(TGtk3Widget)
327   private
GetAlignmentnull328     function GetAlignment: TAlignment;
GetStaticBorderStylenull329     function GetStaticBorderStyle: TStaticBorderStyle;
330     procedure SetAlignment(AValue: TAlignment);
331     procedure SetStaticBorderStyle(AValue: TStaticBorderStyle);
332   protected
getTextnull333     function getText: String; override;
334     procedure setText(AValue: String); override;
CreateWidgetnull335     function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
336   public
337     property Alignment: TAlignment read GetAlignment write SetAlignment;
338     property StaticBorderStyle: TStaticBorderStyle read GetStaticBorderStyle write SetStaticBorderStyle;
339   end;
340 
341   { TGtk3Container }
342 
343   TGtk3Container = class(TGtk3Widget)
344   public
345     procedure AddChild(AWidget: PGtkWidget; const ALeft, ATop: Integer); virtual;
346   end;
347 
348   { TGtk3Page }
349 
350   TGtk3Page = class(TGtk3Container)
351   private
352     FPageLabel: PGtkLabel;
353   protected
354     procedure setText(AValue: String); override;
getTextnull355     function getText: String; override;
CreateWidgetnull356     function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
357     procedure DestroyWidget; override;
358   public
getClientRectnull359     function getClientRect: TRect; override;
360   end;
361 
362   { TGtk3NoteBook }
363 
364   TGtk3NoteBook = class (TGtk3Container)
365   protected
CreateWidgetnull366     function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
367   public
getClientRectnull368     function getClientRect: TRect; override;
getPagesCountnull369     function getPagesCount: integer;
370     procedure InsertPage(ACustomPage: TCustomPage; AIndex: Integer);
371     procedure MovePage(ACustomPage: TCustomPage; ANewIndex: Integer);
372     procedure RemovePage(AIndex: Integer);
373     procedure SetPageIndex(AIndex: Integer);
374     procedure SetShowTabs(const AShowTabs: Boolean);
375     procedure SetTabPosition(const ATabPosition: TTabPosition);
376     procedure SetTabLabelText(AChild: TCustomPage; AText: String);
GetTabLabelTextnull377     function  GetTabLabelText(AChild: TCustomPage): String;
378   end;
379 
380   { TGtk3Bin }
381 
382   TGtk3Bin = class(TGtk3Container)
383 
384   end;
385 
386 
387   { TGtk3Paned }
388 
389   TGtk3Paned = class(TGtk3Container)
390   protected
CreateWidgetnull391     function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
392   end;
393 
394   { TGtk3SplitterSide }
395 
396   TGtk3SplitterSide = class(TGtk3Container)
397   protected
CreateWidgetnull398     function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
399   end;
400 
401 
402   { TGtk3MenuShell }
403 
404   TGtk3MenuShell = class(TGtk3Container)
405   public
406     MenuObject: TMenu;
407     constructor Create(const AMenu: TMenu; AMenuBar: PGtkMenuBar); virtual; overload;
408     procedure Insert(AMenuShell: PGtkMenuShell; APosition: Integer);
409     procedure InitializeWidget; override;
410   end;
411 
412   { TGtk3MenuBar }
413 
414   TGtk3MenuBar = class(TGtk3MenuShell)
415   protected
CreateWidgetnull416     function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
417   end;
418 
419   { TGtk3Menu }
420 
421   TGtk3Menu = class(TGtk3MenuShell)
422   protected
CreateWidgetnull423     function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
424   public
425     PopupPoint: TPoint;
426     constructor CreateFromMenuItem(const AMenuItem: TMenuItem); virtual; overload;
427   end;
428 
429   { TGtk3MenuItem }
430 
431   TGtk3MenuItem = class(TGtk3Bin)
432   private
GetCaptionnull433     function GetCaption: string;
434     procedure SetCaption(AValue: string);
435   protected
CreateWidgetnull436     function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
437   public
438     MenuItem: TMenuItem;
439     constructor Create(const AMenuItem: TMenuItem); virtual; overload;
440     procedure InitializeWidget; override;
441     property Caption: string read GetCaption write SetCaption;
442   end;
443 
444   { TGtk3ScrollableWin }
445 
446   TGtk3ScrollableWin = class(TGtk3Container)
447   private
448     FBorderStyle: TBorderStyle;
449     FScrollX: Integer;
450     FScrollY: Integer;
GetHScrollBarPolicynull451     function GetHScrollBarPolicy: TGtkPolicyType;
GetVScrollBarPolicynull452     function GetVScrollBarPolicy: TGtkPolicyType;
453     procedure SetBorderStyle(AValue: TBorderStyle);
454     procedure SetHScrollBarPolicy(AValue: TGtkPolicyType); virtual;
455     procedure SetVScrollBarPolicy(AValue: TGtkPolicyType); virtual;
456   public
457     procedure SetScrollBarsSignalHandlers;
getClientBoundsnull458     function getClientBounds: TRect; override;
getHorizontalScrollbarnull459     function getHorizontalScrollbar: PGtkScrollbar; virtual; abstract;
getVerticalScrollbarnull460     function getVerticalScrollbar: PGtkScrollbar; virtual; abstract;
getScrolledWindownull461     function getScrolledWindow: PGtkScrolledWindow; virtual; abstract;
462     property BorderStyle: TBorderStyle read FBorderStyle write SetBorderStyle;
463     property HScrollBarPolicy: TGtkPolicyType read GetHScrollBarPolicy write SetHScrollBarPolicy;
464     property VScrollBarPolicy: TGtkPolicyType read GetVScrollBarPolicy write SetVScrollBarPolicy;
465     property ScrollX: Integer read FScrollX write FScrollX;
466     property ScrollY: Integer read FScrollY write FScrollY;
467   end;
468 
469   { TGtk3ToolBar }
470 
471   TGtk3ToolBar = class(TGtk3Container)
472   protected
CreateWidgetnull473     function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
474   end;
475 
476   { TGtk3Memo }
477 
478   TGtk3Memo = class(TGtk3ScrollableWin)
479   private
GetAlignmentnull480     function GetAlignment: TAlignment;
GetReadOnlynull481     function GetReadOnly: Boolean;
GetWantTabsnull482     function GetWantTabs: Boolean;
GetWordWrapnull483     function GetWordWrap: Boolean;
484     procedure SetAlignment(AValue: TAlignment);
485     procedure SetReadOnly(AValue: Boolean);
486     procedure SetWantTabs(AValue: Boolean);
487     procedure SetWordWrap(AValue: Boolean);
488   protected
getTextnull489     function getText: String; override;
490     procedure setText(AValue: String); override;
CreateWidgetnull491     function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
EatArrowKeysnull492     function EatArrowKeys(const AKey: Word): Boolean; override;
493   public
getHorizontalScrollbarnull494     function getHorizontalScrollbar: PGtkScrollbar; override;
getVerticalScrollbarnull495     function getVerticalScrollbar: PGtkScrollbar; override;
GetScrolledWindownull496     function GetScrolledWindow: PGtkScrolledWindow; override;
497   public
498     property Alignment: TAlignment read GetAlignment write SetAlignment;
499     property ReadOnly: Boolean read GetReadOnly write SetReadOnly;
500     property WantTabs: Boolean read GetWantTabs write SetWantTabs;
501     property WordWrap: Boolean read GetWordWrap write SetWordWrap;
502   end;
503 
504   { TGtk3ListBox }
505 
506   TGtk3ListBox = class(TGtk3ScrollableWin)
507   private
508     FListBoxStyle: TListBoxStyle;
GetItemIndexnull509     function GetItemIndex: Integer;
GetMultiSelectnull510     function GetMultiSelect: Boolean;
511     procedure SetItemIndex(AValue: Integer);
512     procedure SetListBoxStyle(AValue: TListBoxStyle);
513     procedure SetMultiSelect(AValue: Boolean);
514   protected
CreateWidgetnull515     function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
EatArrowKeysnull516     function EatArrowKeys(const AKey: Word): Boolean; override;
517     procedure InitializeWidget; override;
518   public
getHorizontalScrollbarnull519     function getHorizontalScrollbar: PGtkScrollbar; override;
getVerticalScrollbarnull520     function getVerticalScrollbar: PGtkScrollbar; override;
GetScrolledWindownull521     function GetScrolledWindow: PGtkScrolledWindow; override;
522   public
GetSelCountnull523     function GetSelCount: Integer;
GetSelectionnull524     function GetSelection: PGtkTreeSelection;
GetItemSelectednull525     function GetItemSelected(const AIndex: Integer): Boolean;
526     procedure SelectItem(const AIndex: Integer; ASelected: Boolean);
527     procedure SetTopIndex(const AIndex: Integer);
528     property ItemIndex: Integer read GetItemIndex write SetItemIndex;
529     property MultiSelect: Boolean read GetMultiSelect write SetMultiSelect;
530     property ListBoxStyle: TListBoxStyle read FListBoxStyle write SetListBoxStyle;
531   end;
532 
533   { TGtk3CheckListBox }
534 
535   TGtk3CheckListBox = class(TGtk3ListBox)
536   protected
CreateWidgetnull537     function CreateWidget(const Params: TCreateParams): PGtkWidget; override;
538   end;
539 
540   { TGtk3ListView }
541 
542   TGtk3ListView = class(TGtk3ScrollableWin)
543   private
544     FPreselectedIndices: TFPList;
545     FImages: TFPList;
546     FIsTreeView: Boolean;
547   protected
CreateWidgetnull548     function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
EatArrowKeysnull549     function EatArrowKeys(const AKey: Word): Boolean; override;
550   public
551     destructor Destroy; override;
552     {interface implementation}
getHorizontalScrollbarnull553     function getHorizontalScrollbar: PGtkScrollbar; override;
getVerticalScrollbarnull554     function getVerticalScrollbar: PGtkScrollbar; override;
GetScrolledWindownull555     function GetScrolledWindow: PGtkScrolledWindow; override;
556     procedure ClearImages;
557     procedure ColumnDelete(AIndex: Integer);
ColumnGetWidthnull558     function ColumnGetWidth(AIndex: Integer): Integer;
559     procedure ColumnInsert(AIndex: Integer; AColumn: TListColumn);
560     procedure SetAlignment(AIndex: Integer; AColumn: TListColumn; AAlignment: TAlignment);
561     procedure SetColumnAutoSize(AIndex: Integer; AColumn: TListColumn; AAutoSize: Boolean);
562     procedure SetColumnCaption(AIndex: Integer; AColumn: TListColumn; const ACaption: String);
563     procedure SetColumnMaxWidth(AIndex: Integer; AColumn: TListColumn; AMaxWidth: Integer);
564     procedure SetColumnMinWidth(AIndex: Integer; AColumn: TListColumn; AMinWidth: Integer);
565     procedure SetColumnWidth(AIndex: Integer; AColumn: TListColumn; AWidth: Integer);
566     procedure SetColumnVisible(AIndex: Integer; AColumn: TListColumn; AVisible: Boolean);
567     procedure ColumnSetSortIndicator(const AIndex: Integer; const AColumn: TListColumn; const ASortIndicator: TSortIndicator);
568 
569     procedure ItemDelete(AIndex: Integer);
570     procedure ItemInsert(AIndex: Integer; AItem: TListItem);
571     procedure ItemSetText(AIndex, ASubIndex: Integer; AItem: TListItem; const AText: String);
572     procedure ItemSetState(const AIndex: Integer; const AItem: TListItem; const AState: TListItemState;
573       const AIsSet: Boolean);
ItemGetStatenull574     function ItemGetState(const AIndex: Integer; const AItem: TListItem; const AState: TListItemState;
575       out AIsSet: Boolean): Boolean;
576 
577     procedure UpdateImageCellsSize;
578 
579     property Images: TFPList read FImages write FImages;
580     property IsTreeView: Boolean read FIsTreeView;
581   end;
582 
583   { TGtk3Box }
584 
585   TGtk3Box = class(TGtk3Container)
586 
587   end;
588 
589   { TGtk3StatusBar }
590 
591   TGtk3StatusBar = class(TGtk3Box)
592   protected
CreateWidgetnull593     function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
594   end;
595 
596   { TGtk3Panel }
597 
598   TGtk3Panel = class(TGtk3Bin)
599   private
600     FBevelInner: TBevelCut;
601     FBevelOuter: TBevelCut;
602     FBorderStyle: TBorderStyle;
603     FText: String;
604   protected
605     procedure SetColor(AValue: TColor); override;
CreateWidgetnull606     function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
607     procedure DoBeforeLCLPaint; override;
getTextnull608     function getText: String; override;
609     procedure setText(AValue: String); override;
610   public
611     property BevelInner: TBevelCut read FBevelInner write FBevelInner;
612     property BevelOuter: TBevelCut read FBevelOuter write FBevelOuter;
613     property BorderStyle: TBorderStyle read FBorderStyle write FBorderStyle;
614   end;
615 
616   { TGtk3GroupBox }
617 
618   TGtk3GroupBox = class(TGtk3Bin)
619   protected
CreateWidgetnull620     function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
getTextnull621     function getText: String; override;
622     procedure setText(AValue: String); override;
623   public
624   end;
625 
626   { TGtk3ComboBox }
627 
628   TGtk3ComboBox = class(TGtk3Bin)
629   private
630     FCellView: PGtkCellView;
GetItemIndexnull631     function GetItemIndex: Integer;
632     procedure SetDroppedDown(AValue: boolean);
633     procedure SetItemIndex(AValue: Integer);
GetDroppedDownnull634     function GetDroppedDown: boolean;
635   protected
CreateWidgetnull636     function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
EatArrowKeysnull637     function EatArrowKeys(const AKey: Word): Boolean; override;
getTextnull638     function getText: String; override;
639     procedure setText(AValue: String); override;
640   public
641     procedure DumpPrivateStructValues(ADbgEvent: String);
642   public
CanFocusnull643     function CanFocus: Boolean; override;
644     procedure SetFocus; override;
GetCellViewnull645     function GetCellView: PGtkCellView;
GetPopupWidgetnull646     function GetPopupWidget: PGtkWidget;
GetButtonWidgetnull647     function GetButtonWidget: PGtkWidget;
GetCellViewFramenull648     function GetCellViewFrame: PGtkWidget;
649     procedure InitializeWidget; override;
650     property DroppedDown: boolean read GetDroppedDown write SetDroppedDown;
651     property ItemIndex: Integer read GetItemIndex write SetItemIndex;
652   end;
653 
654   { TGtk3Button }
655 
656   TGtk3Button = class(TGtk3Bin)
657   private
658     FMargin: Integer;
659     FLayout: Integer;
660     FSpacing: Integer;
getLayoutnull661     function getLayout: Integer;
getMarginnull662     function getMargin: Integer;
663     procedure SetLayout(AValue: Integer);
664     procedure SetMargin(AValue: Integer);
665     procedure SetSpacing(AValue: Integer);
666   protected
getTextnull667     function getText: String; override;
668     procedure setText(AValue: String); override;
CreateWidgetnull669     function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
670   public
IsWidgetOknull671     function IsWidgetOk: Boolean; override;
672     procedure SetDefault(const ADefault: Boolean);
673     property Layout: Integer read getLayout write SetLayout;
674     property Margin: Integer read getMargin write SetMargin;
675     property Spacing: Integer read FSpacing write SetSpacing;
676   end;
677 
678   { TGtk3ToggleButton }
679 
680   TGtk3ToggleButton = class(TGtk3Button)
681   protected
CreateWidgetnull682     function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
683   public
684     procedure InitializeWidget; override;
685   end;
686 
687   { TGtk3CheckBox }
688 
689   TGtk3CheckBox = class(TGtk3ToggleButton)
690   private
GetStatenull691     function GetState: TCheckBoxState;
692     procedure SetState(AValue: TCheckBoxState);
693   protected
CreateWidgetnull694     function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
695   public
696     property State: TCheckBoxState read GetState write SetState;
697   end;
698 
699   { TGtk3RadioButton }
700 
701   TGtk3RadioButton = class(TGtk3CheckBox)
702   private
703   protected
CreateWidgetnull704     function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
705     procedure InitializeWidget; override;
706   public
707   end;
708 
709   { TGtk3CustomControl }
710 
711   TGtk3CustomControl = class(TGtk3ScrollableWin)
712     private
713     protected
CreateWidgetnull714       function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
EatArrowKeysnull715       function EatArrowKeys(const AKey: Word): Boolean; override;
716     public
717       procedure InitializeWidget; override;
getClientRectnull718       function getClientRect: TRect; override;
getHorizontalScrollbarnull719       function getHorizontalScrollbar: PGtkScrollbar; override;
getVerticalScrollbarnull720       function getVerticalScrollbar: PGtkScrollbar; override;
GetScrolledWindownull721       function GetScrolledWindow: PGtkScrolledWindow; override;
722   end;
723 
724   { TGtk3ScrollingWinControl }
725 
726   TGtk3ScrollingWinControl = class(TGtk3CustomControl)
727     protected
CreateWidgetnull728       function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
729   end;
730 
731   { TGtk3Splitter }
732 
733   TGtk3Splitter = class(TGtk3Panel)
734   public
735   end;
736 
737 
738   { TGtk3Window }
739 
740   TGtk3Window = class(TGtk3ScrollableWin) {we are TGtk3Bin actually, but it won't hurt since we need scroll}
741   private
742     FIcon: PGdkPixBuf;
743     FScrollWin: PGtkScrolledWindow;
744     FMenuBar: PGtkMenuBar;
745     FBox: PGtkBox;
GetSkipTaskBarHintnull746     function GetSkipTaskBarHint: Boolean;
GetTitlenull747     function GetTitle: String;
748     procedure SetIcon(AValue: PGdkPixBuf);
749     procedure SetSkipTaskBarHint(AValue: Boolean);
750     procedure SetTitle(AValue: String);
751   protected
CreateWidgetnull752     function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
EatArrowKeysnull753     function EatArrowKeys(const AKey: Word): Boolean; override;
getTextnull754     function getText: String; override;
755     procedure setText(AValue: String); override;
756   public
getClientBoundsnull757     // function getClientBounds: TRect; override;
758     function getClientRect: TRect; override;
getHorizontalScrollbarnull759     function getHorizontalScrollbar: PGtkScrollbar; override;
getVerticalScrollbarnull760     function getVerticalScrollbar: PGtkScrollbar; override;
GetScrolledWindownull761     function GetScrolledWindow: PGtkScrolledWindow; override;
762   public
763     destructor Destroy; override;
764     procedure Activate; override;
765     procedure Gtk3ActivateWindow(AEvent: PGdkEvent);
Gtk3CloseQuerynull766     function Gtk3CloseQuery: Boolean;
GetWindownull767     function GetWindow: PGdkWindow; override;
GetMenuBarnull768     function GetMenuBar: PGtkMenuBar;
GetBoxnull769     function GetBox: PGtkBox;
GetWindowStatenull770     function GetWindowState: TGdkWindowState;
771     property Icon: PGdkPixBuf read FIcon write SetIcon;
772     property SkipTaskBarHint: Boolean read GetSkipTaskBarHint write SetSkipTaskBarHint;
773     property Title: String read GetTitle write SetTitle;
774   end;
775 
776   { TGtk3HintWindow }
777 
778   TGtk3HintWindow = class(TGtk3Window)
779     private
780       FText: String;
781     protected
getTextnull782       function getText: String; override;
783       procedure setText(AValue: String); override;
CreateWidgetnull784       function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
785   end;
786 
787   { TGtk3Dialog }
788 
789   TGtk3Dialog = class(TGtk3Widget)
790   protected
CreateWidgetnull791     function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
792   public
793     CommonDialog: TCommonDialog;
794     procedure InitializeWidget; override;
795   end;
796 
797   { TGtk3FileDialog }
798 
799   TGtk3FileDialog = class(TGtk3Dialog)
800   private
801   protected
CreateWidgetnull802     function CreateWidget(const Params: TCreateParams): PGtkWidget; override;
803   public
804     constructor Create(const ACommonDialog: TCommonDialog); virtual; overload;
805   end;
806 
807   { TGtk3FontSelectionDialog }
808 
809   TGtk3FontSelectionDialog = class(TGtk3Dialog)
810   protected
CreateWidgetnull811     function CreateWidget(const Params: TCreateParams): PGtkWidget; override;
812   public
813     constructor Create(const ACommonDialog: TCommonDialog); virtual; overload;
814   end;
815 
816 {main event filter for all widgets, also called from widgetset main eventfilter}
Gtk3WidgetEventnull817 function Gtk3WidgetEvent(widget: PGtkWidget; event: PGdkEvent; data: GPointer): gboolean; cdecl;
818 
819 implementation
820 
821 uses gtk3int;
822 
Gtk3EventToStrnull823 function Gtk3EventToStr(AEvent: TGdkEventType): String;
824 begin
825   Result := 'GDK_NOTHING';
826   case AEvent of
827     // GDK_DELETE:
828     0: Result := 'GDK_DELETE';
829     // GDK_DESTROY:
830     1: Result := 'GDK_DESTROY';
831     // GDK_EXPOSE:
832     2: Result := 'GDK_EXPOSE';
833     // GDK_MOTION_NOTIFY:
834     3: Result := 'GDK_MOTION_NOTIFY';
835     // GDK_BUTTON_PRESS:
836     4: Result := 'GDK_BUTTON_PRESS';
837     // GDK_2BUTTON_PRESS:
838     5: Result := 'GDK_2BUTTON_PRESS';
839     // GDK_3BUTTON_PRESS:
840     6: Result := 'GDK_3BUTTON_PRESS';
841     // GDK_BUTTON_RELEASE:
842     7: Result := 'GDK_BUTTON_RELEASE';
843     // GDK_KEY_PRESS:
844     8: Result := 'GDK_KEY_PRESS';
845     // GDK_KEY_RELEASE:
846     9: Result := 'GDK_KEY_RELEASE';
847     // GDK_ENTER_NOTIFY:
848     10: Result := 'GDK_ENTER_NOTIFY';
849     // GDK_LEAVE_NOTIFY:
850     11: Result := 'GDK_LEAVE_NOTIFY';
851     // GDK_FOCUS_CHANGE:
852     12: Result := 'GDK_FOCUS_CHANGE';
853     // GDK_CONFIGURE:
854     13: Result := 'GDK_CONFIGURE';
855     // GDK_MAP:
856     14: Result := 'GDK_MAP';
857     // GDK_UNMAP:
858     15: Result := 'GDK_UNMAP';
859     // GDK_PROPERTY_NOTIFY:
860     16: Result := 'GDK_PROPERTY_NOTIFY';
861     // GDK_SELECTION_CLEAR:
862     17: Result := 'GDK_SELECTION_CLEAR';
863     // GDK_SELECTION_REQUEST:
864     18: Result := 'GDK_SELECTION_REQUEST';
865     // GDK_SELECTION_NOTIFY:
866     19: Result := 'GDK_SELECTION_NOTIFY';
867     // GDK_PROXIMITY_IN:
868     20: Result := 'GDK_PROXIMITY_IN';
869     // GDK_PROXIMITY_OUT:
870     21: Result := 'GDK_PROXIMITY_OUT';
871     // GDK_DRAG_ENTER:
872     22: Result := 'GDK_DRAG_ENTER';
873     // GDK_DRAG_LEAVE:
874     23: Result := 'GDK_DRAG_LEAVE';
875     // GDK_DRAG_MOTION_:
876     24: Result := 'GDK_DRAG_MOTION_';
877     // GDK_DRAG_STATUS_:
878     25: Result := 'GDK_DRAG_STATUS_';
879     // GDK_DROP_START:
880     26: Result := 'GDK_DROP_START';
881     // GDK_DROP_FINISHED:
882     27: Result := 'GDK_DROP_FINISHED';
883     // GDK_CLIENT_EVENT:
884     28: Result := 'GDK_CLIENT_EVENT';
885     // GDK_VISIBILITY_NOTIFY:
886     29: Result := 'GDK_VISIBILITY_NOTIFY';
887     // GDK_SCROLL:
888     31: Result := 'GDK_SCROLL';
889     // GDK_WINDOW_STATE:
890     32: Result := 'GDK_WINDOW_STATE';
891     // GDK_SETTING:
892     33: Result := 'GDK_SETTING';
893     // GDK_OWNER_CHANGE:
894     34: Result := 'GDK_OWNER_CHANGE';
895     // GDK_GRAB_BROKEN:
896     35: Result := 'GDK_GRAB_BROKEN';
897     // GDK_DAMAGE:
898     36: Result := 'GDK_DAMAGE';
899     // GDK_TOUCH_BEGIN:
900     37: Result := 'GDK_TOUCH_BEGIN';
901     // GDK_TOUCH_UPDATE:
902     38: Result := 'GDK_TOUCH_UPDATE';
903     // GDK_TOUCH_END:
904     39: Result := 'GDK_TOUCH_END';
905     // GDK_TOUCH_CANCEL:
906     40: Result := 'GDK_TOUCH_CANCEL';
907     // GDK_EVENT_LAST:
908     41: Result := 'GDK_EVENT_LAST';
909   end;
910 end;
911 
Gtk3MenuItemEventnull912 function Gtk3MenuItemEvent(widget: PGtkWidget; event: PGdkEvent; data: GPointer): gboolean; cdecl;
913 begin
914   Result := False;
915 
916   if not Assigned(Application) or (Assigned(Application) and Application.Terminated) then
917       exit;
918 
919   // DebugLn('Gtk3MenuItemEvent triggered ',dbgsName(TGtk3MenuItem(Data).MenuItem),
920   //  ' ',Gtk3EventToStr(event^.type_));
921 
922   case event^.type_ of
923     // GDK_DELETE
924     0:
925     begin
926       // DebugLn('****** GDK_DELETE FOR ',dbgsName(TGtk3Widget(Data).LCLObject),' main_level=',dbgs(gtk_main_level));
927     end;
928     // GDK_DESTROY
929     1:
930     begin
931      // DebugLn('****** GDK_DESTROY FOR ' + dbgsName(TGtk3Widget(Data).LCLObject));
932     end;
933     // GDK_EXPOSE
934     2:
935     begin
936       // DebugLn('****** GDK_EXPOSE FOR ' + dbgsName(TGtk3Widget(Data).LCLObject));
937       // Gtk3DrawWidget is attached to 'draw' signal, Expose event doesn't trigger
938       // under gtk3.
939        // we use 'draw' signal Gtk3DrawEvent()
940       // Result := TGtk3Widget(Data).GtkEventPaint(Widget, Event);
941     end;
942     // GDK_MOTION_NOTIFY
943     3:
944     begin
945       // Result := TGtk3Widget(Data).GtkEventMouseMove(Widget, Event);
946     end;
947     // GDK_BUTTON_PRESS:
948     4:
949     begin
950       // Result := TGtk3Widget(Data).GtkEventMouse(Widget , Event);
951     end;
952     // GDK_2BUTTON_PRESS:
953     5:
954     begin
955       // if not TGtk3Widget(Data).LCLObject.Focused and TGtk3Widget(Data).LCLObject.CanFocus then
956       //  LCLIntf.SetFocus(HWND(TGtk3Widget(Data)));
957       // Result := TGtk3Widget(Data).GtkEventMouse(Widget , Event);
958     end;
959     // GDK_3BUTTON_PRESS:
960     6:
961     begin
962       // if not TGtk3Widget(Data).LCLObject.Focused and TGtk3Widget(Data).LCLObject.CanFocus then
963       //  LCLIntf.SetFocus(HWND(TGtk3Widget(Data)));
964       // Result := TGtk3Widget(Data).GtkEventMouse(Widget , Event);
965     end;
966     // GDK_BUTTON_RELEASE:
967     7:
968     begin
969       // Result := TGtk3Widget(Data).GtkEventMouse(Widget , Event);
970     end;
971 
972     // GDK_KEY_PRESS
973     8:
974     begin
975       // if Widget^.has_focus then // or (Widget = TGtk3Widget(data).GetContainerWidget) then
976       //  Result := TGtk3Widget(Data).GtkEventKey(Widget, Event, True);
977     end;
978     // GDK_KEY_RELEASE
979     9:
980     begin
981       // if Widget^.has_focus then // or (Widget = TGtk3Widget(data).GetContainerWidget) then
982       //  Result := TGtk3Widget(Data).GtkEventKey(Widget, Event, False);
983     end;
984 
985     // GDK_ENTER_NOTIFY
986     10:
987     begin
988       // TGtk3Widget(Data).GtkEventMouseEnterLeave(Widget, Event);
989     end;
990     // GDK_LEAVE_NOTIFY
991     11:
992     begin
993       // TGtk3Widget(Data).GtkEventMouseEnterLeave(Widget, Event);
994     end;
995     12:
996     begin
997       // GDK_FOCUS_CHANGE
998     end;
999     // GDK_CONFIGURE
1000     13:
1001     begin
1002       // GDK_CONFIGURE
1003     end;
1004     14: // GDK_MAP
1005     begin
1006       // DebugLn('****** GDK_MAP FOR ',dbgsName(TGtk3Widget(Data).LCLObject));
1007     end;
1008     16: // GDK_PROPERTY_NOTIFY
1009     begin
1010       // DebugLn('****** GDK_PROPERTY_NOTIFY FOR ',dbgsName(TGtk3Widget(Data).LCLObject));
1011     end;
1012     28: // GDK_CLIENT_EVENT
1013     begin
1014       // DebugLn('****** GDK_CLIENT_EVENT FOR ',dbgsName(TGtk3Widget(Data).LCLObject));
1015     end;
1016     29: // GDK_VISIBILITY_NOTIFY
1017     begin
1018       // Result := TGtk3Widget(Data).GtkEventShowHide(Widget, Event);
1019       // DebugLn('****** GDK_VISIBILITY_NOTIFY FOR ' + dbgsName(TGtk3Widget(Data).LCLObject));
1020     end;
1021     31: // GDK_SCROLL
1022     begin
1023       // DebugLn('****** GDK_SCROLL ' + dbgsName(TGtk3Widget(Data).LCLObject));
1024     end;
1025   end;
1026 end;
1027 
1028 function Gtk3WidgetEvent(widget: PGtkWidget; event: PGdkEvent; data: GPointer): gboolean; cdecl;
1029 begin
1030   {$IFDEF GTK3DEBUGCOMBOBOX}
1031   if (Data <> nil) and (wtComboBox in TGtk3Widget(Data).WidgetType) and
1032     (event^.type_ <> GDK_MOTION_NOTIFY) then
1033   begin
1034     if (Widget = TGtk3ComboBox(Data).GetPopupWidget) then
1035     DebugLn('***** Gtk3WidgetEvent(MENU triggered ',dbgsName(TGtk3Widget(Data).LCLObject),
1036       ' ',Gtk3EventToStr(event^.type_))
1037     else
1038     if (Widget = TGtk3ComboBox(Data).GetButtonWidget) then
1039     DebugLn('***** Gtk3WidgetEvent(BUTTON triggered ',dbgsName(TGtk3Widget(Data).LCLObject),
1040       ' ',Gtk3EventToStr(event^.type_))
1041     else
1042     if (Widget = PGtkWidget(TGtk3ComboBox(Data).GetCellView)) then
1043     DebugLn('***** Gtk3WidgetEvent(CELLVIEW triggered ',dbgsName(TGtk3Widget(Data).LCLObject),
1044       ' ',Gtk3EventToStr(event^.type_))
1045     else
1046     if (Widget = TGtk3ComboBox(Data).Widget) then
1047       DebugLn('***** Gtk3WidgetEvent(EVENTBOX triggered ',dbgsName(TGtk3Widget(Data).LCLObject),
1048         ' ',Gtk3EventToStr(event^.type_));
1049   end;
1050   {$ENDIF}
1051   {$IFDEF GTK3DEBUGCORE}
1052   // if event^.type_ = GDK_EXPOSE then
1053   if event^.type_ <> GDK_MOTION_NOTIFY then
1054     DebugLn('Gtk3WidgetEvent triggered ',dbgsName(TGtk3Widget(Data).LCLObject),
1055       ' ',Gtk3EventToStr(event^.type_));
1056   {$ENDIF}
1057   Result := False;
1058   if Assigned(Application) and Application.Terminated then
1059       exit;
1060   case event^.type_ of
1061     // GDK_DELETE
1062     0:
1063     begin
1064       // DebugLn('****** GDK_DELETE FOR ',dbgsName(TGtk3Widget(Data).LCLObject),' main_level=',dbgs(gtk_main_level));
1065       if wtWindow in TGtk3Widget(Data).WidgetType then
1066       begin
1067         TGtk3Window(Data).Gtk3CloseQuery;
1068         // let lcl destroy widget
1069         Result := True;
1070       end;
1071     end;
1072     // GDK_DESTROY
1073     1:
1074     begin
1075      // DebugLn('****** GDK_DESTROY FOR ' + dbgsName(TGtk3Widget(Data).LCLObject));
1076     end;
1077     // GDK_EXPOSE
1078     2:
1079     begin
1080       DebugLn('****** GDK_EXPOSE FOR ' + dbgsName(TGtk3Widget(Data).LCLObject));
1081       // Gtk3DrawWidget is attached to 'draw' signal, Expose event doesn't trigger
1082       // under gtk3.
1083        // we use 'draw' signal Gtk3DrawEvent()
1084       // Result := TGtk3Widget(Data).GtkEventPaint(Widget, Event);
1085     end;
1086     // GDK_MOTION_NOTIFY
1087     3:
1088     begin
1089       if wtWindow in TGtk3Widget(Data).WidgetType then
1090       begin
1091         if Widget = TGtk3Widget(Data).Widget then
1092           exit;
1093       end;
1094       Result := TGtk3Widget(Data).GtkEventMouseMove(Widget, Event);
1095     end;
1096     // GDK_BUTTON_PRESS:
1097     4:
1098     begin
1099       // set focus before gtk does that, so we have same behaviour as other ws
1100       if TGtk3Widget(Data).GetFocusableByMouse and
1101         not TGtk3Widget(Data).LCLObject.Focused and
1102         TGtk3Widget(Data).LCLObject.CanFocus then
1103       begin
1104         //FIXME: combobox updates popup-shown property too late
1105         // so we dont know yet if its dropped down or not
1106         if (wtComboBox in TGtk3Widget(Data).WidgetType) then
1107         begin
1108           TGtk3ComboBox(Data).DumpPrivateStructValues('GDK_BUTTON_PRESS btn='+IntToStr(Event^.button.button));
1109         end;
1110         if (wtWindow in TGtk3Widget(Data).WidgetType) then
1111         begin
1112           TGtk3Widget(Data).Activate;
1113         end else
1114           LCLIntf.SetFocus(HWND(TGtk3Widget(Data)));
1115       end;
1116 
1117       Result := TGtk3Widget(Data).GtkEventMouse(Widget , Event);
1118     end;
1119     // GDK_2BUTTON_PRESS:
1120     5:
1121     begin
1122       // set focus before gtk does that, so we have same behaviour as other ws
1123       if TGtk3Widget(Data).GetFocusableByMouse and
1124         not TGtk3Widget(Data).LCLObject.Focused and
1125         TGtk3Widget(Data).LCLObject.CanFocus then
1126           LCLIntf.SetFocus(HWND(TGtk3Widget(Data)));
1127       Result := TGtk3Widget(Data).GtkEventMouse(Widget , Event);
1128     end;
1129     // GDK_3BUTTON_PRESS:
1130     6:
1131     begin
1132       // set focus before gtk does that, so we have same behaviour as other ws
1133       if TGtk3Widget(Data).GetFocusableByMouse and
1134         not TGtk3Widget(Data).LCLObject.Focused and
1135         TGtk3Widget(Data).LCLObject.CanFocus then
1136           LCLIntf.SetFocus(HWND(TGtk3Widget(Data)));
1137       Result := TGtk3Widget(Data).GtkEventMouse(Widget , Event);
1138     end;
1139     // GDK_BUTTON_RELEASE:
1140     7:
1141     begin
1142       Result := TGtk3Widget(Data).GtkEventMouse(Widget , Event);
1143     end;
1144 
1145     // GDK_KEY_PRESS
1146     8:
1147     begin
1148       if Widget^.has_focus then
1149         Result := TGtk3Widget(Data).GtkEventKey(Widget, Event, True);
1150     end;
1151     // GDK_KEY_RELEASE
1152     9:
1153     begin
1154       if Widget^.has_focus then // or (Widget = TGtk3Widget(data).GetContainerWidget) then
1155         Result := TGtk3Widget(Data).GtkEventKey(Widget, Event, False);
1156     end;
1157 
1158     // GDK_ENTER_NOTIFY
1159     10:
1160     begin
1161       if wtWindow in TGtk3Widget(Data).WidgetType then
1162       begin
1163         if Widget <> TGtk3Widget(Data).GetContainerWidget then
1164           exit;
1165       end;
1166       (*
1167       DebugLn('** ENTER_NOTIFY ',dbgs(Event^.crossing.send_event),' TIME ',dbgs(Event^.crossing.time),' MODE ',dbgs(Event^.crossing.mode),
1168        ' WIDGET ',dbgHex(PtrUInt(Widget)),' LCLObject ',dbgsName(TGtk3Widget(Data).LCLObject),
1169        ' Widget=?',dbgs(Widget=TGtk3Widget(Data).GetContainerWidget));
1170       *)
1171       (*
1172       if wtComboBox in TGtk3Widget(Data).WidgetType then
1173       begin
1174         // DebugLn('** ENTER_NOTIFY ',dbgs(Event^.crossing.send_event),' TIME ',dbgs(Event^.crossing.time),' MODE ',dbgs(Event^.crossing.mode),
1175         //  ' ENTERLEAVETIME=',dbgs(TGtk3ComboBox(Data).FEnterLeaveTime),' WIDGET ',dbgHex(PtrUInt(Widget)));
1176         TGtk3ComboBox(Data).DumpPrivateStructValues('GDK_ENTER_NOTIFY');
1177         if Widget <> TGtk3Widget(Data).Widget then
1178           exit;
1179         // upisi u combobox enter time, ako je enter.time - leave.time < 20 onda ne salji msg !
1180         TGtk3ComboBox(Data).FEnterLeaveTime := Event^.crossing.time;
1181       end;
1182       *)
1183       TGtk3Widget(Data).GtkEventMouseEnterLeave(Widget, Event);
1184     end;
1185     // GDK_LEAVE_NOTIFY
1186     11:
1187     begin
1188       if wtWindow in TGtk3Widget(Data).WidgetType then
1189       begin
1190         if Widget <> TGtk3Widget(Data).GetContainerWidget then
1191           exit;
1192       end;
1193       (*
1194       DebugLn('** LEAVE_NOTIFY ',dbgs(Event^.crossing.send_event),' TIME ',dbgs(Event^.crossing.time),' MODE ',dbgs(Event^.crossing.mode),
1195        ' WIDGET ',dbgHex(PtrUInt(Widget)),' LCLObject ',dbgsName(TGtk3Widget(Data).LCLObject),
1196        ' Widget=?',dbgs(Widget=TGtk3Widget(Data).GetContainerWidget));
1197       *)
1198       (*
1199       if wtComboBox in TGtk3Widget(Data).WidgetType then
1200       begin
1201         // DebugLn('** LEAVE_NOTIFY ',dbgs(Event^.crossing.send_event),' TIME ',dbgs(Event^.crossing.time),' MODE ',dbgs(Event^.crossing.mode),
1202         // ' TIME DIFF=',dbgs(Event^.crossing.time - TGtk3ComboBox(Data).FEnterLeaveTime),
1203         // ' WIDGET ',dbgHex(PtrUInt(Widget)));
1204         if Widget <> TGtk3Widget(Data).Widget then
1205           exit;
1206         if Event^.crossing.time - TGtk3ComboBox(Data).FEnterLeaveTime < 100 then
1207         begin
1208           exit(False);
1209         end;
1210       end;
1211       *)
1212       TGtk3Widget(Data).GtkEventMouseEnterLeave(Widget, Event);
1213     end;
1214     // GDK_FOCUS_CHANGE
1215     12:
1216     begin
1217       if wtComboBox in TGtk3Widget(Data).WidgetType then
1218       begin
1219         TGtk3ComboBox(Data).DumpPrivateStructValues('GDK_FOCUS_CHANGE='+IntToStr(Event^.focus_change.in_));
1220         //FIXME: combobox updates popup-shown property too late
1221         // so we dont know yet if its dropped down or not
1222         if TGtk3ComboBox(Data).DroppedDown then
1223           exit;
1224       end;
1225       TGtk3Widget(Data).GtkEventFocus(Widget, Event);
1226     end;
1227     // GDK_CONFIGURE
1228     13:
1229     begin
1230       (* DOES NOT WORK AS DOCUMENTATION SAYS
1231       if Data <> nil then
1232       begin
1233         if wtWindow in TGtk3Widget(Data).WidgetType then
1234         begin
1235           TGtk3Window(Data).Gtk3ActivateWindow(Event);
1236           DebugLn('** WindowState event ',dbgsName(TGtk3Widget(Data).LCLObject),' windowState=',dbgs(TGtk3Window(Data).GetWindowState));
1237         end else
1238           DebugLn('** WindowState event not wtWindow ',dbgsName(TGtk3Widget(Data).LCLObject));
1239       end;
1240       *)
1241       Result := TGtk3Widget(Data).GtkEventResize(Widget, Event);
1242     end;
1243     14: // GDK_MAP
1244     begin
1245       // DebugLn('****** GDK_MAP FOR ',dbgsName(TGtk3Widget(Data).LCLObject));
1246     end;
1247     15: // GDK_UNMAP
1248     begin
1249        // DebugLn('****** GDK_UNMAP FOR ',dbgsName(TGtk3Widget(Data).LCLObject));
1250     end;
1251     16: // GDK_PROPERTY_NOTIFY
1252     begin
1253       // DebugLn('****** GDK_PROPERTY_NOTIFY FOR ',dbgsName(TGtk3Widget(Data).LCLObject));
1254     end;
1255     28: // GDK_CLIENT_EVENT
1256     begin
1257       // DebugLn('****** GDK_CLIENT_EVENT FOR ',dbgsName(TGtk3Widget(Data).LCLObject));
1258     end;
1259     29: // GDK_VISIBILITY_NOTIFY
1260     begin
1261       // ONLY HERE WE CAN CATCH Activate/Deactivate but problem is that
1262       // PGtkWindow does not update active property properly
1263       // so PGtkWindow(Widget)^.is_active returns TRUE even if window isn't active anymore
1264       if wtWindow in TGtk3Widget(Data).WidgetType then
1265       begin
1266         TGtk3Window(Data).Gtk3ActivateWindow(Event);
1267       end;
1268       // Result := TGtk3Widget(Data).GtkEventShowHide(Widget, Event);
1269       // DebugLn('****** GDK_VISIBILITY_NOTIFY FOR ' + dbgsName(TGtk3Widget(Data).LCLObject));
1270     end;
1271     31: // GDK_SCROLL
1272     begin
1273       // DebugLn('****** GDK_SCROLL ' + dbgsName(TGtk3Widget(Data).LCLObject));
1274       Result := TGtk3Widget(Data).GtkEventMouseWheel(Widget, Event);
1275     end;
1276     32: // GDK_WINDOW_STATE
1277     begin
1278       // DebugLn('****** GDK_WINDOW_STATE FOR ' + dbgsName(TGtk3Widget(Data).LCLObject));
1279       // this doesn't work as expected ... must use GDK_CONFIGURE to get active status ?!?
1280     end;
1281     35: // GDK_GRAB_BROKEN  could be broken eg. because of popupmenu
1282     begin
1283       DebugLn('****** GDK_GRAB_BROKEN (no problem if popupmenu is activated) ' + dbgsName(TGtk3Widget(Data).LCLObject));
1284     end;
1285   otherwise
1286     DebugLn('****** GDK unhandled event type ' + dbgsName(TGtk3Widget(Data).LCLObject));
1287     WriteLn(event^.type_);
1288 
1289   end;
1290 end;
1291 
Gtk3DrawWidgetnull1292 function Gtk3DrawWidget(AWidget: PGtkWidget; AContext: Pcairo_t; Data: gpointer): gboolean; cdecl;
1293 var
1294   ARect: TGdkRectangle;
1295 begin
1296   Result := False;
1297   if Data <> nil then
1298   begin
1299     gdk_cairo_get_clip_rectangle(AContext, @ARect);
1300     // DebugLn('**** Sending paint event to ',dbgsName(TGtk3Widget(Data).LCLObject),' clip ',dbgs(RectFromGdkRect(ARect)),' w=',dbgs(ARect.Width),' h=',dbgs(ARect.height));
1301     Result := TGtk3Widget(Data).GtkEventPaint(AWidget, AContext);
1302     // workaround for lcl painted widgets until we found why gtk3 sends wrong rect
1303     if (TGtk3Widget(Data).FHasPaint) and
1304       (ARect.height < (TGtk3Widget(Data).GetContainerWidget^.get_allocated_height div 4) ) then
1305         AWidget^.queue_draw;
1306   end;
1307 end;
1308 
1309 procedure Gtk3MapWidget(AWidget: PGtkWidget; Data: gPointer); cdecl;
1310 var
1311   Allocation: TGtkAllocation;
1312   ARect: TRect;
1313   AWindow: PGdkWindow;
1314   xx,yy,w,h: Gint;
1315 begin
1316   AWidget^.get_allocation(@Allocation);
1317   {$IFDEF GTK3DEBUGCORE}
1318   DebugLn('**** Gtk3MapWidget ....',dbgsName(TGtk3Widget(Data).LCLObject));
1319   with Allocation do
1320     DebugLn(' Allocation ',Format('x %d y %d w %d h %d',[x,y,width,height]));
1321   {$ENDIF}
1322   ARect := TGtk3Widget(Data).LCLObject.BoundsRect;
1323   {$IFDEF GTK3DEBUGCORE}
1324   with ARect do
1325     DebugLn(' Rect ',Format('x %d y %d w %d h %d',[Left,Top,Right - Left, Bottom - Top]));
1326   {$ENDIF}
1327   AWindow := AWidget^.get_window;
1328   // at least TPanel needs this
1329   if Gtk3IsGdkWindow(AWindow) and (g_object_get_data(AWindow,'lclwidget') = nil) then
1330     g_object_set_data(AWindow,'lclwidget', TGtk3Widget(Data));
1331   if (AWindow <> nil) and AWidget^.get_has_window then
1332   begin
1333     // do resize to lcl size when mapping widget
1334     gdk_window_set_events(AWindow, GDK_ALL_EVENTS_MASK);
1335     if not (wtWindow in TGtk3Widget(Data).WidgetType) then
1336     begin
1337       with TGtk3Widget(Data).LCLObject do
1338       begin
1339         xx := Left;
1340         yy := Top;
1341         w := Width;
1342         h := Height;
1343       end;
1344       TGtk3Widget(Data).BeginUpdate;
1345       AWindow^.move(xx, yy);
1346       AWindow^.resize(w, h);
1347       TGtk3Widget(Data).EndUpdate;
1348     end else
1349     begin
1350       // DebugLn('TGtk3Window is mapped , setting lclwidget property to PGdkWindow ...');
1351       // now we set 'lclwidget' to our window.
1352       // g_object_set_data(AWindow,'lclwidget', TGtk3Widget(Data));
1353     end;
1354   end else
1355   begin
1356     if wtMemo in TGtk3Widget(Data).WidgetType then
1357     begin
1358       // gdk_window_get_geometry(AWindow, @xx,@yy,@w,@h);
1359       // gdk_window_get_position(AWindow, @xx,@yy);
1360       // DebugLn(' ***** Window ',Format('x %d y %d w %d h %d',[xx,yy,w,h]),' lclobject ',dbgsName(TGtk3Widget(Data).LCLObject));
1361     end;
1362   end;
1363 end;
1364 
1365 procedure Gtk3SizeAllocate(AWidget: PGtkWidget; AGdkRect: PGdkRectangle; Data: gpointer); cdecl;
1366 var
1367   Msg: TLMSize;
1368   MoveMsg: TLMMove;
1369   NewSize: TSize;
1370   ACtl: TGtk3Widget;
1371 begin
1372   //TODO: Move to TGtk3Widget.GtkResizeEvent
1373   {$IFDEF GTK3DEBUGSIZE}
1374   with AGdkRect^ do
1375     DebugLn('**** Gtk3SizeAllocate **** ....',dbgsName(TGtk3Widget(Data).LCLObject),
1376       ' ',Format('x %d y %d w %d h %d',[x, y, width, height]));
1377   {$ENDIF}
1378 
1379   ACtl := TGtk3Widget(Data);
1380 
1381   // return size w/o frame
1382   NewSize.cx := AGdkRect^.width;
1383   NewSize.cy := AGdkRect^.height;
1384 
1385   if not Assigned(ACtl.LCLObject) then exit;
1386 
1387   // do not loop with LCL but do not apply it to TQtMainWindow !
1388   if not (csDesigning in ACtl.LCLObject.ComponentState) then
1389   begin
1390     if ACtl.InUpdate then
1391       exit;
1392     //  if not (ClassType = TQtMainWindow) and InUpdate then
1393     //    exit;
1394   end;
1395 
1396   if ((NewSize.cx <> ACtl.LCLObject.Width) or (NewSize.cy <> ACtl.LCLObject.Height) or
1397      ACtl.LCLObject.ClientRectNeedsInterfaceUpdate) then
1398   begin
1399     ACtl.LCLObject.DoAdjustClientRectChange;
1400   end;
1401 
1402   FillChar(Msg, SizeOf(Msg), #0);
1403 
1404   Msg.Msg := LM_SIZE;
1405   (*
1406   case getWindowState of
1407     GtkWindowMinimized: Msg.SizeType := SIZE_MINIMIZED;
1408     GtkWindowMaximized: Msg.SizeType := SIZE_MAXIMIZED;
1409     GtkWindowFullScreen: Msg.SizeType := SIZE_FULLSCREEN;
1410   else
1411     Msg.SizeType := SIZE_RESTORED;
1412   end;
1413   *)
1414   Msg.SizeType := SIZE_RESTORED;
1415 
1416   Msg.SizeType := Msg.SizeType or Size_SourceIsInterface;
1417   Msg.Width := Word(NewSize.cx);
1418   Msg.Height := Word(NewSize.cy);
1419   ACtl.DeliverMessage(Msg);
1420 
1421   if (wtWindow in ACtl.WidgetType) and
1422     ((AGdkRect^.x <> ACtl.LCLObject.Left) or (AGdkRect^.y <> ACtl.LCLObject.Top)) then
1423   begin
1424     FillChar(MoveMsg, SizeOf(MoveMsg), #0);
1425     MoveMsg.Msg := LM_MOVE;
1426     MoveMsg.MoveType := MoveMsg.MoveType or Move_SourceIsInterface;
1427     MoveMsg.XPos := SmallInt(AGdkRect^.x);
1428     MoveMsg.YPos := SmallInt(AGdkRect^.y);
1429     {$IFDEF GTK3DEBUGEVENTS}
1430     DebugLn('SEND MOVE MESSAGE X=',dbgs(AGdkRect^.x),' Y=',dbgs(AGdkRect^.y),' control ',dbgsName(ACtl.LCLObject));
1431     {$ENDIF}
1432     ACtl.DeliverMessage(MoveMsg);
1433   end;
1434 end;
1435 
Gtk3ResizeEventnull1436 function Gtk3ResizeEvent(AWidget: PGtkWidget; AEvent: PGdkEvent; Data: gpointer): gboolean; cdecl;
1437 var
1438   ARect: TGdkRectangle;
1439 begin
1440   Result := False;
1441   ARect.X := AEvent^.configure.x;
1442   ARect.Y := AEvent^.configure.y;
1443   ARect.width := AEvent^.configure.width;
1444   ARect.height := AEvent^.configure.height;
1445   // DebugLn('**** Gtk3ResizeEvent(CONFIGURE) **** ....',dbgsName(TGtk3Widget(Data).LCLObject),' ARect ',dbgs(RectFromGdkRect(ARect)));
1446   Gtk3SizeAllocate(AWidget, @ARect, Data);
1447 end;
1448 
1449 procedure Gtk3WidgetHide(AWidget: PGtkWidget; AData: gpointer); cdecl;
1450 var
1451   Msg: TLMShowWindow;
1452   Gtk3Widget: TGtk3Widget;
1453 begin
1454   Gtk3Widget := TGtk3Widget(AData);
1455   {do not pass message to LCL if LCL setted up control visibility}
1456   if Gtk3Widget.inUpdate then
1457     exit;
1458   // DebugLn('SEND LM_HIDE FOR ',dbgsName(Gtk3Widget.LCLObject));
1459   FillChar(Msg, SizeOf(Msg), #0);
1460 
1461   Msg.Msg := LM_SHOWWINDOW;
1462   Msg.Show := False;
1463 
1464   Gtk3Widget.DeliverMessage(Msg);
1465 end;
1466 
1467 procedure Gtk3WidgetShow(AWidget: PGtkWidget; AData: gpointer); cdecl;
1468 var
1469   Msg: TLMShowWindow;
1470   Gtk3Widget: TGtk3Widget;
1471 begin
1472   Gtk3Widget := TGtk3Widget(AData);
1473   {do not pass message to LCL if LCL setted up control visibility}
1474   if Gtk3Widget.inUpdate then
1475     exit;
1476   // DebugLn('SEND LM_SHOW FOR ',dbgsName(Gtk3Widget.LCLObject));
1477   FillChar(Msg, SizeOf(Msg), #0);
1478 
1479   Msg.Msg := LM_SHOWWINDOW;
1480   Msg.Show := True;
1481 
1482   Gtk3Widget.DeliverMessage(Msg);
1483 end;
1484 
GtkModifierStateToShiftStatenull1485 function GtkModifierStateToShiftState(AState: TGdkModifierType;
1486     AIsKeyEvent: Boolean): Cardinal;
1487 begin
1488   Result := 0;
1489   if AState and GDK_SHIFT_MASK <> 0 then
1490     Result := Result or MK_SHIFT;
1491   if AState and GDK_CONTROL_MASK <> 0 then
1492     Result := Result or MK_CONTROL;
1493   if AState and GDK_MOD1_MASK <> 0 then
1494   begin
1495     if AIsKeyEvent then
1496       Result := Result or KF_ALTDOWN
1497     else
1498       Result := Result or MK_ALT;
1499   end;
1500 end;
1501 
SubtractScrollnull1502 function SubtractScroll(AWidget: PGtkWidget; APosition: TPoint): TPoint;
1503 begin
1504   Result := APosition;
1505   if Gtk3IsScrolledWindow(AWidget) then
1506   begin
1507     with gtk_scrolled_window_get_hadjustment(PGtkScrolledWindow(AWidget))^ do
1508       dec(Result.x, Trunc(value - lower));
1509     with gtk_scrolled_window_get_vadjustment(PGtkScrolledWindow(AWidget))^ do
1510       dec(Result.y, Trunc(value - lower));
1511   end;
1512 end;
1513 
Gtk3ScrolledWindowScrollEventnull1514 function Gtk3ScrolledWindowScrollEvent(AScrollWindow: PGtkScrolledWindow; AEvent: PGdkEvent; AData: gPointer): gboolean; cdecl;
1515 var
1516   Msg: TLMVScroll;
1517   AValue: Double;
1518   Range: PGtkRange;
1519 begin
1520   {$IFDEF SYNSCROLLDEBUG}
1521   debugln(['Gtk3ScrolledWindowScrollEvent ']);
1522   {$ENDIF}
1523   Result := False;
1524   case AEvent^.scroll.direction of
1525     0, 1{GDK_SCROLL_UP,
1526     GDK_SCROLL_DOWN}: Msg.Msg := LM_VSCROLL;
1527     2, 3{GDK_SCROLL_LEFT,
1528     GDK_SCROLL_RIGHT}: Msg.Msg := LM_HSCROLL;
1529   end;
1530 
1531   case Msg.Msg of
1532     LM_VSCROLL: Range := PGtkRange(AScrollWindow^.get_vscrollbar);
1533     LM_HSCROLL: Range := PGtkRange(AScrollWindow^.get_hscrollbar);
1534   end;
1535 
1536   AValue :=  power(Range^.adjustment^.page_size, 2 / 3);
1537 
1538   if (AEvent^.scroll.direction = GDK_SCROLL_UP) or
1539      (AEvent^.scroll.direction = GDK_SCROLL_LEFT)
1540   then
1541     AValue := -AValue;
1542 
1543   AValue := gtk_range_get_value(Range) + AValue;
1544 
1545   AValue := Max(AValue, Range^.adjustment^.lower);
1546   AValue := Min(AValue, Range^.adjustment^.upper - Range^.adjustment^.page_size);
1547 
1548   with Msg do
1549   begin
1550     Pos := Round(AValue);
1551     if Pos < High(SmallPos) then
1552       SmallPos := Pos
1553     else
1554       SmallPos := High(SmallPos);
1555 
1556     ScrollBar := HWND(PtrUInt(AData));
1557     ScrollCode := SB_THUMBPOSITION;
1558   end;
1559   Result := TGtk3Widget(AData).DeliverMessage(Msg) <> 0;
1560   // DeliverMessage(.LCLObject, Msg) <> 0;
1561 end;
1562 
Gtk3ScrollEventnull1563 function Gtk3ScrollEvent(AWidget: PGtkWidget; AEvent: PGdkEvent; AData: GPointer): GBoolean; cdecl;
1564 var
1565   AWinControl: TWinControl;
1566   EventXY: TPoint;
1567   AState: Cardinal;
1568   ShiftState: TShiftState;
1569   MappedXY: TPoint;
1570   MessE : TLMMouseEvent;
1571 begin
1572   Result := False;
1573   AWinControl := TGtk3Widget(AData).LCLObject;
1574 
1575   if AEvent^.scroll.send_event = NO_PROPAGATION_TO_PARENT then
1576     exit;
1577 
1578   EventXY := Point(TruncToInt(AEvent^.Scroll.X),TruncToInt(AEvent^.scroll.Y));
1579   AState := GtkModifierStateToShiftState(AEvent^.scroll.state, False);
1580   ShiftState := [];
1581   if AState and MK_SHIFT <> 0 then
1582     ShiftState := ShiftState + [ssShift];
1583   if AState and MK_CONTROL <> 0 then
1584     ShiftState := ShiftState + [ssCtrl];
1585   if AState and MK_ALT <> 0 then
1586     ShiftState := ShiftState + [ssAlt];
1587   // MappedXY := TranslateGdkPointToClientArea(AEvent^.scroll.window, EventXY,
1588   //                                        {%H-}TGtk3Widget(AWinControl.Handle).GetContainerWidget);
1589   MappedXY := EventXY;
1590   MappedXY := SubtractScroll(TGtk3Widget(AWinControl.Handle).GetContainerWidget, MappedXY);
1591   //DebugLn('gtkMouseWheelCB ',DbgSName(AWinControl),' Mapped=',dbgs(MappedXY.X),',',dbgs(MappedXY.Y),' Event=',dbgs(EventXY.X),',',dbgs(EventXY.Y));
1592 
1593   // this is a mouse wheel event
1594   FillChar(MessE,SizeOf(MessE),0);
1595   MessE.Msg := LM_MOUSEWHEEL;
1596   case AEvent^.scroll.direction of
1597     0 {GDK_SCROLL_UP}: MessE.WheelDelta := 120;
1598     1 {GDK_SCROLL_DOWN}: MessE.WheelDelta := -120;
1599   else
1600     exit;
1601   end;
1602   MessE.X := MappedXY.X;
1603   MessE.Y := MappedXY.Y;
1604   MessE.State := ShiftState;
1605   MessE.UserData := AWinControl;
1606   MessE.Button := 0;
1607 
1608   // send the message directly to the LCL
1609   NotifyApplicationUserInput(AWinControl, MessE.Msg);
1610   if DeliverMessage(AWinControl, MessE) <> 0 then
1611     Result := True // message handled by LCL, stop processing
1612   else
1613     AEvent^.scroll.send_event := NO_PROPAGATION_TO_PARENT;
1614 
1615   // DebugLn('Gtk3ScrollEvent for ', dbgsName(TGtk3Widget(AData).LCLObject),' Result ',dbgs(Result));
1616 end;
1617 
1618 { TGtk3SplitterSide }
1619 
TGtk3SplitterSide.CreateWidgetnull1620 function TGtk3SplitterSide.CreateWidget(const Params: TCreateParams): PGtkWidget;
1621 begin
1622   Result:=TGtkScrolledWindow.new(nil, nil);
1623 end;
1624 
1625 { TGtk3Paned }
1626 
TGtk3Paned.CreateWidgetnull1627 function TGtk3Paned.CreateWidget(const Params: TCreateParams): PGtkWidget;
1628 const
1629   ornt:array[TPairSplitterType] of TGtkOrientation=(
1630     GTK_ORIENTATION_HORIZONTAL,
1631     GTK_ORIENTATION_VERTICAL
1632     );
1633 begin
1634   Result:=TGtkPaned.new(ornt[TPairSplitter(Self.LCLObject).SplitterType]);
1635 end;
1636 
1637 { TGtk3Widget }
1638 
GtkEventMouseEnterLeavenull1639 function TGtk3Widget.GtkEventMouseEnterLeave(Sender: PGtkWidget; Event: PGdkEvent): Boolean;
1640   cdecl;
1641 var
1642   Msg: TLMessage;
1643   // MouseMsg: TLMMouseMove absolute Msg;
1644   {$IFDEF GTK3DEBUGCORE}
1645   MousePos: TPoint;
1646   {$ENDIF}
1647 begin
1648   Result := False;
1649   FillChar(Msg, SizeOf(Msg), #0);
1650   if Event^.type_ = GDK_ENTER_NOTIFY then
1651     Msg.Msg := LM_MOUSEENTER
1652   else
1653     Msg.Msg := LM_MOUSELEAVE;
1654 
1655   NotifyApplicationUserInput(LCLObject, Msg.Msg);
1656   Result := DeliverMessage(Msg, True) <> 0;
1657   {$IFDEF GTK3DEBUGCORE}
1658   MousePos.X := Round(Event^.crossing.x);
1659   MousePos.Y := Round(Event^.crossing.y);
1660   DebugLn('GtkEventMouseEnterLeave: mousePos ',dbgs(MousePos),' Object ',dbgsName(LCLObject),
1661     ' IsEnter ',dbgs(Event^.type_ = GDK_ENTER_NOTIFY),' Result=',dbgs(Result));
1662   {$ENDIF}
1663 end;
1664 
GtkEventMouseMovenull1665 function TGtk3Widget.GtkEventMouseMove(Sender: PGtkWidget; Event: PGdkEvent
1666   ): Boolean; cdecl;
1667 var
1668   Msg: TLMMouseMove;
1669   MousePos: TPoint;
1670 begin
1671   Result := False;
1672 
1673   {$IFDEF GTK3DEBUGEVENTS}
1674   R := GetClientBounds;
1675   DebugLn(['GtkEventMouseMove: ',dbgsName(LCLObject),' Send=',dbgs(Event^.motion.send_event),
1676   ' state=',dbgs(event^.motion.state),
1677   ' x=',dbgs(Round(event^.motion.x)),
1678   ' y=',dbgs(Round(event^.motion.y)),
1679   ' x_root=',dbgs(Round(event^.motion.x_root)),
1680   ' y_root=',dbgs(Round(event^.motion.y_root)),
1681   ' STOP PROCESSING ? ',dbgs(Event^.motion.send_event = NO_PROPAGATION_TO_PARENT),
1682   ' GtkBounds ',dbgs(R),' LCLBounds ',dbgs(LCLObject.BoundsRect),' W=',dbgs(LCLObject.Width)]
1683   );
1684   {$ENDIF}
1685 
1686   if Event^.motion.send_event = NO_PROPAGATION_TO_PARENT then
1687     exit;
1688 
1689   FillChar(Msg, SizeOf(Msg), #0);
1690 
1691   MousePos.x := Round(Event^.motion.x);
1692   MousePos.y := Round(Event^.motion.y);
1693 
1694   OffsetMousePos(@MousePos);
1695 
1696   Msg.XPos := SmallInt(MousePos.X);
1697   Msg.YPos := SmallInt(MousePos.Y);
1698 
1699   Msg.Keys := GdkModifierStateToLCL(Event^.motion.state, False);
1700 
1701   Msg.Msg := LM_MOUSEMOVE;
1702 
1703 
1704   NotifyApplicationUserInput(LCLObject, Msg.Msg);
1705   if Widget^.get_parent <> nil then
1706     Event^.motion.send_event := NO_PROPAGATION_TO_PARENT;
1707   DeliverMessage(Msg, True);
1708 end;
1709 
TGtk3Widget.GtkEventPaintnull1710 function TGtk3Widget.GtkEventPaint(Sender: PGtkWidget; AContext: Pcairo_t
1711   ): Boolean; cdecl;
1712 var
1713   Msg: TLMPaint;
1714   AStruct: TPaintStruct;
1715   P: TPoint;
1716   AClipRect: TGdkRectangle;
1717   localClip:TRect;
1718 begin
1719   Result := False;
1720 
1721   if not FHasPaint then
1722     exit;
1723 
1724   FillChar(Msg, SizeOf(Msg), #0);
1725 
1726   Msg.Msg := LM_PAINT;
1727   //New(AStruct);
1728   FillChar(AStruct, SizeOf(TPaintStruct), 0);
1729   Msg.PaintStruct := @AStruct;
1730 
1731   with PaintData do
1732   begin
1733     if GetContainerWidget = nil then
1734       PaintWidget := Widget
1735     else
1736       PaintWidget := GetContainerWidget;
1737     ClipRegion := nil;
1738     // gdk_cairo_region(AContext, ClipRegion);
1739     // Event^.expose.region;
1740     //if ClipRect = nil then
1741     //  New(ClipRect);
1742     gdk_cairo_get_clip_rectangle(AContext, @AClipRect);
1743     localClip:=RectFromGdkRect(AClipRect);
1744     ClipRect := @localClip;
1745   end;
1746 
1747   FCairoContext := AContext;
1748   Msg.DC := BeginPaint(THandle(Self), AStruct);
1749   FContext := Msg.DC;
1750 
1751   Msg.PaintStruct^.rcPaint := PaintData.ClipRect^;
1752   Msg.PaintStruct^.hdc := FContext;
1753 
1754   // P := Point(0, 0);
1755 
1756   P := Self.getClientOffset;
1757   if wtCustomControl in WidgetType then
1758   begin
1759     // ofsetting
1760     P := Point(0, 0);
1761     //TGtk3DeviceContext(Msg.DC).TranslateCairoToDevice;
1762     //P.X := Round(TGtk3CustomControl(Self).getHorizontalScrollbar^.get_adjustment^.get_value);
1763     //P.Y := Round(TGtk3CustomControl(Self).getVerticalScrollbar^.get_adjustment^.get_value);
1764   end else
1765   if wtScrollingWinControl in WidgetType then
1766   begin
1767     P := Point(0, 0);
1768     //DebugLn('GtkEventPaint Scrollable ScrollX=',dbgs(TGtk3ScrollableWin(Self).ScrollX),
1769     //  ' scrollY=',dbgs(TGtk3ScrollableWin(Self).ScrollY),' P=',dbgs(P));
1770     //Inc(P.X, TGtk3ScrollableWin(Self).ScrollX);
1771     //Inc(P.Y, TGtk3ScrollableWin(Self).ScrollY);
1772     // cairo_surface_get_device_offset(cairo_get_target(AContext), @dx, @dy);
1773     // TGtk3DeviceContext(Msg.DC).TranslateCairoToDevice;
1774   end else
1775   if wtGroupBox in WidgetType then
1776   begin
1777     // why is gtk3 so crazy about parent/child relation ?!?
1778     // in this case child PGtkFixed has same top (+top caption) as parent TGtkFrame ... crap
1779     // debugln('groupbox paint offset ',dbgs(p));
1780     TGtk3DeviceContext(Msg.DC).TranslateCairoToDevice;
1781     P := Point(0, 0);
1782   end;
1783 
1784   {$NOTE Currently TGtk3DeviceContext(Msg.DC).Translate(P) is creating incorrect offsets inside TPages for TLabel and maybe others}
1785   TGtk3DeviceContext(Msg.DC).Translate(P);
1786 
1787   try
1788     try
1789       // DebugLn('**** Sending paint event to ',dbgsName(LCLObject),' clipRect=',dbgs(PaintData.ClipRect^),' P=',dbgs(P));
1790       DoBeforeLCLPaint;
1791       LCLObject.WindowProc(TLMessage(Msg));
1792     finally
1793       FCairoContext := nil;
1794       //Dispose(PaintData.ClipRect);
1795       Fillchar(FPaintData, SizeOf(FPaintData), 0);
1796       FContext := 0;
1797       EndPaint(THandle(Self), AStruct);
1798       //Dispose(AStruct);
1799     end;
1800   except
1801     Application.HandleException(nil);
1802   end;
1803 end;
1804 
GtkEventResizenull1805 function TGtk3Widget.GtkEventResize(Sender: PGtkWidget; Event: PGdkEvent
1806   ): Boolean; cdecl;
1807 begin
1808   {$IF DEFINED(GTK3DEBUGEVENTS) OR DEFINED(GTK3DEBUGSIZE)}
1809   DebugLn('GtkEventResize: ',dbgsName(LCLObject),' Send=',dbgs(Event^.configure.send_event),
1810   ' x=',dbgs(Round(event^.configure.x)),
1811   ' y=',dbgs(Round(event^.configure.y)),
1812   ' w=',dbgs(Round(event^.configure.height)),
1813   ' h=',dbgs(Round(event^.configure.width)));
1814   {$ENDIF}
1815   Result := False;
1816 end;
1817 
1818 procedure TGtk3Widget.GtkEventFocus(Sender: PGtkWidget; Event: PGdkEvent);
1819   cdecl;
1820 var
1821   Msg: TLMessage;
1822 begin
1823   {$IF DEFINED(GTK3DEBUGEVENTS) OR DEFINED(GTK3DEBUGFOCUS)}
1824   DebugLn('TGtk3Widget.GtkEventFocus ',dbgsName(LCLObject),' FocusIn ',dbgs(Event^.focus_change.in_ <> 0));
1825   {$ENDIF}
1826   FillChar(Msg, SizeOf(Msg), #0);
1827   if Event^.focus_change.in_ <> 0 then
1828     Msg.Msg := LM_SETFOCUS
1829   else
1830     Msg.Msg := LM_KILLFOCUS;
1831   DeliverMessage(Msg);
1832 end;
1833 
1834 procedure TGtk3Widget.GtkEventDestroy; cdecl;
1835 var
1836   Msg: TLMessage;
1837 begin
1838   FillChar(Msg, SizeOf(Msg), #0);
1839   Msg.Msg := LM_DESTROY;
1840   DeliverMessage(Msg);
1841   Release;
1842 end;
1843 
TGtk3Widget.GtkEventMouseWheelnull1844 function TGtk3Widget.GtkEventMouseWheel(Sender: PGtkWidget; Event: PGdkEvent
1845   ): Boolean; cdecl;
1846 var
1847   Msg: TLMMouseEvent;
1848   EventXY: TPoint;
1849 begin
1850   // gtk3 have ugly bug with scroll-event
1851   // https://bugzilla.gnome.org/show_bug.cgi?id=675959
1852   Result := False;
1853   EventXY := Point(TruncToInt(Event^.scroll.x), TruncToInt(Event^.scroll.y));
1854   FillChar(Msg{%H-},SizeOf(Msg),0);
1855   Msg.Msg := LM_MOUSEWHEEL;
1856   //DebugLn('Scroll ',Format('deltaX %2.2n deltaY %2.2n x %2.2n y %2.2n rootx %2.2n rooty %2.2n',
1857   //  [Event^.scroll.delta_x, Event^.scroll.delta_y, Event^.scroll.x, Event^.scroll.y,
1858   //  Event^.scroll.x_root, Event^.scroll.y_root]));
1859   if Event^.scroll.direction = GDK_SCROLL_UP then
1860     Msg.WheelDelta := 120
1861   else
1862   if Event^.scroll.direction = GDK_SCROLL_DOWN then
1863     Msg.WheelDelta := -120
1864   else
1865     exit;
1866   Msg.X := EventXY.X;
1867   Msg.Y := EventXY.Y;
1868   Msg.State := GdkModifierStateToShiftState(Event^.scroll.state);
1869   Msg.UserData := LCLObject;
1870   Msg.Button := 0;
1871 
1872   NotifyApplicationUserInput(LCLObject, Msg.Msg);
1873   if Widget^.get_parent <> nil then
1874     Event^.motion.send_event := NO_PROPAGATION_TO_PARENT;
1875   if DeliverMessage(Msg, True) <> 0 then
1876     Result := True;
1877 end;
1878 
TGtk3Widget.IsValidHandlenull1879 function TGtk3Widget.IsValidHandle: Boolean;
1880 begin
1881   Result := Assigned(FWidget) and Gtk3IsWidget(FWidget) and not FWidget^.in_destruction;
1882 end;
1883 
IsWidgetOknull1884 function TGtk3Widget.IsWidgetOk: Boolean;
1885 begin
1886   Result := (FWidget <> nil) and Gtk3IsWidget(FWidget);
1887 end;
1888 
TGtk3Widget.IsIconicnull1889 function TGtk3Widget.IsIconic: Boolean;
1890 begin
1891   Result := False;
1892   if IsWidgetOk then
1893   begin
1894     if FWidget^.get_window <> nil then
1895       Result := gdk_window_get_state(FWidget^.get_window) and GDK_WINDOW_STATE_ICONIFIED <> 0;
1896   end;
1897 end;
1898 
getTypenull1899 function TGtk3Widget.getType: TGType;
1900 begin
1901   Result := getContainerWidget^.g_type_instance.g_class^.g_type;
1902 end;
1903 
getTypeNamenull1904 function TGtk3Widget.getTypeName: PgChar;
1905 begin
1906   Result := g_type_name(getType);
1907 end;
1908 
1909 procedure TGtk3Widget.lowerWidget;
1910 begin
1911   if Gtk3IsGdkWindow(FWidget^.window) then
1912     FWidget^.window^.lower;
1913 end;
1914 
1915 procedure TGtk3Widget.raiseWidget;
1916 begin
1917   if Gtk3IsGdkWindow(FWidget^.window) then
1918     FWidget^.window^.raise_;
1919 end;
1920 
1921 procedure TGtk3Widget.stackUnder(AWidget: PGtkWidget);
1922 begin
1923   // FWidget^.
1924 end;
1925 
TGtk3Widget.GetCapturenull1926 function TGtk3Widget.GetCapture: TGtk3Widget;
1927 var
1928   AHandle: HWND;
1929 begin
1930   AHandle := HwndFromGtkWidget(gtk_grab_get_current);
1931   if AHandle <> 0 then
1932     Result := TGtk3Widget(AHandle);
1933 end;
1934 
SetCapturenull1935 function TGtk3Widget.SetCapture: HWND;
1936 begin
1937   Result := HWND(GetCapture);
1938   gtk_grab_add(GetContainerWidget);
1939 end;
1940 
GtkEventKeynull1941 function TGtk3Widget.GtkEventKey(Sender: PGtkWidget; Event: PGdkEvent; AKeyPress: Boolean): Boolean;
1942   cdecl;
1943 const
1944   CN_KeyDownMsgs: array[Boolean] of UINT = (CN_KEYDOWN, CN_SYSKEYDOWN);
1945   CN_KeyUpMsgs: array[Boolean] of UINT = (CN_KEYUP, CN_SYSKEYUP);
1946   LM_KeyDownMsgs: array[Boolean] of UINT = (LM_KEYDOWN, LM_SYSKEYDOWN);
1947   LM_KeyUpMsgs: array[Boolean] of UINT = (LM_KEYUP, LM_SYSKEYUP);
1948   CN_CharMsg: array[Boolean] of UINT = (CN_CHAR, CN_SYSCHAR);
1949   LM_CharMsg: array[Boolean] of UINT = (LM_CHAR, LM_SYSCHAR);
1950 var
1951   AEvent: TGdkEventKey;
1952   Msg: TLMKey;
1953   CharMsg: TLMChar;
1954   KeyCode: Word;
1955   AShiftState: TShiftState;
1956   AEventString: String;
1957   KeyValue, ACharCode: Word;
1958   LCLModifiers: Word;
1959   IsSysKey: Boolean;
1960   UTF8Char: TUTF8Char;
1961   AChar: Char;
1962   IsArrowKey: Boolean;
1963 begin
1964   //TODO: finish LCL messaging
1965   Result := False;
1966   AEvent := Event^.key;
1967   FillChar(Msg, SizeOf(Msg), 0);
1968   AEventString := AEvent.string_;
1969 
1970   if gdk_keyval_is_lower(AEvent.keyval) then
1971     KeyValue := Word(gdk_keyval_to_upper(AEvent.keyval))
1972   else
1973     KeyValue := Word(AEvent.keyval);
1974 
1975   // state=16 = numlock= on.
1976 
1977   LCLModifiers := GtkModifierStateToShiftState(AEvent.state, True);
1978 
1979   if length(AEventString) = 0 then
1980   begin
1981     if KeyValue = GDK_KEY_Alt_L then
1982       LCLModifiers := LCLModifiers or KF_ALTDOWN
1983     else
1984     if (KeyValue = GDK_KEY_Control_L) or (KeyValue = GDK_KEY_Control_R)  then
1985       LCLModifiers := LCLModifiers or MK_CONTROL
1986     else
1987     if (KeyValue = GDK_KEY_Shift_L) or (KeyValue = GDK_KEY_Shift_R) then
1988       LCLModifiers := LCLModifiers or MK_SHIFT;
1989     // writeln('MODIFIERS BY KEYS ',LCLModifiers);
1990   end;
1991 
1992   IsSysKey := LCLModifiers and KF_ALTDOWN <> 0;
1993 
1994   if not AKeyPress then
1995     LCLModifiers := LCLModifiers or KF_UP;
1996 
1997   // else
1998   //  writeln('KeyRelease: ',dbgsName(LCLObject),' Dump state=',AEvent.state,' hwkey=',KeyCode,' keyvalue=',KeyValue,' modifier=',AEvent.Bitfield0.is_modifier);
1999 
2000   // this is just for testing purposes.
2001   ACharCode := GdkKeyToLCLKey(KeyValue);
2002   if KeyValue > VK_UNDEFINED then
2003     KeyValue := ACharCode; // VK_UNKNOWN;
2004 
2005   if AKeyPress and (ACharCode = VK_TAB) then
2006   begin
2007 
2008   end;
2009 
2010   IsArrowKey := ((ACharCode = VK_UP) or (ACharCode = VK_DOWN) or (ACharCode = VK_LEFT) or (ACharCode = VK_RIGHT));
2011 
2012   {$IFDEF GTK3DEBUGKEYPRESS}
2013   if AKeyPress then
2014     writeln('EVENT KeyPress: ',dbgsName(LCLObject),' Dump state=',AEvent.state,' keyvalue=',KeyValue,' modifier=',AEvent.Bitfield0.is_modifier,
2015     ' KeyValue ',KeyValue,' MODIFIERS ',LCLModifiers,' CharCode ',ACharCode,' EAT ',EatArrowKeys(ACharCode))
2016   else
2017     writeln('EVENT KeyRelease: ',dbgsName(LCLObject),' Dump state=',AEvent.state,' keyvalue=',KeyValue,' modifier=',AEvent.Bitfield0.is_modifier,
2018     ' KeyValue ',KeyValue,' MODIFIERS ',LCLModifiers,' CharCode ',ACharCode,
2019     ' EAT ',EatArrowKeys(ACharCode));
2020   {$ENDIF}
2021 
2022   if (ACharCode <> VK_UNKNOWN) then
2023   begin
2024     if AKeyPress then
2025       Msg.Msg := CN_KeyDownMsgs[IsSysKey]
2026     else
2027       Msg.Msg := CN_KeyUpMsgs[IsSysKey];
2028     Msg.CharCode := ACharCode;
2029     Msg.KeyData := PtrInt((KeyValue shl 16) or (LCLModifiers shl 16) or $0001);
2030 
2031     NotifyApplicationUserInput(LCLObject, Msg.Msg);
2032 
2033     if not CanSendLCLMessage then
2034       exit;
2035 
2036     if (DeliverMessage(Msg, True) <> 0) or (Msg.CharCode = VK_UNKNOWN) or EatArrowKeys(ACharCode) then
2037     begin
2038       {$IFDEF GTK3DEBUGKEYPRESS}
2039       DebugLn('CN_KeyDownMsgs handled ... exiting');
2040       {$ENDIF}
2041       exit(True);
2042     end;
2043 
2044     if not CanSendLCLMessage then
2045       exit;
2046 
2047     if AKeyPress then
2048       Msg.Msg := LM_KeyDownMsgs[IsSysKey]
2049     else
2050       Msg.Msg := LM_KeyUpMsgs[IsSysKey];
2051     Msg.CharCode := ACharCode;
2052     Msg.KeyData := PtrInt((KeyValue shl 16) or (LCLModifiers shl 16) or $0001);
2053 
2054     NotifyApplicationUserInput(LCLObject, Msg.Msg);
2055 
2056     if not CanSendLCLMessage then
2057       exit;
2058 
2059     {$warning workaround for GtkTreeView key bindings.Must find out what LCL does with
2060      this keys.}
2061     if IsArrowKey and ([wtListBox,wtListView] * WidgetType <> []) then
2062     // let gtk3 select cell for now. Must check what LCL does with arrow keys
2063     // since gtk3 becomes crazy after delivery of this message
2064     else
2065     if (DeliverMessage(Msg, True) <> 0) or (Msg.CharCode = 0) then
2066     begin
2067       Result := Msg.CharCode = 0;
2068       {$IFDEF GTK3DEBUGKEYPRESS}
2069       DebugLn('LM_KeyDownMsgs handled ... exiting ',dbgs(ACharCode),' Result=',dbgs(Result),' AKeyPress=',dbgs(AKeyPress));
2070       {$ENDIF}
2071       exit;
2072     end;
2073 
2074     if not CanSendLCLMessage then
2075       exit;
2076 
2077   end;
2078 
2079   if AKeyPress and (length(AEventString) > 0) then
2080   begin
2081     UTF8Char := AEventString;
2082     // TODO: If not IsControlKey
2083     Result := LCLObject.IntfUTF8KeyPress(UTF8Char, 1, IsSysKey);
2084 
2085     if not CanSendLCLMessage then
2086       exit;
2087 
2088     if Result then
2089     begin
2090       {$IFDEF GTK3DEBUGKEYPRESS}
2091       DebugLn('LCLObject.IntfUTF8KeyPress handled ... exiting');
2092       {$ENDIF}
2093       exit;
2094     end;
2095 
2096     // create the CN_CHAR / CN_SYSCHAR message
2097     FillChar(CharMsg, SizeOf(CharMsg), 0);
2098     CharMsg.Msg := CN_CharMsg[IsSysKey];
2099     CharMsg.KeyData := Msg.KeyData;
2100     AChar := AEventString[1];
2101     CharMsg.CharCode := Word(AChar);
2102     NotifyApplicationUserInput(LCLObject, CharMsg.Msg);
2103 
2104     if not CanSendLCLMessage then
2105       exit;
2106 
2107     Result := (DeliverMessage(CharMsg, True) <> 0) or (CharMsg.CharCode = VK_UNKNOWN);
2108 
2109     if not CanSendLCLMessage then
2110       exit;
2111 
2112     if Result then
2113     begin
2114       {$IFDEF GTK3DEBUGKEYPRESS}
2115       DebugLn('CN_CharMsg handled ... exiting');
2116       {$ENDIF}
2117       exit;
2118     end;
2119 
2120     //Send a LM_(SYS)CHAR
2121     CharMsg.Msg := LM_CharMsg[IsSysKey];
2122 
2123     NotifyApplicationUserInput(LCLObject, CharMsg.Msg);
2124 
2125     if not CanSendLCLMessage then
2126       exit;
2127 
2128     DeliverMessage(CharMsg, True);
2129 
2130     if not CanSendLCLMessage then
2131       exit;
2132   end;
2133   if AKeyPress then
2134   begin
2135     {$IFDEF GTK3DEBUGKEYPRESS}
2136     if Msg.CharCode in FKeysToEat then
2137     begin
2138       DebugLn('EVENT: ******* KeyPress charcode is in keys to eat (FKeysToEat), charcode=',dbgs(Msg.CharCode));
2139     end;
2140     {$ENDIF}
2141     Result := Msg.CharCode in FKeysToEat;
2142   end;
2143 end;
2144 
GtkEventMousenull2145 function TGtk3Widget.GtkEventMouse(Sender: PGtkWidget; Event: PGdkEvent): Boolean;
2146   cdecl;
2147 var
2148   Msg: TLMMouse;
2149   MsgPopup : TLMMouse;
2150   MousePos: TPoint;
2151   MButton: guint;
2152 begin
2153   Result := False;
2154   {$IF DEFINED(GTK3DEBUGEVENTS) OR DEFINED(GTK3DEBUGMOUSE)}
2155   DebugLn('TGtk3Widget.GtkEventMouse ',dbgsName(LCLObject),
2156     ' propagate=',dbgs(not (Event^.button.send_event = NO_PROPAGATION_TO_PARENT)));
2157   {$ENDIF}
2158   if Event^.button.send_event = NO_PROPAGATION_TO_PARENT then
2159     exit;
2160 
2161   FillChar(Msg, SizeOf(Msg), #0);
2162 
2163   MousePos.x := Round(Event^.button.x);
2164   MousePos.y := Round(Event^.button.y);
2165 
2166   OffsetMousePos(@MousePos);
2167 
2168   Msg.Keys := GdkModifierStateToLCL(Event^.button.state, False);
2169 
2170   Msg.XPos := SmallInt(MousePos.X);
2171   Msg.YPos := SmallInt(MousePos.Y);
2172 
2173   MButton := Event^.button.button;
2174 
2175   case Event^.type_ of
2176     // GDK_BUTTON_PRESS
2177     4:
2178     begin
2179       if MButton = GTK3_LEFT_BUTTON then
2180         Msg.Msg := LM_LBUTTONDOWN
2181       else
2182       if MButton = GTK3_RIGHT_BUTTON then
2183         Msg.Msg := LM_RBUTTONDOWN
2184       else
2185       if MButton = GTK3_MIDDLE_BUTTON then
2186         Msg.Msg := LM_MBUTTONDOWN;
2187     end;
2188     // GDK_BUTTON2_PRESS -> double click
2189     5: Msg.Msg := LM_LBUTTONDBLCLK;
2190     // GDK_BUTTON_RELEASE: TGdkEventType = 7;
2191     7:
2192     begin
2193       if MButton = GTK3_LEFT_BUTTON then
2194         Msg.Msg := LM_LBUTTONUP
2195       else
2196       if MButton = GTK3_RIGHT_BUTTON then
2197         Msg.Msg := LM_RBUTTONUP
2198       else
2199       if MButton = GTK3_MIDDLE_BUTTON then
2200         Msg.Msg := LM_MBUTTONUP;
2201     end;
2202   end;
2203 
2204   {$IF DEFINED(GTK3DEBUGEVENTS) OR DEFINED(GTK3DEBUGMOUSE)}
2205   DebugLn('TGtk3Widget.GtkEventMouse ',dbgsName(LCLObject),
2206     ' msg=',dbgs(msg.Msg), ' point=',dbgs(Msg.XPos),',',dbgs(Msg.YPos));
2207   {$ENDIF}
2208   NotifyApplicationUserInput(LCLObject, Msg.Msg);
2209   Event^.button.send_event := NO_PROPAGATION_TO_PARENT;
2210 
2211   Result := false;
2212   if Msg.Msg = LM_RBUTTONDOWN then
2213   begin
2214     MsgPopup := Msg;
2215     MsgPopup.Msg := LM_CONTEXTMENU;
2216     MsgPopup.XPos := SmallInt(Round(Event^.button.x_root));
2217     MsgPopup.YPos := SmallInt(Round(Event^.button.y_root));
2218     Result := DeliverMessage(MsgPopup, True) <> 0;
2219   end;
2220   if not Result then
2221     Result := DeliverMessage(Msg, True) <> 0;
2222   if Event^.type_ = GDK_BUTTON_RELEASE then
2223   begin
2224     Msg.Msg := LM_CLICKED;
2225     DeliverMessage(Msg, True);
2226   end;
2227 end;
2228 
GetVisiblenull2229 function TGtk3Widget.GetVisible: Boolean;
2230 begin
2231   Result := Assigned(FWidget) and FWidget^.visible;
2232 end;
2233 
2234 procedure TGtk3Widget.SetEnabled(AValue: Boolean);
2235 begin
2236   if IsWidgetOK then
2237     FWidget^.set_sensitive(AValue);
2238 end;
2239 
2240 procedure TGtk3Widget.SetFont(AValue: PPangoFontDescription);
2241 begin
2242   if IsWidgetOk then
2243   begin
2244     GetContainerWidget^.override_font(AValue);
2245   end;
2246 end;
2247 
2248 procedure TGtk3Widget.SetFontColor(AValue: TColor);
2249 var
2250   AColor: TGdkRGBA;
2251   i: TGtkStateType;
2252 begin
2253   if IsWidgetOK then
2254   begin
2255     AColor := TColortoTGdkRGBA(AValue);
2256     if FWidget <> GetContainerWidget then
2257     begin
2258       with FWidget^ do
2259       begin
2260         for i := GTK_STATE_NORMAL to GTK_STATE_INSENSITIVE do
2261           override_color(i, @AColor);
2262       end;
2263     end;
2264     with GetContainerWidget^ do
2265     begin
2266       for i := GTK_STATE_NORMAL to GTK_STATE_INSENSITIVE do
2267         override_color(i, @AColor);
2268     end;
2269   end;
2270 end;
2271 
2272 procedure TGtk3Widget.SetColor(AValue: TColor);
2273 var
2274   AColor: TGdkRGBA;
2275   i: TGtkStateType;
2276   ARgba: TGdkRGBA;
2277   R: Double;
2278   G: Double;
2279   B: Double;
2280 begin
2281   // new way (gtk3) but still buggy
2282   if IsWidgetOK and (0 > 1) then
2283   begin
2284     if AValue = clDefault then
2285     begin
2286       (*
2287       with FDefaultRGBA do
2288       begin
2289         writeln('clDefault ',Format('R %2.2n G %2.2n B %2.2n A %2.2n',[R, G, B , Alpha]));
2290         ARgba.red := R;
2291         ARgba.green := G;
2292         ARgba.blue := B;
2293         ARgba.alpha := Alpha;
2294       end;
2295       *)
2296     end else
2297     begin
2298       ARgba := TColortoTGdkRGBA(AValue);
2299       {$info GTK3: set GdkRGBA.alpah to 1.0?}
2300 
2301       {ColorToCairoRGB(ColorToRGB(AValue), R, G, B);
2302       ARgba.red := R;
2303       ARgba.green := G;
2304       ARgba.blue := B;
2305       ARgba.alpha := 1.0;}
2306     end;
2307     if FWidget <> GetContainerWidget then
2308     begin
2309       with FWidget^ do
2310       begin
2311         for i := GTK_STATE_NORMAL to GTK_STATE_INSENSITIVE do
2312         begin
2313           if AValue = clDefault then
2314           begin
2315             ARgba.red := FWidgetRGBA[i].R;
2316             ARgba.green := FWidgetRGBA[i].G;
2317             ARgba.blue := FWidgetRGBA[i].B;
2318             ARgba.alpha := FWidgetRGBA[i].Alpha;
2319           end;
2320           FWidget^.override_background_color(i, @ARgba);
2321         end;
2322       end;
2323     end;
2324     with GetContainerWidget^ do
2325     begin
2326       for i := GTK_STATE_NORMAL to GTK_STATE_INSENSITIVE do
2327       begin
2328         //if AVAlue = clDefault then
2329         //  GetContainerWidget^.get_style_context^.get_background_color(GTK_STATE_NORMAL, @ARgba);
2330         if AValue = clDefault then
2331         begin
2332           ARgba.red := FCentralWidgetRGBA[i].R;
2333           ARgba.green := FCentralWidgetRGBA[i].G;
2334           ARgba.blue := FCentralWidgetRGBA[i].B;
2335           ARgba.alpha := FCentralWidgetRGBA[i].Alpha;
2336         end;
2337         GetContainerWidget^.override_background_color(i, @ARgba);
2338       end;
2339     end;
2340   end;
2341 
2342   if IsWidgetOK then
2343   begin
2344     AColor := TColortoTGdkRGBA(AValue);
2345     if FWidget <> GetContainerWidget then
2346     begin
2347       with FWidget^ do
2348       begin
2349         for i := GTK_STATE_NORMAL to GTK_STATE_INSENSITIVE do
2350           if AValue = clDefault then
2351             override_background_color(i, nil)
2352           else
2353             override_background_color(i, @AColor);
2354       end;
2355     end;
2356     with GetContainerWidget^ do
2357     begin
2358       for i := GTK_STATE_NORMAL to GTK_STATE_INSENSITIVE do
2359       begin
2360         if AValue = clDefault then
2361           override_background_color(i, nil)
2362         else
2363           override_background_color(i, @AColor);
2364       end;
2365     end;
2366   end;
2367 end;
2368 
TGtk3Widget.GetStyleContextnull2369 function TGtk3Widget.GetStyleContext: PGtkStyleContext;
2370 begin
2371   Result := nil;
2372   if IsWidgetOK then
2373     Result := GetContainerWidget^.get_style_context;
2374 end;
2375 
TGtk3Widget.GetFontnull2376 function TGtk3Widget.GetFont: PPangoFontDescription;
2377 var
2378   AContext: PPangoContext;
2379 begin
2380   Result := nil;
2381   if IsWidgetOK then
2382   begin
2383     AContext := GetContainerWidget^.get_pango_context;
2384     Result := pango_context_get_font_description(AContext);
2385   end;
2386 end;
2387 
CanSendLCLMessagenull2388 function TGtk3Widget.CanSendLCLMessage: Boolean;
2389 begin
2390   Result := IsWidgetOk and (LCLObject <> nil);
2391 end;
2392 
GetCairoContextnull2393 function TGtk3Widget.GetCairoContext: Pcairo_t;
2394 begin
2395   Result := FCairoContext;
2396 end;
2397 
GetEnablednull2398 function TGtk3Widget.GetEnabled: Boolean;
2399 begin
2400   Result := False;
2401   if IsWidgetOK then
2402     Result := FWidget^.get_sensitive;
2403 end;
2404 
TGtk3Widget.GetFontColornull2405 function TGtk3Widget.GetFontColor: TColor;
2406 var
2407   AStyle: PGtkStyleContext;
2408   AGdkRGBA: TGdkRGBA;
2409 begin
2410   Result := clDefault;
2411   if IsWidgetOK then
2412   begin
2413     AStyle := GetStyleContext;
2414     AStyle^.get_background_color(GTK_STATE_NORMAL, @AGdkRGBA);
2415     Result := TGdkRGBAToTColor(AGdkRGBA);
2416   end;
2417 end;
2418 
GetColornull2419 function TGtk3Widget.GetColor: TColor;
2420 var
2421   AStyle: PGtkStyleContext;
2422   AColor: TGdkRGBA;
2423 begin
2424   Result := clDefault;
2425   if IsWidgetOK then
2426   begin
2427     AStyle := GetStyleContext;
2428     AStyle^.get_background_color(GTK_STATE_NORMAL, @AColor);
2429     Result := TGdkRGBAToTColor(AColor);
2430   end;
2431 end;
2432 
2433 procedure TGtk3Widget.SetStyleContext(AValue: PGtkStyleContext);
2434 begin
2435   {$NOTE Gtk3: Find a nice way to assign StyleContext}
2436   {if IsWidgetOK then
2437     GetContainerWidget^.set_style(AValue);}
2438 end;
2439 
TGtk3Widget.getTextnull2440 function TGtk3Widget.getText: String;
2441 begin
2442   Result := '';
2443 end;
2444 
2445 procedure TGtk3Widget.setText(AValue: String);
2446 begin
2447   // DebugLn('WARNING: ',dbgsName(LCLObject),' self=',dbgsName(Self),' does not implement setText !');
2448 end;
2449 
2450 procedure TGtk3Widget.SetVisible(AValue: Boolean);
2451 begin
2452   if IsWidgetOK then
2453     FWidget^.Visible := AValue;
2454 end;
2455 
QueryInterfacenull2456 function TGtk3Widget.QueryInterface(constref iid: TGuid; out obj): LongInt;
2457   cdecl;
2458 begin
2459   if GetInterface(iid, obj) then
2460     Result := 0
2461   else
2462     Result := E_NOINTERFACE;
2463 end;
2464 
TGtk3Widget._AddRefnull2465 function TGtk3Widget._AddRef: LongInt; cdecl;
2466 begin
2467   Result := -1; // no ref counting
2468 end;
2469 
_Releasenull2470 function TGtk3Widget._Release: LongInt; cdecl;
2471 begin
2472   Result := -1;
2473 end;
2474 
EatArrowKeysnull2475 function TGtk3Widget.EatArrowKeys(const AKey: Word): Boolean;
2476 begin
2477   Result := AKey in [VK_LEFT, VK_UP, VK_RIGHT, VK_DOWN];
2478 end;
2479 
TGtk3Widget.GetContextnull2480 function TGtk3Widget.GetContext: HDC;
2481 begin
2482   Result := FContext;
2483 end;
2484 
TGtk3Widget.CreateWidgetnull2485 function TGtk3Widget.CreateWidget(const Params: TCreateParams): PGtkWidget;
2486 begin
2487   Result := PGtkWidget(TGtkWidget.newv(32, 0 ,nil));
2488 end;
2489 
2490 procedure TGtk3Widget.DestroyWidget;
2491 begin
2492   if IsWidgetOk and FOwnWidget then
2493     FWidget^.destroy_;
2494   FWidget := nil;
2495 end;
2496 
2497 procedure TGtk3Widget.DoBeforeLCLPaint;
2498 begin
2499   //
2500 end;
2501 
2502 constructor TGtk3Widget.Create(const AWinControl: TWinControl;
2503   const AParams: TCreateParams);
2504 begin
2505   inherited Create;
2506   FContext := 0;
2507   FHasPaint := False;
2508   FWidget := nil;
2509   FOwner := nil;
2510   FCentralWidget := nil;
2511   FOwnWidget := True;
2512   // Initializes the properties
2513   FProps := nil;
2514   LCLObject := AWinControl;
2515   FKeysToEat := [VK_TAB, VK_RETURN, VK_ESCAPE];
2516   // FHasPaint := False;
2517 
2518   FParams := AParams;
2519   InitializeWidget;
2520 end;
2521 
2522 constructor TGtk3Widget.CreateFrom(const AWinControl: TWinControl;
2523   AWidget: PGtkWidget);
2524 begin
2525   inherited Create;
2526   FContext := 0;
2527   FHasPaint := False;
2528   FWidget := nil;
2529   FOwner := nil;
2530   FCentralWidget := nil;
2531   FOwnWidget := False;
2532   // Initializes the properties
2533   FProps := nil;
2534   LCLObject := AWinControl;
2535   FWidget := AWidget;
2536   // FKeysToEat := [VK_TAB, VK_RETURN, VK_ESCAPE];
2537   // FHasPaint := False;
2538 end;
2539 
2540 procedure TGtk3Widget.InitializeWidget;
2541 var
2542   ARect: TGdkRectangle;
2543   ARgba: TGdkRGBA;
2544   i: TGtkStateType;
2545 begin
2546   FFocusableByMouse := False;
2547   FCentralWidget := nil;
2548   FCairoContext := nil;
2549   FContext := 0;
2550   FEnterLeaveTime := 0;
2551 
2552   FWidgetType := [wtWidget];
2553   FWidget := CreateWidget(FParams);
2554 
2555   // connect events
2556   if not (wtWindow in FWidgetType) then
2557   begin
2558     FWidget^.show_all;
2559     with ARect do
2560     begin
2561       x := LCLObject.Left;
2562       y := LCLObject.Top;
2563       width := LCLObject.Width;
2564       height := LCLObject.Height;
2565     end;
2566     FWidget^.set_allocation(@ARect);
2567   end;
2568   LCLIntf.SetProp(HWND(Self),'lclwidget',Self);
2569 
2570   // move signal connections into attach events
2571   FWidget^.set_events(GDK_ALL_EVENTS_MASK);
2572   g_signal_connect_data(FWidget, 'event', TGCallback(@Gtk3WidgetEvent), Self, nil, 0);
2573 
2574 
2575   for i := GTK_STATE_NORMAL to GTK_STATE_INSENSITIVE do
2576   begin
2577     FWidget^.get_style_context^.get_background_color(i, @ARgba);
2578     with FWidgetRGBA[i] do
2579     begin
2580       R := ARgba.red;
2581       G := ARgba.green;
2582       B := ARgba.blue;
2583       Alpha := ARgba.alpha;
2584     end;
2585   end;
2586 
2587 
2588   if FCentralWidget <> nil then
2589   begin
2590     FCentralWidget^.set_events(GDK_ALL_EVENTS_MASK);
2591     g_signal_connect_data(FCentralWidget, 'event', TGCallback(@Gtk3WidgetEvent), Self, nil, 0);
2592     for i := GTK_STATE_NORMAL to GTK_STATE_INSENSITIVE do
2593     begin
2594       FCentralWidget^.get_style_context^.get_background_color(i, @ARgba);
2595       with FCentralWidgetRGBA[i] do
2596       begin
2597         R := ARgba.red;
2598         G := ARgba.green;
2599         B := ARgba.blue;
2600         Alpha := ARgba.alpha;
2601       end;
2602     end;
2603   end else
2604   begin
2605     for i := GTK_STATE_NORMAL to GTK_STATE_INSENSITIVE do
2606       FCentralWidgetRGBA[i] := FWidgetRGBA[i];
2607   end;
2608   g_signal_connect_data(GetContainerWidget,'draw', TGCallback(@Gtk3DrawWidget), Self, nil, 0);
2609   g_signal_connect_data(GetContainerWidget,'scroll-event', TGCallback(@Gtk3ScrollEvent), Self, nil, 0);
2610 
2611   // must hide all by default
2612   FWidget^.hide;
2613 
2614   g_signal_connect_data(FWidget,'hide', TGCallback(@Gtk3WidgetHide), Self, nil, 0);
2615   g_signal_connect_data(FWidget,'show', TGCallback(@Gtk3WidgetShow), Self, nil, 0);
2616   g_signal_connect_data(FWidget,'map', TGCallback(@Gtk3MapWidget), Self, nil, 0);
2617   g_signal_connect_data(FWidget,'size-allocate',TGCallback(@Gtk3SizeAllocate), Self, nil, 0);
2618   // g_signal_connect_data(FWidget, 'motion_notify_event', TGCallback(@Gtk3MotionNotifyEvent), LCLObject, nil, 0);
2619 end;
2620 
2621 procedure TGtk3Widget.DeInitializeWidget;
2622 begin
2623 
2624 end;
2625 
2626 procedure TGtk3Widget.RecreateWidget;
2627 begin
2628 
2629 end;
2630 
2631 procedure TGtk3Widget.DestroyNotify(AWidget: PGtkWidget);
2632 begin
2633 
2634 end;
2635 
2636 destructor TGtk3Widget.Destroy;
2637 begin
2638   DestroyWidget;
2639   inherited Destroy;
2640 end;
2641 
CanFocusnull2642 function TGtk3Widget.CanFocus: Boolean;
2643 begin
2644   Result := False;
2645   if IsWidgetOK then
2646     Result := FWidget^.can_focus or GetContainerWidget^.can_focus;
2647 end;
2648 
GetFocusableByMousenull2649 function TGtk3Widget.GetFocusableByMouse: Boolean;
2650 begin
2651   Result := FFocusableByMouse;
2652 end;
2653 
getClientOffsetnull2654 function TGtk3Widget.getClientOffset: TPoint;
2655 var
2656   Allocation: TGtkAllocation;
2657   R: TRect;
2658 begin
2659   {offset between inner and outer rect of widget.
2660    It tricky since some widgets have regular offset eg
2661    Parent (FWidget) = (120,80) Child (FCentralWidget) = (2,2)
2662    but some are
2663    Parent (FWidget) = (120,80) Child (FCentralWidget) = (122,82).
2664    Such widgets are usually those with FCentralWidget^.get_has_window}
2665   Result := Point(0, 0);
2666   if Widget <> getContainerWidget then
2667   begin
2668     GetContainerWidget^.get_allocation(@Allocation);
2669     Result.X := Allocation.X;
2670     Result.Y := Allocation.Y;
2671   end else
2672     exit;
2673   R := getClientBounds;
2674   Result := Point(Result.x + R.Left, Result.y + R.Top);
2675 end;
2676 
getWidgetPosnull2677 function TGtk3Widget.getWidgetPos: TPoint;
2678 var
2679   Allocation: TGtkAllocation;
2680 begin
2681   Result := Point(0, 0);
2682   if IsWidgetOk then
2683   begin
2684     FWidget^.get_allocation(@Allocation);
2685     Result := Point(Allocation.X, Allocation.Y);
2686   end;
2687 end;
2688 
2689 procedure TGtk3Widget.OffsetMousePos(APoint: PPoint);
2690 begin
2691   with getClientOffset do
2692   begin
2693     dec(APoint^.x, x);
2694     dec(APoint^.y, y);
2695   end;
2696 end;
2697 
TGtk3Widget.DeliverMessagenull2698 function TGtk3Widget.DeliverMessage(var Msg; const AIsInputEvent: Boolean
2699   ): LRESULT;
2700 begin
2701   Result := LRESULT(AIsInputEvent);
2702   if LCLObject = nil then
2703     Exit;
2704   try
2705     if LCLObject.HandleAllocated then
2706     begin
2707       LCLObject.WindowProc(TLMessage(Msg));
2708       Result := TLMessage(Msg).Result;
2709     end;
2710   except
2711     Application.HandleException(nil);
2712   end;
2713 end;
2714 
getClientRectnull2715 function TGtk3Widget.getClientRect: TRect;
2716 var
2717   AAlloc: TGtkAllocation;
2718 begin
2719   Result := Rect(0, 0, 0, 0);
2720   if not IsWidgetOK then
2721     exit;
2722   if GetContainerWidget^.get_realized then
2723   begin
2724     GetContainerWidget^.get_allocation(@AAlloc);
2725     Result := Rect(AAlloc.x, AAlloc.y, AAlloc.width + AAlloc.x,AAlloc.height + AAlloc.y);
2726   end else
2727   if FWidget^.get_realized then
2728   begin
2729     FWidget^.get_allocation(@AAlloc);
2730     Result := Rect(AAlloc.x, AAlloc.y, AAlloc.width + AAlloc.x,AAlloc.height + AAlloc.y);
2731   end;
2732 
2733   OffsetRect(Result, -Result.Left, -Result.Top);
2734 end;
2735 
getClientBoundsnull2736 function TGtk3Widget.getClientBounds: TRect;
2737 var
2738   AAlloc: TGtkAllocation;
2739 begin
2740   Result := Rect(0, 0, 0, 0);
2741   if IsWidgetOk then
2742   begin
2743     if FWidget^.get_realized then
2744     begin
2745       FWidget^.get_allocation(@AAlloc);
2746       Result := Rect(AAlloc.x, AAlloc.y, AAlloc.width + AAlloc.x,AAlloc.height + AAlloc.y);
2747     end else
2748     if GetContainerWidget^.get_realized then
2749     begin
2750       GetContainerWidget^.get_allocation(@AAlloc);
2751       Result := Rect(AAlloc.x, AAlloc.y, AAlloc.width + AAlloc.x,AAlloc.height + AAlloc.y);
2752     end;
2753   end;
2754 end;
2755 
GetContainerWidgetnull2756 function TGtk3Widget.GetContainerWidget: PGtkWidget;
2757 begin
2758   if Assigned(FCentralWidget) then
2759     Result := FCentralWidget
2760   else
2761     Result := FWidget;
2762 end;
2763 
TGtk3Widget.GetPositionnull2764 function TGtk3Widget.GetPosition(out APoint: TPoint): Boolean;
2765 var
2766   ALeft, ATop: gint;
2767 begin
2768   APoint := Point(0, 0);
2769   Result := False;
2770   if IsWidgetOk then
2771   begin
2772     if FWidget^.get_realized then
2773     begin
2774       if FWidget^.get_has_window then
2775       begin
2776         gdk_window_get_position(FWidget^.window, @ALeft, @ATop);
2777         APoint.X := ALeft;
2778         APoint.Y := ATop;
2779         Result := True;
2780       end;
2781     end;
2782     if not Result then
2783     begin
2784       APoint := Point(LCLObject.Left, LCLObject.Top);
2785       Result := True;
2786     end;
2787   end;
2788 end;
2789 
2790 procedure TGtk3Widget.Release;
2791 begin
2792   LCLObject := nil;
2793   Free;
2794 end;
2795 
2796 procedure TGtk3Widget.Hide;
2797 begin
2798   if Assigned(FWidget) then
2799     FWidget^.hide;
2800 end;
2801 
TGtk3Widget.getParentnull2802 function TGtk3Widget.getParent: TGtk3Widget;
2803 begin
2804   Result := Gtk3WidgetFromGtkWidget(Widget^.get_parent);
2805 end;
2806 
GetWindownull2807 function TGtk3Widget.GetWindow: PGdkWindow;
2808 begin
2809   Result := FWidget^.window;
2810 end;
2811 
2812 procedure TGtk3Widget.Move(ALeft, ATop: Integer);
2813 var
2814   AParent: TGtk3Widget;
2815 begin
2816   AParent := getParent;
2817   if (AParent <> nil) then
2818   begin
2819     if (wtContainer in AParent.WidgetType) then
2820       PGtkFixed(AParent.GetContainerWidget)^.move(FWidget, ALeft, ATop)
2821     else
2822     if (wtLayout in AParent.WidgetType) then
2823       PGtkLayout(AParent.GetContainerWidget)^.move(FWidget, ALeft, ATop);
2824   end;
2825 end;
2826 
2827 procedure TGtk3Widget.Activate;
2828 begin
2829   if IsWidgetOK then
2830   begin
2831     if not FWidget^.visible then
2832       exit;
2833     if Gtk3IsGdkWindow(FWidget^.window) then
2834       FWidget^.window^.raise_
2835     else
2836     begin
2837       FWidget^.get_parent_window^.raise_;
2838     end;
2839     if FWidget^.can_focus then
2840       FWidget^.grab_focus;
2841   end;
2842 end;
2843 
2844 procedure TGtk3Widget.preferredSize(var PreferredWidth,
2845   PreferredHeight: integer; WithThemeSpace: Boolean);
2846 var
2847   AMinH: gint;
2848   AMinW: gint;
2849 begin
2850   if IsWidgetOK then
2851   begin
2852     {$IFDEF GTK3DEBUGPREFERREDSIZE}
2853     Widget^.get_size_request(@AMinW, @AMinH);
2854     DebugLn('>',dbgsName(LCLObject),'.preferredSize W=',dbgs(PreferredWidth),' H=',dbgs(PreferredHeight),' WithThemeSpace ',dbgs(WithThemeSpace),' AMinW=',dbgs(AMinW),' AMinH=',dbgs(AMinH));
2855     {$ENDIF}
2856     GetContainerWidget^.get_preferred_height(@AMinH, @PreferredHeight);
2857     GetContainerWidget^.get_preferred_width(@AMinW, @PreferredWidth);
2858     {$IFDEF GTK3DEBUGPREFERREDSIZE}
2859     if WithThemeSpace then
2860     begin
2861       GetContainerWidget^.get_style_context^.get_margin(GTK_STATE_NORMAL, @ABorder);
2862       with ABorder do
2863         DebugLn('BorderSpaces ',Format('L %d T %d R %d B %d',[Left, Top, Right, Bottom]));
2864       GetContainerWidget^.get_style_context^.get_padding(GTK_STATE_NORMAL, @ABorder);
2865       with ABorder do
2866         DebugLn('Padding ',Format('L %d T %d R %d B %d',[Left, Top, Right, Bottom]));
2867     end;
2868     DebugLn('<',dbgsName(LCLObject),'.preferredSize W=',dbgs(PreferredWidth),' H=',dbgs(PreferredHeight),' WithThemeSpace ',dbgs(WithThemeSpace),' AMinH=',dbgs(AMinH),' AMinW=',dbgs(AMinW));
2869     {$ENDIF}
2870   end;
2871 end;
2872 
2873 procedure TGtk3Widget.SetCursor(ACursor: HCURSOR);
2874 begin
2875   if IsWidgetOk then
2876   begin
2877     if GetContainerWidget^.get_has_window and Gtk3IsGdkWindow(GetContainerWidget^.window) then
2878       SetWindowCursor(GetContainerWidget^.window, ACursor, False, True)
2879     else
2880     if Widget^.get_has_window and Gtk3IsGdkWindow(Widget^.window) then
2881       SetWindowCursor(Widget^.window, ACursor, False, True);
2882   end;
2883 end;
2884 
2885 procedure TGtk3Widget.SetFocus;
2886 begin
2887   if GetContainerWidget^.can_focus then
2888     GetContainerWidget^.grab_focus
2889   else
2890   if FWidget^.can_focus then
2891     FWidget^.grab_focus;
2892 end;
2893 
2894 procedure TGtk3Widget.SetParent(AParent: TGtk3Widget; const ALeft, ATop: Integer
2895   );
2896 begin
2897   if wtLayout in AParent.WidgetType then
2898     PGtkLayout(AParent.GetContainerWidget)^.put(FWidget, ALeft, ATop)
2899   else
2900   if wtContainer in AParent.WidgetType then
2901     PGtkFixed(AParent.GetContainerWidget)^.put(FWidget, ALeft, ATop)
2902   else
2903   if wtNotebook in AParent.WidgetType then
2904     // do nothing !
2905   else
2906     FWidget^.set_parent(AParent.GetContainerWidget);
2907 end;
2908 
2909 procedure TGtk3Widget.Show;
2910 begin
2911   if IsValidHandle then
2912     FWidget^.show;
2913 end;
2914 
2915 procedure TGtk3Widget.ShowAll;
2916 begin
2917   if IsValidHandle then
2918     FWidget^.show_all;
2919 end;
2920 
2921 procedure TGtk3Widget.Update(ARect: PRect);
2922 begin
2923   if IsWidgetOK then
2924   begin
2925     if ARect <> nil then
2926     begin
2927       with ARect^ do
2928         FWidget^.queue_draw_area(Left, Top, Right - Left, Bottom - Top);
2929       if FWidget <> GetContainerWidget then
2930         with ARect^ do
2931           GetContainerWidget^.queue_draw_area(Left, Top, Right - Left, Bottom - Top);
2932     end else
2933     begin
2934       FWidget^.queue_draw;
2935       if FWidget <> GetContainerWidget then
2936         GetContainerWidget^.queue_draw;
2937     end;
2938   end;
2939 end;
2940 
2941 { TGtk3StatusBar }
2942 
CreateWidgetnull2943 function TGtk3StatusBar.CreateWidget(const Params: TCreateParams): PGtkWidget;
2944 begin
2945   Result := TGtkEventBox.new;
2946   FCentralWidget := TGtkHBox.new(GTK_ORIENTATION_HORIZONTAL, 1);
2947   PGtkBox(FCentralWidget)^.set_homogeneous(True);
2948   PGtkEventBox(Result)^.add(FCentralWidget);
2949   //TODO: add routines to set panels
2950 end;
2951 
2952 { TGtk3Panel }
2953 
2954 procedure TGtk3Panel.SetColor(AValue: TColor);
2955 var
2956   AGdkRGBA: TGdkRGBA;
2957   AColor: TGdkColor;
2958 begin
2959   inherited SetColor(AValue);
2960   exit;
2961   if (AValue = clDefault) or (AValue = clBackground) then
2962   begin
2963     // this is just to test if we can get transparent panel again
2964     // clDefault must be extracted from style
2965 
2966     // nil resets color to gtk default
2967     FWidget^.override_background_color(GTK_STATE_FLAG_NORMAL, nil);
2968     StyleContext^.get_background_color(GTK_STATE_FLAG_NORMAL, @AGdkRGBA);
2969 
2970     // writeln('ACOLOR R=',AColor.Red,' G=',AColor.green,' B=',AColor.blue);
2971     // AColor := TColortoTGDKColor(AValue);
2972     {AGdkRGBA.alpha := 0;
2973     AGdkRGBA.red := AColor.red / 65535.00;
2974     AGdkRGBA.blue := AColor.blue / 65535.00;
2975     AGdkRGBA.green := AColor.red / 65535.00;}
2976     FWidget^.override_background_color(GTK_STATE_FLAG_NORMAL, @AGdkRGBA);
2977     FWidget^.override_background_color(GTK_STATE_FLAG_ACTIVE, @AGdkRGBA);
2978     FWidget^.override_background_color(GTK_STATE_FLAG_FOCUSED, @AGdkRGBA);
2979     FWidget^.override_background_color(GTK_STATE_FLAG_PRELIGHT, @AGdkRGBA);
2980     FWidget^.override_background_color(GTK_STATE_FLAG_SELECTED, @AGdkRGBA);
2981   end else
2982   begin
2983     AColor := TColortoTGDKColor(AValue);
2984     // writeln('ACOLOR R=',AColor.Red,' G=',AColor.green,' B=',AColor.blue);
2985     //inherited SetColor(AValue);
2986   end;
2987 end;
2988 
CreateWidgetnull2989 function TGtk3Panel.CreateWidget(const Params: TCreateParams): PGtkWidget;
2990 var
2991   AGdkRGBA: TGdkRGBA;
2992 begin
2993   FHasPaint := True;
2994   FBorderStyle := bsNone;
2995   FBevelInner := bvNone;
2996   FBevelOuter := bvNone;
2997   // wtLayout = using GtkLayout
2998   // FWidgetType := [wtWidget, wtLayout];
2999   // Result := TGtkLayout.new(nil, nil);
3000   FWidgetType := [wtWidget, wtContainer];
3001   Result := TGtkFixed.new;
3002   Result^.set_has_window(True);
3003   // AColor := Result^.style^.bg[0];
3004   // writeln('BG COLOR R=',AColor.red,' G=',AColor.green,' B=',AColor.blue);
3005   // now we make panel completely transparent.
3006   // SetColor must usr override_background_color for panel
3007   // we must implement cairo_pattern_t since background can be brush
3008   AGdkRGBA.alpha := 0;
3009   AGdkRGBA.red := 0; // AColor.Red / 65535.00;
3010   AGdkRGBA.blue := 0; // AColor.Blue / 65535.00;
3011   AGdkRGBA.green := 0; // AColor.green / 65535.00;
3012   Result^.override_background_color(GTK_STATE_FLAG_NORMAL, @AGdkRGBA);
3013   Result^.override_background_color(GTK_STATE_FLAG_ACTIVE, @AGdkRGBA);
3014   Result^.override_background_color(GTK_STATE_FLAG_FOCUSED, @AGdkRGBA);
3015   Result^.override_background_color(GTK_STATE_FLAG_PRELIGHT, @AGdkRGBA);
3016   Result^.override_background_color(GTK_STATE_FLAG_SELECTED, @AGdkRGBA);
3017 end;
3018 
3019 procedure TGtk3Panel.DoBeforeLCLPaint;
3020 var
3021   DC: TGtk3DeviceContext;
3022 begin
3023   inherited DoBeforeLCLPaint;
3024   // example how to paint borderstyle/bevels of TPanel before we send event to lcl
3025   DC := TGtk3DeviceContext(FContext);
3026   if not Visible then
3027     exit;
3028   if BorderStyle <> bsNone then
3029     DC.drawRect(0, 0, LCLObject.Width, LCLObject.Height, LCLObject.Color <> clDefault);
3030 end;
3031 
TGtk3Panel.getTextnull3032 function TGtk3Panel.getText: String;
3033 begin
3034   Result := FText;
3035 end;
3036 
3037 procedure TGtk3Panel.setText(AValue: String);
3038 begin
3039   if FText = AValue then
3040     exit;
3041   FText := AValue;
3042   if Self.Visible then
3043     FWidget^.queue_draw;
3044 end;
3045 
3046 { TGtk3GroupBox }
3047 
TGtk3GroupBox.CreateWidgetnull3048 function TGtk3GroupBox.CreateWidget(const Params: TCreateParams): PGtkWidget;
3049 begin
3050   FHasPaint := True;
3051   //dont use layout for now
3052   FWidgetType := [wtWidget, wtContainer, wtGroupBox];
3053   Result := TGtkFrame.new('');
3054   // FCentralWidget := TGtkLayout.new(nil, nil);
3055   FCentralWidget := TGtkFixed.new;
3056   PGtkBin(Result)^.add(FCentralWidget);
3057   FCentralWidget^.set_has_window(True);
3058 end;
3059 
getTextnull3060 function TGtk3GroupBox.getText: String;
3061 begin
3062   Result := '';
3063   if IsWidgetOK then
3064   begin
3065     if PGtkFrame(Widget)^.get_label_widget = nil then
3066       exit;
3067     Result := PGtkFrame(Widget)^.get_label;
3068   end;
3069 end;
3070 
3071 procedure TGtk3GroupBox.setText(AValue: String);
3072 begin
3073   if IsWidgetOK then
3074   begin
3075     if AValue = '' then
3076       PGtkFrame(Widget)^.set_label_widget(nil)
3077       // maybe DoAdjustClientRect here
3078     else
3079     begin
3080       if PGtkFrame(Widget)^.get_label_widget = nil then
3081         PGtkFrame(Widget)^.set_label_widget(TGtkLabel.new(''));
3082       PGtkFrame(Widget)^.set_label(PgChar(AValue));
3083     end;
3084   end;
3085 end;
3086 
3087 
3088 { TGtk3Editable }
3089 
gtk3EditableDelayedSelStartnull3090 function gtk3EditableDelayedSelStart(AData: Pointer): gboolean; cdecl;
3091 var
3092   AWidget: PGtkEditable;
3093   AEditable: TGtk3Editable;
3094 begin
3095   Result := False;
3096   AEditable := TGtk3Editable(AData);
3097   AWidget := PGtkEditable(TGtk3Widget(AData).Widget);
3098   if (AEditable.PrivateCursorPos <> -1) and (AEditable.PrivateSelection <> -1) then
3099   begin
3100     gtk_editable_select_region(AWidget,AEditable.PrivateCursorPos, AEditable.PrivateSelection);
3101     // gtk_editable_set_position(AWidget, TGtk3Editable(AData).PrivateCursorPos);
3102   end;
3103   AEditable.PrivateCursorPos := -1;
3104   AEditable.PrivateSelection := -1;
3105   g_idle_remove_by_data(AData);
3106 end;
3107 
GetReadOnlynull3108 function TGtk3Editable.GetReadOnly: Boolean;
3109 begin
3110   Result := False;
3111   if IsWidgetOK then
3112     Result := not PGtkEditable(FWidget)^.get_editable;
3113 end;
3114 
3115 procedure TGtk3Editable.SetReadOnly(AValue: Boolean);
3116 begin
3117   if IsWidgetOK then
3118     PGtkEditable(FWidget)^.set_editable(not AValue);
3119 end;
3120 
TGtk3Editable.getCaretPosnull3121 function TGtk3Editable.getCaretPos: TPoint;
3122 begin
3123   Result := Point(0, 0);
3124   if not IsWidgetOk then
3125     exit;
3126   Result.X := PGtkEditable(FWidget)^.get_position;
3127 end;
3128 
3129 procedure TGtk3Editable.SetCaretPos(AValue: TPoint);
3130 begin
3131   if not IsWidgetOk then
3132     exit;
3133   PGtkEditable(FWidget)^.set_position(AValue.X);
3134 end;
3135 
getSelStartnull3136 function TGtk3Editable.getSelStart: Integer;
3137 var
3138   AStart: gint;
3139   AStop: gint;
3140 begin
3141   Result := 0;
3142   if not IsWidgetOk then
3143     exit;
3144   if PGtkEditable(FWidget)^.get_selection_bounds(@AStart, @AStop) then
3145   begin
3146     Result := AStart;
3147   end;
3148 end;
3149 
TGtk3Editable.getSelLengthnull3150 function TGtk3Editable.getSelLength: Integer;
3151 var
3152   AStart: gint;
3153   AStop: gint;
3154 begin
3155   Result := 0;
3156   if not IsWidgetOk then
3157     exit;
3158   if PGtkEditable(FWidget)^.get_selection_bounds(@AStart, @AStop) then
3159   begin
3160     Result := AStop - AStart;
3161   end;
3162 end;
3163 
3164 procedure TGtk3Editable.setSelStart(AValue: Integer);
3165 begin
3166   if not IsWidgetOk then
3167     exit;
3168   CaretPos := Point(AValue, 0);
3169   (*
3170   if InUpdate then
3171   begin
3172     PrivateCursorPos := AValue;
3173     CaretPos := Point(AValue, 0);
3174     // setDelayed when mouse events are finished.
3175     // This is needed to SetSelStart/SetSelLength inside changed event of text edit
3176     // g_idle_add(@gtk3EditableDelayedSelStart, Self);
3177   end else
3178     CaretPos := Point(AValue, 0);
3179     *)
3180   // DebugLn('TGtk3Editable.SetSelStart ',dbgsName(LCLObject),' value=',dbgs(AValue));
3181   (*
3182   PGtkEditable(FWidget)^.get_selection_bounds(@AStart, @AStop);
3183   if AStop < AValue then
3184     AStop := AValue;
3185   PGtkEditable(FWidget)^.select_region(AValue, AStop);
3186   *)
3187 end;
3188 
3189 procedure TGtk3Editable.setSelLength(AValue: Integer);
3190 var
3191   AStart: gint;
3192   AStop: gint;
3193 begin
3194   if not IsWidgetOk then
3195     exit;
3196   PGtkEditable(FWidget)^.get_selection_bounds(@AStart, @AStop);
3197   AStart := CaretPos.X;
3198   // DebugLn('TGtk3Editable.SetSelLength ',dbgsName(LCLObject),' value=',dbgs(AValue),' AStart=',dbgs(AStart),' InUpdate ',dbgs(InUpdate));
3199   if InUpdate then
3200   begin
3201     PrivateCursorPos := AStart;
3202     PrivateSelection := AValue;
3203     // g_idle_add(@gtk3EditableDelayedSelStart, Self)
3204     // setDelayed later
3205     PGtkEditable(FWidget)^.select_region(AStart, AStart + AValue)
3206   end else
3207     PGtkEditable(FWidget)^.select_region(AStart, AStart + AValue);
3208 end;
3209 
3210 { TGtk3Entry }
3211 
3212 procedure Gtk3EntryDeletedText(AEntry: PGtkEntryBuffer; APosition: guint; ANumChars: guint; AData: GPointer); cdecl;
3213 var
3214   Msg: TLMessage;
3215 begin
3216   FillChar(Msg, SizeOf(Msg), 0);
3217   Msg.Msg := CM_TEXTCHANGED;
3218   TGtk3Widget(AData).DeliverMessage(Msg);
3219 end;
3220 
3221 procedure Gtk3EntryInsertedText(AEntry: PGtkEntryBuffer; APosition: guint; AChars: PGChar; ANumChars: guint; AData: GPointer); cdecl;
3222 var
3223   Msg: TLMessage;
3224 begin
3225   FillChar(Msg, SizeOf(Msg), 0);
3226   Msg.Msg := CM_TEXTCHANGED;
3227   TGtk3Widget(AData).DeliverMessage(Msg);
3228 end;
3229 
3230 procedure Gtk3EntryChanged(AEntry: PGtkEntryBuffer; AData: GPointer); cdecl;
3231 var
3232   Msg: TLMessage;
3233 begin
3234   FillChar(Msg, SizeOf(Msg), 0);
3235   Msg.Msg := CM_TEXTCHANGED;
3236   TGtk3Widget(AData).DeliverMessage(Msg);
3237 end;
3238 
GetAlignmentnull3239 function TGtk3Entry.GetAlignment: TAlignment;
3240 var
3241   AFloat: GFloat;
3242 begin
3243   Result := taLeftJustify;
3244   if not IsWidgetOk then
3245     exit;
3246   AFloat := PGtkEntry(FWidget)^.get_alignment;
3247   if AFloat = 1 then
3248     Result := taRightJustify
3249   else
3250   if AFloat = 0.5 then
3251     Result := taCenter;
3252 end;
3253 
3254 procedure TGtk3Entry.SetAlignment(AValue: TAlignment);
3255 var
3256   AFloat: GFloat;
3257 begin
3258   AFloat := 0;
3259   if not IsWidgetOk then
3260     exit;
3261   case AValue of
3262     taCenter: AFloat := 0.5;
3263     taRightJustify: AFloat := 1.0;
3264   end;
3265   PGtkEntry(FWidget)^.set_alignment(AFloat);
3266 end;
3267 
EatArrowKeysnull3268 function TGtk3Entry.EatArrowKeys(const AKey: Word): Boolean;
3269 begin
3270   Result := AKey in [VK_UP, VK_DOWN];
3271 end;
3272 
getTextnull3273 function TGtk3Entry.getText: String;
3274 begin
3275   if IsValidHandle and IsWidgetOk then
3276     Result := StrPas(PGtkEntry(Widget)^.get_text)
3277   else
3278     Result := '';
3279 end;
3280 
3281 procedure TGtk3Entry.setText(AValue: String);
3282 begin
3283   if IsValidHandle and IsWidgetOK then
3284     PGtkEntry(Widget)^.set_text(PgChar(AValue));
3285 end;
3286 
TGtk3Entry.CreateWidgetnull3287 function TGtk3Entry.CreateWidget(const Params: TCreateParams): PGtkWidget;
3288 begin
3289   Result := PGtkWidget(TGtkEntry.new);
3290   FWidgetType := FWidgetType + [wtEntry];
3291   PrivateCursorPos := -1;
3292   PrivateSelection := -1;
3293 end;
3294 
3295 procedure TGtk3Entry.InitializeWidget;
3296 begin
3297   inherited InitializeWidget;
3298   g_signal_connect_data(PGtkEntry(FWidget), 'changed', TGCallback(@Gtk3EntryChanged), Self, nil, 0);
3299   //g_signal_connect_data(PGtkEntry(FWidget)^.get_buffer, 'deleted-text', TGCallback(@Gtk3EntryDeletedText), Self, nil, 0);
3300   //g_signal_connect_data(PGtkEntry(FWidget)^.get_buffer, 'inserted-text', TGCallback(@Gtk3EntryInsertedText), Self, nil, 0);
3301 end;
3302 
3303 procedure TGtk3Entry.SetPasswordChar(APasswordChar: Char);
3304 var
3305   PWChar: Integer;
3306 begin
3307   if IsWidgetOK then
3308   begin
3309     PWChar := ord(APasswordChar);
3310     if (PWChar < 192) or (PWChar = ord('*')) then
3311       PWChar := 9679;
3312     PGtkEntry(FWidget)^.set_invisible_char(PWChar);
3313   end;
3314 end;
3315 
3316 procedure TGtk3Entry.SetEchoMode(AVisible: Boolean);
3317 begin
3318   if IsWidgetOK then
3319     PGtkEntry(FWidget)^.set_visibility(AVisible);
3320 end;
3321 
3322 procedure TGtk3Entry.SetMaxLength(AMaxLength: Integer);
3323 begin
3324   if IsWidgetOK then
3325     PGtkEntry(FWidget)^.set_max_length(AMaxLength);
3326 end;
3327 
TGtk3Entry.IsWidgetOknull3328 function TGtk3Entry.IsWidgetOk: Boolean;
3329 begin
3330   Result := (FWidget <> nil) and Gtk3IsEntry(FWidget);
3331 end;
3332 
3333 { TGtk3SpinEdit }
3334 
GetMaximumnull3335 function TGtk3SpinEdit.GetMaximum: Double;
3336 var
3337   AFloat: gdouble;
3338 begin
3339   Result := 0;
3340   if IsWidgetOk then
3341     PGtkSpinButton(FWidget)^.get_range(@AFloat ,@Result);
3342 end;
3343 
TGtk3SpinEdit.GetMinimumnull3344 function TGtk3SpinEdit.GetMinimum: Double;
3345 var
3346   AFloat: gdouble;
3347 begin
3348   Result := 0;
3349   if IsWidgetOk then
3350     PGtkSpinButton(FWidget)^.get_range(@Result ,@AFloat);
3351 end;
3352 
TGtk3SpinEdit.GetNumDigitsnull3353 function TGtk3SpinEdit.GetNumDigits: Integer;
3354 begin
3355   Result := 0;
3356   if IsWidgetOk then
3357     Result := Integer(PGtkSpinButton(FWidget)^.get_digits);
3358 end;
3359 
TGtk3SpinEdit.GetNumericnull3360 function TGtk3SpinEdit.GetNumeric: Boolean;
3361 begin
3362   Result := False;
3363   if IsWidgetOk then
3364     Result := PGtkSpinButton(FWidget)^.get_numeric;
3365 end;
3366 
TGtk3SpinEdit.GetStepnull3367 function TGtk3SpinEdit.GetStep: Double;
3368 var
3369   AFloat: Double;
3370 begin
3371   Result := 0;
3372   if IsWidgetOk then
3373     PGtkSpinButton(FWidget)^.get_increments(@Result, @AFloat);
3374 end;
3375 
TGtk3SpinEdit.GetValuenull3376 function TGtk3SpinEdit.GetValue: Double;
3377 begin
3378   Result := 0;
3379   if IsWidgetOk then
3380     Result := PGtkSpinButton(FWidget)^.get_value;
3381 end;
3382 
3383 procedure TGtk3SpinEdit.SetNumDigits(AValue: Integer);
3384 begin
3385   if IsWidgetOk then
3386     PGtkSpinButton(FWidget)^.set_digits(GUint(AValue));
3387 end;
3388 
3389 procedure TGtk3SpinEdit.SetNumeric(AValue: Boolean);
3390 begin
3391   if IsWidgetOk then
3392     PGtkSpinButton(FWidget)^.set_numeric(AValue);
3393 end;
3394 
3395 procedure TGtk3SpinEdit.SetStep(AValue: Double);
3396 var
3397   AStep: gdouble;
3398   APage: gdouble;
3399 begin
3400   if IsWidgetOk then
3401   begin
3402     PGtkSpinButton(FWidget)^.get_increments(@AStep, @APage);
3403     PGtkSpinButton(FWidget)^.set_increments(AValue, APage);
3404   end;
3405 end;
3406 
3407 procedure TGtk3SpinEdit.SetValue(AValue: Double);
3408 begin
3409   if IsWidgetOk then
3410   begin
3411     PGtkSpinButton(FWidget)^.set_value(AValue);
3412   end;
3413 end;
3414 
CreateWidgetnull3415 function TGtk3SpinEdit.CreateWidget(const Params: TCreateParams): PGtkWidget;
3416 var
3417   ASpin: TCustomSpinEdit;
3418 begin
3419   PrivateCursorPos := -1;
3420   PrivateSelection := -1;
3421   ASpin := TCustomSpinEdit(LCLObject);
3422   FWidgetType := FWidgetType + [wtSpinEdit];
3423   // Adjustment := TGtkAdjustment.new(ASpin.Value, ASpin.MinValue, ASpin.MaxValue, ASpin.Increment,
3424   //  ASpin.Increment, ASpin.Increment);
3425   Result := TGtkSpinButton.new_with_range(ASpin.MinValue, ASpin.MaxValue, ASpin.Increment);
3426 end;
3427 
EatArrowKeysnull3428 function TGtk3SpinEdit.EatArrowKeys(const AKey: Word): Boolean;
3429 begin
3430   Result := False;
3431 end;
3432 
TGtk3SpinEdit.IsWidgetOknull3433 function TGtk3SpinEdit.IsWidgetOk: Boolean;
3434 begin
3435   Result := (FWidget <> nil) and Gtk3IsSpinButton(FWidget);
3436 end;
3437 
3438 procedure TGtk3SpinEdit.SetRange(AMin, AMax: Double);
3439 begin
3440   if IsWidgetOk then
3441     PGtkSpinButton(FWidget)^.set_range(AMin, AMax);
3442 end;
3443 
3444 { TGtk3Range }
3445 
3446 procedure Gtk3RangeChanged(ARange: PGtkRange; AData: gPointer); cdecl;
3447 var
3448   Msg: TLMessage;
3449 begin
3450   if AData <> nil then
3451   begin
3452     if TGtk3Widget(AData).InUpdate then
3453       Exit;
3454     FillChar(Msg, SizeOf(Msg), #0);
3455     Msg.Msg := LM_CHANGED;
3456     TGtk3Widget(AData).DeliverMessage(Msg);
3457   end;
3458 end;
3459 
GetPositionnull3460 function TGtk3Range.GetPosition: Integer;
3461 begin
3462   Result := 0;
3463   if IsWidgetOK then
3464     Result := Round(PGtkRange(FWidget)^.get_value);
3465 end;
3466 
GetRangenull3467 function TGtk3Range.GetRange: TPoint;
3468 begin
3469   Result := Point(0, 0);
3470   if IsWidgetOK then
3471     PGtkRange(FWidget)^.get_slider_range(@Result.X, @Result.Y);
3472 end;
3473 
3474 procedure TGtk3Range.SetPosition(AValue: Integer);
3475 begin
3476   if IsWidgetOK then
3477     PGtkRange(FWidget)^.set_value(gDouble(AValue));
3478 end;
3479 
3480 procedure TGtk3Range.SetRange(AValue: TPoint);
3481 var
3482   dx,dy: gdouble;
3483 begin
3484   if IsWidgetOK then
3485   begin
3486     dx := AValue.X;
3487     dy := AValue.Y;
3488     PGtkRange(FWidget)^.set_range(dx, dy);
3489   end;
3490 end;
3491 
3492 procedure TGtk3Range.InitializeWidget;
3493 begin
3494   inherited InitializeWidget;
3495   g_signal_connect_data(GetContainerWidget, 'value-changed', TGCallback(@Gtk3RangeChanged), Self, nil, 0);
3496 end;
3497 
3498 procedure TGtk3Range.SetStep(AStep: Integer; APageSize: Integer);
3499 begin
3500   if IsWidgetOk then
3501     PGtkRange(FWidget)^.set_increments(gDouble(AStep), gDouble(APageSize));
3502 end;
3503 
3504 { TGtk3TrackBar }
3505 
GetReversednull3506 function TGtk3TrackBar.GetReversed: Boolean;
3507 begin
3508   Result := False;
3509   if IsWidgetOK then
3510     Result := PGtkScale(FWidget)^.get_inverted;
3511 end;
3512 
3513 procedure TGtk3TrackBar.SetReversed(AValue: Boolean);
3514 begin
3515   if IsWidgetOK then
3516     PGtkScale(FWidget)^.set_inverted(AValue);
3517 end;
3518 
TGtk3TrackBar.CreateWidgetnull3519 function TGtk3TrackBar.CreateWidget(const Params: TCreateParams): PGtkWidget;
3520 var
3521   ATrack: TCustomTrackBar;
3522 begin
3523   ATrack := TCustomTrackBar(LCLObject);
3524   FWidgetType := FWidgetType + [wtTrackBar];
3525   Result := PGtkWidget(TGtkScale.new(Ord(ATrack.Orientation), nil));
3526   FOrientation := ATrack.Orientation;
3527   if ATrack.Reversed then
3528     PGtkScale(Result)^.set_inverted(True);
3529 
3530   PGtkScale(Result)^.set_digits(0);
3531 end;
3532 
GetTrackBarOrientationnull3533 function TGtk3TrackBar.GetTrackBarOrientation: TTrackBarOrientation;
3534 begin
3535   Result := FOrientation;
3536 end;
3537 
3538 procedure TGtk3TrackBar.SetScalePos(AValue: TTrackBarScalePos);
3539 begin
3540   if IsWidgetOK then
3541     PGtkScale(FWidget)^.set_value_pos(Ord(AValue));
3542 end;
3543 
3544 procedure TGtk3TrackBar.SetTickMarks(AValue: TTickMark; ATickStyle: TTickStyle);
3545 var
3546   i: Integer;
3547 begin
3548   if IsWidgetOK then
3549   begin
3550     if ATickStyle = tsNone then
3551       PGtkScale(FWidget)^.clear_marks
3552     else
3553     begin
3554       for i := TCustomTrackbar(LCLObject).Min to TCustomTrackbar(LCLObject).Max do
3555       begin
3556         if TCustomTrackbar(LCLObject).Orientation = trHorizontal then
3557         begin
3558           if AValue in [tmBoth, tmTopLeft] then
3559             PGtkScale(FWidget)^.add_mark(gDouble(i), GTK_POS_TOP, nil);
3560           if AValue in [tmBoth, tmBottomRight] then
3561             PGtkScale(FWidget)^.add_mark(gDouble(i), GTK_POS_BOTTOM, nil);
3562         end else
3563         begin
3564           if AValue in [tmBoth, tmTopLeft] then
3565             PGtkScale(FWidget)^.add_mark(gDouble(i), GTK_POS_LEFT, nil);
3566           if AValue in [tmBoth, tmBottomRight] then
3567             PGtkScale(FWidget)^.add_mark(gDouble(i), GTK_POS_RIGHT, nil);
3568         end;
3569       end;
3570     end;
3571   end;
3572 end;
3573 
3574 { TGtk3ScrollBar }
3575 
CreateWidgetnull3576 function TGtk3ScrollBar.CreateWidget(const Params: TCreateParams): PGtkWidget;
3577 var
3578   AScrollbar: TCustomScrollBar;
3579   ARange: PGtkRange;
3580 begin
3581   AScrollBar := TCustomScrollBar(LCLObject);
3582   FWidgetType := FWidgetType + [wtScrollBar];
3583   Result := TGtkScrollbar.new(Ord(AScrollBar.Kind), nil);
3584   ARange := PGtkRange(Result);
3585   // ARange^.set_can_focus(True);
3586   with AScrollBar do
3587   begin
3588     ARange^.adjustment^.configure(Position, Min, Max + PageSize,
3589       SmallChange, LargeChange, PageSize);
3590     ARange^.adjustment^.set_value(Position);
3591   end;
3592 end;
3593 
3594 procedure TGtk3ScrollBar.SetParams;
3595 var
3596   ARange: PGtkRange;
3597 begin
3598   if not IsWidgetOk then
3599     exit;
3600   ARange := PGtkRange(Widget);
3601   with TCustomScrollbar(LCLObject) do
3602   begin
3603     ARange^.adjustment^.configure(Position, Min, Max + PageSize,
3604       SmallChange, LargeChange, PageSize);
3605     ARange^.adjustment^.set_value(Position);
3606     ARange^.adjustment^.changed;
3607    // gtk_adjustment_changed(Range^.adjustment);
3608   end;
3609 end;
3610 
3611 { TGtk3Calendar }
3612 
TGtk3Calendar.CreateWidgetnull3613 function TGtk3Calendar.CreateWidget(const Params: TCreateParams): PGtkWidget;
3614 begin
3615   FWidgetType := [wtWidget, wtCalendar];
3616   Result := TGtkFrame.new(nil);
3617   FCentralWidget := TGtkCalendar.new;
3618   PGtkContainer(Result)^.add(FCentralWidget);
3619   FCentralWidget^.set_can_focus(True);
3620 end;
3621 
3622 procedure TGtk3Calendar.GetDate(out AYear, AMonth, ADay: Word);
3623 begin
3624   AYear := 0;
3625   AMonth := 0;
3626   ADay := 0;
3627   if IsWidgetOk then
3628     PGtkCalendar(GetContainerWidget)^.get_date(@AYear, @AMonth, @ADay);
3629 end;
3630 
3631 procedure TGtk3Calendar.SetDate(const AYear, AMonth, ADay: Word);
3632 begin
3633   if IsWidgetOK then
3634   begin
3635     PGtkCalendar(GetContainerWidget)^.select_month(AMonth, AYear);
3636     PGtkCalendar(GetContainerWidget)^.select_day(ADay);
3637   end;
3638 end;
3639 
3640 procedure TGtk3Calendar.SetDisplayOptions(
3641   const ADisplayOptions: TGtkCalendarDisplayOptions);
3642 begin
3643   if IsWidgetOK then
3644     PGtkCalendar(GetContainerWidget)^.set_display_options(ADisplayOptions);
3645 end;
3646 
3647 { TGtk3StaticText }
3648 
TGtk3StaticText.GetAlignmentnull3649 function TGtk3StaticText.GetAlignment: TAlignment;
3650 var
3651   X: gfloat;
3652   Y: gfloat;
3653 begin
3654   Result := taLeftJustify;
3655   if IsWidgetOK then
3656   begin
3657     PGtkLabel(GetContainerWidget)^.get_alignment(@X, @Y);
3658     if X = 1 then
3659       Result := taRightJustify
3660     else
3661     if X = 0.5 then
3662       Result := taCenter;
3663   end;
3664 end;
3665 
TGtk3StaticText.GetStaticBorderStylenull3666 function TGtk3StaticText.GetStaticBorderStyle: TStaticBorderStyle;
3667 var
3668   AShadowType: TGtkShadowType;
3669 begin
3670   Result := sbsNone;
3671   if IsWidgetOK then
3672   begin
3673     AShadowType := PGtkFrame(Widget)^.get_shadow_type;
3674     if AShadowType = GTK_SHADOW_ETCHED_IN then
3675       Result := sbsSingle
3676     else
3677     if AShadowType = GTK_SHADOW_IN then
3678       Result := sbsSunken;
3679   end;
3680 end;
3681 
3682 procedure TGtk3StaticText.SetAlignment(AValue: TAlignment);
3683 begin
3684   if IsWidgetOk then
3685     PGtkLabel(GetContainerWidget)^.set_alignment(AGtkJustificationF[AValue], 0);
3686 end;
3687 
3688 procedure TGtk3StaticText.SetStaticBorderStyle(AValue: TStaticBorderStyle);
3689 begin
3690   if IsWidgetOK then
3691     PGtkFrame(Widget)^.set_shadow_type(StaticBorderShadowMap[AValue]);
3692 end;
3693 
TGtk3StaticText.getTextnull3694 function TGtk3StaticText.getText: String;
3695 begin
3696   Result := '';
3697   if IsWidgetOk then
3698     Result := PGtkLabel(getContainerWidget)^.get_text;
3699 end;
3700 
3701 procedure TGtk3StaticText.setText(AValue: String);
3702 begin
3703   if IsWidgetOk then
3704     PGtkLabel(getContainerWidget)^.set_text(PgChar(AValue));
3705 end;
3706 
CreateWidgetnull3707 function TGtk3StaticText.CreateWidget(const Params: TCreateParams): PGtkWidget;
3708 var
3709   AStaticText: TCustomStaticText;
3710 begin
3711   FWidgetType := FWidgetType + [wtStaticText];
3712   AStaticText := TCustomStaticText(LCLObject);
3713   Result := TGtkFrame.new('');
3714   PGtkFrame(Result)^.set_shadow_type(StaticBorderShadowMap[AStaticText.BorderStyle]);
3715   FCentralWidget := TGtkLabel.new('');
3716   FCentralWidget^.set_has_window(True);
3717   PGtkFrame(Result)^.set_label_widget(nil);
3718   PGtkFrame(Result)^.add(FCentralWidget);
3719   PGtkLabel(FCentralWidget)^.set_alignment(AGtkJustificationF[AStaticText.Alignment], 0.0);
3720 end;
3721 
3722 { TGtk3ProgressBar }
3723 
TGtk3ProgressBar.GetOrientationnull3724 function TGtk3ProgressBar.GetOrientation: TProgressBarOrientation;
3725 var
3726   AOrientation: TGtkOrientation;
3727 begin
3728   Result := pbHorizontal;
3729   if IsWidgetOk then
3730   begin
3731     AOrientation := PGtkOrientable(getContainerWidget)^.get_orientation;
3732     if AOrientation = GTK_ORIENTATION_HORIZONTAL then
3733     begin
3734       if PGtkProgressBar(getContainerWidget)^.get_inverted then
3735         Result := pbRightToLeft
3736       else
3737         Result := pbHorizontal;
3738     end else
3739     begin
3740       if PGtkProgressBar(getContainerWidget)^.get_inverted then
3741         Result := pbTopDown
3742       else
3743         Result := pbVertical;
3744     end;
3745   end;
3746 end;
3747 
GetPositionnull3748 function TGtk3ProgressBar.GetPosition: Integer;
3749 begin
3750   Result := 0;
3751   if IsWidgetOk then
3752     Result := Round(PGtkProgressBar(GetContainerWidget)^.get_fraction);
3753 end;
3754 
GetShowTextnull3755 function TGtk3ProgressBar.GetShowText: Boolean;
3756 begin
3757   Result := False;
3758   if IsWidgetOK then
3759     Result := PGtkProgressBar(GetContainerWidget)^.get_show_text;
3760 end;
3761 
GetStylenull3762 function TGtk3ProgressBar.GetStyle: TProgressBarStyle;
3763 begin
3764   Result := pbstNormal;
3765   if Assigned(LCLObject) and IsWidgetOk then
3766     Result := TCustomProgressBar(LCLObject).Style;
3767 end;
3768 
3769 procedure TGtk3ProgressBar.SetOrientation(AValue: TProgressBarOrientation);
3770 begin
3771   if IsWidgetOk then
3772   begin
3773     case AValue of
3774       pbHorizontal,pbRightToLeft:
3775       begin
3776         PGtkOrientable(GetContainerWidget)^.set_orientation(GTK_ORIENTATION_HORIZONTAL);
3777         PGtkProgressBar(GetContainerWidget)^.set_inverted(AValue = pbRightToLeft);
3778       end;
3779       pbVertical, pbTopDown:
3780       begin
3781         PGtkOrientable(GetContainerWidget)^.set_orientation(GTK_ORIENTATION_VERTICAL);
3782         PGtkProgressBar(GetContainerWidget)^.set_inverted(AValue = pbVertical);
3783       end;
3784     end;
3785   end;
3786 end;
3787 
3788 procedure TGtk3ProgressBar.SetPosition(AValue: Integer);
3789 var
3790   ABar: TCustomProgressBar;
3791   fraction: gDouble;
3792 begin
3793   if not Assigned(LCLObject) or not IsWidgetOK then
3794     exit;
3795   ABar := TCustomProgressBar(LCLObject);
3796   if ((ABar.Max - ABar.Min) <> 0) then
3797     fraction := (AValue - ABar.Min) / (ABar.Max - ABar.Min)
3798   else
3799     fraction := 0;
3800   PGtkProgressBar(GetContainerWidget)^.set_fraction(fraction);
3801 end;
3802 
3803 procedure TGtk3ProgressBar.SetShowText(AValue: Boolean);
3804 begin
3805   if IsWidgetOK then
3806     PGtkProgressBar(GetContainerWidget)^.set_show_text(AValue);
3807 end;
3808 
ProgressPulseTimeoutnull3809 function ProgressPulseTimeout(data: gpointer): gboolean; cdecl;
3810 begin
3811   Result := {%H-}PtrUInt(g_object_get_data(data, 'lclprogressbarstyle')) = 1;
3812   if Result then
3813     PGtkProgressBar(Data)^.pulse;
3814 end;
3815 
3816 procedure ProgressDestroy(data: gpointer); cdecl;
3817 begin
3818   g_source_remove({%H-}PtrUInt(data));
3819 end;
3820 
3821 procedure TGtk3ProgressBar.SetStyle(AValue: TProgressBarStyle);
3822 begin
3823   if IsWidgetOk then
3824   begin
3825     g_object_set_data(GetContainerWidget,'lclprogressbarstyle', Pointer(PtrUInt(Ord(AValue))));
3826     if AValue = pbstNormal then
3827     begin
3828       Position := TCustomProgressBar(LCLObject).Position;
3829     end else
3830     begin
3831       g_object_set_data_full(GetContainerWidget, 'timeout',
3832         {%H-}Pointer(PtrUInt(g_timeout_add(100, @ProgressPulseTimeout, GetContainerWidget))), @ProgressDestroy);
3833       PGtkProgressBar(GetContainerWidget)^.pulse;
3834     end;
3835   end;
3836 end;
3837 
3838 {we must override preferred width since gtk3 have strange opinion about minimum width of progress bar}
3839 procedure get_progress_preferred_width(widget: PGtkWidget; minimum_width: Pgint; natural_width: Pgint); cdecl;
3840 var
3841   Handle: HWND;
3842 begin
3843   Handle := HwndFromGtkWidget(Widget);
3844   if Handle <> 0 then
3845   begin
3846     minimum_width^ := TGtk3Widget(Handle).LCLObject.Width;
3847     natural_width^ := TGtk3Widget(Handle).LCLObject.Width;
3848   end else
3849   begin
3850     minimum_width^ := 0;
3851     natural_width^ := 0;
3852     DebugLn('ERROR: get_progress_preferred_width cannot find GtkWidget LCL Handle ....');
3853   end;
3854 end;
3855 
3856 {we must override preferred height since gtk3 have strange opinion about height of progress bar}
3857 procedure get_progress_preferred_height(widget: PGtkWidget; minimum_height: Pgint; natural_height: Pgint); cdecl;
3858 var
3859   Handle: HWND;
3860 begin
3861   Handle := HwndFromGtkWidget(Widget);
3862   if Handle <> 0 then
3863   begin
3864     minimum_height^ := TGtk3Widget(Handle).LCLObject.Height;
3865     natural_height^ := TGtk3Widget(Handle).LCLObject.Height;
3866     // TODO: get spacing from style property
3867     // Widget^.get_style_context^.get_style_property();
3868   end else
3869   begin
3870     minimum_height^ := 0;
3871     natural_height^ := 0;
3872     DebugLn('ERROR: get_progress_preferred_height cannot find GtkWidget LCL Handle ....');
3873   end;
3874 end;
3875 
TGtk3ProgressBar.CreateWidgetnull3876 function TGtk3ProgressBar.CreateWidget(const Params: TCreateParams): PGtkWidget;
3877 var
3878   AProgres: TCustomProgressBar;
3879 begin
3880   AProgres := TCustomProgressBar(LCLObject);
3881   FWidgetType := FWidgetType + [wtProgressBar];
3882   Result := TGtkEventBox.new;
3883   FCentralWidget := TGtkProgressBar.new;
3884   PGtkEventBox(Result)^.add(FCentralWidget);
3885   FCentralWidget^.set_can_focus(True);
3886 end;
3887 
3888 var
3889   AProgressClassHookInitialized: Boolean = False;
3890 
3891 procedure TGtk3ProgressBar.InitializeWidget;
3892 var
3893   AClass: PGTypeClass;
3894 begin
3895   inherited InitializeWidget;
3896   //TODO: move hookers check variable code into Gtk3WidgetSet.
3897   if not AProgressClassHookInitialized then
3898   begin
3899     AProgressClassHookInitialized := True;
3900     AClass := g_type_class_ref(gtk_progress_bar_get_type);
3901     PGtkWidgetClass(AClass)^.get_preferred_width := @get_progress_preferred_width;
3902     PGtkWidgetClass(AClass)^.get_preferred_height := @get_progress_preferred_height;
3903     g_type_class_unref(AClass);
3904   end;
3905 end;
3906 
3907 { TGtk3Container }
3908 
3909 procedure TGtk3Container.AddChild(AWidget: PGtkWidget; const ALeft, ATop: Integer);
3910 begin
3911   if Assigned(FCentralWidget) then
3912     PGtkFixed(PGtkScrolledWindow(FCentralWidget)^.get_child)^.put(AWidget, ALeft, ATop)
3913   else
3914     PGtkContainer(FWidget)^.add(AWidget);
3915 end;
3916 
3917 { TGtk3ToolBar }
3918 
TGtk3ToolBar.CreateWidgetnull3919 function TGtk3ToolBar.CreateWidget(const Params: TCreateParams): PGtkWidget;
3920 var
3921   AToolBar: TToolBar;
3922 begin
3923   AToolBar := TToolBar(LCLObject);
3924   FHasPaint := False;
3925   FWidgetType := [wtWidget, wtContainer];
3926   Result:=PGtkWidget(TGtkToolbar.new);
3927 end;
3928 
3929 { TGtk3Page }
3930 
3931 procedure TGtk3Page.setText(AValue: String);
3932 begin
3933   if Assigned(FPageLabel) then
3934     FPageLabel^.set_text(PChar(AValue));
3935 end;
3936 
TGtk3Page.getTextnull3937 function TGtk3Page.getText: String;
3938 begin
3939   if Assigned(FPageLabel) then
3940     Result := FPageLabel^.get_text
3941   else
3942     Result := '';
3943 end;
3944 
CreateWidgetnull3945 function TGtk3Page.CreateWidget(const Params: TCreateParams): PGtkWidget;
3946 begin
3947   FWidgetType := FWidgetType + [wtContainer];
3948   FPageLabel:= TGtkLabel.new(PChar(Params.Caption));
3949   // ref it to save it in case TabVisble is set to false
3950   FPageLabel^.ref;
3951   Result := TGtkHBox.new(GTK_ORIENTATION_HORIZONTAL, 0);
3952   FCentralWidget := TGtkFixed.new;
3953   PGtkHBox(Result)^.pack_start(FCentralWidget, True , True, 0);
3954   PGtkFixed(FCentralWidget)^.set_has_window(True);
3955   // PGtkFixed(FCentralWidget)^.set_can_focus(True);
3956 end;
3957 
3958 procedure TGtk3Page.DestroyWidget;
3959 begin
3960   inherited DestroyWidget;
3961   // unref it to allow it to be destroyed
3962   FPageLabel^.unref;
3963 end;
3964 
getClientRectnull3965 function TGtk3Page.getClientRect: TRect;
3966 var
3967   AParent: PGtkWidget;
3968   AParentObject: TGtk3Widget;
3969 begin
3970   if not getContainerWidget^.get_realized then
3971   begin
3972     AParent := FWidget^.get_parent;
3973     AParentObject := TGtk3Widget(HwndFromGtkWidget(AParent));
3974     if AParentObject <> nil then
3975       Result := AParentObject.getClientRect
3976     else
3977       Result := inherited getClientRect;
3978   end else
3979     Result := inherited getClientRect;
3980   // DebugLn('TGtk3Page.GetClientRect Result=',dbgs(Result),' Realized ',dbgs(getContainerWidget^.get_realized));
3981 end;
3982 
3983 { TGtk3NoteBook }
3984 
NotebookPageRealToLCLIndexnull3985 function NotebookPageRealToLCLIndex(const ATabControl: TCustomTabControl; AIndex: integer): integer;
3986 var
3987   I: Integer;
3988 begin
3989   Result := AIndex;
3990   if csDesigning in ATabControl.ComponentState then exit;
3991   I := 0;
3992   while (I < ATabControl.PageCount) and (I <= Result) do
3993   begin
3994     if not ATabControl.Page[I].TabVisible then Inc(Result);
3995     Inc(I);
3996   end;
3997 end;
3998 
GtkNotebookAfterSwitchPagenull3999 // function GtkNotebookAfterSwitchPage(widget: PGtkWidget; pagenum: integer; data: gPointer): GBoolean; cdecl;
4000 procedure GtkNotebookAfterSwitchPage(widget: PGtkWidget; {%H-}page: PGtkWidget; pagenum: integer; data: gPointer); cdecl;
4001 var
4002   Mess: TLMNotify;
4003   NMHdr: tagNMHDR;
4004   LCLPageIndex: Integer;
4005 begin
4006   if TGtk3Widget(Data).InUpdate then
4007     exit;
4008   {page is deleted}
4009   DebugLn('GtkNotebookAfterSwitchPage ');
4010   if TGtk3NoteBook(Data).getPagesCount < TCustomTabControl(TGtk3NoteBook(Data).LCLObject).PageCount then
4011   begin
4012     DebugLn('GtkNotebookAfterSwitchPage PageIsDeleted');
4013     exit;
4014   end;
4015   FillChar(Mess{%H-}, SizeOf(Mess), 0);
4016   Mess.Msg := LM_NOTIFY;
4017   FillChar(NMHdr{%H-}, SizeOf(NMHdr), 0);
4018   NMHdr.code := TCN_SELCHANGE;
4019   NMHdr.hwndFrom := HWND(TGtk3Widget(Data));
4020   LCLPageIndex := NotebookPageRealToLCLIndex(TCustomTabControl(TGtk3Widget(Data).LCLObject), pagenum);  //use this to set pageindex to the correct page.
4021   NMHdr.idFrom := LCLPageIndex;
4022   Mess.NMHdr := @NMHdr;
4023   TGtk3Widget(Data).DeliverMessage(Mess);
4024 end;
4025 
BackNoteBookSignalnull4026 function BackNoteBookSignal(AData: Pointer): gboolean; cdecl;
4027 var
4028   AWidget: PGtkNotebook;
4029   APageNum: PtrInt;
4030   ACurrentPage: gint;
4031 begin
4032   Result := False;
4033   AWidget := AData;
4034   if not Gtk3IsWidget(AWidget) then
4035     exit;
4036   if g_object_get_data(AWidget,'switch-page-signal-stopped') <> nil then
4037   begin
4038     Result := True;
4039     APageNum := PtrInt(g_object_get_data(AWidget,'switch-page-signal-stopped'));
4040     ACurrentPage := AWidget^.get_current_page;
4041     g_object_set_data(AWidget,'switch-page-signal-stopped', nil);
4042     DebugLn('BackNoteBookSignal back notebook switch-page signal currpage=',dbgs(AWidget^.get_current_page),' blockedPage ',dbgs(APageNum));
4043     // must hook into notebook^.priv to unlock APageNum
4044     // AWidget^.set_current_page(AWidget^.get_current_page);
4045     // g_object_thaw_notify(AWidget^.get_nth_page(AWidget^.get_current_page));
4046     // PGtkFixed(AWidget^.get_nth_page(AWidget^.get_current_page))^.
4047     // g_signal_emit_by_name(AWidget,'switch-page',[AWidget^.get_nth_page(APageNum), APageNum, gPointer(HwndFromGtkWidget(AWidget)), nil{AWidget, True, gPointer(HwndFromGtkWidget(AWidget))}]);
4048     // AWidget^.notify('page');
4049     // g_signal_stop_emission_by_name(AWidget, 'switch-page');
4050     // g_signal_emit_by_name(AWidget,'switch-page',[G_TYPE_NONE, AWidget, AWidget^.get_nth_page(AWidget^.get_current_page), AWidget^.get_current_page, gPointer(HwndFromGtkWidget(AWidget))]);
4051   end;
4052   g_idle_remove_by_data(AData);
4053 end;
4054 
4055 procedure GtkNotebookSwitchPage(widget: PGtkWidget; {%H-}page: PGtkWidget; pagenum: integer; data: gPointer); cdecl;
4056 var
4057   Mess: TLMNotify;
4058   NMHdr: tagNMHDR;
4059 begin
4060   if TGtk3Widget(Data).InUpdate then
4061     exit;
4062 
4063   DebugLn('GtkNotebookSwitchPage Data ',dbgHex(PtrUInt(Data)),' Realized ',dbgs(Widget^.get_realized),' pageNum=',dbgs(pageNum));
4064 
4065   {page is deleted}
4066   if TGtk3NoteBook(Data).getPagesCount < TCustomTabControl(TGtk3NoteBook(Data).LCLObject).PageCount then
4067   begin
4068     DebugLn('GtkNotebookSwitchPage PageIsDeleted ');
4069     exit;
4070   end;
4071 
4072   FillChar(Mess{%H-}, SizeOf(Mess), 0);
4073   Mess.Msg := LM_NOTIFY;
4074   FillChar(NMHdr{%H-}, SizeOf(NMHdr), 0);
4075   NMHdr.code := TCN_SELCHANGING;
4076   NMHdr.hwndFrom := HWND(TGtk3Widget(Data));
4077   NMHdr.idFrom := NotebookPageRealToLCLIndex(TCustomTabControl(TGtk3Widget(Data).LCLObject), pagenum);  //use this to set pageindex to the correct page.
4078   // DebugLn('LCLObject ',dbgsName(TGtk3Widget(Data).LCLObject),' IdFrom ',dbgs(NMHdr.idFrom));
4079   Mess.NMHdr := @NMHdr;
4080   Mess.Result := 0;
4081   // DebugLn('LCLObject ',dbgsName(TGtk3Widget(Data).LCLObject),' sending message ....');
4082   TGtk3Widget(Data).DeliverMessage(Mess);
4083   if Mess.Result <> 0 then
4084   begin
4085     g_object_set_data(Widget,'switch-page-signal-stopped', GPointer(pageNum));
4086     g_signal_stop_emission_by_name(PGObject(Widget), 'switch-page');
4087     // GtkNotebookAfterSwitchPage(Widget, page, pagenum, data);
4088     g_idle_add(@BackNoteBookSignal, Widget);
4089     Exit;
4090   end;
4091 end;
4092 
GtkNotebookSelectPagenull4093 function GtkNotebookSelectPage(ANoteBook: PGtkNotebook; p1: gboolean; Data: gPointer): GBoolean; cdecl;
4094 begin
4095   // does not trigger for some reason
4096   DebugLn('GtkNotebookSelectPage ');
4097 end;
4098 
CreateWidgetnull4099 function TGtk3NoteBook.CreateWidget(const Params: TCreateParams): PGtkWidget;
4100 begin
4101   FWidgetType := FWidgetType + [wtNotebook];
4102   Result := TGtkEventBox.new;
4103   PGtkEventBox(Result)^.set_has_window(True);
4104   FCentralWidget := TGtkNotebook.new;
4105   PGtkEventBox(Result)^.add(FCentralWidget);
4106   PGtkNoteBook(FCentralWidget)^.set_scrollable(True);
4107   if (nboHidePageListPopup in TCustomTabControl(LCLObject).Options) then
4108     PGtkNoteBook(FCentralWidget)^.popup_disable;
4109   PGtkNoteBook(FCentralWidget)^.show;
4110 
4111   g_signal_connect_data(FCentralWidget,'switch-page', TGCallback(@GtkNotebookSwitchPage), Self, nil, 0);
4112   // this one triggers after above switch-page
4113   g_signal_connect_data(FCentralWidget,'switch-page', TGCallback(@GtkNotebookAfterSwitchPage), Self, nil, 0);
4114 
4115   // those signals doesn't trigger with gtk3-3.6
4116   // g_signal_connect_data(FCentralWidget,'change-current-page', TGCallback(@GtkNotebookAfterSwitchPage), Self, nil, 0);
4117   // g_signal_connect_data(FCentralWidget,'select-page', TGCallback(@GtkNotebookSelectPage), Self, nil, 0);
4118 end;
4119 
getClientRectnull4120 function TGtk3NoteBook.getClientRect: TRect;
4121 var
4122   AAlloc: TGtkAllocation;
4123   ACurrentPage: gint;
4124   APage: PGtkWidget;
4125 begin
4126   Result := Rect(0, 0, 0, 0);
4127   if PGtkNoteBook(GetContainerWidget)^.get_n_pages = 0 then
4128   begin
4129     GetContainerWidget^.get_allocation(@AAlloc);
4130     Result := RectFromGtkAllocation(AAlloc);
4131     OffsetRect(Result, -Result.Left, -Result.Top);
4132   end else
4133   begin
4134     ACurrentPage := PGtkNoteBook(GetContainerWidget)^.get_current_page;
4135     if (ACurrentPage >= 0) then
4136     begin
4137       APage := PGtkNoteBook(GetContainerWidget)^.get_nth_page(ACurrentPage);
4138       if APage^.get_realized then
4139         APage^.get_allocation(@AAlloc)
4140       else
4141         GetContainerWidget^.get_allocation(@AAlloc);
4142       Result := RectFromGtkAllocation(AAlloc);
4143       OffsetRect(Result, -Result.Left, -Result.Top);
4144     end;
4145   end;
4146   // DebugLn('TGtk3NoteBook.getClientRect Result ',dbgs(Result));
4147 end;
4148 
getPagesCountnull4149 function TGtk3NoteBook.getPagesCount: integer;
4150 begin
4151   Result := 0;
4152   if IsWidgetOk then
4153     Result := PGtkNoteBook(GetContainerWidget)^.get_n_pages;
4154 end;
4155 
4156 procedure EnumerateChildren(ANotebook: PGtkNoteBook);
4157 var
4158   AList: PGList;
4159   i: Integer;
4160   AWidget: PGtkWidget;
4161   AMinimumH, ANaturalH, ANaturalW, AMinimumW: gint;
4162   AChild: PGtkWidget;
4163 begin
4164   AList := ANoteBook^.get_children;
4165   for i := 0 to g_list_length(AList) - 1 do
4166   begin
4167     AWidget := PGtkWidget(g_list_nth_data(AList, I));
4168     AWidget^.get_preferred_height(@AMinimumH, @ANaturalH);
4169     AWidget^.get_preferred_width(@AMinimumW, @ANaturalW);
4170     AChild := nil;
4171     DebugLn(Format('Child[%d] MinH %d NatH %d MinW %d NatW %d ALLOCW %d ALLOCH %d child_type %s',
4172       [I, AMinimumH, ANaturalH, AMinimumW, ANaturalW,
4173       AWidget^.get_allocated_width, AWidget^.get_allocated_height, g_type_name(ANotebook^.child_type)]));
4174   end;
4175   g_list_free(AList);
4176 end;
4177 
4178 procedure TGtk3NoteBook.InsertPage(ACustomPage: TCustomPage; AIndex: Integer);
4179 var
4180   Gtk3Page: TGtk3Page;
4181   AMinSize, ANaturalSize: gint;
4182 begin
4183   if IsWidgetOK then
4184   begin
4185     Gtk3Page := TGtk3Page(ACustomPage.Handle);
4186     PGtkNoteBook(GetContainerWidget)^.insert_page(Gtk3Page.Widget, Gtk3Page.FPageLabel, AIndex);
4187     PGtkNoteBook(GetContainerWidget)^.get_preferred_width(@AMinSize, @ANaturalSize);
4188     PGtkNoteBook(GetContainerWidget)^.get_preferred_height(@AMinSize, @ANaturalSize);
4189     if gtk_notebook_get_n_pages(PGtkNoteBook(GetContainerWidget)) > 1 then
4190       PGtkNoteBook(GetContainerWidget)^.resize_children;
4191   end;
4192 end;
4193 
4194 procedure TGtk3NoteBook.MovePage(ACustomPage: TCustomPage; ANewIndex: Integer);
4195 begin
4196   if IsWidgetOK then
4197     PGtkNoteBook(GetContainerWidget)^.reorder_child(TGtk3Widget(ACustomPage.Handle).Widget, ANewIndex);
4198 end;
4199 
4200 procedure TGtk3NoteBook.RemovePage(AIndex: Integer);
4201 var
4202   AMinSizeW, AMinSizeH, ANaturalSizeW, ANaturalSizeH: gint;
4203 begin
4204   if IsWidgetOK then
4205   begin
4206     PGtkNotebook(GetContainerWidget)^.remove_page(AIndex);
4207     PGtkNoteBook(GetContainerWidget)^.get_preferred_width(@AMinSizeW, @ANaturalSizeW);
4208     PGtkNoteBook(GetContainerWidget)^.get_preferred_height(@AMinSizeH, @ANaturalSizeH);
4209     PGtkNoteBook(GetContainerWidget)^.resize_children;
4210   end;
4211 end;
4212 
4213 procedure TGtk3NoteBook.SetPageIndex(AIndex: Integer);
4214 begin
4215   if IsWidgetOK then
4216   begin
4217     PGtkNotebook(GetContainerWidget)^.set_current_page(AIndex);
4218   end;
4219 end;
4220 
4221 procedure TGtk3NoteBook.SetShowTabs(const AShowTabs: Boolean);
4222 begin
4223   if IsWidgetOK then
4224     PGtkNoteBook(GetContainerWidget)^.set_show_tabs(AShowTabs);
4225 end;
4226 
4227 procedure TGtk3NoteBook.SetTabPosition(const ATabPosition: TTabPosition);
4228 const
4229   GtkPositionTypeMap: array[TTabPosition] of TGtkPositionType =
4230   (
4231     2, // { tpTop    } GTK_POS_TOP,
4232     3, // { tpBottom } GTK_POS_BOTTOM,
4233     0, // { tpLeft   } GTK_POS_LEFT,
4234     1 // { tpRight  } GTK_POS_RIGHT
4235   );
4236 begin
4237   if IsWidgetOK then
4238     PGtkNoteBook(GetContainerWidget)^.set_tab_pos(GtkPositionTypeMap[ATabPosition]);
4239 end;
4240 
4241 procedure TGtk3NoteBook.SetTabLabelText(AChild: TCustomPage; AText: String);
4242 begin
4243   if IsWidgetOK then
4244     TGtk3Widget(AChild.Handle).setText(AText);
4245 end;
4246 
GetTabLabelTextnull4247 function TGtk3NoteBook.GetTabLabelText(AChild: TCustomPage): String;
4248 begin
4249   if IsWidgetOK then
4250     Result := TGtk3Widget(AChild.Handle).getText
4251   else
4252     Result := '';
4253 end;
4254 
4255 { TGtk3MenuShell }
4256 
4257 procedure TGtk3MenuShell.Insert(AMenuShell: PGtkMenuShell; APosition: Integer);
4258 begin
4259   if IsWidgetOK then
4260     PGtkMenuShell(Widget)^.insert(AMenuShell, APosition);
4261 end;
4262 
4263 constructor TGtk3MenuShell.Create(const AMenu: TMenu; AMenuBar: PGtkMenuBar);
4264 begin
4265   inherited Create;
4266   MenuObject := AMenu;
4267   FContext := 0;
4268   FHasPaint := False;
4269   FWidget := nil;
4270   FOwner := nil;
4271   FCentralWidget := nil;
4272   if AMenuBar <> nil then
4273   begin
4274     FOwnWidget := False;
4275     FWidget := AMenuBar;
4276   end else
4277     FOwnWidget := True;
4278   // Initializes the properties
4279   FProps := nil;
4280   LCLObject := nil;
4281   // FKeysToEat := [VK_TAB, VK_RETURN, VK_ESCAPE];
4282   // FHasPaint := False;
4283 
4284   // FParams := AParams;
4285   InitializeWidget;
4286 end;
4287 
4288 procedure TGtk3MenuShell.InitializeWidget;
4289 begin
4290   FCentralWidget := nil;
4291   FCairoContext := nil;
4292   FContext := 0;
4293   FEnterLeaveTime := 0;
4294   if FOwnWidget then
4295     FWidget := CreateWidget(FParams);
4296 
4297   LCLIntf.SetProp(HWND(Self),'lclwidget',Self);
4298 end;
4299 
4300 
4301 { TGtk3MenuBar }
4302 
CreateWidgetnull4303 function TGtk3MenuBar.CreateWidget(const Params: TCreateParams): PGtkWidget;
4304 begin
4305   FWidgetType := [wtWidget, wtMenuBar];
4306   Result := TGtkMenuBar.new;
4307   PGtkMenuBar(Result)^.set_pack_direction(MenuDirection[TMenu(MenuObject).UseRightToLeftAlignment]);
4308 end;
4309 
4310 { TGtk3Menu }
4311 
CreateWidgetnull4312 function TGtk3Menu.CreateWidget(const Params: TCreateParams): PGtkWidget;
4313 begin
4314   FWidgetType := [wtWidget, wtMenu];
4315   Result := TGtkMenu.new;
4316 end;
4317 
4318 constructor TGtk3Menu.CreateFromMenuItem(const AMenuItem: TMenuItem);
4319 begin
4320   inherited Create(AMenuItem.GetParentMenu, nil);
4321 end;
4322 
4323 { TGtk3MenuItem }
4324 
GetCaptionnull4325 function TGtk3MenuItem.GetCaption: string;
4326 begin
4327   Result := '';
4328   if IsWidgetOK then
4329     Result := PGtkMenuItem(FWidget)^.get_label;
4330 end;
4331 
4332 procedure TGtk3MenuItem.SetCaption(AValue: string);
4333 begin
4334   if IsWidgetOK then
4335     PGtkMenuItem(FWidget)^.set_label(PgChar(AValue));
4336 end;
4337 
CreateWidgetnull4338 function TGtk3MenuItem.CreateWidget(const Params: TCreateParams): PGtkWidget;
4339 var
4340   ndx:integer;
4341   pmenu:TMenuItem;
4342   pl:PGsList;
4343 begin
4344   FWidgetType := [wtWidget, wtMenuItem];
4345   if MenuItem.Caption = cLineCaption then
4346     Result := TGtkSeparatorMenuItem.new
4347   else
4348   if MenuItem.RadioItem and not MenuItem.HasIcon then
4349   begin
4350     Result := TGtkRadioMenuItem.new(nil);
4351     if Assigned(menuItem.Parent) then
4352     begin
4353       ndx:=menuItem.Parent.IndexOf(MenuItem);
4354       if (ndx>0) then
4355       begin
4356         pMenu:=menuItem.Parent.Items[ndx-1];
4357         if (MenuItem.GroupIndex>0) and (pMenu.GroupIndex=MenuItem.GroupIndex) then
4358         begin
4359           pl:=PGtkRadioMenuItem(TGtk3MenuItem(pMenu.Handle).Widget)^.get_group;
4360           PGtkRadioMenuItem(Result)^.set_group(pl);
4361         end;
4362       end;
4363     end;
4364   end
4365   else
4366   if MenuItem.IsCheckItem or MenuItem.HasIcon then
4367     Result := TGtkCheckMenuItem.new
4368   else
4369     Result := TGtkMenuItem.new;
4370 
4371   if MenuItem.Caption <> cLineCaption then
4372   begin
4373     PGtkMenuItem(Result)^.set_label(PgChar(MenuItem.Caption));
4374     PGtkMenuItem(Result)^.set_sensitive(MenuItem.Enabled);
4375     // there's nothing like this in Gtk3
4376     // if MenuItem.RightJustify then
4377     //  gtk_menu_item_right_justify(PGtkMenuItem(Widget));
4378 
4379   end;
4380 end;
4381 
4382 constructor TGtk3MenuItem.Create(const AMenuItem: TMenuItem);
4383 begin
4384   inherited Create;
4385   MenuItem := AMenuItem;
4386   FContext := 0;
4387   FHasPaint := False;
4388   FWidget := nil;
4389   FOwner := nil;
4390   FCentralWidget := nil;
4391   FOwnWidget := True;
4392   // Initializes the properties
4393   FProps := nil;
4394   LCLObject := nil;
4395   // FKeysToEat := [VK_TAB, VK_RETURN, VK_ESCAPE];
4396   // FHasPaint := False;
4397 
4398   // FParams := AParams;
4399   InitializeWidget;
4400 end;
4401 
4402 procedure Gtk3MenuItemActivated(AItem: PGtkMenuItem; AData: GPointer); cdecl;
4403 var
4404   Msg: TLMessage;
4405 begin
4406   // DebugLn('Gtk3MenuItemActivated ',dbgsName(TGtk3MenuItem(Adata)));
4407   FillChar(Msg, SizeOf(Msg), #0);
4408   Msg.Msg := LM_ACTIVATE;
4409   if Assigned(TGtk3MenuItem(AData).MenuItem) then
4410     TGtk3MenuItem(AData).MenuItem.Dispatch(Msg);
4411 end;
4412 
4413 procedure TGtk3MenuItem.InitializeWidget;
4414 begin
4415   FCentralWidget := nil;
4416   FCairoContext := nil;
4417   FContext := 0;
4418   FEnterLeaveTime := 0;
4419 
4420   FWidget := CreateWidget(FParams);
4421   LCLIntf.SetProp(HWND(Self),'lclwidget',Self);
4422 
4423   // move signal connections into attach events
4424   FWidget^.set_events(GDK_ALL_EVENTS_MASK);
4425   g_signal_connect_data(FWidget, 'event', TGCallback(@Gtk3MenuItemEvent), Self, nil, 0);
4426   g_signal_connect_data(FWidget,'activate',TGCallBack(@Gtk3MenuItemActivated), Self, nil, 0);
4427   // must hide all by default
4428   // FWidget^.hide;
4429 end;
4430 
4431 
4432 { TGtk3ScrollableWin}
4433 
TGtk3ScrollableWin.GetHScrollBarPolicynull4434 function TGtk3ScrollableWin.GetHScrollBarPolicy: TGtkPolicyType;
4435 var
4436   AScrollWin: PGtkScrolledWindow;
4437   APolicy: TGtkPolicyType;
4438 begin
4439   Result := GTK_POLICY_AUTOMATIC;
4440   AScrollWin := getScrolledWindow;
4441   if not Gtk3IsScrolledWindow(AScrollWin) then
4442     exit;
4443   AScrollWin^.get_policy(@Result, @APolicy);
4444 end;
4445 
TGtk3ScrollableWin.GetVScrollBarPolicynull4446 function TGtk3ScrollableWin.GetVScrollBarPolicy: TGtkPolicyType;
4447 var
4448   AScrollWin: PGtkScrolledWindow;
4449   APolicy: TGtkPolicyType;
4450 begin
4451   Result := GTK_POLICY_AUTOMATIC;
4452   AScrollWin := getScrolledWindow;
4453   if not Gtk3IsScrolledWindow(AScrollWin) then
4454     exit;
4455   AScrollWin^.get_policy(@APolicy, @Result);
4456 end;
4457 
4458 procedure TGtk3ScrollableWin.SetBorderStyle(AValue: TBorderStyle);
4459 begin
4460   if FBorderStyle=AValue then Exit;
4461   FBorderStyle:=AValue;
4462   if IsWidgetOK then
4463   begin
4464     if AValue = bsNone then
4465       getScrolledWindow^.set_shadow_type(GTK_SHADOW_NONE)
4466     else
4467       getScrolledWindow^.set_shadow_type(GTK_SHADOW_ETCHED_IN);
4468   end;
4469 end;
4470 
4471 procedure TGtk3ScrollableWin.SetHScrollBarPolicy(AValue: TGtkPolicyType);
4472 var
4473   AScrollWin: PGtkScrolledWindow;
4474   APolicyH, APolicyV: TGtkPolicyType;
4475 begin
4476   AScrollWin := getScrolledWindow;
4477   if not Gtk3IsScrolledWindow(AScrollWin) then
4478     exit;
4479   AScrollWin^.get_policy(@APolicyH, @APolicyV);
4480   AScrollWin^.set_policy(AValue, APolicyV);
4481 end;
4482 
4483 procedure TGtk3ScrollableWin.SetVScrollBarPolicy(AValue: TGtkPolicyType);
4484 var
4485   AScrollWin: PGtkScrolledWindow;
4486   APolicyH, APolicyV: TGtkPolicyType;
4487 begin
4488   AScrollWin := getScrolledWindow;
4489   if not Gtk3IsScrolledWindow(AScrollWin) then
4490     exit;
4491   AScrollWin^.get_policy(@APolicyH, @APolicyV);
4492   AScrollWin^.set_policy(APolicyH, AValue);
4493 end;
4494 
Gtk3RangeScrollCBnull4495 function Gtk3RangeScrollCB(ARange: PGtkRange; AScrollType: TGtkScrollType;
4496   AValue: gdouble; AData: TGtk3Widget): gboolean; cdecl;
4497 var
4498   Msg: TLMVScroll;
4499   MaxValue: gdouble;
4500   Widget: PGtkWidget;
4501 begin
4502   Result := False;
4503 
4504   Widget := PGTKWidget(ARange);
4505   {$IFDEF SYNSCROLLDEBUG}
4506   DebugLn(Format('Trace:[Gtk3RangeScrollCB] Value: %d', [RoundToInt(AValue)]),' IsHScrollBar ',dbgs(PGtkOrientable(ARange)^.get_orientation = GTK_ORIENTATION_HORIZONTAL));
4507   {$ENDIF}
4508   if PGtkOrientable(ARange)^.get_orientation = GTK_ORIENTATION_HORIZONTAL then
4509     Msg.Msg := LM_HSCROLL
4510   else
4511     Msg.Msg := LM_VSCROLL;
4512 
4513   if ARange^.adjustment^.page_size > 0 then
4514     MaxValue := ARange^.adjustment^.upper - ARange^.adjustment^.page_size
4515   else
4516     MaxValue := ARange^.adjustment^.upper;
4517 
4518   if (AValue > MaxValue) then
4519     AValue := MaxValue
4520   else if (AValue < ARange^.adjustment^.lower) then
4521     AValue := ARange^.adjustment^.lower;
4522 
4523   with Msg do
4524   begin
4525     Pos := Round(AValue);
4526     if Pos < High(SmallPos) then
4527       SmallPos := Pos
4528     else
4529       SmallPos := High(SmallPos);
4530     {$note to get this correct we must use TQtWidget.CreateFrom() for scrollbars}
4531     ScrollBar := HWND(AData); // HWND({%H-}PtrUInt(ARange));
4532     ScrollCode := Gtk3ScrollTypeToScrollCode(AScrollType);
4533   end;
4534   DeliverMessage(AData.LCLObject, Msg);
4535 
4536   if Msg.Scrollcode = SB_THUMBTRACK then
4537   begin
4538     if Widget^.get_state_flags = GTK_STATE_NORMAL then
4539     begin
4540       Msg.ScrollCode := SB_THUMBPOSITION;
4541       DeliverMessage(AData.LCLObject, Msg);
4542       Msg.ScrollCode := SB_ENDSCROLL;
4543       DeliverMessage(AData.LCLObject, Msg);
4544     end;
4545   end else
4546     Widget^.set_state_flags(GTK_STATE_FLAG_ACTIVE, True);
4547 
4548   if (AData.LCLObject is TScrollingWinControl) and
4549   ((Msg.ScrollCode=SB_LINEUP) or (Msg.ScrollCode=SB_LINEDOWN)) then
4550     Result:=True;
4551 end;
4552 
4553 procedure TGtk3ScrollableWin.SetScrollBarsSignalHandlers;
4554 begin
4555   {TODO: create real instances for scrollbars via TGtk3Widget.CreateFrom() ?}
4556   FBorderStyle := bsNone;
4557   g_signal_connect_data(getHorizontalScrollbar, 'change-value',
4558     TGCallback(@Gtk3RangeScrollCB), Self, nil, 0);
4559   g_signal_connect_data(getVerticalScrollbar, 'change-value',
4560     TGCallback(@Gtk3RangeScrollCB), Self, nil, 0);
4561 end;
4562 
getClientBoundsnull4563 function TGtk3ScrollableWin.getClientBounds: TRect;
4564 var
4565   Allocation: TGtkAllocation;
4566 begin
4567   Result := Rect(0, 0, 0, 0);
4568   if IsWidgetOK then
4569   begin
4570     getContainerWidget^.get_allocation(@Allocation);
4571     Result := RectFromGtkAllocation(Allocation);
4572   end;
4573   // DebugLn('TGtk3ScrollableWin.getClientBounds result ',dbgs(Result));
4574 end;
4575 
4576 { TGtk3Memo }
4577 
CreateWidgetnull4578 function TGtk3Memo.CreateWidget(const Params: TCreateParams): PGtkWidget;
4579 var
4580   AMemo: TCustomMemo;
4581   ABuffer: PGtkTextBuffer;
4582   AScrollStyle: TPoint;
4583 begin
4584   FScrollX := 0;
4585   FScrollY := 0;
4586 
4587   FKeysToEat := [];
4588   AMemo := TCustomMemo(LCLObject);
4589 
4590   FWidgetType := FWidgetType + [wtMemo, wtScrollingWin];
4591   Result := PGtkScrolledWindow(TGtkScrolledWindow.new(nil, nil));
4592 
4593   FCentralWidget := PGtkTextView(TGtkTextView.new);
4594 
4595   FCentralWidget^.set_has_window(True);
4596 
4597   if AMemo.WordWrap then
4598     PGtkTextView(FCentralWidget)^.set_wrap_mode(GTK_WRAP_WORD)
4599   else
4600     PGtkTextView(FCentralWidget)^.set_wrap_mode(GTK_WRAP_NONE);
4601 
4602   ABuffer := PGtkTextBuffer^.new(PGtkTextTagTable^.new);
4603   ABuffer^.set_text(PgChar(AMemo.Text), -1);
4604   PGtkTextView(FCentralWidget)^.set_buffer(ABuffer);
4605 
4606   PGtkScrolledWindow(Result)^.add(FCentralWidget);
4607 
4608   // PGtkScrolledWindow(Result)^.set_focus_child(FCentralWidget);
4609 
4610   AScrollStyle := Gtk3TranslateScrollStyle(AMemo.ScrollBars);
4611 
4612   // Gtk3 GtkTextView is weird. When scrollbars policy is GTK_POLICY_NONE
4613   // then GtkTextView resizes itself (resizes parent) while adding text,
4614   // so our TMemo size grows.
4615   // http://stackoverflow.com/questions/2695843/gtktextview-automatically-resizing/16881764#16881764
4616   // http://stackoverflow.com/questions/15534475/how-can-i-create-a-fixed-size-gtk-textview-in-gtk3
4617   // https://bugzilla.gnome.org/show_bug.cgi?id=690099
4618   // seem to be fixed in 3.8.2
4619 
4620 
4621   if (gtk_get_major_version = 3) and (gtk_get_minor_version <= 8) then
4622   begin
4623     if AScrollStyle.X = GTK_POLICY_NEVER then
4624       AScrollStyle.X := GTK_POLICY_AUTOMATIC;
4625     if AScrollStyle.Y = GTK_POLICY_NEVER then
4626       AScrollStyle.Y := GTK_POLICY_AUTOMATIC;
4627   end;
4628 
4629 
4630   PGtkScrolledWindow(Result)^.set_policy(AScrollStyle.X, AScrollStyle.Y);
4631 
4632   PGtkScrolledWindow(Result)^.set_shadow_type(BorderStyleShadowMap[AMemo.BorderStyle]);
4633   PGtkScrolledWindow(Result)^.get_vscrollbar^.set_can_focus(False);
4634   PGtkScrolledWindow(Result)^.get_hscrollbar^.set_can_focus(False);
4635 
4636   FCentralWidget^.set_can_focus(True);
4637   PGtkScrolledWindow(Result)^.set_can_focus(False);
4638 end;
4639 
EatArrowKeysnull4640 function TGtk3Memo.EatArrowKeys(const AKey: Word): Boolean;
4641 begin
4642   Result := False;
4643 end;
4644 
TGtk3Memo.getHorizontalScrollbarnull4645 function TGtk3Memo.getHorizontalScrollbar: PGtkScrollbar;
4646 begin
4647   Result := nil;
4648   if not IsWidgetOk then
4649     exit;
4650   Result := PGtkScrollBar(PGtkScrolledWindow(Widget)^.get_hscrollbar);
4651 end;
4652 
getVerticalScrollbarnull4653 function TGtk3Memo.getVerticalScrollbar: PGtkScrollbar;
4654 begin
4655   Result := nil;
4656   if not IsWidgetOk then
4657     exit;
4658   Result := PGtkScrollBar(PGtkScrolledWindow(Widget)^.get_vscrollbar);
4659 end;
4660 
TGtk3Memo.GetScrolledWindownull4661 function TGtk3Memo.GetScrolledWindow: PGtkScrolledWindow;
4662 begin
4663   if IsWidgetOK then
4664     Result := PGtkScrolledWindow(Widget)
4665   else
4666     Result := nil;
4667 end;
4668 
GetAlignmentnull4669 function TGtk3Memo.GetAlignment: TAlignment;
4670 var
4671   AJustification: TGtkJustification;
4672 begin
4673   Result := taLeftJustify;
4674   if IsWidgetOk then
4675   begin
4676     AJustification := PGtkTextView(GetContainerWidget)^.get_justification;
4677     if AJustification = GTK_JUSTIFY_RIGHT then
4678       Result := taRightJustify
4679     else
4680     if AJustification = GTK_JUSTIFY_CENTER then
4681       Result := taCenter;
4682   end;
4683 end;
4684 
TGtk3Memo.GetReadOnlynull4685 function TGtk3Memo.GetReadOnly: Boolean;
4686 begin
4687   Result := True;
4688   if IsWidgetOk then
4689     Result := not PGtkTextView(GetContainerWidget)^.get_editable;
4690 end;
4691 
TGtk3Memo.GetWantTabsnull4692 function TGtk3Memo.GetWantTabs: Boolean;
4693 begin
4694   Result := False;
4695   if IsWidgetOK then
4696     Result := PGtkTextView(GetContainerWidget)^.get_accepts_tab;
4697 end;
4698 
TGtk3Memo.GetWordWrapnull4699 function TGtk3Memo.GetWordWrap: Boolean;
4700 begin
4701   Result := True;
4702   if IsWidgetOk then
4703     Result := PGtkTextView(GetContainerWidget)^.get_wrap_mode = GTK_WRAP_WORD;
4704 end;
4705 
4706 procedure TGtk3Memo.SetAlignment(AValue: TAlignment);
4707 begin
4708   if IsWidgetOk then
4709     PGtkTextView(GetContainerWidget)^.set_justification(AGtkJustification[AValue]);
4710 end;
4711 
4712 procedure TGtk3Memo.SetReadOnly(AValue: Boolean);
4713 begin
4714   if IsWidgetOk then
4715     PGtkTextView(GetContainerWidget)^.set_editable(not AValue);
4716 end;
4717 
4718 procedure TGtk3Memo.SetWantTabs(AValue: Boolean);
4719 begin
4720   if IsWidgetOK then
4721     PGtkTextView(GetContainerWidget)^.set_accepts_tab(AValue);
4722 end;
4723 
4724 procedure TGtk3Memo.SetWordWrap(AValue: Boolean);
4725 begin
4726   if IsWidgetOk then
4727   begin
4728     if AValue then
4729       PGtkTextView(GetContainerWidget)^.set_wrap_mode(GTK_WRAP_WORD)
4730     else
4731       PGtkTextView(GetContainerWidget)^.set_wrap_mode(GTK_WRAP_NONE);
4732   end;
4733 end;
4734 
getTextnull4735 function TGtk3Memo.getText: String;
4736 var
4737   ABuffer: PGtkTextBuffer;
4738   AIter: TGtkTextIter;
4739   ALastIter: TGtkTextIter;
4740 begin
4741   Result := '';
4742   if IsWidgetOk then
4743   begin
4744     ABuffer := PGtkTextView(FCentralWidget)^.get_buffer;
4745     ABuffer^.get_start_iter(@AIter);
4746     ABuffer^.get_end_iter(@ALastIter);
4747     Result := ABuffer^.get_text(@AIter, @ALastIter, False);
4748   end;
4749   // DebugLn('TGtk3Memo.getText Result=',Result);
4750 end;
4751 
4752 procedure TGtk3Memo.setText(AValue: String);
4753 var
4754   ABuffer: PGtkTextBuffer;
4755   AIter: PGtkTextIter;
4756 begin
4757   // DebugLn('TGtk3Memo.setText AValue=',AValue);
4758   if IsWidgetOk then
4759   begin
4760     ABuffer := PGtkTextView(FCentralWidget)^.get_buffer;
4761     ABuffer^.set_text(PgChar(AValue), -1);
4762     ABuffer^.get_start_iter(AIter);
4763     ABuffer^.place_cursor(AIter);
4764   end;
4765 end;
4766 
4767 { TGtk3ListBox }
4768 
4769 procedure Gtk3ListBoxSelectionChanged(ASelection: PGtkTreeSelection; AData: GPointer); cdecl;
4770 var
4771   Msg: TLMessage;
4772 begin
4773   // DebugLn('Gtk3ListBoxSelectionChanged ');
4774   FillChar(Msg, SizeOf(Msg), #0);
4775   Msg.Msg := LM_SELCHANGE;
4776   if not TGtk3Widget(AData).InUpdate then
4777     TGtk3Widget(AData).DeliverMessage(Msg, False);
4778 end;
4779 
CreateWidgetnull4780 function TGtk3ListBox.CreateWidget(const Params: TCreateParams): PGtkWidget;
4781 var
4782   AListBox: TCustomListBox;
4783   ListStore: PGtkListStore;
4784   ItemList: TGtkListStoreStringList;
4785   AColumn: PGtkTreeViewColumn;
4786   Renderer : PGtkCellRenderer;
4787 begin
4788   FScrollX := 0;
4789   FScrollY := 0;
4790   FListBoxStyle := lbStandard;
4791 
4792   FWidgetType := FWidgetType + [wtTreeModel, wtListBox, wtScrollingWin];
4793   AListBox := TCustomListBox(LCLObject);
4794 
4795 
4796   Result := PGtkScrolledWindow(TGtkScrolledWindow.new(nil, nil));
4797   Result^.show;
4798 
4799   ListStore := gtk_list_store_new (2, [G_TYPE_STRING, G_TYPE_POINTER, nil]);
4800   FCentralWidget := TGtkTreeView.new_with_model(PGtkTreeModel(ListStore));
4801   PGtkTreeView(FCentralWidget)^.set_headers_visible(False);
4802   g_object_unref (liststore);
4803 
4804   ItemList := TGtkListStoreStringList.Create(PGtkListStore(PGtkTreeView(FCentralWidget)^.get_model), 0, LCLObject);
4805   g_object_set_data(PGObject(FCentralWidget),GtkListItemLCLListTag, ItemList);
4806 
4807   Renderer := LCLIntfCellRenderer_New();
4808 
4809   g_object_set_data(PGObject(renderer), 'lclwidget', Self);
4810 
4811   AColumn := gtk_tree_view_column_new_with_attributes ('LISTITEMS', renderer,
4812              ['text', 0, nil]);
4813 
4814   g_object_set_data(PGObject(AColumn), 'lclwidget', Self);
4815 
4816   // maybe create GtkCellLayout class with our implementation and set that layout
4817   // to AColumn
4818   // PGtkCellLayout(AColumn)^.set_cell_data_func()
4819   // PGtkCellLayout(AColumn)^.set_cell_data_func(renderer, @LCLIntfRenderer_GtkCellLayoutDataFunc, Self, nil);
4820   AColumn^.set_cell_data_func(renderer, @LCLIntfRenderer_ColumnCellDataFunc, Self, nil);
4821 
4822   PGtkTreeView(FCentralWidget)^.append_column(AColumn);
4823 
4824   AColumn^.set_clickable(True);
4825 
4826   // AColumn^set_cell_data_func(AColumn, renderer, @LCLIntfRenderer_ColumnCellDataFunc, Self, nil);
4827 
4828   PGtkScrolledWindow(Result)^.add(FCentralWidget);
4829 
4830   PGtkScrolledWindow(Result)^.get_vscrollbar^.set_can_focus(False);
4831   PGtkScrolledWindow(Result)^.get_hscrollbar^.set_can_focus(False);
4832   PGtkScrolledWindow(Result)^.set_policy(GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
4833   FListBoxStyle := AListBox.Style;
4834   if FListBoxStyle <> lbOwnerDrawVariable then
4835   begin
4836     AColumn^.set_sizing(GTK_TREE_VIEW_COLUMN_FIXED);
4837     PGtkTreeView(FCentralWidget)^.set_fixed_height_mode(True);
4838   end;
4839 
4840 end;
4841 
EatArrowKeysnull4842 function TGtk3ListBox.EatArrowKeys(const AKey: Word): Boolean;
4843 begin
4844   Result := False;
4845 end;
4846 
4847 procedure TGtk3ListBox.InitializeWidget;
4848 begin
4849   inherited InitializeWidget;
4850   g_signal_connect_data(GetSelection, 'changed', TGCallback(@Gtk3ListBoxSelectionChanged), Self, nil, 0);
4851 end;
4852 
TGtk3ListBox.getHorizontalScrollbarnull4853 function TGtk3ListBox.getHorizontalScrollbar: PGtkScrollbar;
4854 begin
4855   Result := nil;
4856   if not IsWidgetOk then
4857     exit;
4858   Result := PGtkScrollBar(PGtkScrolledWindow(Widget)^.get_hscrollbar);
4859 end;
4860 
TGtk3ListBox.getVerticalScrollbarnull4861 function TGtk3ListBox.getVerticalScrollbar: PGtkScrollbar;
4862 begin
4863   Result := nil;
4864   if not IsWidgetOk then
4865     exit;
4866   Result := PGtkScrollBar(PGtkScrolledWindow(Widget)^.get_vscrollbar);
4867 end;
4868 
TGtk3ListBox.GetScrolledWindownull4869 function TGtk3ListBox.GetScrolledWindow: PGtkScrolledWindow;
4870 begin
4871   if IsWidgetOK then
4872     Result := PGtkScrolledWindow(Widget)
4873   else
4874     Result := nil;
4875 end;
4876 
GetItemIndexnull4877 function TGtk3ListBox.GetItemIndex: Integer;
4878 var
4879   TreeView: PGtkTreeView;
4880   Path: PGtkTreePath;
4881   Column: PGtkTreeViewColumn;
4882   Selection: PGtkTreeSelection;
4883 begin
4884   Result := -1;
4885   if Gtk3IsWidget(FWidget) then
4886   begin
4887     Path := nil;
4888     Column := nil;
4889     TreeView := PGtkTreeView(GetContainerWidget);
4890     TreeView^.get_cursor(@Path, @Column);
4891     if Path <> nil then
4892     begin
4893       Result := gtk_tree_path_get_indices(Path)^;
4894       if Result = 0 then
4895       begin
4896         Selection :=  TreeView^.get_selection;
4897         if not Selection^.path_is_selected(Path) then
4898           Result := -1;
4899       end;
4900     end;
4901   end;
4902 end;
4903 
TGtk3ListBox.GetMultiSelectnull4904 function TGtk3ListBox.GetMultiSelect: Boolean;
4905 var
4906   Selection: PGtkTreeSelection;
4907 begin
4908   if IsWidgetOk then
4909   begin
4910     Selection := GetSelection;
4911     if Selection <> nil then
4912       Result := Selection^.get_mode <> GTK_SELECTION_SINGLE;
4913   end;
4914 end;
4915 
4916 procedure TGtk3ListBox.SetItemIndex(AValue: Integer);
4917 var
4918   TreeView: PGtkTreeView;
4919   Selection: PGtkTreeSelection;
4920   Path: PGtkTreePath;
4921 begin
4922   if Gtk3IsWidget(FWidget) then
4923   begin
4924     TreeView := PGtkTreeView(GetContainerWidget);
4925     Selection := GetSelection;
4926     if (AValue < 0) then
4927       Path := nil
4928     else
4929       Path := gtk_tree_path_new_from_indices(AValue, [-1]);
4930 
4931     // if singleselection mode then selection = itemindex
4932     if Path <> nil then
4933     begin
4934       gtk_tree_view_set_cursor(TreeView, Path, nil, False);
4935     end else
4936     begin
4937       Path := gtk_tree_path_new_from_indices(0, [-1]);
4938         gtk_tree_view_set_cursor(TreeView, Path, nil, False);
4939       gtk_tree_selection_unselect_all(Selection);
4940     end;
4941 
4942     if Path <> nil then
4943       gtk_tree_path_free(Path);
4944   end;
4945 end;
4946 
4947 procedure TGtk3ListBox.SetListBoxStyle(AValue: TListBoxStyle);
4948 begin
4949   if FListBoxStyle=AValue then Exit;
4950   FListBoxStyle:=AValue;
4951 end;
4952 
4953 procedure TGtk3ListBox.SetMultiSelect(AValue: Boolean);
4954 var
4955   Selection: PGtkTreeSelection;
4956 begin
4957   if IsWidgetOk then
4958   begin
4959     Selection := GetSelection;
4960     if Selection <> nil then
4961     begin
4962       if AValue then
4963         Selection^.set_mode(GTK_SELECTION_MULTIPLE)
4964       else
4965         Selection^.set_mode(GTK_SELECTION_SINGLE);
4966     end;
4967   end;
4968 end;
4969 
GetSelCountnull4970 function TGtk3ListBox.GetSelCount: Integer;
4971 var
4972   Selection: PGtkTreeSelection;
4973   Rows: PGList;
4974   ListStoreModel: PGtkTreeModel;
4975 begin
4976   Result := 0;
4977   if not Gtk3IsWidget(FWidget) then
4978     exit;
4979   Selection := GetSelection;
4980   if Selection = nil then
4981     exit;
4982   Rows := Selection^.get_selected_rows(@ListStoreModel);
4983   Result := g_list_length(Rows);
4984   g_list_free(Rows);
4985 end;
4986 
GetSelectionnull4987 function TGtk3ListBox.GetSelection: PGtkTreeSelection;
4988 begin
4989   if not IsWidgetOk then
4990     exit(nil);
4991   Result := PGtkTreeView(GetContainerWidget)^.get_selection;
4992 end;
4993 
GetItemSelectednull4994 function TGtk3ListBox.GetItemSelected(const AIndex: Integer): Boolean;
4995 var
4996   ASelection: PGtkTreeSelection;
4997   AModel: PGtkTreeModel;
4998   Item: TGtkTreeIter;
4999 begin
5000   Result := False;
5001 
5002   if not IsWidgetOK then
5003     exit;
5004 
5005   AModel := PGtkTreeView(GetContainerWidget)^.model;
5006 
5007   if AModel = nil then
5008     exit;
5009 
5010   ASelection := GetSelection;
5011 
5012   if ASelection = nil then
5013     exit;
5014 
5015   if AModel^.iter_nth_child(@Item, nil, AIndex) then
5016     Result := ASelection^.iter_is_selected(@Item);
5017 end;
5018 
5019 procedure TGtk3ListBox.SelectItem(const AIndex: Integer; ASelected: Boolean);
5020 var
5021   ASelection: PGtkTreeSelection;
5022   AModel: PGtkTreeModel;
5023   Iter: TGtkTreeIter;
5024 begin
5025   if not IsWidgetOK then
5026     exit;
5027 
5028   AModel := PGtkTreeView(getContainerWidget)^.model;
5029 
5030   if AModel = nil then
5031     exit;
5032 
5033   ASelection := GetSelection;
5034 
5035   if AModel^.iter_nth_child(@Iter, nil, AIndex) then
5036   begin
5037     case ASelected of
5038       True:
5039         if not ASelection^.iter_is_selected(@Iter) then
5040           ASelection^.select_iter(@Iter);
5041       False:
5042         if ASelection^.iter_is_selected(@Iter) then
5043           ASelection^.unselect_iter(@Iter);
5044     end;
5045   end;
5046 end;
5047 
5048 procedure TGtk3ListBox.SetTopIndex(const AIndex: Integer);
5049 var
5050   AModel: PGtkTreeModel;
5051   Iter: TGtkTreeIter;
5052   APath: PGtkTreePath;
5053 begin
5054   AModel := PGtkTreeView(getContainerWidget)^.model;
5055 
5056   if not AModel^.iter_nth_child(@Iter, nil, AIndex) then
5057     exit;
5058   APath := AModel^.get_path(@Iter);
5059   PGtkTreeView(getContainerWidget)^.scroll_to_cell(APath, nil, False, 0.0, 0.0);
5060   APath^.free;
5061 end;
5062 
5063 { TGtk3CheckListBox }
5064 
5065 procedure Gtk3WS_CheckListBoxDataFunc({%H-}tree_column: PGtkTreeViewColumn;
5066   cell: PGtkCellRenderer; tree_model: PGtkTreeModel; iter: PGtkTreeIter; {%H-}data: Pointer); cdecl;
5067 var
5068   b: byte;
5069   ADisabled: gboolean;
5070   AValue: TCheckBoxState;
5071 begin
5072   B := 0;
5073   ADisabled := False;
5074   gtk_tree_model_get(tree_model, iter, [gtk3CLBState, @b, -1]);
5075   gtk_tree_model_get(tree_model, iter, [gtk3CLBDisabled, @ADisabled, -1]);
5076   AValue := TCheckBoxState(b); // TCheckBoxState is 4 byte
5077   g_object_set(cell, 'inconsistent', [gboolean(AValue = cbGrayed), nil]);
5078   if AValue <> cbGrayed then
5079     gtk_cell_renderer_toggle_set_active(PGtkCellRendererToggle(cell), AValue = cbChecked);
5080 
5081   g_object_set(cell, 'activatable', [gboolean(not ADisabled), nil]);
5082 end;
5083 
5084 procedure Gtk3WS_CheckListBoxToggle({%H-}cellrenderertoggle : PGtkCellRendererToggle;
5085   arg1 : PGChar; AData: GPointer); cdecl;
5086 var
5087   Mess: TLMessage;
5088   Param: PtrInt;
5089   Iter : TGtkTreeIter;
5090   TreeView: PGtkTreeView;
5091   ListStore: PGtkTreeModel;
5092   Path: PGtkTreePath;
5093   AState: TCheckBoxState;
5094 begin
5095   Val(arg1, Param);
5096 
5097   TreeView := PGtkTreeView(TGtk3CheckListBox(AData).GetContainerWidget);
5098   ListStore := gtk_tree_view_get_model(TreeView);
5099   if gtk_tree_model_iter_nth_child(ListStore, @Iter, nil, Param) then
5100     begin
5101       TCustomCheckListBox(TGtk3Widget(AData).LCLObject).Toggle(Param);
5102       AState := TCustomCheckListBox(TGtk3Widget(AData).LCLObject).State[Param];
5103       gtk_list_store_set(PGtkListStore(ListStore), @Iter, [gtk3CLBState,
5104         Byte(AState), -1]);
5105     end;
5106 
5107 
5108   Path := gtk_tree_path_new_from_indices(Param, [-1]);
5109   if Path <> nil then
5110   begin
5111     gtk_tree_view_set_cursor(TreeView, Path, nil, False);
5112     gtk_tree_path_free(Path);
5113   end;
5114 
5115   FillChar(Mess{%H-}, SizeOf(Mess), #0);
5116   Mess.Msg := LM_CHANGED;
5117 
5118   Mess.Result := 0;
5119   Mess.WParam := Param;
5120   DeliverMessage(TGtk3Widget(AData).LCLObject, Mess);
5121 
5122 end;
5123 
CreateWidgetnull5124 function TGtk3CheckListBox.CreateWidget(const Params: TCreateParams
5125   ): PGtkWidget;
5126 var
5127   ACheckListBox: TCustomCheckListBox;
5128   ListStore: PGtkListStore;
5129   ItemList: TGtkListStoreStringList;
5130   AColumn: PGtkTreeViewColumn;
5131   Toggle: PGtkCellRendererToggle;
5132   Renderer : PGtkCellRenderer;
5133 begin
5134   FScrollX := 0;
5135   FScrollY := 0;
5136   FWidgetType := FWidgetType + [wtTreeModel, wtListBox, wtCheckListBox, wtScrollingWin];
5137   ACheckListBox := TCustomCheckListBox(LCLObject);
5138   FListBoxStyle := lbStandard;
5139 
5140   Result := PGtkScrolledWindow(TGtkScrolledWindow.new(nil, nil));
5141   Result^.show;
5142 
5143   ListStore := gtk_list_store_new (4, [G_TYPE_UCHAR, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_BOOLEAN, nil]);
5144   FCentralWidget := TGtkTreeView.new_with_model(PGtkTreeModel(ListStore));
5145   PGtkTreeView(FCentralWidget)^.set_headers_visible(False);
5146   g_object_unref (liststore);
5147 
5148   AColumn := gtk_tree_view_column_new;
5149 
5150   // checkable column
5151   Toggle := gtk_cell_renderer_toggle_new;
5152   g_object_set_data(PGObject(Toggle), 'lclwidget', Self);
5153 
5154   AColumn^.set_title('CHECKBINS');
5155   AColumn^.pack_start(Toggle, True);
5156   AColumn^.set_cell_data_func(Toggle, @Gtk3WS_CheckListBoxDataFunc, Self, nil);
5157   Toggle^.set_active(True);
5158   PGtkTreeView(FCentralWidget)^.append_column(AColumn);
5159   AColumn^.set_clickable(True);
5160 
5161   g_signal_connect_data(Toggle, 'toggled', TGCallback(@Gtk3WS_CheckListBoxToggle), Self, nil, 0);
5162 
5163   Renderer := LCLIntfCellRenderer_New(); // gtk_cell_renderer_text_new;
5164 
5165   g_object_set_data(PGObject(Renderer), 'lclwidget', Self);
5166 
5167   AColumn := gtk_tree_view_column_new_with_attributes ('LISTITEMS', Renderer,
5168              ['text', 1, nil]);
5169 
5170   g_object_set_data(PGObject(AColumn), 'lclwidget', Self);
5171 
5172   // AColumn^.pack_start(Renderer, True);
5173 
5174   AColumn^.set_cell_data_func(Renderer, @LCLIntfRenderer_ColumnCellDataFunc, Self, nil);
5175 
5176   PGtkTreeView(FCentralWidget)^.append_column(AColumn);
5177 
5178   ItemList := TGtkListStoreStringList.Create(PGtkListStore(PGtkTreeView(FCentralWidget)^.get_model), 1, LCLObject);
5179   g_object_set_data(PGObject(FCentralWidget),GtkListItemLCLListTag, ItemList);
5180 
5181 
5182   AColumn^.set_clickable(True);
5183 
5184   // AColumn^set_cell_data_func(AColumn, renderer, @LCLIntfRenderer_ColumnCellDataFunc, Self, nil);
5185 
5186   PGtkScrolledWindow(Result)^.add(FCentralWidget);
5187 
5188 
5189   PGtkScrolledWindow(Result)^.get_vscrollbar^.set_can_focus(False);
5190   PGtkScrolledWindow(Result)^.get_hscrollbar^.set_can_focus(False);
5191   PGtkScrolledWindow(Result)^.set_policy(GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
5192   FListBoxStyle := ACheckListBox.Style;
5193   if ACheckListBox.MultiSelect then
5194     PGtkTreeView(FCentralWidget)^.get_selection^.set_mode(GTK_SELECTION_MULTIPLE)
5195   else
5196     PGtkTreeView(FCentralWidget)^.get_selection^.set_mode(GTK_SELECTION_SINGLE);
5197   // AListBox.Style;
5198   if FListBoxStyle <> lbOwnerDrawVariable then
5199   begin
5200     //AColumn^.set_sizing(GTK_TREE_VIEW_COLUMN_FIXED);
5201     //PGtkTreeView(FCentralWidget)^.set_fixed_height_mode(True);
5202   end;
5203 
5204 end;
5205 
5206 { TGtk3ListView }
5207 
Gtk3WS_ListViewItemPreSelectednull5208 function Gtk3WS_ListViewItemPreSelected({%H-}selection: PGtkTreeSelection; {%H-}model: PGtkTreeModel;
5209   path: PGtkTreePath; path_is_currently_selected: GBoolean; AData: GPointer): GBoolean; cdecl;
5210 begin
5211   // DebugLn('Gtk3WS_ListViewItemSelected ,path selected ',dbgs(path_is_currently_selected));
isnull5212   // this function is called *before* the item is selected
5213   // The result should be True to allow the Item to change selection
5214   Result := True;
5215 
5216   if (AData = nil) or TGtk3Widget(AData).InUpdate then
5217     exit;
5218 
5219   if not Assigned(TGtk3ListView(AData).FPreselectedIndices) then
5220     TGtk3ListView(AData).FPreselectedIndices := TFPList.Create;
5221 
5222   if TGtk3ListView(AData).FPreselectedIndices.IndexOf(Pointer(PtrInt(gtk_tree_path_get_indices(path)^))) = -1 then
5223     TGtk3ListView(AData).FPreselectedIndices.Add(Pointer(PtrInt(gtk_tree_path_get_indices(path)^)));
5224 end;
5225 
5226 procedure Gtk3WS_ListViewItemSelected(ASelection: PGtkTreeSelection; AData: GPointer); cdecl;
5227 var
5228   ATreeView: PGtkTreeView;
5229   AList: PGList;
5230   Msg: TLMNotify;
5231   NM: TNMListView;
5232   Path: PGtkTreePath;
5233   Indices: Integer;
5234   i, j: Integer;
5235   B: Boolean;
5236 begin
5237   if (AData = nil) or TGtk3Widget(AData).InUpdate then
5238     exit;
5239   if not Assigned(TGtk3ListView(AData).FPreselectedIndices) then
5240     exit;
5241   ATreeView := gtk_tree_selection_get_tree_view(ASelection);
5242   AList := gtk_tree_selection_get_selected_rows(ASelection, nil);
5243   TGtk3Widget(AData).BeginUpdate; // dissalow entering Gtk3WS_ListViewItemPreSelected
5244   try
5245     for i := 0 to TGtk3ListView(AData).FPreselectedIndices.Count - 1 do
5246     begin
5247       FillChar(Msg{%H-}, SizeOf(Msg), 0);
5248       Msg.Msg := CN_NOTIFY;
5249       FillChar(NM{%H-}, SizeOf(NM), 0);
5250       NM.hdr.hwndfrom := HWND(TGtk3Widget(AData));
5251       NM.hdr.code := LVN_ITEMCHANGED;
5252       NM.iItem := PtrInt(TGtk3ListView(AData).FPreselectedIndices.Items[i]);
5253       NM.iSubItem := 0;
5254       B := False;
5255       for j := 0 to g_list_length(AList) - 1 do
5256       begin
5257         Path := g_list_nth_data(AList, guint(j));
5258         if Path <> nil then
5259         begin
5260           Indices := gtk_tree_path_get_indices(Path)^;
5261           B := Indices = PtrInt(TGtk3ListView(AData).FPreselectedIndices.Items[i]);
5262           if B then
5263             break;
5264         end;
5265       end;
5266       if not B then
5267         NM.uOldState := LVIS_SELECTED
5268       else
5269         NM.uNewState := LVIS_SELECTED;
5270       NM.uChanged := LVIF_STATE;
5271       Msg.NMHdr := @NM.hdr;
5272       DeliverMessage(TGtk3Widget(AData).LCLObject, Msg);
5273     end;
5274   finally
5275     FreeAndNil(TGtk3ListView(AData).FPreselectedIndices);
5276     if AList <> nil then
5277       g_list_free(AList);
5278     TGtk3Widget(AData).EndUpdate;
5279   end;
5280 end;
5281 
TGtk3ListView.CreateWidgetnull5282 function TGtk3ListView.CreateWidget(const Params: TCreateParams): PGtkWidget;
5283 var
5284   AListView: TCustomListView;
5285   AScrollStyle: TPoint;
5286   PtrType: GType;
5287   TreeModel: PGtkTreeModel;
5288 begin
5289   FImages := nil;
5290   FScrollX := 0;
5291   FScrollY := 0;
5292   FPreselectedIndices := nil;
5293   FWidgetType := FWidgetType + [wtTreeModel, wtListView, wtScrollingWin];
5294   AListView := TCustomListView(LCLObject);
5295   Result := PGtkScrolledWindow(TGtkScrolledWindow.new(nil, nil));
5296 
5297   PtrType := G_TYPE_POINTER;
5298 
5299   TreeModel := PGtkTreeModel(gtk_list_store_newv(1, @PtrType));
5300 
5301   if TListView(AListView).ViewStyle in [vsIcon,vsSmallIcon] then
5302     FCentralWidget := TGtkIconView.new_with_model(TreeModel)
5303   else
5304     FCentralWidget := TGtkTreeView.new_with_model(TreeModel);
5305 
5306   FIsTreeView := not (TListView(AListView).ViewStyle in [vsIcon,vsSmallIcon]);
5307 
5308   FCentralWidget^.set_has_window(True);
5309   FCentralWidget^.show;
5310 
5311   PGtkScrolledWindow(Result)^.add(FCentralWidget);
5312   //PGtkScrolledWindow(Result)^.set_focus_child(FCentralWidget);
5313 
5314   AScrollStyle := Gtk3TranslateScrollStyle(TListView(AListView).ScrollBars);
5315   // gtk3 scrolled window hates GTK_POLICY_NONE
5316   PGtkScrolledWindow(Result)^.set_policy(AScrollStyle.X, AScrollStyle.Y);
5317   PGtkScrolledWindow(Result)^.set_shadow_type(BorderStyleShadowMap[AListView.BorderStyle]);
5318   PGtkScrolledWindow(Result)^.get_vscrollbar^.set_can_focus(False);
5319   PGtkScrolledWindow(Result)^.get_hscrollbar^.set_can_focus(False);
5320   g_object_unref (PGObject(TreeModel));
5321   PGtkScrolledWindow(Result)^.set_can_focus(False);
5322   PGtkTreeView(FCentralWidget)^.set_can_focus(True);
5323   if FIsTreeView then
5324   begin
GtkTreeViewnull5325     gtk_tree_selection_set_select_function(PGtkTreeView(FCentralWidget)^.get_selection, TGtkTreeSelectionFunc(@Gtk3WS_ListViewItemPreSelected),
5326       Self, nil);
5327     g_signal_connect_data(PGtkTreeView(FCentralWidget)^.get_selection, 'changed', TGCallback(@Gtk3WS_ListViewItemSelected), Self, nil, 0);
5328   end;
5329   // if FIsTreeView then
5330   //  PGtkTreeView(FCentralWidget)^.set_search_column(0);
5331 end;
5332 
TGtk3ListView.EatArrowKeysnull5333 function TGtk3ListView.EatArrowKeys(const AKey: Word): Boolean;
5334 begin
5335   Result := False;
5336 end;
5337 
5338 destructor TGtk3ListView.Destroy;
5339 begin
5340   ClearImages;
5341   FreeAndNil(FImages);
5342   FreeAndNil(FPreselectedIndices);
5343   inherited Destroy;
5344 end;
5345 
getHorizontalScrollbarnull5346 function TGtk3ListView.getHorizontalScrollbar: PGtkScrollbar;
5347 begin
5348   Result := nil;
5349   if not IsWidgetOk then
5350     exit;
5351   Result := PGtkScrollBar(PGtkScrolledWindow(Widget)^.get_hscrollbar);
5352 end;
5353 
TGtk3ListView.getVerticalScrollbarnull5354 function TGtk3ListView.getVerticalScrollbar: PGtkScrollbar;
5355 begin
5356   Result := nil;
5357   if not IsWidgetOk then
5358     exit;
5359   Result := PGtkScrollBar(PGtkScrolledWindow(Widget)^.get_vscrollbar);
5360 end;
5361 
TGtk3ListView.GetScrolledWindownull5362 function TGtk3ListView.GetScrolledWindow: PGtkScrolledWindow;
5363 begin
5364   if IsWidgetOK then
5365     Result := PGtkScrolledWindow(Widget)
5366   else
5367     Result := nil;
5368 end;
5369 
5370 procedure TGtk3ListView.ClearImages;
5371 var
5372   i: Integer;
5373 begin
5374   if Assigned(FImages) then
5375   begin
5376     for i := FImages.Count - 1 downto 0 do
5377       if FImages[i] <> nil then
5378         TGtk3Object(FImages[i]).Free;
5379     FImages.Clear;
5380   end;
5381 end;
5382 
5383 procedure TGtk3ListView.ColumnDelete(AIndex: Integer);
5384 var
5385   AColumn: PGtkTreeViewColumn;
5386 begin
5387   if IsWidgetOK and IsTreeView then
5388   begin
5389     AColumn := PGtkTreeView(GetContainerWidget)^.get_column(AIndex);
5390     if (AColumn<>nil) then
5391       PGtkTreeView(GetContainerWidget)^.remove_column(AColumn);
5392   end;
5393 end;
5394 
ColumnGetWidthnull5395 function TGtk3ListView.ColumnGetWidth(AIndex: Integer): Integer;
5396 var
5397   AColumn: PGtkTreeViewColumn;
5398 begin
5399   Result := 0;
5400   if IsWidgetOK and IsTreeView then
5401   begin
5402     AColumn := PGtkTreeView(GetContainerWidget)^.get_column(AIndex);
5403     if (AColumn<>nil) then
5404       Result := AColumn^.get_width;
5405   end;
5406 end;
5407 
5408 procedure Gtk3WSLV_ListViewGetPixbufDataFuncForColumn(tree_column: PGtkTreeViewColumn;
5409   cell: PGtkCellRenderer; tree_model: PGtkTreeModel; iter: PGtkTreeIter; AData: GPointer); cdecl;
5410 var
5411   ListItem: TListItem;
5412   Images: TFPList;
5413   // Widgets: PTVWidgets;
5414   ListColumn: TListColumn;
5415   ImageIndex: Integer;
5416   ColumnIndex: Integer;
5417   APath: PGtkTreePath;
5418 begin
5419   // TODO: set_property('pixbuf', TGValue);
5420   // PGtkCellRendererPixbuf(cell)^.pixbuf := nil;
5421   gtk_tree_model_get(tree_model, iter, [0, @ListItem, -1]);
5422 
5423   ListColumn := TListColumn(g_object_get_data(tree_column, 'TListColumn'));
5424   if ListColumn = nil then
5425     Exit;
5426   ColumnIndex := ListColumn.Index;
5427   // Images := Widgets^.Images;
5428   Images := TGtk3ListView(AData).Images;
5429   if Images = nil then
5430     Exit;
5431   ImageIndex := -1;
5432 
5433   if (ListItem = nil) and TCustomListView(TGtk3Widget(AData).LCLObject).OwnerData then
5434   begin
5435     APath := gtk_tree_model_get_path(tree_model,iter);
5436     ListItem := TCustomListView(TGtk3Widget(AData).LCLObject).Items[gtk_tree_path_get_indices(APath)^];
5437     gtk_tree_path_free(APath);
5438   end;
5439 
5440   if ListItem = nil then
5441     Exit;
5442 
5443   if ColumnIndex = 0 then
5444     ImageIndex := ListItem.ImageIndex
5445   else
5446     if ColumnIndex -1 <= ListItem.SubItems.Count-1 then
5447       ImageIndex := ListItem.SubItemImages[ColumnIndex-1];
5448 
5449   (* TODO: set property
5450   if (ImageIndex > -1) and (ImageIndex <= Images.Count-1) then
5451     PGtkCellRendererPixbuf(cell)^.pixbuf := PGdkPixbuf(Images.Items[ImageIndex])
5452   else
5453     PGtkCellRendererPixbuf(cell)^.pixbuf := nil;
5454    *)
5455 end;
5456 
5457 procedure Gtk3WS_ListViewColumnClicked(column: PGtkTreeViewColumn; AData: GPointer); cdecl;
5458 var
5459   AColumn: TListColumn;
5460   Msg: TLMNotify;
5461   NM: TNMListView;
5462 begin
5463   AColumn := TListColumn(g_object_get_data(PGObject(column), 'TListColumn'));
5464 
5465   if (AColumn = nil) or (AData = nil) then
5466     exit;
5467 
5468   FillChar(Msg{%H-}, SizeOf(Msg), 0);
5469   Msg.Msg := CN_NOTIFY;
5470 
5471   FillChar(NM{%H-}, SizeOf(NM), 0);
5472   NM.hdr.hwndfrom := {%H-}PtrUInt(AData);
5473   NM.hdr.code := LVN_COLUMNCLICK;
5474   NM.iItem := -1;
5475   NM.iSubItem := AColumn.Index;
5476   Msg.NMHdr := @NM.hdr;
5477   DeliverMessage(TGtk3Widget(AData).LCLObject, Msg);
5478 end;
5479 
5480 procedure TGtk3ListView.ColumnInsert(AIndex: Integer; AColumn: TListColumn);
5481 var
5482   AGtkColumn: PGtkTreeViewColumn;
5483   PixRenderer,
5484   TextRenderer: PGtkCellRenderer;
5485 begin
5486   if not IsWidgetOK or not IsTreeView then
5487     exit;
5488   AGtkColumn := TGtkTreeViewColumn.new;
5489 
5490   PixRenderer := gtk_cell_renderer_pixbuf_new();
5491   TextRenderer := LCLIntfCellRenderer_New;
5492 
5493   AGtkColumn^.pack_start(PixRenderer, False);
5494   AGtkColumn^.pack_start(TextRenderer, True);
5495 
5496   // gtk_tree_view_column_set_cell_data_func(column, pixrenderer,  TGtkTreeCellDataFunc(@Gtk2WSLV_ListViewGetPixbufDataFuncForColumn), WidgetInfo, nil);
5497   // gtk_tree_view_column_set_cell_data_func(column, textrenderer, TGtkTreeCellDataFunc(@LCLIntfCellRenderer_CellDataFunc), Self, nil);
5498   AGtkColumn^.set_cell_data_func(PixRenderer, @Gtk3WSLV_ListViewGetPixbufDataFuncForColumn, Self, nil);
5499 
5500 
5501   AGtkColumn^.set_cell_data_func(PGtkCellRenderer(TextRenderer), TGtkTreeCellDataFunc(@LCLIntfCellRenderer_CellDataFunc), Self, nil);
5502 
5503   //store the TColumn in the column data for callbacks
5504   g_object_set_data(AGtkColumn, PgChar('TListColumn'), gpointer(AColumn));
5505 
5506   g_signal_connect_data(AGtkColumn,'clicked', TGCallback(@Gtk3WS_ListViewColumnClicked), Self, nil, 0);
5507   PGtkTreeView(GetContainerWidget)^.insert_column(AGtkColumn, AIndex);
5508   AGtkColumn^.set_clickable(True);
5509 
5510 end;
5511 
5512 procedure TGtk3ListView.SetAlignment(AIndex: Integer; AColumn: TListColumn;
5513   AAlignment: TAlignment);
5514 var
5515   AGtkColumn: PGtkTreeViewColumn;
5516   AFloat: Double;
5517   AList: PGList;
5518   textrenderer: PGtkCellRenderer;
5519   Value: TGValue;
5520 begin
5521   if not IsWidgetOK or not IsTreeView then
5522     exit;
5523   AGtkColumn := PGtkTreeView(getContainerWidget)^.get_column(AIndex);
5524   if AGtkColumn = nil then
5525     exit;
5526 
5527   AFloat := 0;
5528   case AAlignment of
5529     taRightJustify: AFloat := 1;
5530     taCenter: AFloat := 0.5;
5531   end;
5532 
5533 
5534   AList := PGtkCellLayout(AGtkColumn)^.get_cells;
5535   // AList := gtk_tree_view_column_get_cell_renderers(AColumn);
5536   textrenderer := PGtkCellRenderer(g_list_last(AList)^.data);
5537   g_list_free(AList);
5538 
5539   Value.g_type := G_TYPE_FLOAT;
5540   Value.set_float(AFloat);
5541   g_object_set_property(textrenderer, PChar('xalign'), @Value);
5542 
5543   {now we call set alignment because it calls update over visible rows in col}
5544   AGtkColumn^.set_alignment(AFloat);
5545 end;
5546 
5547 procedure TGtk3ListView.SetColumnAutoSize(AIndex: Integer;
5548   AColumn: TListColumn; AAutoSize: Boolean);
5549 const
5550   SizingMap: array[Boolean] of TGtkTreeViewColumnSizing = (
5551     2 {GTK_TREE_VIEW_COLUMN_FIXED},
5552     1 {GTK_TREE_VIEW_COLUMN_AUTOSIZE}
5553   );
5554 var
5555   AGtkColumn: PGtkTreeViewColumn;
5556 begin
5557   if not IsWidgetOK or not IsTreeView then
5558     exit;
5559   AGtkColumn := PGtkTreeView(getContainerWidget)^.get_column(AIndex);
5560   if AGtkColumn <> nil then
5561   begin
5562     AGtkColumn^.set_resizable(True);
5563     AGtkColumn^.set_sizing(SizingMap[AAutoSize]);
5564   end;
5565 end;
5566 
5567 procedure TGtk3ListView.SetColumnCaption(AIndex: Integer; AColumn: TListColumn;
5568   const ACaption: String);
5569 var
5570   AGtkColumn: PGtkTreeViewColumn;
5571 begin
5572   if not IsWidgetOK or not IsTreeView then
5573     exit;
5574   AGtkColumn := PGtkTreeView(getContainerWidget)^.get_column(AIndex);
5575   if AGtkColumn <> nil then
5576   begin
5577     AGtkColumn^.set_title(PgChar(ACaption));
5578   end;
5579 end;
5580 
5581 procedure TGtk3ListView.SetColumnMaxWidth(AIndex: Integer;
5582   AColumn: TListColumn; AMaxWidth: Integer);
5583 var
5584   AGtkColumn: PGtkTreeViewColumn;
5585 begin
5586   if not IsWidgetOK or not IsTreeView then
5587     exit;
5588   AGtkColumn := PGtkTreeView(getContainerWidget)^.get_column(AIndex);
5589   if AGtkColumn <> nil then
5590   begin
5591     if AMaxWidth <= 0 then
5592       AGtkColumn^.set_max_width(10000)
5593     else
5594       AGtkColumn^.set_max_width(AMaxWidth);
5595   end;
5596 end;
5597 
5598 procedure TGtk3ListView.SetColumnMinWidth(AIndex: Integer;
5599   AColumn: TListColumn; AMinWidth: Integer);
5600 var
5601   AGtkColumn: PGtkTreeViewColumn;
5602 begin
5603   if not IsWidgetOK or not IsTreeView then
5604     exit;
5605   AGtkColumn := PGtkTreeView(getContainerWidget)^.get_column(AIndex);
5606   if AGtkColumn <> nil then
5607     AGtkColumn^.set_min_width(AMinWidth);
5608 end;
5609 
5610 procedure TGtk3ListView.SetColumnWidth(AIndex: Integer; AColumn: TListColumn;
5611   AWidth: Integer);
5612 var
5613   AGtkColumn: PGtkTreeViewColumn;
5614 begin
5615   if not IsWidgetOK or not IsTreeView then
5616     exit;
5617   AGtkColumn := PGtkTreeView(getContainerWidget)^.get_column(AIndex);
5618   if AGtkColumn <> nil then
5619   begin
5620     AGtkColumn^.set_fixed_width(AWidth + Ord(AWidth < 1));
5621   end;
5622   //  AGtkColumn^.set_widget();
5623 end;
5624 
5625 procedure TGtk3ListView.SetColumnVisible(AIndex: Integer; AColumn: TListColumn;
5626   AVisible: Boolean);
5627 var
5628   AGtkColumn: PGtkTreeViewColumn;
5629 begin
5630   if not IsWidgetOK or not IsTreeView then
5631     exit;
5632   AGtkColumn := PGtkTreeView(getContainerWidget)^.get_column(AIndex);
5633   if AGtkColumn <> nil then
5634   begin
5635     AGtkColumn^.set_visible(AVisible and (TListView(LCLObject).ViewStyle in [vsList, vsReport]));
5636   end;
5637 end;
5638 
5639 procedure TGtk3ListView.ColumnSetSortIndicator(const AIndex: Integer;
5640   const AColumn: TListColumn; const ASortIndicator: TSortIndicator);
5641 const
5642   GtkOrder : array [ TSortIndicator] of TGtkSortType = (0, {GTK_SORT_ASCENDING}0, {GTK_SORT_DESCENDING}1);
5643 var
5644   AGtkColumn: PGtkTreeViewColumn;
5645 begin
5646   AGtkColumn := PGtkTreeView(getContainerWidget)^.get_column(AIndex);
5647 
5648   if AGtkColumn <> nil then
5649   begin
5650     if ASortIndicator = siNone then
5651       AGtkColumn^.set_sort_indicator(false)
5652     else
5653     begin
5654       AGtkColumn^.set_sort_indicator(true);
5655       AgtkColumn^.set_sort_order(GtkOrder[ASortIndicator]);
5656     end;
5657   end;
5658 end;
5659 
5660 procedure TGtk3ListView.ItemDelete(AIndex: Integer);
5661 var
5662   AModel: PGtkTreeModel;
5663   Iter: TGtkTreeIter;
5664 begin
5665   if IsTreeView then
5666     AModel := PGtkTreeView(getContainerWidget)^.get_model
5667   else
5668     AModel := PGtkIconView(getContainerWidget)^.get_model;
5669   if gtk_tree_model_iter_nth_child(AModel, @Iter, nil, AIndex) then
5670     gtk_list_store_remove(PGtkListStore(AModel), @Iter);
5671 end;
5672 
5673 procedure TGtk3ListView.ItemInsert(AIndex: Integer; AItem: TListItem);
5674 var
5675   AModel: PGtkTreeModel;
5676   Iter: TGtkTreeIter;
5677   NewIndex: Integer;
5678 begin
5679   if not IsWidgetOK then
5680     exit;
5681   if IsTreeView then
5682     AModel := PGtkTreeView(getContainerWidget)^.get_model
5683   else
5684     AModel := PGtkIconView(getContainerWidget)^.get_model;
5685 
5686   if AIndex = -1 then
5687     NewIndex := AModel^.iter_n_children(nil)
5688   else
5689     NewIndex := AIndex;
5690   // AGValue.g_type := G_TYPE_POINTER;
5691   // AGValue.set_pointer(AItem);
5692   gtk_list_store_insert_with_values(PGtkListStore(AModel), @Iter, NewIndex, [0, Pointer(AItem), -1]);
5693   // PGtkListStore(AModel)^.insert_with_valuesv(@Iter, NewIndex, @AColumns, @AGValue, 1);
5694 end;
5695 
5696 procedure TGtk3ListView.ItemSetText(AIndex, ASubIndex: Integer;
5697   AItem: TListItem; const AText: String);
5698 var
5699   Path: PGtkTreePath;
5700   ItemRect: TGdkRectangle;
5701 begin
5702   if not IsWidgetOK then
5703     exit;
5704   if not getContainerWidget^.get_realized then
5705     exit;
5706   if IsTreeView then
5707   begin
5708     Path := gtk_tree_path_new_from_indices(AIndex, [-1]);
5709     PGtkTreeView(GetContainerWidget)^.get_cell_area(Path, nil, @ItemRect);
5710     gtk_tree_path_free(Path);
5711   end else
5712     ItemRect.Height := 1;
5713   if GetContainerWidget^.get_visible and (ItemRect.height <> 0) then // item is visible
5714     GetContainerWidget^.queue_draw;
5715 end;
5716 
5717 procedure TGtk3ListView.ItemSetState(const AIndex: Integer;
5718   const AItem: TListItem; const AState: TListItemState; const AIsSet: Boolean);
5719 var
5720   Path: PGtkTreePath;
5721   ATreeSelection: PGtkTreeSelection;
5722 begin
5723   if not IsWidgetOK then
5724     exit;
5725 
5726   case AState of
5727     lisCut,
5728     lisDropTarget:
5729     begin
5730       //TODO: do something with the rowcolor ?
5731     end;
5732 
5733     lisFocused:
5734     begin
5735       Path := gtk_tree_path_new_from_string(PgChar(IntToStr(AIndex)));
5736       if IsTreeView then
5737       begin
5738         if AIsSet then
5739           PGtkTreeView(getContainerWidget)^.set_cursor(Path, nil, False)
5740         else
5741           PGtkTreeView(GetContainerWidget)^.set_cursor(Path, nil, False);
5742       end else
5743         PGtkIconView(GetContainerWidget)^.set_cursor(Path, nil, False);
5744       if Path <> nil then
5745         gtk_tree_path_free(Path);
5746     end;
5747 
5748     lisSelected:
5749     begin
5750       Path := gtk_tree_path_new_from_string(PgChar(IntToStr(AIndex)));
5751       if IsTreeView then
5752       begin
5753         ATreeSelection := PGtkTreeView(GetContainerWidget)^.get_selection;
5754         if AIsSet and not ATreeSelection^.path_is_selected(Path) then
5755         begin
5756           ATreeSelection^.select_path(Path);
5757           // BroadcastMsg := True;
5758         end else
5759         if not AIsSet and ATreeSelection^.path_is_selected(Path) then
5760         begin
5761           ATreeSelection^.unselect_path(Path);
5762           // BroadcastMsg := True;
5763         end;
5764       end else
5765       begin
5766         if AIsSet and not PGtkIconView(GetContainerWidget)^.path_is_selected(Path) then
5767         begin
5768           PGtkIconView(GetContainerWidget)^.select_path(Path);
5769           // BroadCastMsg := True;
5770         end else
5771         if not AIsSet and PGtkIconView(GetContainerWidget)^.path_is_selected(Path) then
5772         begin
5773           PGtkIconView(GetContainerWidget)^.unselect_path(Path);
5774           // BroadCastMsg := True;
5775         end;
5776       end;
5777       if Path <> nil then
5778         gtk_tree_path_free(Path);
5779       // if BroadcastMsg then
5780       //  BroadCastListSelection(ALV, {%H-}PtrUInt(MainView), AIndex, not AIsSet);
5781     end;
5782   end;
5783 
5784 end;
5785 
ItemGetStatenull5786 function TGtk3ListView.ItemGetState(const AIndex: Integer;
5787   const AItem: TListItem; const AState: TListItemState; out AIsSet: Boolean
5788   ): Boolean;
5789 var
5790   Path: PGtkTreePath;
5791   Column: PPGtkTreeViewColumn;
5792   Cell: PPGtkCellRenderer;
5793   APath: PGtkTreePath;
5794   AStr: PChar;
5795 begin
5796   Result := False;
5797   AIsSet := False;
5798   if not IsWidgetOK then
5799     exit;
5800   case AState of
5801     lisCut,
5802     lisDropTarget:
5803     begin
5804       //TODO: do something with the rowcolor ?
5805     end;
5806     lisFocused:
5807     begin
5808       Path := nil;
5809       Column := nil;
5810       if IsTreeView then
5811         PGtkTreeView(GetContainerWidget)^.get_cursor(@Path, Column)
5812       else
5813         PGtkIconView(GetContainerWidget)^.get_cursor(@Path, Cell);
5814       if Assigned(Path) then
5815       begin
5816         AStr := gtk_tree_path_to_string(Path);
5817         AIsSet := (StrToIntDef(AStr,-1) = AIndex);
5818         if AStr <> nil then
5819           g_free(AStr);
5820         gtk_tree_path_free(Path);
5821         Result := True;
5822       end;
5823     end;
5824 
5825     lisSelected:
5826     begin
5827       APath := gtk_tree_path_new_from_string(PChar(IntToStr(AIndex)));
5828       if IsTreeView then
5829         AIsSet := PGtkTreeView(GetContainerWidget)^.get_selection^.path_is_selected(APath)
5830       else
5831         AIsSet := PGtkIconView(GetContainerWidget)^.path_is_selected(APath);
5832 
5833       if APath <> nil then
5834         gtk_tree_path_free(APath);
5835       Result := True;
5836     end;
5837   end;
5838 end;
5839 
5840 procedure TGtk3ListView.UpdateImageCellsSize;
5841 begin
5842   // must get renderer via property
5843   // gtk_tree_view_column_get_cell_renderers
5844 end;
5845 
5846 { TGtk3ComboBox }
5847 
GetItemIndexnull5848 function TGtk3ComboBox.GetItemIndex: Integer;
5849 begin
5850   Result := -1;
5851   if Assigned(FWidget) and Gtk3IsComboBox(GetContainerWidget) then
5852     Result := PGtkComboBox(GetContainerWidget)^.get_active;
5853 end;
5854 
5855 procedure TGtk3ComboBox.SetDroppedDown(AValue: boolean);
5856 begin
5857   if Assigned(FWidget) and Gtk3IsComboBox(GetContainerWidget) then
5858   begin
5859     if AValue then
5860       PGtkComboBox(GetContainerWidget)^.popup
5861     else
5862       PGtkComboBox(GetContainerWidget)^.popdown;
5863   end;
5864 end;
5865 
5866 procedure TGtk3ComboBox.SetItemIndex(AValue: Integer);
5867 begin
5868   if IsWidgetOK and Gtk3IsComboBox(GetContainerWidget) then
5869     PGtkComboBox(GetContainerWidget)^.set_active(AValue);
5870 end;
5871 
TGtk3ComboBox.GetCellViewnull5872 function TGtk3ComboBox.GetCellView: PGtkCellView;
5873 var
5874   AList: PGList;
5875   i: Integer;
5876 begin
5877   if FCellView = nil then
5878   begin
5879     AList := PGtkComboBox(getContainerWidget)^.get_children;
5880     for i := 0 to g_list_length(AList) -1 do
5881     begin
5882       if Gtk3IsCellView(g_list_nth(AList, i)^.data) then
5883       begin
5884         FCellView := PGtkCellView(g_list_first(AList)^.data);
5885         break;
5886       end;
5887     end;
5888     g_list_free(AList);
5889   end;
5890   Result := FCellView;
5891 end;
5892 
GetPopupWidgetnull5893 function TGtk3ComboBox.GetPopupWidget: PGtkWidget;
5894 begin
5895   Result := nil;
5896   if not IsWidgetOk then
5897     exit;
5898   if PGtkComboBox(GetContainerWidget)^.priv3^.popup_widget <> nil then
5899     Result := PGtkComboBox(GetContainerWidget)^.priv3^.popup_widget
5900   else
5901   if PGtkComboBox(GetContainerWidget)^.priv3^.tree_view <> nil then
5902     Result := PGtkComboBox(GetContainerWidget)^.priv3^.tree_view;
5903 end;
5904 
GetButtonWidgetnull5905 function TGtk3ComboBox.GetButtonWidget: PGtkWidget;
5906 begin
5907   Result := nil;
5908   if not IsWidgetOk then
5909     exit;
5910   if PGtkComboBox(GetContainerWidget)^.priv3^.button <> nil then
5911     Result := PGtkComboBox(GetContainerWidget)^.priv3^.button;
5912 end;
5913 
GetCellViewFramenull5914 function TGtk3ComboBox.GetCellViewFrame: PGtkWidget;
5915 begin
5916   Result := nil;
5917   if not IsWidgetOk then
5918     exit;
5919   if PGtkComboBox(GetContainerWidget)^.priv3^.cell_view_frame <> nil then
5920     Result := PGtkComboBox(GetContainerWidget)^.priv3^.cell_view_frame;
5921 end;
5922 
CreateWidgetnull5923 function TGtk3ComboBox.CreateWidget(const Params: TCreateParams): PGtkWidget;
5924 var
5925   ACombo: TCustomComboBox;
5926   ListStore: PGtkListStore;
5927   ItemList: TGtkListStoreStringList;
5928   Renderer : PGtkCellRenderer;
5929 begin
5930   FWidgetType := FWidgetType + [wtTreeModel, wtComboBox];
5931   ACombo := TCustomComboBox(LCLObject);
5932   ListStore := gtk_list_store_new (2, [G_TYPE_STRING, G_TYPE_POINTER, nil]);
5933   if ACombo.Style in [csDropDown, csSimple] then
5934     Result := PGtkWidget(TGtkComboBox.new_with_model_and_entry(PGtkTreeModel(ListStore)))
5935   else
5936     Result := PGtkWidget(TGtkComboBox.new_with_model(PGtkTreeModel(ListStore)));
5937 
5938   if ACombo.Style in [csDropDown, csSimple] then
5939   begin
5940     ItemList := TGtkListStoreStringList.Create(PGtkListStore(PGtkComboBox(Result)^.get_model), 0, LCLObject);
5941     g_object_set_data(PGObject(Result), GtkListItemLCLListTag, ItemList);
5942 
5943     PGtkComboBox(Result)^.set_entry_text_column(0);
5944     if ACombo.Style = csDropDownList then
5945       PGtkEditable(PGtkComboBox(Result)^.get_child)^.set_editable(False);
5946     // do not allow combo button to get focus, entry should take focus
5947     if PGtkComboBox(Result)^.priv3^.button <> nil then
5948       PGtkComboBox(Result)^.priv3^.button^.set_can_focus(False);
5949     // set lclwidget data to entry
5950     g_object_set_data(PGtkComboBox(Result)^.get_child, 'lclwidget', Self);
5951     // when we scroll with mouse wheel over entry our scrollevent doesn't catch entry
5952     // but parent control with window (eg. form), so we are settint all events mask to
5953     // catch all mouse events on gtkentry.
5954     PGtkEntry(PGtkComboBox(Result)^.get_child)^.set_events(GDK_ALL_EVENTS_MASK);
5955   end else
5956   begin
5957     // FCentralWidget := PGtkWidget(TGtkComboBox.new_with_model(PGtkTreeModel(ListStore)));
5958     FCentralWidget := Result;
5959 
5960     ItemList := TGtkListStoreStringList.Create(PGtkListStore(PGtkComboBox(FCentralWidget)^.get_model), 0, LCLObject);
5961     g_object_set_data(PGObject(FCentralWidget), GtkListItemLCLListTag, ItemList);
5962 
5963     renderer := LCLIntfCellRenderer_New();
5964     g_object_set_data(PGObject(renderer), 'lclwidget', Self);
5965 
5966     gtk_cell_layout_clear(PGtkCellLayout(FCentralWidget));
5967     gtk_cell_layout_pack_start(PGtkCellLayout(FCentralWidget), renderer, True);
5968     if not (ACombo.Style in [csOwnerDrawFixed, csOwnerDrawVariable, csOwnerDrawEditableFixed, csOwnerDrawEditableVariable]) then
5969       gtk_cell_layout_set_attributes(PGtkCellLayout(FCentralWidget), renderer, ['text', 0, nil]);
5970     gtk_cell_layout_set_cell_data_func(PGtkCellLayout(FCentralWidget), renderer,
5971       @LCLIntfCellRenderer_CellDataFunc, Self, nil);
5972 
5973     FCentralWidget := nil;   //FWidget will be returned from getContainerWidget
5974     // we need cell renderer, but we need f***g GtkEventBox too
5975     // maybe an workaround is possible for csDropDownList (use entry with readonly param).
5976     // if we have GtkEventBox, then ComboBox becomes FCentralWidget.
5977     // Maybe the best thing would be to organize complete combo around GtkEntry
5978     // Anyway , I dont see any mouse button event in this case, only when entry_set_above_child is used.
5979     // FCentralWidget := PGtkComboBox(TGtkComboBox.new_with_model(PGtkTreeModel(ListStore)));
5980     // PGtkEventBox(Result)^.add(FCentralWidget);
5981     // ItemList := TGtkListStoreStringList.Create(PGtkListStore(PGtkComboBox(FCentralWidget)^.get_model), 0, LCLObject);
5982     // g_object_set_data(PGObject(FCentralWidget), GtkListItemLCLListTag, ItemList);
5983     // PGtkEventBox(Result)^.set_visible_window(True);
5984   end;
5985   g_object_unref(ListStore);
5986 
5987 end;
5988 
EatArrowKeysnull5989 function TGtk3ComboBox.EatArrowKeys(const AKey: Word): Boolean;
5990 begin
5991   Result := AKey in [VK_UP, VK_DOWN];
5992 end;
5993 
getTextnull5994 function TGtk3ComboBox.getText: String;
5995 begin
5996   Result := inherited getText;
5997   if Gtk3IsComboBox(GetContainerWidget) then
5998     Result := StrPas(PGtkComboBox(GetContainerWidget)^.get_title);
5999 end;
6000 
6001 procedure TGtk3ComboBox.setText(AValue: String);
6002 begin
6003   if Gtk3IsComboBox(FWidget) then
6004     PGtkComboBox(GetContainerWidget)^.set_title(PgChar(AValue));
6005 end;
6006 
6007 procedure TGtk3ComboBox.DumpPrivateStructValues(ADbgEvent: String);
6008 var
6009   AComboWidget: PGtkComboBox;
6010   APrivate: PGtkComboBoxPrivate;
6011 begin
6012   exit;
6013   AComboWidget := PGtkComboBox(GetContainerWidget);
6014   APrivate := PGtkComboBoxPrivate(AComboWidget^.priv3);
6015   DebugLn('** COMBO DUMP OF PGtkComboBoxPrivate struct EVENT=',ADbgEvent);
6016   DebugLn('BUTTON=',dbgHex(PtrUInt(APrivate^.button)),' ARROW=',dbgHex(PtrUInt(APrivate^.arrow)),
6017     ' SCROLLEDWINDOW=',dbgHex(PtrUInt(APrivate^.scrolled_window)),
6018     ' CELLVIEW=',dbgHex(PtrUInt(APrivate^.cell_view)),
6019     ' CELLAREA=',dbgHex(PtrUInt(APrivate^.area)));
6020   DebugLn(' PrivatePopupW ',dbgHex(PtrUInt(APrivate^.popup_widget)),
6021   ' PrivatePopupWin ',dbgHex(PtrUInt(APrivate^.popup_window)),' TreeView ',dbgHex(PtrUInt(APrivate^.tree_view)));
6022   if Gtk3IsWidget(APrivate^.popup_widget) then
6023   begin
6024     DebugLn('POPUPWIDGET VISIBLE ',dbgs(APrivate^.popup_widget^.get_visible),
6025       ' PopupInProgress=',dbgs(APrivate^.popup_in_progress),' POPUPSHOWN=',
6026       dbgs(APrivate^.popup_shown),' POPUPIDLE_ID=',dbgs(APrivate^.popup_idle_id));
6027     if Gtk3IsMenu(APrivate^.popup_widget) then
6028       DebugLn('POPUPWIDGET IS MENU ')
6029     else
6030     if Gtk3IsMenuItem(APrivate^.popup_widget) then
6031       DebugLn('POPUPWIDGET IS MENUITEM ');
6032   end;
6033 end;
6034 
CanFocusnull6035 function TGtk3ComboBox.CanFocus: Boolean;
6036 begin
6037   Result := False;
6038   if IsWidgetOK then
6039   begin
6040     if PGtkComboBox(FWidget)^.has_entry then
6041       Result := PGtkComboBox(FWidget)^.get_child^.can_focus
6042     else
6043     if GetButtonWidget <> nil then
6044       Result := GetButtonWidget^.can_focus;
6045   end;
6046 end;
6047 
6048 procedure TGtk3ComboBox.SetFocus;
6049 begin
6050   {$IFDEF GTK3DEBUGFOCUS}
6051   DebugLn('TGtk3ComboBox.SetFocus LCLObject ',dbgsName(LCLObject),' WidgetOK ',dbgs(IsWidgetOK),
6052   ' FWidget <> GetContainerWidget ',dbgs(FWidget <> GetContainerWidget));
6053   {$ENDIF}
6054   if Assigned(LCLObject) then
6055   begin
6056     if IsWidgetOK  then // and (FWidget <> GetContainerWidget) then
6057     begin
6058       if PGtkComboBox(FWidget)^.has_entry then
6059         FWidget^.grab_focus
6060       else
6061       if GetButtonWidget <> nil then
6062         GetButtonWidget^.grab_focus;
6063     end else
6064       inherited SetFocus;
6065   end else
6066     inherited SetFocus;
6067 end;
6068 
6069 procedure Gtk3ComboBoxChanged(ACombo: PGtkComboBox; AData: gpointer); cdecl;
6070 var
6071   Msg: TLMessage;
6072 begin
6073   if AData <> nil then
6074   begin
6075     if TGtk3Widget(AData).InUpdate then
6076       Exit;
6077     FillChar(Msg, SizeOf(Msg), #0);
6078     Msg.Msg := LM_CHANGED;
6079     TGtk3Widget(AData).DeliverMessage(Msg);
6080   end;
6081 end;
6082 
6083 function GtkPopupCloseUp(AData: Pointer): gboolean; cdecl;
6084 var
6085   ComboBox: TCustomComboBox;
6086 begin
6087   ComboBox := TCustomComboBox(TGtk3Widget(AData).LCLObject);
6088   LCLSendCloseUpMsg(TGtk3Widget(AData).LCLObject);
6089   Result := False;// stop the timer
6090 end;
6091 
6092 procedure GtkNotifyCombo(AObject: PGObject; pspec: PGParamSpec; AData: GPointer); cdecl;
6093 var
6094   AValue: TGValue;
6095   ComboBox: TCustomComboBox;
6096 begin
6097   if pspec^.name = 'popup-shown' then
6098   begin
6099     ComboBox := TCustomComboBox(TGtk3Widget(AData).LCLObject);
6100     AValue.g_type := G_TYPE_BOOLEAN;
6101     g_object_get_property(AObject, pspec^.name, @AValue); // get property value
6102     if AValue.data[0].v_int = 0 then // if 0 = False then it is close up
6103       g_timeout_add(0,@GtkPopupCloseUp, AData)
6104     else // in other case it is drop down
6105     begin
6106       ComboBox.IntfGetItems;
6107       LCLSendDropDownMsg(ComboBox);
6108     end;
6109   end;
6110 end;
6111 
6112 procedure Gtk3ComboMenuRealized(AWidget: PGtkWidget; AData: gPointer); cdecl;
6113 begin
6114   DebugLn('Gtk3ComboMenuRealized *****',dbgsName(TGtk3ComboBox(AData).LCLObject));
6115 end;
6116 
6117 procedure TGtk3ComboBox.InitializeWidget;
6118 begin
6119   inherited InitializeWidget;
6120   // appears-as-list make it appear as list ... no way, its read only property.
6121   //OnChange
6122   g_signal_connect_data(GetContainerWidget, 'changed', TGCallback(@Gtk3ComboBoxChanged), Self, nil, 0);
6123   //OnCloseUp
6124   g_signal_connect_data(GetContainerWidget, 'notify', TGCallback(@GtkNotifyCombo), Self, nil, 0);
6125 
6126   //TODO: if we have an entry then use CreateFrom() to create TGtk3Entry
6127   if Gtk3IsEntry(PGtkComboBox(FWidget)^.get_child) then
6128   begin
6129     g_object_set_data(PGtkComboBox(FWidget)^.get_child, 'lclwidget', Self);
6130     g_signal_connect_data(PGtkComboBox(FWidget)^.get_child, 'event', TGCallback(@Gtk3WidgetEvent), Self, nil, 0);
6131   end;
6132   if GetCellView <> nil then
6133   begin
6134     gtk_widget_set_events(FCellView, GDK_ALL_EVENTS_MASK);
6135     g_object_set_data(FCellView, 'lclwidget', Self);
6136     g_signal_connect_data(FCellView, 'event', TGCallback(@Gtk3WidgetEvent), Self, nil, 0);
6137   end;
6138   // set to all combo widgets lclwidget data, so we will easy find TGtk3ComboBox in events.
6139   if PGtkComboBox(GetContainerWidget)^.priv3^.button <> nil then
6140   begin
6141     g_object_set_data(PGObject(PGtkComboBox(GetContainerWidget)^.priv3^.button), 'lclwidget', Self);
6142     g_signal_connect_data(PGObject(PGtkComboBox(GetContainerWidget)^.priv3^.button), 'event', TGCallback(@Gtk3WidgetEvent), Self, nil, 0);
6143   end;
6144   if PGtkComboBox(GetContainerWidget)^.priv3^.popup_widget <> nil then
6145   begin
6146     g_object_set_data(PGObject(PGtkComboBox(GetContainerWidget)^.priv3^.popup_widget), 'lclwidget', Self);
6147     g_signal_connect_data(PGObject(PGtkComboBox(GetContainerWidget)^.priv3^.popup_widget), 'event', TGCallback(@Gtk3WidgetEvent), Self, nil, 0);
6148     PGtkComboBox(GetContainerWidget)^.priv3^.popup_widget^.set_has_window(True);
6149     PGtkComboBox(GetContainerWidget)^.priv3^.popup_widget^.set_can_focus(True);
6150     // g_signal_connect_data(PGObject(PGtkComboBox(GetContainerWidget)^.priv3^.popup_widget), 'map', TGCallback(@Gtk3ComboMenuRealized), Self, nil, 0);
6151   end;
6152   if PGtkComboBox(GetContainerWidget)^.priv3^.area <> nil then
6153     g_object_set_data(PGObject(PGtkComboBox(GetContainerWidget)^.priv3^.area), 'lclwidget', Self);
6154   // if combo doesnt use menu
6155   if PGtkComboBox(GetContainerWidget)^.priv3^.tree_view <> nil then
6156     g_object_set_data(PGObject(PGtkComboBox(GetContainerWidget)^.priv3^.tree_view), 'lclwidget', Self);
6157 end;
6158 
GetDroppedDownnull6159 function TGtk3ComboBox.GetDroppedDown: boolean;
6160 var
6161   AValue: TGValue;
6162 begin
6163   Result := False;
6164   if Assigned(FWidget) and Gtk3IsComboBox(GetContainerWidget) then
6165   begin
6166     AValue.g_type := G_TYPE_BOOLEAN;
6167     g_object_get_property(PGObject(GetContainerWidget), 'popup-shown', @AValue);
6168     Result := AValue.data[0].v_int <> 0;
6169   end;
6170 end;
6171 
6172 { TGtk3Button }
6173 
getLayoutnull6174 function TGtk3Button.getLayout: Integer;
6175 begin
6176   Result := FLayout;
6177   // PGtkButton(FWidget)^.get_image_position;
6178 end;
6179 
getMarginnull6180 function TGtk3Button.getMargin: Integer;
6181 begin
6182   Result := FMargin;
6183 end;
6184 
6185 procedure TGtk3Button.SetLayout(AValue: Integer);
6186 begin
6187   FLayout := AValue;
6188   if IsWidgetOk then
6189   begin
6190     PGtkButton(FWidget)^.set_image_position(AValue);
6191     // set margin and spacing when layout is changed
6192     SetMargin(FMargin);
6193   end;
6194 end;
6195 
6196 procedure TGtk3Button.SetMargin(AValue: Integer);
6197 begin
6198   FMargin := AValue;
6199   if not IsWidgetOK then
6200     exit;
6201   if FMargin = -1 then
6202     PGtkButton(FWidget)^.set_alignment(0.5, 0.5)
6203   else
6204   begin
6205     case FLayout of
6206       0 {GTK_POS_LEFT}: PGtkButton(FWidget)^.set_alignment(0, 0.5);
6207       1 {GTK_POS_RIGHT}: PGtkButton(FWidget)^.set_alignment(1.0, 0.5);
6208       2 {GTK_POS_TOP}: PGtkButton(FWidget)^.set_alignment(0.5, 0);
6209       3 {GTK_POS_BOTTOM}: PGtkButton(FWidget)^.set_alignment(0.5, 1);
6210     end;
6211   end;
6212 end;
6213 
6214 procedure TGtk3Button.SetSpacing(AValue: Integer);
6215 var
6216   ATGValue: TGValue;
6217   AImage: PGtkWidget;
6218 begin
6219   // if FSpacing=AValue then Exit;
6220   FSpacing:=AValue;
6221   if AValue < 0 then
6222     FSpacing := 2;
6223   ATGValue.g_type := G_TYPE_INT;
6224   ATGValue.set_int(AValue);
6225 
6226   // no way under gtk3 ... we cannot set style property image-spacing
6227   // so we are using cheat
6228   AImage := PGtkButton(FWidget)^.get_image;
6229   if AImage <> nil then
6230   begin
6231     if AValue < 0 then
6232       AVAlue := 0;
6233     //TODO: margin depends on layout ! This is ok for left (default) layout
6234     PGtkImage(AImage)^.set_margin_right(AValue);
6235   end;
6236 end;
6237 
getTextnull6238 function TGtk3Button.getText: String;
6239 begin
6240   if IsWidgetOK then
6241     Result := PGtkButton(FWidget)^.get_label
6242   else
6243     Result := '';
6244 end;
6245 
6246 procedure TGtk3Button.setText(AValue: String);
6247 begin
6248   if IsWidgetOk then
6249     PGtkButton(FWidget)^.set_label(PgChar(AValue));
6250 end;
6251 
CreateWidgetnull6252 function TGtk3Button.CreateWidget(const Params: TCreateParams): PGtkWidget;
6253 begin
6254   Result := PGtkWidget(TGtkButton.new);
6255   FMargin := -1;
6256   FLayout := GTK_POS_LEFT;
6257   FSpacing := 2; // default gtk3 spacing is 2
6258 end;
6259 
IsWidgetOknull6260 function TGtk3Button.IsWidgetOk: Boolean;
6261 begin
6262   Result := (FWidget <> nil) and Gtk3IsButton(FWidget);
6263 end;
6264 
6265 procedure TGtk3Button.SetDefault(const ADefault: Boolean);
6266 begin
6267   if IsWidgetOk then
6268     GetContainerWidget^.set_can_default(ADefault);
6269 end;
6270 
6271 { TGtk3ToggleButton }
6272 procedure Gtk3Toggled(AWidget: PGtkToggleButton; AData: gPointer); cdecl;
6273 var
6274   Msg: TLMessage;
6275 begin
6276   FillChar(Msg, SizeOf(Msg), 0);
6277   Msg.Msg := LM_CHANGED;
6278   if (TGtk3Widget(AData).LCLObject <> nil) and not TGtk3Widget(AData).InUpdate then
6279     TGtk3Widget(AData).DeliverMessage(Msg, False);
6280 end;
6281 
6282 procedure TGtk3ToggleButton.InitializeWidget;
6283 begin
6284   inherited InitializeWidget;
6285   g_signal_connect_data(FWidget, 'toggled', TGCallback(@Gtk3Toggled), Self, nil, 0);
6286 end;
6287 
CreateWidgetnull6288 function TGtk3ToggleButton.CreateWidget(const Params: TCreateParams
6289   ): PGtkWidget;
6290 begin
6291   Result := PGtkWidget(TGtkToggleButton.new);
6292 end;
6293 
6294 { TGtk3CheckBox }
6295 
GetStatenull6296 function TGtk3CheckBox.GetState: TCheckBoxState;
6297 begin
6298   Result := cbUnchecked;
6299   if IsWidgetOk then
6300   begin
6301     if PGtkCheckButton(FWidget)^.get_inconsistent then
6302       Result := cbGrayed
6303     else
6304     if PGtkCheckButton(FWidget)^.get_active then
6305       Result := cbChecked;
6306   end;
6307 end;
6308 
6309 procedure TGtk3CheckBox.SetState(AValue: TCheckBoxState);
6310 begin
6311   if IsWidgetOK then
6312   begin
6313     if AValue = cbGrayed then
6314       PGtkCheckButton(FWidget)^.set_inconsistent(True)
6315     else
6316       PGtkCheckButton(FWidget)^.set_active(AValue = cbChecked);
6317   end;
6318 end;
6319 
CreateWidgetnull6320 function TGtk3CheckBox.CreateWidget(const Params: TCreateParams): PGtkWidget;
6321 begin
6322   Result := PGtkWidget(TGtkCheckButton.new);
6323 end;
6324 
6325 { TGtk3RadioButton }
6326 
CreateWidgetnull6327 function TGtk3RadioButton.CreateWidget(const Params: TCreateParams): PGtkWidget;
6328 var
6329   w: PGtkWidget;
6330   ctl, Parent: TWinControl;
6331   rb: TRadioButton;
6332   pl: PGsList;
6333 begin
6334   if Self.LCLObject.Name='HiddenRadioButton' then
6335     exit;
6336   Result := PGtkWidget(TGtkRadioButton.new(nil));
6337   ctl := Self.LCLObject;
6338   if Assigned(ctl) then
6339   begin
6340     Parent := ctl.Parent;
6341     if (Parent is TRadioGroup) and (TRadioGroup(Parent).Items.Count>0) then
6342     begin
6343       rb := TRadioButton(Parent.Controls[0]);
6344       if rb<>ctl then
6345       begin
6346         w := TGtk3RadioButton(rb.Handle).Widget;
6347         pl := PGtkRadioButton(w)^.get_group;
6348         PGtkRadioButton(Result)^.set_group(pl);
6349       end;
6350     end;
6351   end;
6352 end;
6353 
6354 procedure TGtk3RadioButton.InitializeWidget;
6355 begin
6356   if Self.LCLObject.Name='HiddenRadioButton' then
6357   begin
6358     exit;
6359    { PGtkRadioButton(Self.Widget)^.set_group(nil);
6360    // PGtkRadioButton(Self.Widget)^.set_inconsistent(true);
6361     PGtkRadioButton(Self.Widget)^.set_visible(false);}
6362   end;
6363   inherited InitializeWidget;
6364 end;
6365 
6366 { TGtk3CustomControl }
6367 
CreateWidgetnull6368 function TGtk3CustomControl.CreateWidget(const Params: TCreateParams
6369   ): PGtkWidget;
6370 var
6371   FUseLayout: Boolean;
6372 begin
6373   FScrollX := 0;
6374   FScrollY := 0;
6375   FHasPaint := True;
6376   FUseLayout := False;
6377   if FUseLayout then
6378     FWidgetType := [wtWidget, wtLayout, wtScrollingWin, wtCustomControl]
6379   else
6380     FWidgetType := [wtWidget, wtContainer, wtTabControl, wtScrollingWin, wtCustomControl];
6381   Result := PGtkScrolledWindow(TGtkScrolledWindow.new(nil, nil));
6382 
6383   if FUseLayout then
6384     FCentralWidget := TGtkLayout.new(nil, nil)
6385   else
6386     FCentralWidget := TGtkFixed.new;
6387 
6388   FCentralWidget^.set_has_window(True);
6389 
6390   // this is deprecated since 3.8 .add() should be used
6391   // in this case viewport should be blocked somehow.....
6392   //if FUseLayout or (gtk_get_major_version > 3 or gtk_get_minor_version >=8 )then
6393   //  PGtkScrolledWindow(Result)^.add(FCentralWidget)
6394   //else
6395   //  PGtkScrolledWindow(Result)^.add_with_viewport(FCentralWidget);
6396 
6397   // gtk_container_add() will now automatically add a GtkViewport if the child doesn't implement GtkScrollable.
6398 
6399   PGtkScrolledWindow(Result)^.add(FCentralWidget);
6400 
6401   // PGtkViewport(PGtkScrolledWindow(Result)^.get_child)^.;
6402   // also works fine with 3.6 but raises asserts
6403   // PGtkScrolledWindow(Result)^.add(FCentralWidget);
6404   Result^.set_can_focus(False);
6405   FCentralWidget^.set_can_focus(True);
6406 end;
6407 
EatArrowKeysnull6408 function TGtk3CustomControl.EatArrowKeys(const AKey: Word): Boolean;
6409 begin
6410   Result := False;
6411 end;
6412 
6413 procedure TGtk3CustomControl.InitializeWidget;
6414 begin
6415   inherited InitializeWidget;
6416   SetScrollBarsSignalHandlers;
6417   g_signal_connect_data(GetScrolledWindow,'scroll-event', TGCallback(@Gtk3ScrolledWindowScrollEvent), Self, nil, 0);
6418 end;
6419 
getClientRectnull6420 function TGtk3CustomControl.getClientRect: TRect;
6421 var
6422   Allocation: TGtkAllocation;
6423   R: TRect;
6424   w: gint;
6425   h: gint;
6426   x: gint;
6427   y: gint;
6428   AViewPort: PGtkViewport;
6429 begin
6430   // Result := inherited getClientRect;
6431   AViewPort := PGtkViewPort(FCentralWidget^.get_parent);
6432   if Gtk3IsViewPort(AViewPort) and Gtk3IsGdkWindow(AViewPort^.get_view_window) then
6433   begin
6434     AViewPort^.get_view_window^.get_geometry(@x, @y, @w, @h);
6435     Result := Rect(0, 0, AViewPort^.get_view_window^.get_width, AViewPort^.get_view_window^.get_height);
6436     // DebugLn('TGtk3CustomControl.GetClientRect via Viewport ',dbgsName(LCLObject),' Result ',dbgs(Result),' X=',dbgs(X),' Y=',dbgs(Y));
6437     exit;
6438   end else
6439     FCentralWidget^.get_allocation(@Allocation);
6440 
6441   with Allocation do
6442     R := Rect(x, y, width + x, height + y);
6443 
6444   if IsRectEmpty(R) then
6445     R := Rect(0, 0, 0, 0);
6446 
6447   Result := R;
6448   // DebugLn('TGtk3CustomControl.GetClientRect normal ',dbgsName(LCLObject),' Result ',dbgs(Result));
6449   OffsetRect(Result, -Result.Left, -Result.Top);
6450 end;
6451 
getHorizontalScrollbarnull6452 function TGtk3CustomControl.getHorizontalScrollbar: PGtkScrollbar;
6453 begin
6454   Result := nil;
6455   if not IsWidgetOk then
6456     exit;
6457   Result := PGtkScrollBar(PGtkScrolledWindow(Widget)^.get_hscrollbar);
6458   g_object_set_data(Result,'lclwidget',Self);
6459 end;
6460 
getVerticalScrollbarnull6461 function TGtk3CustomControl.getVerticalScrollbar: PGtkScrollbar;
6462 begin
6463   Result := nil;
6464   if not IsWidgetOk then
6465     exit;
6466   Result := PGtkScrollBar(PGtkScrolledWindow(Widget)^.get_vscrollbar);
6467   g_object_set_data(Result,'lclwidget',Self);
6468 end;
6469 
TGtk3CustomControl.GetScrolledWindownull6470 function TGtk3CustomControl.GetScrolledWindow: PGtkScrolledWindow;
6471 begin
6472   if IsWidgetOK then
6473     Result := PGtkScrolledWindow(Widget)
6474   else
6475     Result := nil;
6476 end;
6477 
6478 { TGtk3ScrollingWinControl }
6479 
CreateWidgetnull6480 function TGtk3ScrollingWinControl.CreateWidget(const Params: TCreateParams
6481   ): PGtkWidget;
6482 begin
6483   FHasPaint := True;
6484   FScrollX := 0;
6485   FScrollY := 0;
6486   // layout is crap under gtk3
6487   (*
6488   FWidgetType := [wtWidget, wtLayout, wtScrollingWin];
6489   Result := PGtkScrolledWindow(TGtkScrolledWindow.new(nil, nil));
6490   FCentralWidget := TGtkLayout.new(nil, nil);
6491   FCentralWidget^.set_has_window(True);
6492   FCentralWidget^.show;
6493 
6494   PGtkScrolledWindow(Result)^.add(FCentralWidget);
6495   *)
6496   FWidgetType := [wtWidget, wtContainer, wtScrollingWin, wtScrollingWinControl];
6497   Result := PGtkScrolledWindow(TGtkScrolledWindow.new(nil, nil));
6498   FCentralWidget := TGtkFixed.new;
6499   FCentralWidget^.set_has_window(True);
6500   FCentralWidget^.show;
6501 
6502   PGtkScrolledWindow(Result)^.add_with_viewport(FCentralWidget);
6503   // PGtkScrolledWindow(Result)^.add(FCentralWidget);
6504 
6505   PGtkViewport(PGtkScrolledWindow(Result)^.get_child)^.set_shadow_type(BorderStyleShadowMap[bsNone]);
6506   PGtkScrolledWindow(Result)^.set_shadow_type(BorderStyleShadowMap[TScrollingWinControl(LCLObject).BorderStyle]);
6507   PGtkScrolledWindow(Result)^.get_vscrollbar^.set_can_focus(False);
6508   PGtkScrolledWindow(Result)^.get_hscrollbar^.set_can_focus(False);
6509   PGtkScrolledWindow(Result)^.set_policy(GTK_POLICY_NEVER, GTK_POLICY_NEVER);
6510 
6511   // this is very important
6512   PGtkScrolledWindow(Result)^.set_can_focus(False);
6513   FCentralWidget^.set_can_focus(True);
6514 end;
6515 
6516 { TGtk3Window }
6517 
TGtk3Window.GetTitlenull6518 function TGtk3Window.GetTitle: String;
6519 begin
6520   Result:=PGtkWindow(FWidget)^.get_title();
6521 end;
6522 
6523 procedure TGtk3Window.SetIcon(AValue: PGdkPixBuf);
6524 begin
6525   // if FIcon=AValue then Exit;
6526   if Assigned(FIcon) then
6527   begin
6528     FIcon^.unref;
6529     FIcon := nil;
6530   end;
6531   if Gtk3IsGdkPixbuf(AValue) then
6532     FIcon := PGdkPixbuf(AValue)^.copy
6533   else
6534     FIcon := nil;
6535   //  DebugLn('Setting icon ',dbgHex(PtrUInt(FIcon)),' AppIcon ',dbgHex(PtrUInt(GTK3WidgetSet.AppIcon)));
6536   PGtkWindow(Widget)^.set_icon(FIcon);
6537 end;
6538 
GetSkipTaskBarHintnull6539 function TGtk3Window.GetSkipTaskBarHint: Boolean;
6540 begin
6541   Result := False;
6542   if IsWidgetOK then
6543     Result := PGtkWindow(Widget)^.get_skip_taskbar_hint;
6544 end;
6545 
6546 procedure TGtk3Window.SetSkipTaskBarHint(AValue: Boolean);
6547 begin
6548   if IsWidgetOK then
6549     PGtkWindow(Widget)^.set_skip_taskbar_hint(AValue);
6550 end;
6551 
6552 procedure TGtk3Window.SetTitle(AValue: String);
6553 begin
6554   PGtkWindow(FWidget)^.set_title(PGChar(AValue));
6555 end;
6556 
Gtk3WindowStatenull6557 function Gtk3WindowState(AWidget: PGtkWidget; AEvent: PGdkEvent; AData: gPointer): GBoolean; cdecl;
6558 var
6559   Msg: TLMSize;
6560   AState: TGdkWindowState;
6561   AScreen: PGdkScreen;
6562   ActiveWindow: PGdkWindow;
6563 begin
6564   Result := False;
6565   FillChar(Msg, SizeOf(Msg), #0);
6566 
6567   AScreen := AWidget^.window^.get_screen;
6568   ActiveWindow := AScreen^.get_active_window;
6569   (*
6570   if ActiveWindow <> AWidget^.window then
6571     TGtk3Window(AData).Gtk3ActivateWindow(nil)
6572   else
6573     TGtk3Window(AData).Gtk3ActivateWindow(AEvent);
6574   *)
6575   // window state isn't changed on activate/deactivate, so must provide another solution
6576   // DebugLn('Gtk3WindowState ',dbgsName(TGtk3Widget(AData).LCLObject),' changedmask=',dbgs(AEvent^.window_state.changed_mask),
6577   // ' newstate ',dbgs(AEvent^.window_state.new_window_state),' currentState ', dbgs(TGtk3Window(AData).GetWindowState),
6578   // ' WITHDRAWN ? ',dbgs(TGtk3Window(AData).getWindowState and GDK_WINDOW_STATE_WITHDRAWN));
6579 
6580   Msg.Msg := LM_SIZE;
6581   Msg.SizeType := SIZE_RESTORED;
6582 
6583   AState := AEvent^.window_state.new_window_state AND NOT GDK_WINDOW_STATE_FOCUSED;
6584 
6585   if AState = 0 then
6586   begin
6587     if (AEvent^.window_state.changed_mask = GDK_WINDOW_STATE_ICONIFIED) or
6588       (AEvent^.window_state.changed_mask = GDK_WINDOW_STATE_MAXIMIZED) or
6589       (AEvent^.window_state.changed_mask = GDK_WINDOW_STATE_FULLSCREEN) then
6590       // restore win
6591     else
6592       exit;
6593   end;
6594   // PGtkWindow(nil)^.window^.get_state;
6595   if AState and GDK_WINDOW_STATE_ICONIFIED <> 0 then
6596     Msg.SizeType := SIZE_MINIMIZED
6597   else
6598   if AState and GDK_WINDOW_STATE_FULLSCREEN <> 0 then
6599     Msg.SizeType := SIZE_FULLSCREEN
6600   else
6601   if AState and GDK_WINDOW_STATE_MAXIMIZED <> 0 then
6602     Msg.SizeType := SIZE_MAXIMIZED;
6603 
6604   Msg.SizeType := Msg.SizeType or Size_SourceIsInterface;
6605 
6606   Msg.Width := Word(AWidget^.window^.get_width);
6607   Msg.Height := Word(AWidget^.window^.get_height);
6608   // DebugLn('GetWindowState SizeType=',dbgs(Msg.SizeType),' realized ',dbgs(AWidget^.get_realized));
6609   TGtk3Window(AData).DeliverMessage(Msg);
6610   // DeliverMessage(Msg);
6611 end;
6612 
CreateWidgetnull6613 function TGtk3Window.CreateWidget(const Params: TCreateParams): PGtkWidget;
6614 var
6615   AForm: TCustomForm;
6616 begin
6617   FIcon := nil;
6618   FScrollX := 0;
6619   FScrollY := 0;
6620 
6621   FHasPaint := True;
6622   AForm := TCustomForm(LCLObject);
6623 
6624   if not Assigned(LCLObject.Parent) then
6625   begin
6626     Result := TGtkWindow.new(GTK_WINDOW_TOPLEVEL);
6627     FWidgetType := [wtWidget, wtLayout, wtScrollingWin, wtWindow];
6628   end else
6629   begin
6630     Result := PGtkScrolledWindow(TGtkScrolledWindow.new(nil, nil));
6631     FWidgetType := [wtWidget, wtLayout, wtScrollingWin, wtCustomControl]
6632   end;
6633 
6634   FBox := TGtkVBox.new(GTK_ORIENTATION_VERTICAL, 0);
6635 
6636   //TODO: when menu is added dynamically to the form create FMenuBar
6637   if (AForm.Menu <> nil) then
6638   begin
6639     FMenuBar := TGtkMenuBar.new; // our menubar (needed for main menu)
6640     // MenuBar
6641     //  -> Menu    Menu2
6642     //    Item 1   Item 3
6643     //    Item 2
6644     g_object_set_data(Result,'lclmenubar',GPointer(1));
6645     FBox^.pack_start(FMenuBar, False, False, 0);
6646   end;
6647 
6648   FScrollWin := PGtkScrolledWindow(TGtkScrolledWindow.new(nil, nil));
6649   g_object_set_data(FScrollWin,'lclscrollingwindow',GPointer(1));
6650   g_object_set_data(PGObject(FScrollWin), 'lclwidget', Self);
6651 
6652 
6653   FCentralWidget := TGtkLayout.new(nil, nil);
6654   FCentralWidget^.set_has_window(True);
6655 
6656   if AForm.AutoScroll then
6657     FScrollWin^.add(FCentralWidget)
6658   else
6659     FScrollWin^.add_with_viewport(FCentralWidget);
6660 
6661   FScrollWin^.show;
6662   FBox^.pack_end(FScrollWin, True, True, 0);
6663   FBox^.show;
6664 
6665   FScrollWin^.get_vscrollbar^.set_can_focus(False);
6666   FScrollWin^.get_hscrollbar^.set_can_focus(False);
6667   FScrollWin^.set_policy(GTK_POLICY_NEVER, GTK_POLICY_NEVER);
6668   PGtkContainer(Result)^.add(FBox);
6669   g_signal_connect_data(Result,'window-state-event', TGCallback(@Gtk3WindowState), Self, nil, 0);
6670 
6671   //REMOVE THIS, USED TO TRACK MOUSE MOVE OVER WIDGET TO SEE SIZE OF FIXED !
6672   //g_object_set_data(PGObject(FScrollWin), 'lcldebugscrollwin', Self);
6673   //g_object_set_data(PGObject(FCentralWidget), 'lcldebugfixed', Self);
6674   //g_object_set_data(PGObject(Result), 'lcldebugwindow', Self);
6675 end;
6676 
EatArrowKeysnull6677 function TGtk3Window.EatArrowKeys(const AKey: Word): Boolean;
6678 begin
6679   Result := False;
6680 end;
6681 
getTextnull6682 function TGtk3Window.getText: String;
6683 begin
6684   Result := Title;
6685 end;
6686 
6687 procedure TGtk3Window.setText(AValue: String);
6688 begin
6689   Title := AValue;
6690 end;
6691 
getClientRectnull6692 function TGtk3Window.getClientRect: TRect;
6693 var
6694   Allocation: TGtkAllocation;
6695   R: TRect;
6696   w: gint;
6697   h: gint;
6698   x: gint;
6699   y: gint;
6700   AViewPort: PGtkViewport;
6701 begin
6702   AViewPort := PGtkViewPort(FCentralWidget^.get_parent);
6703   if Gtk3IsViewPort(AViewPort) and Gtk3IsGdkWindow(AViewPort^.get_view_window) then
6704   begin
6705     AViewPort^.get_view_window^.get_geometry(@x, @y, @w, @h);
6706     Result := Rect(0, 0, AViewPort^.get_view_window^.get_width, AViewPort^.get_view_window^.get_height);
6707     // DebugLn('GetClientRect via Viewport ',dbgsName(LCLObject),' Result ',dbgs(Result));
6708     exit;
6709   end else
6710     FCentralWidget^.get_allocation(@Allocation);
6711 
6712   with Allocation do
6713     R := Rect(x, y, width + x, height + y);
6714 
6715   if IsRectEmpty(R) then
6716     R := Rect(0, 0, 0, 0);
6717 
6718   Result := R;
6719   OffsetRect(Result, -Result.Left, -Result.Top);
6720 
6721   // DebugLn('GetClientRect ',dbgsName(LCLObject),' Result ',dbgs(Result));
6722 end;
6723 
getHorizontalScrollbarnull6724 function TGtk3Window.getHorizontalScrollbar: PGtkScrollbar;
6725 begin
6726   Result := nil;
6727   if not IsWidgetOk then
6728     exit;
6729   Result := PGtkScrollBar(FScrollWin^.get_hscrollbar);
6730 end;
6731 
getVerticalScrollbarnull6732 function TGtk3Window.getVerticalScrollbar: PGtkScrollbar;
6733 begin
6734   Result := nil;
6735   if not IsWidgetOk then
6736     exit;
6737   Result := PGtkScrollBar(FScrollWin^.get_vscrollbar);
6738 end;
6739 
GetScrolledWindownull6740 function TGtk3Window.GetScrolledWindow: PGtkScrolledWindow;
6741 begin
6742   if IsWidgetOK then
6743     Result := FScrollWin
6744   else
6745     Result := nil;
6746 end;
6747 
6748 destructor TGtk3Window.Destroy;
6749 begin
6750   // DebugLn('TGtk3Window.Destroy AWidget ',dbgs(IsWidgetOK));
6751   if Gtk3IsGdkPixbuf(FIcon) then
6752   begin
6753     FIcon^.unref;
6754     FIcon := nil;
6755   end;
6756   inherited Destroy;
6757 end;
6758 
6759 procedure TGtk3Window.Activate;
6760 begin
6761   if IsWidgetOk then
6762   begin
6763     if Gtk3IsGdkWindow(PGtkWindow(FWidget)^.window) then
6764     begin
6765       PGtkWindow(FWidget)^.window^.raise_;
6766       PGtkWindow(FWidget)^.present;
6767       PGtkWindow(FWidget)^.activate;
6768     end;
6769   end;
6770 end;
6771 
6772 procedure TGtk3Window.Gtk3ActivateWindow(AEvent: PGdkEvent);
6773 var
6774   MsgActivate: TLMActivate;
6775   FIsActivated: Boolean;
6776 begin
6777   //gtk3 does not handle activate/deactivate at all
6778   //even cannot catch it via GDK_FOCUS event ?!?
6779   FillChar(MsgActivate, SizeOf(MsgActivate), #0);
6780   MsgActivate.Msg := LM_ACTIVATE;
6781 
6782   if (AEvent <> nil) and PGtkWindow(Widget)^.is_active then
6783     MsgActivate.Active := WA_ACTIVE
6784   else
6785     MsgActivate.Active := WA_INACTIVE;
6786   MsgActivate.ActiveWindow := HWND(Self);
6787 
6788   // DebugLn('TGtk3Window.Gtk3ActivateWindow ',dbgsName(LCLObject),' Active ',dbgs(PGtkWindow(Widget)^.is_active),
6789   // ' CustomFormActive ',dbgs(TCustomForm(LCLObject).Active));
6790   FIsActivated := TCustomForm(LCLObject).Active;
6791   {do not send activate if form is already activated,
6792    also do not send activate if TCustomForm.Parent is assigned
6793    since it's form embedded into another control or form}
6794   if (Boolean(MsgActivate.Active) = FIsActivated) or (LCLObject.Parent <> nil) then
6795   else
6796   begin
6797     // DebugLn('TGtk3Window.Gtk3ActivateWindow Active ',dbgs(MsgActivate.Active = WA_ACTIVE),
6798     //  ' Message delivery to lcl ',dbgs(MsgActivate.Active));
6799     DeliverMessage(MsgActivate);
6800   end;
6801 end;
6802 
Gtk3CloseQuerynull6803 function TGtk3Window.Gtk3CloseQuery: Boolean;
6804 var
6805   Msg : TLMessage;
6806 begin
6807   {$IFDEF GTK3DEBUGCORE}
6808     DebugLn('TGtk3Window.Gtk3CloseQuery');
6809   {$ENDIF}
6810   FillChar(Msg, SizeOf(Msg), 0);
6811 
6812   Msg.Msg := LM_CLOSEQUERY;
6813 
6814   DeliverMessage(Msg);
6815 
6816   Result := False;
6817 end;
6818 
TGtk3Window.GetWindownull6819 function TGtk3Window.GetWindow: PGdkWindow;
6820 begin
6821   Result := FWidget^.window;
6822 end;
6823 
TGtk3Window.GetMenuBarnull6824 function TGtk3Window.GetMenuBar: PGtkMenuBar;
6825 begin
6826   Result := FMenuBar;
6827 end;
6828 
GetBoxnull6829 function TGtk3Window.GetBox: PGtkBox;
6830 begin
6831   Result := FBox;
6832 end;
6833 
GetWindowStatenull6834 function TGtk3Window.GetWindowState: TGdkWindowState;
6835 begin
6836   Result := 0;
6837   if IsWidgetOK and (FWidget^.get_realized) then
6838     Result := FWidget^.window^.get_state;
6839 end;
6840 
6841 { TGtk3HintWindow }
6842 
getTextnull6843 function TGtk3HintWindow.getText: String;
6844 begin
6845   Result := FText;
6846 end;
6847 
6848 procedure TGtk3HintWindow.setText(AValue: String);
6849 begin
6850   FText := AValue;
6851 end;
6852 
TGtk3HintWindow.CreateWidgetnull6853 function TGtk3HintWindow.CreateWidget(const Params: TCreateParams): PGtkWidget;
6854 var
6855   AForm: THintWindow;
6856 begin
6857   FText := '';
6858   FHasPaint := True;
6859   AForm := THintWindow(LCLObject);
6860 
6861   FWidgetType := [wtWidget, wtContainer, wtScrollingWin, wtWindow, wtHintWindow];
6862 
6863   Result := TGtkWindow.new(GTK_WINDOW_POPUP);
6864 
6865   FBox := TGtkVBox.new(GTK_ORIENTATION_VERTICAL, 0);
6866 
6867   if (AForm.Menu <> nil) then
6868   begin
6869     FMenuBar := TGtkMenuBar.new; // our menubar (needed for main menu)
6870     // MenuBar
6871     //  -> Menu    Menu2
6872     //    Item 1   Item 3
6873     //    Item 2
6874     g_object_set_data(Result,'lclmenubar',GPointer(1));
6875     FBox^.pack_start(FMenuBar, False, False, 0);
6876   end;
6877 
6878   FScrollWin := PGtkScrolledWindow(TGtkScrolledWindow.new(nil, nil));
6879   g_object_set_data(FScrollWin,'lclscrollingwindow',GPointer(1));
6880   g_object_set_data(PGObject(FScrollWin), 'lclwidget', Self);
6881 
6882 
6883   //FCentralWidget := TGtkLayout.new(nil, nil);
6884   FCentralWidget := TGtkFixed.new;
6885   FCentralWidget^.set_has_window(True);
6886   FCentralWidget^.show;
6887 
6888   FScrollWin^.add_with_viewport(FCentralWidget);
6889   // FScrollWin^.add(FCentralWidget);
6890   FScrollWin^.show;
6891   FBox^.pack_end(FScrollWin, True, True, 0);
6892   FBox^.show;
6893 
6894   FScrollWin^.get_vscrollbar^.set_can_focus(False);
6895   FScrollWin^.get_hscrollbar^.set_can_focus(False);
6896   FScrollWin^.set_policy(GTK_POLICY_NEVER, GTK_POLICY_NEVER);
6897   PGtkContainer(Result)^.add(FBox);
6898   // FWidgetType := FWidgetType + [wtContainer, wtWindow];
6899   // Result := TGtkWindow.new(GTK_WINDOW_POPUP);
6900   // FCentralWidget := TGtkFixed.new;
6901   // PGtkWindow(Result)^.add(FCentralWidget);
6902 end;
6903 
6904 { TGtk3Dialog }
6905 
TGtk3Dialog.CreateWidgetnull6906 function TGtk3Dialog.CreateWidget(const Params: TCreateParams): PGtkWidget;
6907 begin
6908   FWidgetType := [wtWidget, wtDialog];
6909   Result := TGtkDialog.new;
6910   DebugLn('WARNING: TGtk3Dialog.CreateWidget should be used in real dialog constructor .');
6911 end;
6912 
6913 procedure TGtk3Dialog.InitializeWidget;
6914 begin
6915   g_object_set_data(FWidget,'lclwidget', Self);
6916 end;
6917 
6918 
6919 { TGtk3FileDialog }
6920 
CreateWidgetnull6921 function TGtk3FileDialog.CreateWidget(const Params: TCreateParams): PGtkWidget;
6922 begin
6923   DebugLn('ERROR: TGtk3FileDialog.CreateWidget error.');
6924   // Result := nil;
6925   Result := TGtkFileChooserDialog.new;
6926   // gtk_file_chooser_dialog_new();
6927 end;
6928 
6929 constructor TGtk3FileDialog.Create(const ACommonDialog: TCommonDialog);
6930 
6931 var
6932   FileDialog: TFileDialog absolute ACommonDialog;
6933   Action: TGtkFileChooserAction;
6934   Button1: String;
6935   AFileDialog: PGtkFileChooserDialog;
6936 begin
6937   inherited Create;
6938   FContext := 0;
6939   FHasPaint := False;
6940   FWidget := nil;
6941   FOwner := nil;
6942   FCentralWidget := nil;
6943   FOwnWidget := True;
6944   // Initializes the properties
6945   FProps := nil;
6946   LCLObject := nil;
6947   FKeysToEat := [VK_TAB, VK_RETURN, VK_ESCAPE];
6948   FWidgetType := [wtWidget, wtDialog];
6949 
6950   // FHasPaint := False;
6951   CommonDialog := ACommonDialog;
6952   // Defines an action for the dialog and creates it
6953   Action := GTK_FILE_CHOOSER_ACTION_OPEN;
6954   Button1 := GTK_STOCK_OPEN;
6955 
6956   if (FileDialog is TSaveDialog) or (FileDialog is TSavePictureDialog) then
6957   begin
6958     Action := GTK_FILE_CHOOSER_ACTION_SAVE;
6959     Button1 := GTK_STOCK_SAVE;
6960   end
6961   else
6962   if FileDialog is TSelectDirectoryDialog then
6963   begin
6964     Action := GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
6965     Button1 := GTK_STOCK_OPEN;
6966   end;
6967 
6968   FWidget := gtk_file_chooser_dialog_new(PgChar(FileDialog.Title), nil,
6969     Action, PChar(GTK_STOCK_CANCEL),
6970     [GTK_RESPONSE_CANCEL, PChar(Button1), GTK_RESPONSE_OK, nil]);
6971 
6972   AFileDialog := PGtkFileChooserDialog(FWidget);
6973   if FileDialog is TSaveDialog then
6974   begin
6975     gtk_file_chooser_set_do_overwrite_confirmation(PGtkFileChooser(AFileDialog),
6976       ofOverwritePrompt in TOpenDialog(FileDialog).Options);
6977   end;
6978 
6979   if FileDialog.InitialDir <> '' then
6980     gtk_file_chooser_set_current_folder(PGtkFileChooser(AFileDialog), Pgchar(FileDialog.InitialDir));
6981 
6982   if gtk_file_chooser_get_action(PGtkFileChooser(AFileDialog)) in
6983     [GTK_FILE_CHOOSER_ACTION_SAVE, GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER]
6984   then
6985     gtk_file_chooser_set_current_name(PGtkFileChooser(AFileDialog), Pgchar(FileDialog.FileName));
6986 
6987   InitializeWidget;
6988 end;
6989 
6990 { TGtk3FontSelectionDialog }
6991 
TGtk3FontSelectionDialog.CreateWidgetnull6992 function TGtk3FontSelectionDialog.CreateWidget(const Params: TCreateParams
6993   ): PGtkWidget;
6994 begin
6995   Result := TGtkFontSelectionDialog.new;
6996 end;
6997 
6998 constructor TGtk3FontSelectionDialog.Create(const ACommonDialog: TCommonDialog);
6999 begin
7000   inherited Create;
7001   FContext := 0;
7002   FHasPaint := False;
7003   FWidget := nil;
7004   FOwner := nil;
7005   FCentralWidget := nil;
7006   FOwnWidget := True;
7007   // Initializes the properties
7008   FProps := nil;
7009   LCLObject := nil;
7010   FKeysToEat := [VK_TAB, VK_RETURN, VK_ESCAPE];
7011   FWidgetType := [wtWidget, wtDialog];
7012 
7013   // FHasPaint := False;
7014   CommonDialog := ACommonDialog;
7015 end;
7016 
7017 
7018 end.
7019 
7020