1{
2 *****************************************************************************
3 *                             Gtk2WSStdCtrls.pp                             *
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}
16unit Gtk2WSStdCtrls;
17
18{$mode objfpc}{$H+}
19{$PACKRECORDS c}
20
21interface
22
23uses
24  // RTL
25  glib2,  gdk2, gtk2,
26  Classes, SysUtils, Math,
27  // LazUtils
28  LazLoggerBase, LazTracer,
29  // LCL
30  Controls, Graphics, StdCtrls, LMessages, LCLType, LazUtf8Classes, LazUTF8,
31  // Widgetset
32  WSControls, WSProc, WSStdCtrls, Gtk2Int, Gtk2Def,
33  Gtk2CellRenderer, Gtk2Globals, Gtk2Proc, InterfaceBase,
34  Gtk2WSControls, Gtk2Extra;
35
36type
37
38  { !!! Both are used: TGtkComboBoxEntry (with entry) and TGtkComboBox (without entry),
39           but not the old TGtkCombo !!! }
40
41  PGtkComboBoxPrivate = ^TGtkComboBoxPrivate;
42  TGtkComboBoxPrivate = record
43    model: PGtkTreeModel;
44    col_column,
45    row_column: gint;
46    wrap_width: gint;
47    active_row: PGtkTreeRowReference;
48    tree_view: PGtkWidget;
49    column: PGtkTreeViewColumn;
50    cell_view: PGtkWidget;
51    cell_view_frame: PGtkWidget;
52    button: PGtkwidget;
53    box: PGtkWidget;
54    arrow: PGtkWidget;
55    serarator: PGtkWidget;
56    popup_widget: PGtkWidget;
57    popup_window: PGtkWidget;
58    popup_frame: PGtkWidget;
59    scrolled_window: PGtkwidget;
60  end;
61
62
63
64  { TGtk2WSScrollBar }
65
66  TGtk2WSScrollBar = class(TWSScrollBar)
67  protected
68    class procedure SetCallbacks(const AGtkWidget: PGtkWidget; const AWidgetInfo: PWidgetInfo); virtual;
69  published
70    class function  CreateHandle(const AWinControl: TWinControl; const AParams: TCreateParams): TLCLIntfHandle; override;
71    class procedure SetKind(const AScrollBar: TCustomScrollBar; const {%H-}AIsHorizontal: Boolean); override;
72    class procedure SetParams(const AScrollBar: TCustomScrollBar); override;
73    class procedure ShowHide(const AWinControl: TWinControl); override;
74  end;
75
76  { TGtk2WSCustomGroupBox }
77
78  TGtk2WSCustomGroupBox = class(TWSCustomGroupBox)
79  protected
80    class procedure SetCallbacks(const AGtkWidget: PGtkWidget; const AWidgetInfo: PWidgetInfo); virtual;
81    class procedure SetLabel(AFrame: PGtkFrame; AText: String);
82    class function GetFrameWidget(AEventBox: PGtkEventBox): PGtkFrame;
83  published
84    class function CreateHandle(const AWinControl: TWinControl; const AParams: TCreateParams): TLCLIntfHandle; override;
85    class procedure SetColor(const AWinControl: TWinControl); override;
86    class function GetDefaultClientRect(const AWinControl: TWinControl;
87             const {%H-}aLeft, {%H-}aTop, aWidth, aHeight: integer; var aClientRect: TRect
88             ): boolean; override;
89    class procedure GetPreferredSize(const AWinControl: TWinControl;
90                        var PreferredWidth, PreferredHeight: integer;
91                        WithThemeSpace: Boolean); override;
92    class procedure SetFont(const AWinControl: TWinControl; const AFont: TFont); override;
93    class procedure SetText(const AWinControl: TWinControl; const AText: string); override;
94  end;
95
96  { TGtk2WSGroupBox }
97
98  TGtk2WSGroupBox = class(TWSGroupBox)
99  published
100  end;
101
102  { TGtk2WSCustomComboBox }
103  { !!! Both are used: TGtkComboBoxEntry (with entry) and TGtkComboBox (without entry),
104           but not the old TGtkCombo !!! }
105
106  TGtk2WSCustomComboBox = class(TWSCustomComboBox)
107  protected
108    class procedure ReCreateCombo(const ACustomComboBox: TCustomComboBox; const AWithEntry: Boolean; const AWidgetInfo: PWidgetInfo); virtual;
109    class procedure SetRenderer(const ACustomComboBox: TCustomComboBox; AWidget: PGtkWidget; AWidgetInfo: PWidgetInfo); virtual;
110    class procedure SetCallbacks(const AWinControl: tWinControl; const AWidget: PGtkWidget; const AWidgetInfo: PWidgetInfo); virtual;
111    class procedure SetSensitivity(AWinControl: TWinControl; AWidget: PGtkWidget);
112  published
113    class procedure GetPreferredSize(const AWinControl: TWinControl;
114      var PreferredWidth, PreferredHeight: integer; WithThemeSpace: Boolean); override;
115
116    class function GetDroppedDown(const ACustomComboBox: TCustomComboBox): Boolean; override;
117    class function GetSelStart(const ACustomComboBox: TCustomComboBox): integer; override;
118    class function GetSelLength(const ACustomComboBox: TCustomComboBox): integer; override;
119    class function GetItemIndex(const ACustomComboBox: TCustomComboBox): integer; override;
120    class function GetMaxLength(const ACustomComboBox: TCustomComboBox): integer; override;
121    class function GetText(const AWinControl: TWinControl; var AText: String): Boolean; override;
122
123    class procedure SetArrowKeysTraverseList(const {%H-}ACustomComboBox: TCustomComboBox;
124                                             {%H-}NewTraverseList: boolean); override;
125    class procedure SetDroppedDown(const ACustomComboBox: TCustomComboBox; ADroppedDown: Boolean); override;
126    class procedure SetSelStart(const ACustomComboBox: TCustomComboBox; NewStart: integer); override;
127    class procedure SetSelLength(const ACustomComboBox: TCustomComboBox; NewLength: integer); override;
128    class procedure SetItemIndex(const ACustomComboBox: TCustomComboBox; NewIndex: integer); override;
129    class procedure SetMaxLength(const ACustomComboBox: TCustomComboBox; NewLength: integer); override;
130    class procedure SetStyle(const ACustomComboBox: TCustomComboBox; NewStyle: TComboBoxStyle); override;
131
132    class function  GetItems(const ACustomComboBox: TCustomComboBox): TStrings; override;
133    class procedure Sort(const ACustomComboBox: TCustomComboBox; {%H-}AList: TStrings; IsSorted: boolean); override;
134    class procedure SetColor(const AWinControl: TWinControl); override;
135    class procedure SetFont(const AWinControl: TWinControl; const AFont: TFont); override;
136    class procedure SetText(const AWinControl: TWinControl; const AText: String); override;
137
138    class procedure ShowHide(const AWinControl: TWinControl); override;
139
140    class function  CanFocus(const AWinControl: TWinControl): boolean; override;
141
142    class function  CreateHandle(const AWinControl: TWinControl; const AParams: TCreateParams): TLCLIntfHandle; override;
143    class procedure DestroyHandle(const AWinControl: TWinControl); override;
144  end;
145
146  { TGtk2WSComboBox }
147
148  TGtk2WSComboBox = class(TWSComboBox)
149  published
150  end;
151
152  { TGtk2WSCustomListBox }
153
154  TGtk2WSCustomListBox = class(TWSCustomListBox)
155  protected
156    class procedure SetCallbacks(const AGtkWidget: PGtkWidget; const AWidgetInfo: PWidgetInfo); virtual;
157  published
158    class function CreateHandle(const AWinControl: TWinControl; const AParams: TCreateParams): TLCLIntfHandle; override;
159    class function GetIndexAtXY(const ACustomListBox: TCustomListBox; {%H-}X, Y: integer): integer; override;
160    class function GetItemIndex(const ACustomListBox: TCustomListBox): integer; override;
161    class function GetItemRect(const ACustomListBox: TCustomListBox; Index: integer; var ARect: TRect): boolean; override;
162    class function GetScrollWidth(const ACustomListBox: TCustomListBox): Integer; override;
163    class function GetSelCount(const ACustomListBox: TCustomListBox): integer; override;
164    class function GetSelected(const ACustomListBox: TCustomListBox; const AIndex: integer): boolean; override;
165    class function GetStrings(const ACustomListBox: TCustomListBox): TStrings; override;
166    class function GetTopIndex(const ACustomListBox: TCustomListBox): integer; override;
167
168    class procedure SelectItem(const ACustomListBox: TCustomListBox; AnIndex: integer; ASelected: boolean); override;
169    class procedure SetBorder(const ACustomListBox: TCustomListBox); override;
170    class procedure SetColor(const AWinControl: TWinControl); override;
171    class procedure SetItemIndex(const ACustomListBox: TCustomListBox; const AIndex: integer); override;
172    class procedure SetScrollWidth(const ACustomListBox: TCustomListBox; const AScrollWidth: Integer); override;
173    class procedure SetSelectionMode(const ACustomListBox: TCustomListBox; const {%H-}AExtendedSelect,
174                                     AMultiSelect: boolean); override;
175    class procedure SetStyle(const ACustomListBox: TCustomListBox); override;
176    class procedure SetSorted(const {%H-}ACustomListBox: TCustomListBox; AList: TStrings; ASorted: boolean); override;
177    class procedure SetTopIndex(const ACustomListBox: TCustomListBox; const NewTopIndex: integer); override;
178    class procedure SetFont(const AWinControl: TWinControl; const AFont: TFont); override;
179    class procedure ShowHide(const AWinControl: TWinControl); override;
180  end;
181
182  { TGtk2WSListBox }
183
184  TGtk2WSListBox = class(TWSListBox)
185  published
186  end;
187
188  { TGtk2WSCustomEdit }
189
190  TGtk2WSCustomEdit = class(TWSCustomEdit)
191  published
192    class procedure SetCallbacks(const AGtkWidget: PGtkWidget; const AWidgetInfo: PWidgetInfo); virtual;
193    class function CreateHandle(const AWinControl: TWinControl; const AParams: TCreateParams): TLCLIntfHandle; override;
194    class function GetCaretPos(const ACustomEdit: TCustomEdit): TPoint; override;
195    class function GetSelStart(const ACustomEdit: TCustomEdit): integer; override;
196    class function GetSelLength(const ACustomEdit: TCustomEdit): integer; override;
197
198    class procedure SetCaretPos(const ACustomEdit: TCustomEdit; const NewPos: TPoint); override;
199    class procedure SetCharCase(const {%H-}ACustomEdit: TCustomEdit; {%H-}NewCase: TEditCharCase); override;
200    class procedure SetEchoMode(const ACustomEdit: TCustomEdit; NewMode: TEchoMode); override;
201    class procedure SetMaxLength(const ACustomEdit: TCustomEdit; NewLength: integer); override;
202    class procedure SetPasswordChar(const ACustomEdit: TCustomEdit; {%H-}NewChar: char); override;
203    class procedure SetReadOnly(const ACustomEdit: TCustomEdit; NewReadOnly: boolean); override;
204    class procedure SetSelStart(const ACustomEdit: TCustomEdit; NewStart: integer); override;
205    class procedure SetSelLength(const ACustomEdit: TCustomEdit; NewLength: integer); override;
206    class procedure SetText(const AWinControl: TWinControl; const AText: string); override;
207    class procedure SetSelText(const ACustomEdit: TCustomEdit; const NewSelText: string); override;
208
209    class procedure GetPreferredSize(const AWinControl: TWinControl;
210                        var PreferredWidth, PreferredHeight: integer;
211                        WithThemeSpace: Boolean); override;
212    class procedure SetColor(const AWinControl: TWinControl); override;
213
214    class procedure SetAlignment(const ACustomEdit: TCustomEdit; const AAlignment: TAlignment); override;
215
216    class procedure Cut(const ACustomEdit: TCustomEdit); override;
217    class procedure Copy(const ACustomEdit: TCustomEdit); override;
218    class procedure Paste(const ACustomEdit: TCustomEdit); override;
219    class procedure Undo(const ACustomEdit: TCustomEdit); override;
220  end;
221
222  { TGtk2WSCustomMemo }
223
224  TGtk2WSCustomMemo = class(TWSCustomMemo)
225  protected
226    class procedure SetCallbacks(const AGtkWidget: PGtkWidget; const AWidgetInfo: PWidgetInfo); virtual;
227  published
228    class function  CreateHandle(const AWinControl: TWinControl; const AParams: TCreateParams): TLCLIntfHandle; override;
229
230    class function  GetSelStart(const ACustomEdit: TCustomEdit): integer; override;
231    class function  GetSelLength(const ACustomEdit: TCustomEdit): integer; override;
232    class function  GetStrings(const ACustomMemo: TCustomMemo): TStrings; override;
233
234    class procedure SetAlignment(const ACustomEdit: TCustomEdit; const AAlignment: TAlignment); override;
235    class procedure SetColor(const AWinControl: TWinControl); override;
236    class procedure SetFont(const AWinControl: TWinControl; const AFont: TFont); override;
237    class procedure SetSelStart(const ACustomEdit: TCustomEdit; NewStart: integer); override;
238    class procedure SetSelLength(const ACustomEdit: TCustomEdit; NewLength: integer); override;
239    class procedure SetWantTabs(const ACustomMemo: TCustomMemo; const NewWantTabs: boolean); override;
240    class procedure SetEchoMode(const {%H-}ACustomEdit: TCustomEdit; {%H-}NewMode: TEchoMode); override;
241    class procedure SetPasswordChar(const {%H-}ACustomEdit: TCustomEdit; {%H-}NewChar: char); override;
242    class procedure SetWordWrap(const ACustomMemo: TCustomMemo; const NewWordWrap: boolean); override;
243
244    class procedure SetCharCase(const {%H-}ACustomEdit: TCustomEdit; {%H-}NewCase: TEditCharCase); override;
245    class procedure SetMaxLength(const ACustomEdit: TCustomEdit; NewLength: integer); override;
246    class procedure SetReadOnly(const ACustomEdit: TCustomEdit; NewReadOnly: boolean); override;
247    class procedure SetSelText(const ACustomEdit: TCustomEdit; const NewSelText: string); override;
248    class procedure SetText(const AWinControl: TWinControl; const AText: string); override;
249    class procedure SetScrollbars(const ACustomMemo: TCustomMemo; const NewScrollbars: TScrollStyle); override;
250
251    class procedure GetPreferredSize(const AWinControl: TWinControl;
252                        var PreferredWidth, PreferredHeight: integer;
253                        WithThemeSpace: Boolean); override;
254
255    class function GetCaretPos(const ACustomEdit: TCustomEdit): TPoint; override;
256    class procedure SetCaretPos(const ACustomEdit: TCustomEdit; const NewPos: TPoint); override;
257  end;
258
259  { TGtk2WSEdit }
260
261  TGtk2WSEdit = class(TWSEdit)
262  published
263  end;
264
265  { TGtk2WSMemo }
266
267  TGtk2WSMemo = class(TWSMemo)
268  published
269  end;
270
271  { TGtk2WSCustomLabel }
272
273  {
274  TGtk2WSCustomLabel = class(TWSCustomLabel)
275  private
276  protected
277  public
278  end;
279  }
280  { TGtk2WSLabel }
281
282  {
283  TGtk2WSLabel = class(TWSLabel)
284  private
285  protected
286  public
287  end;
288  }
289
290  { TGtk2WSButtonControl }
291
292  TGtk2WSButtonControl = class(TWSButtonControl)
293  published
294  end;
295
296  { TGtk2WSButton }
297
298  TGtk2WSButton = class(TWSButton)
299  protected
300    class function  GetButtonWidget(AEventBox: PGtkEventBox): PGtkButton;
301    class function  GetLabelWidget(AEventBox: PGtkEventBox): PGtkLabel;
302  public
303    class procedure SetCallbacks(const AGtkWidget: PGtkWidget; const AWidgetInfo: PWidgetInfo); virtual;
304  published
305    class function  CreateHandle(const AWinControl: TWinControl; const AParams: TCreateParams): TLCLIntfHandle; override;
306    class procedure GetPreferredSize(const AWinControl: TWinControl;
307                        var PreferredWidth, PreferredHeight: integer;
308                        WithThemeSpace: Boolean); override;
309    class function  GetText(const {%H-}AWinControl: TWinControl; var {%H-}AText: String): Boolean; override;
310
311    class procedure SetColor(const AWinControl: TWinControl); override;
312    class procedure SetFont(const AWinControl: TWinControl; const AFont: TFont); override;
313    class procedure SetDefault(const AButton: TCustomButton; ADefault: Boolean); override;
314    class procedure SetShortcut(const AButton: TCustomButton; const {%H-}ShortCutK1, {%H-}ShortCutK2: TShortcut); override;
315    class procedure SetText(const AWinControl: TWinControl; const AText: String); override;
316  end;
317
318  { TGtk2WSCustomCheckBox }
319
320  TGtk2WSCustomCheckBox = class(TWSCustomCheckBox)
321  protected
322    class procedure SetCallbacks(const AGtkWidget: PGtkWidget; const AWidgetInfo: PWidgetInfo); virtual;
323  published
324    class function  CreateHandle(const AWinControl: TWinControl;
325                                 const AParams: TCreateParams): TLCLIntfHandle; override;
326    class procedure GetPreferredSize(const AWinControl: TWinControl;
327                        var PreferredWidth, PreferredHeight: integer;
328                        WithThemeSpace: Boolean); override;
329    class function  RetrieveState(const ACustomCheckBox: TCustomCheckBox): TCheckBoxState; override;
330    class procedure SetShortCut(const ACustomCheckBox: TCustomCheckBox; const ShortCutK1, {%H-}ShortCutK2: TShortCut); override;
331    class procedure SetState(const ACustomCheckBox: TCustomCheckBox;
332      const NewState: TCheckBoxState); override;
333    class procedure SetFont(const AWinControl: TWinControl; const AFont: TFont); override;
334    class procedure SetText(const AWinControl: TWinControl; const AText: String); override;
335    class procedure ShowHide(const AWinControl: TWinControl); override;
336  end;
337
338  { TGtk2WSCheckBox }
339
340  TGtk2WSCheckBox = class(TWSCheckBox)
341  published
342  end;
343
344  { TGtk2WSToggleBox }
345
346  TGtk2WSToggleBox = class(TWSToggleBox)
347  published
348    class function CreateHandle(const AWinControl: TWinControl; const AParams: TCreateParams): TLCLIntfHandle; override;
349  end;
350
351  { TGtk2WSRadioButton }
352
353  TGtk2WSRadioButton = class(TWSRadioButton)
354  published
355    class function CreateHandle(const AWinControl: TWinControl; const AParams: TCreateParams): TLCLIntfHandle; override;
356  end;
357
358  { TGtk2WSCustomStaticText }
359
360  TGtk2WSCustomStaticText = class(TWSCustomStaticText)
361  protected
362    class function GetLabelWidget(AFrame: PGtkFrame): PGtkLabel;
363    class function GetBoxWidget(AFrame: PGtkFrame): PGtkEventBox;
364  published
365    class function  CreateHandle(const AWinControl: TWinControl; const AParams: TCreateParams): TLCLIntfHandle; override;
366    class procedure SetAlignment(const ACustomStaticText: TCustomStaticText;
367                                 const NewAlignment: TAlignment); override;
368    class procedure GetPreferredSize(const AWinControl: TWinControl;
369                        var PreferredWidth, PreferredHeight: integer;
370                        WithThemeSpace: Boolean); override;
371    class function  GetText(const {%H-}AWinControl: TWinControl; var {%H-}AText: String): Boolean; override;
372    class procedure SetCallbacks(const AGtkWidget: PGtkWidget; const AWidgetInfo: PWidgetInfo);
373    class procedure SetColor(const AWinControl: TWinControl); override;
374    class procedure SetFont(const AWinControl: TWinControl; const AFont: TFont); override;
375    class procedure SetStaticBorderStyle(const ACustomStaticText: TCustomStaticText; const NewBorderStyle: TStaticBorderStyle); override;
376    class procedure SetText(const AWinControl: TWinControl; const AText: String); override;
377  end;
378
379  { TGtk2WSStaticText }
380
381  TGtk2WSStaticText = class(TWSStaticText)
382  published
383  end;
384
385{$DEFINE MEMOHEADER}
386{$I gtk2memostrings.inc}
387{$UNDEF MEMOHEADER}
388
389function GetComboBoxEntry(Widget: PGtkWidget): PGtkEntry;
390
391implementation
392
393uses
394  LCLMessageGlue, Forms;
395
396const
397  StaticBorderShadowMap: array[TStaticBorderStyle] of TGtkShadowType =
398  (
399    GTK_SHADOW_NONE,
400    GTK_SHADOW_ETCHED_IN,
401    GTK_SHADOW_IN
402  );
403
404
405function GetComboBoxEntry(Widget: PGtkWidget): PGtkEntry;
406begin
407  if GtkWidgetIsA(Widget, GTK_TYPE_COMBO_BOX_ENTRY) then
408    Result := PGtkEntry(GTK_BIN(Widget)^.child)
409  else
410    Result := nil;
411end;
412
413procedure gtkDefaultPopupMenuDeactivate(Widget: PGtkWidget; data: gPointer); cdecl;
414begin
415  LastMouse.Button := 0;
416  LastMouse.ClickCount := 0;
417  LastMouse.Down := False;
418  LastMouse.MousePos := Point(0, 0);
419  LastMouse.Time := 0;
420  LastMouse.WinControl := nil;
421end;
422
423function gtkDefaultPopupMenuCloseFix(Widget: PGtkWidget; Menu: PGtkMenu;
424  data: gPointer): gboolean; cdecl;
425begin
426  // needed because closing popup menu without clicking on any menu item
427  // freezes various controls, eg SpeedButton
428  g_signal_connect(PGtkObject(Menu), 'deactivate',
429    gtk_signal_func(@gtkDefaultPopupMenuDeactivate), nil);
430end;
431
432{$I gtk2memostrings.inc}
433
434{ TGtk2WSCustomListBox }
435
436procedure StoreFirstSelectedPath({%H-}model:PGtkTreeModel; path:PGtkTreePath;
437  {%H-}iter:PGtkTreeIter; data:gpointer); cdecl;
438begin
439  //DebugLn(['StoreFirstSelectedPath ',PInteger(Data)^,' ',gtk_tree_path_get_indices(Path)^]);
440  if PInteger(Data)^ < 0 then
441    PInteger(Data)^ := gtk_tree_path_get_indices(Path)^;
442end;
443
444class function TGtk2WSCustomListBox.GetItemIndex(
445  const ACustomListBox: TCustomListBox): integer;
446var
447  Widget: PGtkWidget;
448  Path: PGtkTreePath;
449  Column: PGtkTreeViewColumn;
450  Selection: PGtkTreeSelection;
451begin
452  Result := -1;
453  if not WSCheckHandleAllocated(ACustomListBox, 'GetItemIndex') then
454    Exit;
455  Widget := GetWidgetInfo({%H-}Pointer(ACustomListBox.Handle), True)^.CoreWidget;
456  if GtkWidgetIsA(Widget, gtk_tree_view_get_type) then
457  begin
458    Path:=nil;
459    Column:=nil;
460    gtk_tree_view_get_cursor(PGtkTreeView(Widget), Path, column);
461
462    if Path <> nil then
463    begin
464      Result := gtk_tree_path_get_indices(Path)^;
465      if Result = 0 then
466      begin
467        Selection := gtk_tree_view_get_selection(PGtkTreeView(Widget));
468        if not gtk_tree_selection_path_is_selected(Selection, Path) then
469          Result := -1;
470      end;
471    end else
472      Result := -1;
473  end;
474end;
475
476class function TGtk2WSCustomListBox.GetItemRect(
477  const ACustomListBox: TCustomListBox; Index: integer; var ARect: TRect
478  ): boolean;
479var
480  Widget: PGtkWidget;
481  Column: PGtkTreeViewColumn;
482  Path: PGtkTreePath;
483  AGdkRect: TGdkRectangle;
484begin
485  Result := False;
486  FillChar(ARect, SizeOf(ARect), 0);
487  if not WSCheckHandleAllocated(ACustomListBox, 'GetItemIndex') then
488    Exit;
489  Widget := GetWidgetInfo({%H-}Pointer(ACustomListBox.Handle), True)^.CoreWidget;
490  if GtkWidgetIsA(Widget, gtk_tree_view_get_type) and (Index >= 0) then
491  begin
492    Path := gtk_tree_path_new_from_indices(Index, -1);
493    Column := gtk_tree_view_get_column(PGtkTreeView(Widget), 0);
494    FillChar(AGdkRect{%H-}, SizeOf(AGdkRect), 0);
495    gtk_tree_view_get_cell_area(PGtkTreeView(Widget), Path, Column, @AGdkRect);
496    ARect := Rect(AGdkRect.x, AGdkRect.y, AGdkRect.x + AGdkRect.width, AGdkRect.y + AGdkRect.height);
497    gtk_tree_path_free(Path);
498    Result := True;
499  end;
500end;
501
502class function TGtk2WSCustomListBox.GetScrollWidth(const ACustomListBox: TCustomListBox): Integer;
503var
504  Adjustment: PGtkAdjustment;
505begin
506  Adjustment := gtk_scrolled_window_get_hadjustment({%H-}PGtkScrolledWindow(ACustomListBox.Handle));
507  Result := Trunc(Adjustment^.upper);
508end;
509
510class function TGtk2WSCustomListBox.GetTopIndex(const ACustomListBox: TCustomListBox): integer;
511begin
512  Result := GetIndexAtXY(ACustomListBox, 0, 1);
513end;
514
515class procedure TGtk2WSCustomListBox.SelectItem(
516  const ACustomListBox: TCustomListBox; AnIndex: integer; ASelected: boolean);
517var
518  Widget: PGtkWidget; // pointer to gtk-widget (local use when neccessary)
519  Selection: PGtkTreeSelection;
520  ListStoreModel: PGtkTreeModel;
521  Iter  : TGtkTreeIter;
522begin
523  if not WSCheckHandleAllocated(ACustomListBox, 'SelectItem') then
524    Exit;
525  Widget := GetWidgetInfo({%H-}Pointer(ACustomListBox.Handle), True)^.CoreWidget;
526  ListStoreModel := gtk_tree_view_get_model(PGtkTreeView(Widget));
527  Selection := gtk_tree_view_get_selection(PGtkTreeView(Widget));
528
529  if gtk_tree_model_iter_nth_child(ListStoreModel, @Iter, nil, AnIndex) then
530  begin
531    if gtk_tree_view_get_model(PGtkTreeView(Widget)) = nil then
532      Exit; // we are in the midst of a begin update end update pair and the following will fail and cause gtk debug messages
533    case ASelected of
534      True:
535        if not gtk_tree_selection_iter_is_selected(Selection, @Iter) then
536          gtk_tree_selection_select_iter(Selection, @Iter);
537      False:
538        if gtk_tree_selection_iter_is_selected(Selection, @Iter) then
539          gtk_tree_selection_unselect_iter(Selection, @Iter);
540    end;
541  end;
542end;
543
544class procedure TGtk2WSCustomListBox.SetBorder(
545  const ACustomListBox: TCustomListBox);
546begin
547  gtk_scrolled_window_set_shadow_type({%H-}PGtkScrolledWindow(ACustomListBox.Handle),
548    BorderStyleShadowMap[ACustomListBox.BorderStyle]);
549end;
550
551class procedure TGtk2WSCustomListBox.SetColor(const AWinControl: TWinControl);
552var
553  AWidget: PGTKWidget;
554begin
555  if not WSCheckHandleAllocated(AWinControl, 'SetColor') then
556    Exit;
557  AWidget := {%H-}PGtkWidget(AWinControl.Handle);
558  AWidget := GetWidgetInfo(AWidget, True)^.CoreWidget;
559  Gtk2WidgetSet.SetWidgetColor(AWidget,
560    AWinControl.Font.Color,
561    AWinControl.Color,
562    [GTK_STATE_NORMAL, GTK_STATE_ACTIVE, GTK_STATE_PRELIGHT, GTK_STYLE_BASE]);
563end;
564
565class procedure TGtk2WSCustomListBox.SetItemIndex(
566  const ACustomListBox: TCustomListBox; const AIndex: integer);
567var
568  Widget: PGtkWidget;
569  WidgetInfo: PWidgetInfo;
570  Selection: PGtkTreeSelection;
571  Path: PGtkTreePath;
572begin
573  if not WSCheckHandleAllocated(ACustomListBox, 'SetItemIndex') then
574    Exit;
575
576  WidgetInfo := GetWidgetInfo({%H-}Pointer(ACustomListBox.Handle), True);
577  Widget := WidgetInfo^.CoreWidget;
578  if not GtkWidgetIsA(Widget, gtk_tree_view_get_type) then
579    raise Exception.Create('');
580
581  Selection := gtk_tree_view_get_selection(PGtkTreeView(Widget));
582
583  Inc(WidgetInfo^.ChangeLock);
584  if (AIndex < 0) then
585    Path := nil
586  else
587    Path := gtk_tree_path_new_from_indices(AIndex, -1);
588
589  // if singleselection mode then selection = itemindex
590  if Path <> nil then
591  begin
592    if PGtkTreeView(Widget)^.priv^.tree <> nil then
593      gtk_tree_view_set_cursor(PGtkTreeView(Widget), Path, nil, False);
594  end
595  else
596  begin
597    Path := gtk_tree_path_new_from_indices(0, -1);
598    if PGtkTreeView(Widget)^.priv^.tree <> nil then
599      gtk_tree_view_set_cursor(PGtkTreeView(Widget), Path, nil, False);
600    gtk_tree_selection_unselect_all(Selection);
601  end;
602
603  if Path <> nil then
604    gtk_tree_path_free(Path);
605
606  Dec(WidgetInfo^.ChangeLock);
607end;
608
609class procedure TGtk2WSCustomListBox.SetScrollWidth(
610  const ACustomListBox: TCustomListBox; const AScrollWidth: Integer);
611const
612  BoolToPolicy: array[Boolean] of TGtkPolicyType = (GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
613var
614  Adjustment: PGtkAdjustment;
615  ScrolledWindow: PGtkScrolledWindow;
616begin
617  ScrolledWindow := {%H-}PGtkScrolledWindow(ACustomListBox.Handle);
618  gtk_scrolled_window_set_policy(ScrolledWindow, BoolToPolicy[AScrollWidth > PgtkWidget(ScrolledWindow)^.allocation.width], GTK_POLICY_AUTOMATIC);
619  Adjustment := gtk_scrolled_window_get_hadjustment(ScrolledWindow);
620  Adjustment^.upper := AScrollWidth;
621  gtk_adjustment_changed(Adjustment);
622end;
623
624class procedure TGtk2WSCustomListBox.SetSelectionMode(
625  const ACustomListBox: TCustomListBox; const AExtendedSelect,
626  AMultiSelect: boolean);
627var
628  Widget: PGtkWidget; // pointer to gtk-widget (local use when neccessary)
629  Selection: PGtkTreeSelection;
630begin
631  if not WSCheckHandleAllocated(ACustomListBox, 'SetSelectionMode') then
632    Exit;
633  Widget := GetWidgetInfo({%H-}Pointer(ACustomListBox.Handle),True)^.CoreWidget;
634  Selection := gtk_tree_view_get_selection(PGtkTreeView(Widget));
635
636  case AMultiSelect of
637    True : gtk_tree_selection_set_mode(Selection, GTK_SELECTION_MULTIPLE);
638    False: gtk_tree_selection_set_mode(Selection, GTK_SELECTION_SINGLE);
639    //GTK_SELECTION_NONE,
640    //GTK_SELECTION_SINGLE,
641    //GTK_SELECTION_BROWSE,
642    //GTK_SELECTION_MULTIPLE
643  end;
644end;
645
646class procedure TGtk2WSCustomListBox.SetStyle(
647  const ACustomListBox: TCustomListBox);
648var
649  AStyle: PtrInt;
650  Widget: PGtkWidget;
651begin
652  if not WSCheckHandleAllocated(ACustomListBox, 'SetStyle') then
653    Exit;
654  Widget := GetWidgetInfo({%H-}Pointer(ACustomListBox.Handle), True)^.CoreWidget;
655  AStyle := {%H-}PtrInt(g_object_get_data(PGObject(Widget), 'lclcustomlistboxstyle'));
656  if (AStyle <> Ord(ACustomListBox.Style)) then
657    RecreateWnd(ACustomListBox);
658end;
659
660class procedure TGtk2WSCustomListBox.SetSorted(const ACustomListBox: TCustomListBox;
661  AList: TStrings; ASorted: boolean);
662begin
663  if AList is TGtkListStoreStringList then
664    TGtkListStoreStringList(AList).Sorted := ASorted
665  //else if AList is TGtkCListStringList then
666  //  TGtkCListStringList(AList).Sorted := ASorted
667  else
668    raise Exception.Create('');
669end;
670
671class procedure TGtk2WSCustomListBox.SetTopIndex(
672  const ACustomListBox: TCustomListBox; const NewTopIndex: integer);
673var
674  Widget: PGtkWidget;
675  ListStoreModel: PGtkTreeModel;
676  Iter: TGtkTreeIter;
677  TreeView: PGtkTreeView;
678  APath: Pointer;
679begin
680  if not WSCheckHandleAllocated(ACustomListBox, 'SetTopIndex') then
681    Exit;
682  Widget := GetWidgetInfo({%H-}Pointer(ACustomListBox.Handle), True)^.CoreWidget;
683  TreeView := PGtkTreeView(Widget);
684  ListStoreModel := gtk_tree_view_get_model(TreeView);
685
686  if not gtk_tree_model_iter_nth_child(ListStoreModel, @Iter, nil, NewTopIndex)
687  then exit;
688
689  APath := gtk_tree_model_get_path(ListStoreModel, @Iter);
690  gtk_tree_view_scroll_to_cell(TreeView, APath, NULL, true, 0.0, 0.0);
691  gtk_tree_path_free(APath);
692end;
693
694class procedure TGtk2WSCustomListBox.SetFont(const AWinControl: TWinControl;
695  const AFont: TFont);
696var
697  Widget: PGtkWidget;
698begin
699  if not WSCheckHandleAllocated(AWinControl, 'SetFont') then
700    Exit;
701  Widget := GetWidgetInfo({%H-}Pointer(AWinControl.Handle))^.CoreWidget;
702
703  Gtk2WidgetSet.SetWidgetColor(Widget, AFont.Color, clNone,
704       [GTK_STATE_NORMAL,GTK_STATE_ACTIVE,GTK_STATE_PRELIGHT,GTK_STATE_SELECTED,
705        GTK_STYLE_TEXT]);
706  Gtk2WidgetSet.SetWidgetFont(Widget, AFont);
707end;
708
709class procedure TGtk2WSCustomListBox.ShowHide(const AWinControl: TWinControl);
710begin
711  // issue #27276
712  if AWinControl.HandleAllocated and AWinControl.HandleObjectShouldBeVisible and
713    (TCustomListBox(AWinControl).ItemIndex = -1) then
714    SetItemIndex(TCustomListBox(AWinControl), TCustomListBox(AWinControl).ItemIndex);
715  // issue #28341
716  if AWinControl.HandleObjectShouldBeVisible then
717      SetFont(AWinControl, AWinControl.Font);
718  Gtk2WidgetSet.SetVisible(AWinControl, AWinControl.HandleObjectShouldBeVisible);
719  InvalidateLastWFPResult(AWinControl, AWinControl.BoundsRect);
720end;
721
722function gtk2ListBoxSelectionChangedAfter({%H-}Widget: PGtkWidget;
723  WidgetInfo: PWidgetInfo): gboolean; cdecl;
724var
725  Mess: TLMessage;
726begin
727  Result := CallBackDefaultReturn;
728  if WidgetInfo^.ChangeLock > 0 then
729    Exit;
730  {$IFDEF EventTrace}
731  EventTrace('gtk2ListSelectionChangedAfter', WidgetInfo^.LCLObject);
732  {$ENDIF}
733  FillChar(Mess{%H-},SizeOf(Mess),0);
734  Mess.msg := LM_SelChange;
735  DeliverMessage(WidgetInfo^.LCLObject, Mess);
736end;
737
738class function TGtk2WSCustomListBox.CreateHandle(const AWinControl: TWinControl;
739  const AParams: TCreateParams): TLCLIntfHandle;
740var
741  TVWidget: PGtkWidget;
742  p: PGtkWidget;                 // ptr to the newly created GtkWidget
743  liststore : PGtkListStore;
744  Selection: PGtkTreeSelection;
745  renderer : PGtkCellRenderer;
746  column : PGtkTreeViewColumn;
747  WidgetInfo: PWidgetInfo;
748begin
749  Result := TGtk2WSBaseScrollingWinControl.CreateHandle(AWinControl, AParams);
750  p := {%H-}PGtkWidget(Result);
751
752  if Result = 0 then exit;
753  {$IFDEF DebugLCLComponents}
754  DebugGtkWidgets.MarkCreated(p,dbgsName(AWinControl));
755  {$ENDIF}
756
757  GTK_WIDGET_UNSET_FLAGS(PGtkScrolledWindow(p)^.hscrollbar, GTK_CAN_FOCUS);
758  GTK_WIDGET_UNSET_FLAGS(PGtkScrolledWindow(p)^.vscrollbar, GTK_CAN_FOCUS);
759  // by default horz scrollbar is invisible. it is set by SetScrollWidth
760  gtk_scrolled_window_set_policy(PGtkScrolledWindow(p),
761                                 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
762  //Set BorderStyle according to the provided Params
763  if (AParams.ExStyle and WS_EX_CLIENTEDGE) > 0 then
764    gtk_scrolled_window_set_shadow_type(PGtkScrolledWindow(p), GTK_SHADOW_ETCHED_IN)
765  else
766    gtk_scrolled_window_set_shadow_type(PGtkScrolledWindow(p), GTK_SHADOW_NONE);
767
768  gtk_widget_show(p);
769
770  liststore := gtk_list_store_new (2, [G_TYPE_STRING, G_TYPE_POINTER, nil]);
771
772  TVWidget:= gtk_tree_view_new_with_model (GTK_TREE_MODEL (liststore));
773  g_object_unref (G_OBJECT (liststore));
774
775  renderer := LCLIntfCellRenderer_New();
776  column := gtk_tree_view_column_new_with_attributes ('LISTITEMS', renderer,
777                                                      ['text', 0, nil]);
778  gtk_cell_layout_set_cell_data_func(PGtkCellLayout(column), renderer,
779    @LCLIntfCellRenderer_CellDataFunc, nil, nil);
780  gtk_tree_view_append_column (GTK_TREE_VIEW (TVWidget), column);
781  gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE);
782
783  gtk_tree_view_set_headers_visible(GTK_TREE_VIEW (TVWidget), False);
784
785  gtk_container_add(GTK_CONTAINER(p), TVWidget);
786  gtk_widget_show(TVWidget);
787
788  SetMainWidget(p, TVWidget);
789  WidgetInfo := GetWidgetInfo(p, false);
790  WidgetInfo^.CoreWidget := TVWidget;
791
792  Selection := gtk_tree_view_get_selection(PGtkTreeView(TVWidget));
793
794  case TCustomListBox(AWinControl).MultiSelect of
795    True : gtk_tree_selection_set_mode(Selection, GTK_SELECTION_MULTIPLE);
796    False: gtk_tree_selection_set_mode(Selection, GTK_SELECTION_SINGLE);
797  end;
798
799  if TListBox(AWinControl).Style = lbOwnerDrawFixed then
800  begin
801    gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
802    gtk_tree_view_set_fixed_height_mode(PGtkTreeView(TVWidget), True);
803  end;
804
805
806  g_signal_connect_after(Selection, 'changed',
807    G_CALLBACK(@gtk2ListBoxSelectionChangedAfter), WidgetInfo);
808
809  g_object_set_data(PGObject(TVWidget), 'lclcustomlistboxstyle', {%H-}gPointer(Ord(TListBox(AWinControl).Style)));
810
811  // Sets the callbacks
812  if not AWinControl.HandleObjectShouldBeVisible and not (csDesigning in AWinControl.ComponentState) then
813    gtk_widget_hide(p);
814  SetCallbacks(p, WidgetInfo);
815end;
816
817class procedure TGtk2WSCustomListBox.SetCallbacks(const AGtkWidget: PGtkWidget;
818  const AWidgetInfo: PWidgetInfo);
819begin
820  TGtk2WSWinControl.SetCallbacks(PGtkObject(AGtkWidget), TComponent(AWidgetInfo^.LCLObject));
821end;
822
823class function TGtk2WSCustomListBox.GetIndexAtXY(
824  const ACustomListBox: TCustomListBox; X, Y: integer): integer;
825var
826  aTreeView: PGtkTreeView;
827  aTreeColumn: PGtkTreeViewColumn;
828  aTreePath: PGtkTreePath;
829begin
830  Result := -1;
831  if not WSCheckHandleAllocated(ACustomListBox, 'GetIndexAtXY') then
832    Exit;
833  case ACustomListBox.fCompStyle of
834  csListBox, csCheckListBox:
835    begin
836      aTreeView :=
837        GTK_TREE_VIEW(GetWidgetInfo({%H-}Pointer(ACustomListBox.Handle), True)^.CoreWidget);
838
839      aTreePath:=nil;
840      aTreeColumn:=nil;
841      if gtk_tree_view_get_path_at_pos(aTreeView, 0, Y, aTreePath, aTreeColumn,
842        nil, nil)
843      then begin
844        Result := gtk_tree_path_get_indices(aTreePath)[0];
845        gtk_tree_path_free(aTreePath);
846        exit;
847      end;
848    end;
849  end;
850end;
851
852class function TGtk2WSCustomListBox.GetSelCount(
853  const ACustomListBox: TCustomListBox): integer;
854var
855  Widget: PGtkWidget; // pointer to gtk-widget (local use when neccessary)
856  Selection: PGtkTreeSelection;
857  ListStoreModel: PGtkTreeModel;
858  Rows: PGList;
859begin
860  Result := 0;
861  if not WSCheckHandleAllocated(ACustomListBox, 'GetSelCount') then
862    Exit;
863  Widget := GetWidgetInfo({%H-}Pointer(ACustomListBox.Handle), True)^.CoreWidget;
864  Selection := gtk_tree_view_get_selection(PGtkTreeView(Widget));
865
866  Rows := gtk_tree_selection_get_selected_rows(Selection, @ListStoreModel);
867  Result := g_list_length(Rows);
868  g_list_free(Rows);
869end;
870
871class function TGtk2WSCustomListBox.GetSelected(
872  const ACustomListBox: TCustomListBox; const AIndex: integer): boolean;
873var
874  Widget: PGtkWidget; // pointer to gtk-widget (local use when neccessary)
875  Selection: PGtkTreeSelection;
876  ListStoreModel: PGtkTreeModel;
877  Item  : TGtkTreeIter;
878begin
879  Result := False;      { assume: nothing found }
880  if not WSCheckHandleAllocated(ACustomListBox, 'GetSelected') then
881    Exit;
882  Widget := GetWidgetInfo({%H-}Pointer(ACustomListBox.Handle), True)^.CoreWidget;
883  ListStoreModel := gtk_tree_view_get_model(PGtkTreeView(Widget));
884  Selection := gtk_tree_view_get_selection(PGtkTreeView(Widget));
885
886  if gtk_tree_view_get_model(PGtkTreeView(Widget)) = nil then
887    Exit;
888  if gtk_tree_model_iter_nth_child(ListStoreModel, @Item, nil, AIndex) then
889  begin
890    Result := gtk_tree_selection_iter_is_selected(Selection, @Item);
891  end;
892end;
893
894class function TGtk2WSCustomListBox.GetStrings(
895  const ACustomListBox: TCustomListBox): TStrings;
896var
897  Widget: PGtkWidget;// pointer to gtk-widget
898begin
899  Result:=nil;
900  if not WSCheckHandleAllocated(ACustomListBox, 'GetStrings') then
901    Exit;
902  case ACustomListBox.fCompStyle of
903    {csCListBox:
904      begin
905        Widget:= GetWidgetInfo(Pointer(Handle), True)^.CoreWidget;
906
907        Result := TGtkCListStringList.Create(PGtkCList(Widget));
908        if ACustomListBox is TCustomListBox then
909          TGtkCListStringList(Result).Sorted :=
910                                          TCustomListBox(ACustomListBox).Sorted;
911      end;
912    }
913    csCheckListBox, csListBox:
914      begin
915        Widget := GetWidgetInfo({%H-}Pointer(ACustomListBox.Handle), True)^.CoreWidget;
916        Result := TGtkListStoreStringList.Create(
917                                gtk_tree_view_get_model(PGtkTreeView(Widget)),
918                                Ord(ACustomListBox.fCompStyle = csCheckListBox),
919                                ACustomListBox);
920        TGtkListStoreStringList(Result).Sorted := ACustomListBox.Sorted;
921      end;
922  else
923    raise Exception.Create('TGtk2WSCustomListBox.GetStrings');
924  end;
925end;
926
927{ TGtk2WSCustomCheckBox }
928
929class procedure TGtk2WSCustomCheckBox.SetCallbacks(const AGtkWidget: PGtkWidget; const AWidgetInfo: PWidgetInfo);
930begin
931  TGtk2WSWinControl.SetCallbacks(PGtkObject(AGtkWidget), TComponent(AWidgetInfo^.LCLObject));
932  TGtk2Widgetset(WidgetSet).SetCallback(LM_CHANGED, PGtkObject(AGtkWidget), AWidgetInfo^.LCLObject);
933end;
934
935
936class function TGtk2WSCustomCheckBox.CreateHandle(
937  const AWinControl: TWinControl; const AParams: TCreateParams
938  ): TLCLIntfHandle;
939var
940  Widget: PGtkWidget;
941  WidgetInfo: PWidgetInfo;
942  Allocation: TGTKAllocation;
943begin
944  { ToDo verify if the check box has correct z-order and disable GTK_WIDGET_NO_WINDOW if not.}
945  Widget := gtk_check_button_new_with_label(AParams.Caption);
946  {$IFDEF DebugLCLComponents}
947  DebugGtkWidgets.MarkCreated(Widget, dbgsName(AWinControl));
948  {$ENDIF}
949  Result := THandle({%H-}PtrUInt(Widget));
950  WidgetInfo := CreateWidgetInfo({%H-}Pointer(Result), AWinControl, AParams);
951
952  Allocation.X := AParams.X;
953  Allocation.Y := AParams.Y;
954  Allocation.Width := AParams.Width;
955  Allocation.Height := AParams.Height;
956  gtk_widget_size_allocate({%H-}PGtkWidget(Result), @Allocation);
957  if AParams.Style and WS_VISIBLE = 0 then
958    gtk_widget_hide({%H-}PGtkWidget(Result))
959  else
960    gtk_widget_show({%H-}PGtkWidget(Result));
961
962  Set_RC_Name(AWinControl, {%H-}PGtkWidget(Result));
963  SetCallbacks({%H-}PGtkWidget(Result), WidgetInfo);
964end;
965
966class procedure TGtk2WSCustomCheckBox.GetPreferredSize(
967  const AWinControl: TWinControl; var PreferredWidth, PreferredHeight: integer;
968  WithThemeSpace: Boolean);
969begin
970  GetGTKDefaultWidgetSize(AWinControl,PreferredWidth,PreferredHeight,
971                          WithThemeSpace);
972end;
973
974class function TGtk2WSCustomCheckBox.RetrieveState(
975  const ACustomCheckBox: TCustomCheckBox): TCheckBoxState;
976var
977  ToggleButton: PGtkToggleButton;
978begin
979  ToggleButton:={%H-}PGtkToggleButton(ACustomCheckBox.Handle);
980  if gtk_toggle_button_get_inconsistent(ToggleButton) then
981    Result := cbGrayed
982  else
983  if gtk_toggle_button_get_active(ToggleButton) then
984    Result := cbChecked
985  else
986    Result := cbUnchecked;
987end;
988
989class procedure TGtk2WSCustomCheckBox.SetShortCut(const ACustomCheckBox: TCustomCheckBox;
990  const ShortCutK1, ShortCutK2: TShortCut);
991begin
992  Accelerate(ACustomCheckBox, {%H-}PGtkWidget(ACustomCheckBox.Handle), ShortcutK1,
993    'clicked'
994    //'activate_item'
995    );
996end;
997
998class procedure TGtk2WSCustomCheckBox.SetState(
999  const ACustomCheckBox: TCustomCheckBox; const NewState: TCheckBoxState);
1000var
1001  GtkObject: PGtkObject;
1002  ToggleButton: PGtkToggleButton;
1003begin
1004  //debugln('TGtk2WSCustomCheckBox.SetState A ',DbgSName(ACustomCheckBox),' State=',dbgs(ord(ACustomCheckBox.State)));
1005  GtkObject := {%H-}PGtkObject(ACustomCheckBox.Handle);
1006  LockOnChange(GtkObject,1);
1007  ToggleButton:=PGtkToggleButton(GtkObject);
1008  gtk_toggle_button_set_inconsistent(ToggleButton, NewState=cbGrayed);
1009  gtk_toggle_button_set_active(ToggleButton, NewState=cbChecked);
1010  LockOnChange(GtkObject,-1);
1011end;
1012
1013class procedure TGtk2WSCustomCheckBox.SetFont(const AWinControl: TWinControl;
1014  const AFont: TFont);
1015var
1016  Widget: PGTKWidget;
1017  LblWidget: PGtkWidget;
1018begin
1019  if not AWinControl.HandleAllocated then exit;
1020
1021  Widget := {%H-}PGtkWidget(AWinControl.Handle);
1022  LblWidget := (pGtkBin(Widget)^.Child);
1023  if LblWidget <> nil then
1024  begin
1025    Gtk2WidgetSet.SetWidgetFont(LblWidget, AFont);
1026    Gtk2WidgetSet.SetWidgetColor(LblWidget, AFont.Color, clNone,
1027       [GTK_STATE_NORMAL,GTK_STATE_ACTIVE,GTK_STATE_PRELIGHT,GTK_STATE_SELECTED]);
1028  end;
1029end;
1030
1031class procedure TGtk2WSCustomCheckBox.SetText(const AWinControl: TWinControl;
1032  const AText: String);
1033var
1034  BoxWidget: PGtkWidget;
1035  B: Boolean;
1036  P: PGChar;
1037begin
1038  if not WSCheckHandleAllocated(AWincontrol, 'SetText') then Exit;
1039  BoxWidget := {%H-}PGtkWidget(AWinControl.Handle);
1040  if AText = '' then
1041  begin
1042    gtk_button_set_label(PGtkButton(BoxWidget), '');
1043    gtk_widget_hide(PGtkBin(BoxWidget)^.child);
1044  end else
1045  begin
1046    P := gtk_label_get_text(PGtkLabel(PGtkBin(BoxWidget)^.child));
1047    B := (StrPas(P) <> AText);
1048    gtk_widget_show(PGtkBin(BoxWidget)^.child);
1049    gtk_button_set_label(PGtkButton(BoxWidget), PChar(Ampersands2Underscore(AText)));
1050    gtk_button_set_use_underline(PGtkButton(BoxWidget), True);
1051    if B then
1052    begin
1053      SetColor(AWinControl);
1054      SetFont(AWinControl, AWinControl.Font);
1055    end;
1056  end;
1057end;
1058
1059class procedure TGtk2WSCustomCheckBox.ShowHide(const AWinControl: TWinControl);
1060begin
1061  // gtk2 doesn't set font properly
1062  // so we are doing it one more time before showing. Issues #21172, #23152
1063  if AWinControl.HandleObjectShouldBeVisible then
1064  begin
1065    SetFont(AWinControl, AWinControl.Font);
1066    AWinControl.InvalidatePreferredSize();
1067    AWinControl.AdjustSize();
1068  end;
1069  TGtk2WSWinControl.ShowHide(AWinControl);
1070end;
1071
1072{$I gtk2wscustommemo.inc}
1073
1074{ TGtk2WSCustomEdit }
1075
1076class procedure TGtk2WSCustomEdit.SetCallbacks(const AGtkWidget: PGtkWidget;
1077  const AWidgetInfo: PWidgetInfo);
1078begin
1079  TGtk2WSWinControl.SetCallbacks(PGtkObject(AGtkWidget), TComponent(AWidgetInfo^.LCLObject));
1080
1081  with TGtk2Widgetset(Widgetset) do
1082  begin
1083    SetCallback(LM_CHANGED, PGtkObject(AGtkWidget), AWidgetInfo^.LCLObject);
1084    SetCallback(LM_ACTIVATE, PGtkObject(AGtkWidget), AWidgetInfo^.LCLObject);
1085    SetCallback(LM_CUT, PGtkObject(AGtkWidget), AWidgetInfo^.LCLObject);
1086    SetCallback(LM_COPY, PGtkObject(AGtkWidget), AWidgetInfo^.LCLObject);
1087    SetCallback(LM_PASTE, PGtkObject(AGtkWidget), AWidgetInfo^.LCLObject);
1088  end;
1089
1090  g_signal_connect_after(PGtkObject(AGtkWidget), 'populate-popup',
1091    gtk_signal_func(@gtkDefaultPopupMenuCloseFix), AWidgetInfo);
1092end;
1093
1094class procedure TGtk2WSCustomEdit.GetPreferredSize(const AWinControl: TWinControl;
1095  var PreferredWidth, PreferredHeight: integer; WithThemeSpace: Boolean);
1096begin
1097  GetGTKDefaultWidgetSize(AWinControl,PreferredWidth,PreferredHeight,
1098                          WithThemeSpace);
1099  //debugln('TGtkWSCustomEdit.GetPreferredSize ',DbgSName(AWinControl),' PreferredWidth=',dbgs(PreferredWidth),' PreferredHeight=',dbgs(PreferredHeight));
1100end;
1101
1102class procedure TGtk2WSCustomEdit.SetColor(const AWinControl: TWinControl);
1103var
1104  AWidget: PGTKWidget;
1105begin
1106  if not WSCheckHandleAllocated(AWinControl, 'SetColor') then Exit;
1107  AWidget := {%H-}PGtkWidget(AWinControl.Handle);
1108  // don't change selected state
1109  Gtk2WidgetSet.SetWidgetColor(AWidget, clNone, AWinControl.Color,
1110    [GTK_STATE_NORMAL, GTK_STYLE_BASE]);
1111end;
1112
1113
1114class procedure TGtk2WSCustomEdit.SetText(const AWinControl: TWinControl;
1115  const AText: string);
1116var
1117  Widget: PGtkWidget;
1118  Mess : TLMessage;
1119begin
1120  if not WSCheckHandleAllocated(AWinControl, 'SetText') then
1121    Exit;
1122  {$IFDEF VerboseTWinControlRealText}
1123  DebugLn(['TGtkWSCustomEdit.SetText START ',DbgSName(AWinControl),' AText="',AText,'"']);
1124  {$ENDIF}
1125  Widget:={%H-}PGtkWidget(AWinControl.Handle);
1126  // some gtk2 versions fire the change event twice
1127  // lock the event and send the message afterwards
1128  // see bug http://bugs.freepascal.org/view.php?id=14615
1129  LockOnChange(PgtkObject(Widget), +1);
1130  try
1131    if GTK_IS_SPIN_BUTTON(Widget) then
1132      gtk_entry_set_text(@PGtkSpinButton(Widget)^.entry, PChar(AText))
1133    else
1134      gtk_entry_set_text(PGtkEntry(Widget), PChar(AText));
1135  finally
1136    LockOnChange(PgtkObject(Widget), -1);
1137  end;
1138  SetSelStart(TCustomEdit(AWinControl), 0);
1139  {$IFDEF VerboseTWinControlRealText}
1140  DebugLn(['TGtkWSCustomEdit.SetText SEND TEXTCHANGED message ',DbgSName(AWinControl),' New="',gtk_entry_get_text(PGtkEntry(AWinControl.Handle)),'"']);
1141  {$ENDIF}
1142  FillByte(Mess{%H-},SizeOf(Mess),0);
1143  Mess.Msg := CM_TEXTCHANGED;
1144  DeliverMessage(AWinControl, Mess);
1145
1146  {$IFDEF VerboseTWinControlRealText}
1147  DebugLn(['TGtkWSCustomEdit.SetText END ',DbgSName(AWinControl),' New="',gtk_entry_get_text(PGtkEntry(AWinControl.Handle)),'"']);
1148  {$ENDIF}
1149end;
1150
1151class procedure TGtk2WSCustomEdit.SetSelText(const ACustomEdit: TCustomEdit;
1152  const NewSelText: string);
1153var
1154  Widget: PGtkWidget;
1155  Entry: PGtkEntry;
1156  Text: string;
1157  SelStart: Integer;
1158  Mess : TLMessage;
1159begin
1160  if not WSCheckHandleAllocated(ACustomEdit, 'SetSelText') then
1161    Exit;
1162  Widget:={%H-}PGtkWidget(ACustomEdit.Handle);
1163  if GTK_IS_SPIN_BUTTON(Widget) then
1164    Entry := @PGtkSpinButton(Widget)^.entry
1165  else
1166    Entry := PGtkEntry(Widget);
1167  Text := gtk_entry_get_text(Entry);
1168  SelStart := GetSelStart(ACustomEdit);
1169  Text := UTF8Copy(Text, 1, SelStart) + NewSelText +
1170    UTF8Copy(Text, SelStart + GetSelLength(ACustomEdit) + 1, MaxInt);
1171  SelStart := SelStart + UTF8Length(NewSelText);
1172  // some gtk2 versions fire the change event twice
1173  // lock the event and send the message afterwards
1174  // see bug http://bugs.freepascal.org/view.php?id=14615
1175  LockOnChange(PgtkObject(Widget), +1);
1176  try
1177    gtk_entry_set_text(Entry, PChar(Text));
1178  finally
1179    LockOnChange(PgtkObject(Widget), -1);
1180  end;
1181  SetSelStart(ACustomEdit, SelStart);
1182  FillByte(Mess{%H-},SizeOf(Mess),0);
1183  Mess.Msg := CM_TEXTCHANGED;
1184  DeliverMessage(ACustomEdit, Mess);
1185end;
1186
1187class procedure TGtk2WSCustomEdit.SetCharCase(const ACustomEdit: TCustomEdit;
1188  NewCase: TEditCharCase);
1189begin
1190  // TODO: TGtk2WSCustomEdit.SetCharCase: implement me!
1191end;
1192
1193class procedure TGtk2WSCustomEdit.SetMaxLength(const ACustomEdit: TCustomEdit;
1194  NewLength: integer);
1195var
1196  Widget: PGtkWidget;
1197begin
1198  Widget:={%H-}PGtkWidget(ACustomEdit.Handle);
1199  if GtkWidgetIsA(Widget, GTK_TYPE_ENTRY) then
1200    gtk_entry_set_max_length(GTK_ENTRY(Widget), guint16(NewLength));
1201end;
1202
1203function CellEntryKeyDown({%H-}Widget: PGtkWidget; Event : pgdkeventkey;
1204  {%H-}Data: gPointer) : GBoolean; cdecl;
1205begin
1206  Result := (Event^.keyval = GDK_KEY_UP) or (Event^.keyval = GDK_KEY_DOWN);
1207end;
1208
1209class function TGtk2WSCustomEdit.CreateHandle(const AWinControl: TWinControl;
1210  const AParams: TCreateParams): TLCLIntfHandle;
1211var
1212  Widget: PGtkWidget; // ptr to the newly created GtkWidget
1213  WidgetInfo: PWidgetInfo;
1214  CellEditable: PGtkCellEditable;
1215begin
1216  Widget := gtk_entry_new();
1217  gtk_editable_set_editable(PGtkEditable(Widget), not TCustomEdit(AWinControl).ReadOnly);
1218  if AParams.Style and WS_VISIBLE = 0 then
1219    gtk_widget_hide(Widget)
1220  else
1221    gtk_widget_show(Widget);
1222  Result := TLCLIntfHandle({%H-}PtrUInt(Widget));
1223  {$IFDEF DebugLCLComponents}
1224  DebugGtkWidgets.MarkCreated(Widget, dbgsName(AWinControl));
1225  {$ENDIF}
1226  if Result = 0 then
1227    Exit;
1228  WidgetInfo := CreateWidgetInfo({%H-}Pointer(Result), AWinControl, AParams);
1229  Set_RC_Name(AWinControl, Widget);
1230  SetCallbacks(Widget, WidgetInfo);
1231
1232  if Result <> 0 then
1233  begin
1234    // hook into GtkEntry interface, so it won't focus another control
1235    // by pressing VK_UP or VK_DOWN. issue #11115
1236    CellEditable := GTK_CELL_EDITABLE(Widget);
1237    g_signal_connect(CellEditable, 'key_press_event',
1238      TGTKSignalFunc(@CellEntryKeyDown), AWinControl);
1239
1240    gtk_entry_set_has_frame({%H-}PGtkEntry(Result),
1241      TCustomEdit(AWinControl).BorderStyle <> bsNone);
1242    // don't select it on focus since LCL do this itself
1243    g_object_set(gtk_widget_get_settings({%H-}PGtkWidget(Result)),
1244	                'gtk-entry-select-on-focus', [0, nil]);
1245  end;
1246end;
1247
1248class function TGtk2WSCustomEdit.GetCaretPos(const ACustomEdit: TCustomEdit
1249  ): TPoint;
1250var
1251  Entry: PGtkEntry;
1252  AInfo: PWidgetInfo;
1253begin
1254  Result := Point(0,0);
1255  if not WSCheckHandleAllocated(ACustomEdit, 'GetCaretPos') then
1256    Exit;
1257  Entry := {%H-}PGtkEntry(ACustomEdit.Handle);
1258  if gtk_widget_has_focus(PGtkWidget(Entry)) then
1259    Result.X := Max(Entry^.current_pos, Entry^.selection_bound)
1260  else begin
1261    AInfo := GetWidgetInfo(PGtkWidget(Entry));
1262    if AInfo <> nil then
1263      Result.X := AInfo^.CursorPos + AInfo^.SelLength;
1264  end;
1265end;
1266
1267class function TGtk2WSCustomEdit.GetSelStart(const ACustomEdit: TCustomEdit
1268  ): integer;
1269var
1270  Entry: PGtkEntry;
1271  AInfo: PWidgetInfo;
1272begin
1273  Result := 0;
1274  if not WSCheckHandleAllocated(ACustomEdit, 'GetSelStart') then
1275    Exit;
1276  Entry := {%H-}PGtkEntry(ACustomEdit.Handle);
1277  if gtk_widget_has_focus(PGtkWidget(Entry)) then
1278    Result := Min(Entry^.current_pos, Entry^.selection_bound)
1279  else begin
1280    AInfo := GetWidgetInfo(PGtkWidget(Entry));
1281    if AInfo <> nil then
1282      Result := AInfo^.CursorPos;
1283  end;
1284end;
1285
1286class function TGtk2WSCustomEdit.GetSelLength(const ACustomEdit: TCustomEdit
1287  ): integer;
1288var
1289  Entry: PGtkEntry;
1290  AInfo: PWidgetInfo;
1291begin
1292  Result := 0;
1293  if not WSCheckHandleAllocated(ACustomEdit, 'GetSelLength') then
1294    Exit;
1295  Entry := {%H-}PGtkEntry(ACustomEdit.Handle);
1296  if gtk_widget_has_focus(PGtkWidget(Entry)) then
1297    Result := ABS(Entry^.current_pos - Entry^.selection_bound)
1298  else begin
1299    AInfo := GetWidgetInfo(PGtkWidget(Entry));
1300    if AInfo <> nil then
1301      Result := AInfo^.SelLength;
1302  end;
1303end;
1304
1305function gtk2WSDelayedSelStart(Data: Pointer): gboolean; cdecl;
1306var
1307  Entry: PGtkEntry;
1308begin
1309  Result := False;
1310  Entry := PGtkEntry(PWidgetInfo(Data)^.CoreWidget);
1311  gtk_editable_set_position(PGtkEditable(Entry), PWidgetInfo(Data)^.CursorPos);
1312  g_idle_remove_by_data(Data);
1313end;
1314
1315function gtk2WSDelayedSetSelLength(Data: Pointer): gboolean; cdecl;
1316var
1317  Entry: PGtkEntry;
1318begin
1319  Result := False;
1320  Entry := PGtkEntry(PWidgetInfo(Data)^.CoreWidget);
1321  gtk_entry_select_region(Entry,
1322      PWidgetInfo(Data)^.CursorPos,
1323      PWidgetInfo(Data)^.CursorPos + PWidgetInfo(Data)^.SelLength);
1324  g_idle_remove_by_data(Data);
1325end;
1326
1327class procedure TGtk2WSCustomEdit.SetCaretPos(const ACustomEdit: TCustomEdit;
1328  const NewPos: TPoint);
1329var
1330  NewStart: Integer;
1331  Entry: PGtkEntry;
1332  WidgetInfo: PWidgetInfo;
1333begin
1334  if not WSCheckHandleAllocated(ACustomEdit, 'SetCaretPos') then
1335    Exit;
1336  SetSelLength(ACustomEdit, 0);
1337  Entry := {%H-}PGtkEntry(ACustomEdit.Handle);
1338  // make gtk2 consistent with others. issue #11802
1339  // if GetCaretPos(ACustomEdit).X = NewPos.X then exit;
1340
1341  if Entry^.text_max_length > 0 then
1342    NewStart := Min(NewPos.X, Entry^.text_max_length)
1343  else
1344    NewStart := Min(NewPos.X, Entry^.text_length);
1345  WidgetInfo := GetWidgetInfo(Entry);
1346  WidgetInfo^.CursorPos := NewStart;
1347  if LockOnChange(PgtkObject(Entry),0) > 0 then
1348  begin
1349    // postpone
1350    g_idle_add(@gtk2WSDelayedSelStart, WidgetInfo);
1351  end else
1352    gtk_editable_set_position(PGtkEditable(Entry), NewStart);
1353end;
1354
1355class procedure TGtk2WSCustomEdit.SetEchoMode(const ACustomEdit: TCustomEdit;
1356  NewMode: TEchoMode);
1357var
1358  Entry: PGtkEntry;
1359begin
1360  if not WSCheckHandleAllocated(ACustomEdit, 'SetEchoMode') then
1361    Exit;
1362  Entry := {%H-}PGtkEntry(ACustomEdit.Handle);
1363  if NewMode in [emNone,emPassword] then begin
1364    gtk_entry_set_visibility(Entry,false);
1365    SetPasswordChar(ACustomEdit,ACustomEdit.PasswordChar);
1366  end else begin
1367    gtk_entry_set_visibility(Entry,true);
1368  end;
1369end;
1370
1371class procedure TGtk2WSCustomEdit.SetPasswordChar(
1372  const ACustomEdit: TCustomEdit; NewChar: char);
1373var
1374  PWChar: Integer;
1375  Entry: PGtkEntry;
1376begin
1377  if not WSCheckHandleAllocated(ACustomEdit, 'SetPasswordChar') then
1378    Exit;
1379  Entry := {%H-}PGtkEntry(ACustomEdit.Handle);
1380  if ACustomEdit.EchoMode=emNone then
1381    PWChar:=0
1382  else begin
1383    PWChar:=ord(ACustomEdit.PasswordChar);
1384    if (PWChar<192) or (PWChar=ord('*')) then
1385      PWChar:=9679;
1386  end;
1387  gtk_entry_set_invisible_char(Entry,PWChar);
1388end;
1389
1390class procedure TGtk2WSCustomEdit.SetReadOnly(const ACustomEdit: TCustomEdit;
1391  NewReadOnly: boolean);
1392var
1393  Widget: PGtkWidget;
1394begin
1395  Widget := {%H-}PGtkWidget(ACustomEdit.Handle);
1396  if GTK_IS_EDITABLE(Widget) then
1397    gtk_editable_set_editable(PGtkEditable(Widget), not NewReadOnly);
1398end;
1399
1400class procedure TGtk2WSCustomEdit.SetSelStart(const ACustomEdit: TCustomEdit;
1401  NewStart: integer);
1402var
1403  NewPos: Integer;
1404  Entry: PGtkEntry;
1405  WidgetInfo: PWidgetInfo;
1406begin
1407  if not WSCheckHandleAllocated(ACustomEdit, 'SetSelStart') then
1408    Exit;
1409  SetSelLength(ACustomEdit, 0);
1410  Entry := {%H-}PGtkEntry(ACustomEdit.Handle);
1411  // make gtk2 consistent with others. issue #11802
1412  // if GetSelStart(ACustomEdit) = NewStart then exit;
1413
1414  if Entry^.text_max_length > 0 then
1415    NewPos := Min(NewStart, Entry^.text_max_length)
1416  else
1417    NewPos := Min(NewStart, Entry^.text_length);
1418  WidgetInfo := GetWidgetInfo(Entry);
1419  WidgetInfo^.CursorPos := NewPos;
1420  if LockOnChange(PgtkObject(Entry),0) > 0 then
1421  begin
1422    // postpone
1423    g_idle_add(@gtk2WSDelayedSelStart, WidgetInfo);
1424  end else
1425    gtk_editable_set_position(PGtkEditable(Entry), NewPos);
1426end;
1427
1428class procedure TGtk2WSCustomEdit.SetSelLength(
1429  const ACustomEdit: TCustomEdit; NewLength: integer);
1430var
1431  Entry: PGtkEntry;
1432  SelStart: Integer;
1433  WidgetInfo: PWidgetInfo;
1434begin
1435  if not WSCheckHandleAllocated(ACustomEdit, 'SetSelLength') then
1436    Exit;
1437  Entry := {%H-}PGtkEntry(ACustomEdit.Handle);
1438  SelStart := GetSelStart(ACustomEdit);
1439
1440  WidgetInfo := GetWidgetInfo(Entry);
1441  if WidgetInfo^.CursorPos = 0 then
1442    WidgetInfo^.CursorPos := SelStart;
1443  WidgetInfo^.SelLength := NewLength;
1444  if LockOnChange(PgtkObject(Entry),0) > 0 then
1445    // delay setting of selection length. issue #20890
1446    g_idle_add(@gtk2WSDelayedSetSelLength, WidgetInfo)
1447  else
1448    gtk_entry_select_region(Entry,
1449      SelStart,
1450      SelStart + NewLength);
1451end;
1452
1453class procedure TGtk2WSCustomEdit.SetAlignment(const ACustomEdit: TCustomEdit;
1454  const AAlignment: TAlignment);
1455var
1456  Entry: PGtkEntry;
1457  Alignment: GFloat;
1458begin
1459  Entry := {%H-}PGtkEntry(ACustomEdit.Handle);
1460  case AAlignment of
1461    taLeftJustify: Alignment := 0;
1462    taRightJustify: Alignment := 1;
1463    taCenter: Alignment := 0.5;
1464  end;
1465  gtk_entry_set_alignment(Entry, Alignment);
1466end;
1467
1468class procedure TGtk2WSCustomEdit.Cut(const ACustomEdit: TCustomEdit);
1469var
1470  ATextView: PGtkTextView;
1471  ABuffer: PGtkTextBuffer;
1472  AStart, AStop: PGtkTextIter;
1473begin
1474  if not WSCheckHandleAllocated(ACustomEdit, 'Cut') then
1475    Exit;
1476
1477  if ACustomEdit.FCompStyle = csMemo then
1478  begin
1479    ATextView := GTK_TEXT_VIEW(GetWidgetInfo({%H-}PGtkWidget(ACustomEdit.Handle))^.CoreWidget);
1480    ABuffer := gtk_text_view_get_buffer(ATextView);
1481    if ABuffer <> nil then
1482    begin
1483      AStart := nil;
1484      AStop := nil;
1485      if gtk_text_buffer_get_selection_bounds(ABuffer, AStart, AStop) then
1486        gtk_text_buffer_cut_clipboard(ABuffer, gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), not ACustomEdit.ReadOnly);
1487    end;
1488  end else
1489    gtk_editable_cut_clipboard({%H-}PGtkEditable(ACustomEdit.Handle));
1490end;
1491
1492class procedure TGtk2WSCustomEdit.Copy(const ACustomEdit: TCustomEdit);
1493var
1494  ATextView: PGtkTextView;
1495  ABuffer: PGtkTextBuffer;
1496  AStart, AStop: PGtkTextIter;
1497begin
1498  if not WSCheckHandleAllocated(ACustomEdit, 'Copy') then
1499    Exit;
1500  if ACustomEdit.FCompStyle = csMemo then
1501  begin
1502    ATextView := GTK_TEXT_VIEW(GetWidgetInfo({%H-}PGtkWidget(ACustomEdit.Handle))^.CoreWidget);
1503    ABuffer := gtk_text_view_get_buffer(ATextView);
1504    if ABuffer <> nil then
1505    begin
1506      AStart := nil;
1507      AStop := nil;
1508      if gtk_text_buffer_get_selection_bounds(ABuffer, AStart, AStop) then
1509        gtk_text_buffer_copy_clipboard(ABuffer, gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
1510    end;
1511  end else
1512    gtk_editable_copy_clipboard({%H-}PGtkEditable(ACustomEdit.Handle));
1513end;
1514
1515class procedure TGtk2WSCustomEdit.Paste(const ACustomEdit: TCustomEdit);
1516var
1517  ATextView: PGtkTextView;
1518  ABuffer: PGtkTextBuffer;
1519begin
1520  if not WSCheckHandleAllocated(ACustomEdit, 'Paste') then
1521    Exit;
1522  if ACustomEdit.FCompStyle = csMemo then
1523  begin
1524    ATextView := GTK_TEXT_VIEW(GetWidgetInfo({%H-}PGtkWidget(ACustomEdit.Handle))^.CoreWidget);
1525    ABuffer := gtk_text_view_get_buffer(ATextView);
1526    if ABuffer <> nil then
1527      gtk_text_buffer_paste_clipboard(ABuffer,
1528        gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), nil, not ACustomEdit.ReadOnly);
1529  end else
1530    gtk_editable_paste_clipboard({%H-}PGtkEditable(ACustomEdit.Handle));
1531end;
1532
1533class procedure TGtk2WSCustomEdit.Undo(const ACustomEdit: TCustomEdit);
1534begin
1535  if not WSCheckHandleAllocated(ACustomEdit, 'Undo') then
1536    Exit;
1537  //TODO: I cannot find anything usefull in gtk2 to do this, seem
1538  //that we have to make our own implementation.
1539end;
1540
1541class procedure TGtk2WSCustomComboBox.ReCreateCombo(
1542  const ACustomComboBox: TCustomComboBox; const AWithEntry: Boolean;
1543  const AWidgetInfo: PWidgetInfo);
1544var
1545  ComboWidget: PGtkWidget;
1546  Model: PGtkTreeModel;
1547  Index: Integer;
1548  Box: PGtkWidget;
1549  ItemList: TGtkListStoreStringList;
1550  LCLIndex: PLongint;
1551begin
1552  Box:={%H-}PGtkWidget(ACustomComboBox.Handle);
1553  ComboWidget := AWidgetInfo^.CoreWidget;
1554
1555  // keep the model (increase ref count)
1556  Model := gtk_combo_box_get_model(PGtkComboBox(ComboWidget));
1557  g_object_ref(G_OBJECT(Model));
1558  // keep items
1559  ItemList := ACustomComboBox.Items as TGtkListStoreStringList;
1560
1561  LCLIndex := AWidgetInfo^.UserData;
1562  if not Assigned(LCLIndex) then begin
1563    //debugln('Gtk2WSCustomComboBox ReCreateCombo: LCLIndex unassigned!');
1564    LCLIndex := New(PLongint);
1565    LCLIndex^ := -1;
1566    AWidgetInfo^.UserData := LCLIndex;
1567    AWidgetInfo^.DataOwner := True;
1568  end;
1569
1570  Index := GetItemIndex(ACustomComboBox);
1571
1572  if PGtkComboBoxPrivate(PGtkComboBox(ComboWidget)^.priv)^.button <> nil then
1573    FreeWidgetInfo(PGtkComboBoxPrivate(PGtkComboBox(ComboWidget)^.priv)^.button);
1574
1575  gtk_event_box_set_above_child(PGtkEventBox(Box), false);
1576  // don't remove Combo from Box, just destroy it and during destroy it will
1577  // be removed by gtk code. Removing from Box and then destroyng can lead to
1578  // double destroying since removing decrease reference and it can be the
1579  // last reference
1580  gtk_widget_destroy(ComboWidget);
1581
1582  // create the new widget with the old model
1583  case AWithEntry of
1584    True : ComboWidget := gtk_combo_box_entry_new_with_model(Model, 0);
1585    False: ComboWidget := gtk_combo_box_new_with_model(Model);
1586  end;
1587  SetSensitivity(ACustomCombobox, ComboWidget);
1588  // undone the above increase of the ref count
1589  g_object_set_data(PGObject(ComboWidget),GtkListItemLCLListTag,ItemList);
1590  g_object_unref (G_OBJECT(Model));
1591
1592  SetMainWidget(Box, GTK_BIN(ComboWidget)^.child);
1593  AWidgetInfo^.CoreWidget := ComboWidget;
1594  g_object_set_data(Pointer(ComboWidget), 'widgetinfo', AWidgetInfo);
1595
1596  SetItemIndex(ACustomComboBox, Index);
1597
1598  if AWithEntry then begin
1599    SetMaxLength(ACustomComboBox, TComboBox(ACustomComboBox).MaxLength);
1600  end;
1601
1602  SetRenderer(ACustomComboBox, ComboWidget, AWidgetInfo);
1603
1604  gtk_container_add(PGtkContainer(Box), ComboWidget);
1605  gtk_widget_show_all(Box);
1606  if ACustomComboBox.HandleObjectShouldBeVisible then
1607    gtk_widget_show(Box)
1608  else
1609    gtk_widget_hide(Box);
1610  if csDesigning in ACustomComboBox.ComponentState then
1611    gtk_event_box_set_above_child(PGtkEventBox(Box), true);
1612
1613  SetCallbacks(ACustomComboBox, Box, AWidgetInfo);
1614end;
1615
1616class procedure TGtk2WSCustomComboBox.SetRenderer(
1617  const ACustomComboBox: TCustomComboBox; AWidget: PGtkWidget; AWidgetInfo: PWidgetInfo);
1618var
1619  renderer : PGtkCellRenderer;
1620begin
1621  renderer := LCLIntfCellRenderer_New();
1622  g_object_set_data(G_OBJECT(renderer), 'widgetinfo', AWidgetInfo);
1623  gtk_cell_layout_clear(PGtkCellLayout(AWidget));
1624  gtk_cell_layout_pack_start(PGtkCellLayout(AWidget), renderer, True);
1625  if not (ACustomComboBox.Style in [csOwnerDrawFixed, csOwnerDrawVariable, csOwnerDrawEditableFixed, csOwnerDrawEditableVariable]) then
1626    gtk_cell_layout_set_attributes(PGtkCellLayout(AWidget), renderer, ['text', 0, nil]);
1627  gtk_cell_layout_set_cell_data_func(PGtkCellLayout(AWidget), renderer,
1628    @LCLIntfCellRenderer_CellDataFunc, AWidgetInfo, nil);
1629end;
1630
1631procedure GtkComboFocus({%H-}AWidget: PGtkWidget; WidgetInfo: PWidgetInfo); cdecl;
1632begin
1633  LCLSendSetFocusMsg(TControl(WidgetInfo^.LCLObject));
1634end;
1635
1636{used only for gtk2 < 2.10 }
1637procedure GtkPopupShowCB({%H-}AMenu: PGtkMenuShell; WidgetInfo: PWidgetInfo); cdecl;
1638begin
1639  g_object_set_data(PGObject(WidgetInfo^.CoreWidget),
1640    'popup-shown-compat',GPointer(PtrUInt(1)));
1641  LCLSendSetFocusMsg(TControl(WidgetInfo^.LCLObject));
1642  // let the LCL change the items on the fly:
1643  LCLSendDropDownMsg(TControl(WidgetInfo^.LCLObject));
1644end;
1645
1646{used only for gtk2 < 2.10 }
1647procedure GtkPopupHideCB({%H-}AMenu: PGtkMenuShell; WidgetInfo: PWidgetInfo); cdecl;
1648begin
1649  g_object_set_data(PGObject(WidgetInfo^.CoreWidget),
1650    'popup-shown-compat',GPointer(PtrUInt(0)));
1651  LCLSendCloseUpMsg(TControl(WidgetInfo^.LCLObject));
1652end;
1653
1654function GtkPopupCloseUp(WidgetInfo: Pointer): gboolean; cdecl;
1655begin
1656  LCLSendCloseUpMsg(TControl(PWidgetInfo(WidgetInfo)^.LCLObject));
1657  Result := gtk_False;// stop the timer
1658end;
1659
1660procedure GtkNotifyCB(AObject: PGObject; pspec: PGParamSpec; WidgetInfo: PWidgetInfo); cdecl;
1661var
1662  AValue: TGValue;
1663  AMenu: PGtkWidget;
1664  ComboBox: TCustomComboBox;
1665begin
1666  if pspec^.name = 'popup-shown' then
1667  begin
1668    LCLSendSetFocusMsg(TControl(WidgetInfo^.LCLObject));
1669    FillChar(AValue{%H-}, SizeOf(AValue), 0); // fill by zeros
1670    g_value_init(@AValue, G_TYPE_BOOLEAN); // initialize for boolean
1671    g_object_get_property(AObject, pspec^.name, @AValue); // get property value
1672    if AValue.data[0].v_int = 0 then // if 0 = False then it is close up
1673      gtk_timeout_add(0,@GtkPopupCloseUp, WidgetInfo)
1674    else // in other case it is drop down
1675    begin
1676      ComboBox:=WidgetInfo^.LCLObject as TCustomComboBox;
1677      ComboBox.IntfGetItems;
1678      LCLSendDropDownMsg(ComboBox);
1679      AMenu := PGtkComboBoxPrivate(PGtkComboBox(WidgetInfo^.CoreWidget)^.priv)^.popup_widget;
1680      if GTK_IS_MENU(AMenu) then
1681        gtk_menu_reposition(PGtkMenu(AMenu));
1682    end;
1683  end;
1684end;
1685
1686procedure GtkChangedCB({%H-}AWidget: PGtkWidget; WidgetInfo: PWidgetInfo); cdecl;
1687var
1688  LCLIndex: PLongint;
1689  Index, GtkIndex: Integer;
1690begin
1691  if WidgetInfo^.ChangeLock > 0 then Exit;
1692  LCLSendChangedMsg(TControl(WidgetInfo^.LCLObject));
1693
1694  Index := -1;
1695  LCLIndex := WidgetInfo^.UserData;
1696  if Assigned(LCLIndex) then
1697    Index := LCLIndex^
1698  else
1699    debugln('Gtk2WSCustomComboBox GtkChangedCB: LCLIndex unassigned!');
1700
1701  GtkIndex := gtk_combo_box_get_active(GTK_COMBO_BOX(WidgetInfo^.CoreWidget));
1702  if Index <> GtkIndex then begin
1703    LCLSendSelectionChangedMsg(TControl(WidgetInfo^.LCLObject));
1704    if Assigned(LCLIndex) then
1705      LCLIndex^ := GtkIndex;
1706  end;
1707end;
1708
1709{procedure GtkSelectedCB(AWidget: PGtkWidget; WidgetInfo: PWidgetInfo); cdecl;
1710begin
1711  if WidgetInfo^.UserData <> nil then Exit;
1712  LCLSendSelectionChangedMsg(TControl(WidgetInfo^.LCLObject));
1713end;}
1714
1715class procedure TGtk2WSCustomComboBox.SetCallbacks(
1716  const AWinControl: TWinControl; const AWidget: PGtkWidget;
1717  const AWidgetInfo: PWidgetInfo);
1718var
1719  AGtkObject: PGtkObject;
1720  AEntry: PGtkObject;
1721  AButton: PGtkObject;
1722  APrivate: PGtkComboBoxPrivate;
1723  AMenu: PGtkObject;
1724  BtnPressID: guint;
1725  HandlerID: guint;
1726  ComboWidget: PGtkComboBox;
1727  InputObject: PGtkObject;
1728begin
1729  ComboWidget:=PGtkComboBox(AWidgetInfo^.CoreWidget);
1730  AGtkObject := PGtkObject(AWidget);
1731  AEntry := PGtkObject(GetComboBoxEntry(PGtkWidget(ComboWidget)));
1732  APrivate := PGtkComboBoxPrivate(ComboWidget^.priv);
1733  AButton := PGtkObject(APrivate^.button);
1734  //DebugLn(['TGtk2WSCustomComboBox.SetCallbacks ',dbgsName(AWinControl),' AButton=',GetWidgetClassName(PGtkWidget(AButton)),' ComboWidget=',GetWidgetClassName(PGtkWidget(ComboWidget))]);
1735
1736  // we have to remove the handler gtk sets up to get the mouse down messages
1737  if AButton <> nil then
1738  begin
1739    BtnPressID := g_signal_lookup('button_press_event', GTK_TYPE_COMBO_BOX);
1740    HandlerID := g_signal_handler_find(AButton, G_SIGNAL_MATCH_ID, BtnPressID, 0, nil, nil, nil);
1741    if HandlerID > 0 then
1742      g_signal_handler_disconnect(AButton, HandlerID);
1743  end;
1744
1745  g_signal_connect(ComboWidget, 'changed', TGCallback(@GtkChangedCB), AWidgetInfo);
1746
1747  // First the combo (or the entry)
1748  if gtk_is_combo_box_entry(ComboWidget) then
1749    InputObject := AEntry
1750  else
1751    InputObject := AGtkObject;
1752
1753  if TCustomComboBox(AWinControl).Style in [csDropDownList, csOwnerDrawFixed, csOwnerDrawVariable] then
1754  begin
1755    // Just a combobox without a edit should handle its own keys. Issue #32458
1756    Gtk2WidgetSet.SetCallbackDirect(LM_KEYDOWN, InputObject, AWinControl);
1757    Gtk2WidgetSet.SetCallbackDirect(LM_KEYUP, InputObject, AWinControl);
1758    Gtk2WidgetSet.SetCallbackDirect(LM_CHAR, InputObject, AWinControl);
1759  end;
1760  Gtk2WidgetSet.SetCallbackDirect(LM_MOUSEMOVE, InputObject, AWinControl);
1761  Gtk2WidgetSet.SetCallbackDirect(LM_LBUTTONDOWN, InputObject, AWinControl);
1762  Gtk2WidgetSet.SetCallbackDirect(LM_LBUTTONUP, InputObject, AWinControl);
1763  Gtk2WidgetSet.SetCallbackDirect(LM_LBUTTONDBLCLK, InputObject, AWinControl);
1764  Gtk2WidgetSet.SetCallbackDirect(LM_RBUTTONDBLCLK, InputObject, AWinControl);
1765  Gtk2WidgetSet.SetCallbackDirect(LM_MBUTTONDBLCLK, InputObject, AWinControl);
1766  Gtk2WidgetSet.SetCallbackDirect(LM_RBUTTONDOWN, InputObject, AWinControl);
1767  Gtk2WidgetSet.SetCallbackDirect(LM_RBUTTONUP, InputObject, AWinControl);
1768  Gtk2WidgetSet.SetCallbackDirect(LM_MBUTTONDOWN, InputObject, AWinControl);
1769  Gtk2WidgetSet.SetCallbackDirect(LM_MBUTTONUP, InputObject, AWinControl);
1770  Gtk2WidgetSet.SetCallbackDirect(LM_MOUSEWHEEL, InputObject, AWinControl);
1771  Gtk2WidgetSet.SetCallbackDirect(LM_MOUSEHWHEEL, InputObject, AWinControl);
1772  Gtk2WidgetSet.SetCallbackDirect(LM_PAINT, InputObject, AWinControl);
1773  Gtk2WidgetSet.SetCallbackDirect(LM_FOCUS, InputObject, AWinControl);
1774
1775  // And now the same for the Button in the combo
1776  if AButton<>nil then begin
1777    if TCustomComboBox(AWinControl).Style in [csDropDownList, csOwnerDrawFixed, csOwnerDrawVariable] then
1778    begin
1779      // Just a combobox without a edit should handle its own keys. Issue #32458
1780      Gtk2WidgetSet.SetCallbackDirect(LM_KEYDOWN, AButton, AWinControl);
1781      Gtk2WidgetSet.SetCallbackDirect(LM_KEYUP, AButton, AWinControl);
1782      Gtk2WidgetSet.SetCallbackDirect(LM_CHAR, AButton, AWinControl);
1783    end;
1784    if not GtkWidgetIsA(PGtkWidget(AButton),GTK_TYPE_CELL_VIEW) then begin
1785      Gtk2WidgetSet.SetCallbackDirect(LM_MOUSEENTER, AButton, AWinControl);
1786      Gtk2WidgetSet.SetCallbackDirect(LM_MOUSELEAVE, AButton, AWinControl);
1787    end;
1788    Gtk2WidgetSet.SetCallbackDirect(LM_MOUSEMOVE, AButton, AWinControl);
1789    Gtk2WidgetSet.SetCallbackDirect(LM_LBUTTONDOWN, AButton, AWinControl);
1790    Gtk2WidgetSet.SetCallbackDirect(LM_LBUTTONUP, AButton, AWinControl);
1791    Gtk2WidgetSet.SetCallbackDirect(LM_LBUTTONUP, AButton, AWinControl);
1792    Gtk2WidgetSet.SetCallbackDirect(LM_RBUTTONDOWN, AButton, AWinControl);
1793    Gtk2WidgetSet.SetCallbackDirect(LM_RBUTTONUP, AButton, AWinControl);
1794    Gtk2WidgetSet.SetCallbackDirect(LM_MBUTTONDOWN, AButton, AWinControl);
1795    Gtk2WidgetSet.SetCallbackDirect(LM_MBUTTONUP, AButton, AWinControl);
1796    Gtk2WidgetSet.SetCallbackDirect(LM_MOUSEWHEEL, AButton, AWinControl);
1797    Gtk2WidgetSet.SetCallbackDirect(LM_MOUSEHWHEEL, AButton, AWinControl);
1798    Gtk2WidgetSet.SetCallbackDirect(LM_PAINT, AButton, AWinControl);
1799    Gtk2WidgetSet.SetCallbackDirect(LM_FOCUS, AButton, AWinControl);
1800  end;
1801
1802  // if we are a GtkComboBoxEntry
1803  if not GtkWidgetIsA(PGtkWidget(AEntry), GTK_TYPE_ENTRY) then
1804    g_signal_connect(Combowidget, 'grab-focus', TGCallback(@GtkComboFocus), AWidgetInfo);
1805
1806  AMenu := nil;
1807  if (APrivate^.popup_widget <> nil)
1808  and (GTK_IS_MENU(APrivate^.popup_widget)) then
1809    AMenu := GTK_OBJECT(APrivate^.popup_widget)
1810  else if (APrivate^.popup_window <> nil)
1811  and (GTK_IS_MENU(APrivate^.popup_window)) then
1812    AMenu := GTK_OBJECT(APrivate^.popup_window);
1813
1814  if Assigned(AMenu) and
1815    (gtk_major_version = 2) and (gtk_minor_version < 10) then
1816  begin
1817    g_signal_connect(AMenu, 'show', G_CALLBACK(@GtkPopupShowCB), AWidgetInfo);
1818    g_signal_connect_after(AMenu, 'selection-done', G_CALLBACK(@GtkPopupHideCB), AWidgetInfo);
1819  end;
1820
1821  g_signal_connect_after(PGtkObject(GTK_BIN(ComboWidget)^.child), 'populate-popup',
1822    gtk_signal_func(@gtkDefaultPopupMenuCloseFix), AWidgetInfo);
1823
1824  if (gtk_major_version >= 2) and (gtk_minor_version >= 10) then
1825    g_signal_connect(ComboWidget, 'notify', TGCallback(@GtkNotifyCB), AWidgetInfo);
1826
1827
1828  // g_signal_connect(ComboWidget, 'set-focus-child', TGCallback(@GtkPopupShowCB), AWidgetInfo);
1829  g_object_set_data(G_OBJECT(AWidget), 'Menu', APrivate^.popup_widget);
1830end;
1831
1832class procedure TGtk2WSCustomComboBox.SetSensitivity(AWinControl: TWinControl; AWidget: PGtkWidget);
1833var
1834  Value: TGValue;
1835begin
1836  if ((gtk_major_version = 2) and (gtk_minor_version < 14)) or
1837     (csDesigning in AWinControl.ComponentState) then
1838    Exit;
1839  Value.g_type := G_TYPE_BOOLEAN;
1840  Value.data[0].v_int := longint(gTRUE);
1841
1842  g_object_set_property(PGObject(AWidget), 'button-sensitivity', @Value);
1843end;
1844
1845class procedure TGtk2WSCustomComboBox.GetPreferredSize(
1846  const AWinControl: TWinControl; var PreferredWidth, PreferredHeight: integer;
1847  WithThemeSpace: Boolean);
1848var
1849  Ignore: Integer;
1850begin
1851  Ignore:=0;
1852  GetGTKDefaultWidgetSize(AWinControl, Ignore, PreferredHeight, WithThemeSpace);
1853  PreferredWidth := 0;
1854end;
1855
1856class function TGtk2WSCustomComboBox.GetDroppedDown(
1857  const ACustomComboBox: TCustomComboBox): Boolean;
1858var
1859  WidgetInfo: PWidgetInfo;
1860  Combo: PGtkComboBox;
1861  AValue: TGValue;
1862begin
1863  WidgetInfo := GetWidgetInfo({%H-}Pointer(ACustomComboBox.Handle), False);
1864  Combo := PGtkComboBox(WidgetInfo^.CoreWidget);
1865
1866  FillChar(AValue{%H-}, SizeOf(AValue), 0);
1867  g_value_init(@AValue, G_TYPE_BOOLEAN);
1868  if (gtk_major_version = 2) and (gtk_minor_version < 10) then
1869  begin
1870    if g_object_get_data(PGObject(Combo),'popup-shown-compat') <> nil then
1871      AValue.data[0].v_int := 1
1872    else
1873      AValue.data[0].v_int := 0;
1874  end else
1875    g_object_get_property(PGObject(Combo), 'popup-shown', @AValue);
1876  Result := AValue.data[0].v_int <> 0;
1877end;
1878
1879class function TGtk2WSCustomComboBox.GetSelStart(
1880  const ACustomComboBox: TCustomComboBox): integer;
1881var
1882  WidgetInfo: PWidgetInfo;
1883  Entry: PGtkEntry;
1884  AStart, AEnd: gint;
1885begin
1886  Result := 0;
1887  WidgetInfo := GetWidgetInfo({%H-}Pointer(ACustomComboBox.Handle));
1888
1889  // if the combo is an editable ...
1890  Entry := GetComboBoxEntry(WidgetInfo^.CoreWidget);
1891  if Entry<>nil then begin
1892    if gtk_editable_get_selection_bounds(PGtkEditable(Entry), @AStart, @AEnd) = False then
1893      Result := gtk_editable_get_position(PGtkEditable(Entry))
1894    else
1895      Result := Min(AStart, AEnd);
1896  end;
1897end;
1898
1899class function TGtk2WSCustomComboBox.GetSelLength(
1900  const ACustomComboBox: TCustomComboBox): integer;
1901var
1902  WidgetInfo: PWidgetInfo;
1903  Entry: PGtkEntry;
1904  AStart, AEnd: gint;
1905begin
1906  Result := 0;
1907  WidgetInfo := GetWidgetInfo({%H-}Pointer(ACustomComboBox.Handle));
1908
1909  // if the combo is an editable ...
1910  Entry := GetComboBoxEntry(WidgetInfo^.CoreWidget);
1911  if Entry<>nil then
1912  begin
1913    if not gtk_editable_get_selection_bounds(PGtkEditable(Entry), @AStart, @AEnd) then
1914      Exit(0);
1915    Result := ABS(AStart - AEnd);
1916  end;
1917end;
1918
1919class function TGtk2WSCustomComboBox.GetItemIndex(
1920  const ACustomComboBox: TCustomComboBox): integer;
1921var
1922  WidgetInfo: PWidgetInfo;
1923begin
1924  WidgetInfo := GetWidgetInfo({%H-}Pointer(ACustomComboBox.Handle));
1925
1926  Result := gtk_combo_box_get_active(PGtkComboBox(WidgetInfo^.CoreWidget));
1927end;
1928
1929class function TGtk2WSCustomComboBox.GetMaxLength(
1930  const ACustomComboBox: TCustomComboBox): integer;
1931var
1932  WidgetInfo: PWidgetInfo;
1933  Entry: PGtkEntry;
1934begin
1935  WidgetInfo := GetWidgetInfo({%H-}Pointer(ACustomComboBox.Handle));
1936
1937  // if the combo is an editable ...
1938  Entry := GetComboBoxEntry(WidgetInfo^.CoreWidget);
1939  if Entry<>nil then begin
1940    Result := gtk_entry_get_max_length(Entry);
1941  end
1942  else begin
1943     Result := integer({%H-}PtrUInt(g_object_get_data(PGObject(WidgetInfo^.CoreWidget), 'max-length')));
1944  end;
1945end;
1946
1947class function TGtk2WSCustomComboBox.GetText(const AWinControl: TWinControl;
1948  var AText: String): Boolean;
1949var
1950  WidgetInfo: PWidgetInfo;
1951  Entry: PGtkEntry;
1952  Index: Integer;
1953begin
1954  Result := True;
1955  WidgetInfo := GetWidgetInfo({%H-}Pointer(AWinControl.Handle));
1956
1957  // if the combo is an editable ...
1958  Entry := GetComboBoxEntry(WidgetInfo^.CoreWidget);
1959  if Entry<>nil then begin
1960    AText := gtk_entry_get_text(Entry);
1961    exit;
1962  end;
1963
1964  // if we are a fixed un-editable combo then ...
1965  Index := GetItemIndex(TCustomComboBox(AWinControl));
1966  if Index > -1 then  AText := TCustomComboBox(AWinControl).Items.Strings[Index];
1967end;
1968
1969class procedure TGtk2WSCustomComboBox.SetArrowKeysTraverseList(
1970  const ACustomComboBox: TCustomComboBox; NewTraverseList: boolean);
1971begin
1972  // TODO: TGtk2WSCustomComboBox.SetArrowKeysTraverseList: not supported
1973  // This is not an option that is available for this widget
1974  // we will have to eat the keystrokes to set this to false
1975end;
1976
1977class procedure TGtk2WSCustomComboBox.SetDroppedDown(
1978  const ACustomComboBox: TCustomComboBox; ADroppedDown: Boolean);
1979var
1980  WidgetInfo: PWidgetInfo;
1981  Combo: PGtkComboBox;
1982begin
1983  WidgetInfo := GetWidgetInfo({%H-}Pointer(ACustomComboBox.Handle), False);
1984  Combo := PGtkComboBox(WidgetInfo^.CoreWidget);
1985
1986  case ADroppedDown of
1987    True : gtk_combo_box_popup(Combo);
1988    False: gtk_combo_box_popdown(Combo);
1989  end;
1990end;
1991
1992class procedure TGtk2WSCustomComboBox.SetSelStart(
1993  const ACustomComboBox: TCustomComboBox; NewStart: integer);
1994var
1995  WidgetInfo: PWidgetInfo;
1996  Entry: PGtkEntry;
1997begin
1998  WidgetInfo := GetWidgetInfo({%H-}Pointer(ACustomComboBox.Handle));
1999
2000  Entry := GetComboBoxEntry(WidgetInfo^.CoreWidget);
2001  if Entry<>nil then begin
2002    //gtk_entry_select_region(Entry, NewStart, NewStart);
2003    gtk_editable_set_position(PGtkEditable(Entry), NewStart);
2004  end;
2005end;
2006
2007class procedure TGtk2WSCustomComboBox.SetSelLength(
2008  const ACustomComboBox: TCustomComboBox; NewLength: integer);
2009var
2010  WidgetInfo: PWidgetInfo;
2011  Entry: PGtkEntry;
2012  Start: Integer;
2013begin
2014  WidgetInfo := GetWidgetInfo({%H-}Pointer(ACustomComboBox.Handle));
2015
2016  Entry := GetComboBoxEntry(WidgetInfo^.CoreWidget);
2017  if Entry<>nil then begin
2018    Start := GetSelStart(ACustomComboBox);
2019    gtk_editable_select_region(PGtkEditable(Entry), Start, Start + NewLength);
2020  end;
2021end;
2022
2023class procedure TGtk2WSCustomComboBox.SetItemIndex(
2024  const ACustomComboBox: TCustomComboBox; NewIndex: integer);
2025var
2026  P: PGtkWidget;
2027  WidgetInfo: PWidgetInfo;
2028  LCLIndex: PLongint;
2029begin
2030  WidgetInfo := GetWidgetInfo({%H-}Pointer(ACustomComboBox.Handle));
2031  p := WidgetInfo^.CoreWidget;
2032  if gtk_combo_box_get_active(PGtkComboBox(p)) = NewIndex then exit;
2033  // to be delphi compatible OnChange only fires in response to user actions not program actions
2034  // so we use WidgetInfo^.ChangeLock as a flag to not signal the OnChange Event
2035  Inc(WidgetInfo^.ChangeLock);
2036  gtk_combo_box_set_active(PGtkComboBox(p), NewIndex);
2037
2038  if (NewIndex = -1) and gtk_is_combo_box_entry(p) then
2039    gtk_entry_set_text(PGtkEntry(GTK_BIN(p)^.child), PChar(''));
2040
2041  LCLIndex := WidgetInfo^.UserData;
2042  if not Assigned(LCLIndex) then
2043  begin
2044    LCLIndex := New(PLongint);
2045    WidgetInfo^.UserData := LCLIndex;
2046    WidgetInfo^.DataOwner := True;
2047  end;
2048  LCLIndex^ := NewIndex;
2049
2050  Dec(WidgetInfo^.ChangeLock);
2051end;
2052
2053class procedure TGtk2WSCustomComboBox.SetMaxLength(
2054  const ACustomComboBox: TCustomComboBox; NewLength: integer);
2055var
2056  WidgetInfo: PWidgetInfo;
2057  Entry: PGtkEntry;
2058begin
2059  WidgetInfo := GetWidgetInfo({%H-}Pointer(ACustomComboBox.Handle));
2060
2061  Entry := GetComboBoxEntry(WidgetInfo^.CoreWidget);
2062  if Entry<>nil then begin
2063    gtk_entry_set_max_length(Entry, NewLength);
2064  end;
2065  // We save this in the CoreWidget for when the Entry Changes styles
2066  g_object_set_data(PGObject(WidgetInfo^.CoreWidget), 'max-length', {%H-}Pointer(PtrInt(NewLength)));
2067end;
2068
2069class procedure TGtk2WSCustomComboBox.SetStyle(
2070  const ACustomComboBox: TCustomComboBox; NewStyle: TComboBoxStyle);
2071var
2072  WidgetInfo: PWidgetInfo;
2073  p: PGtkWidget;
2074  NeedEntry: Boolean;
2075begin
2076  WidgetInfo := GetWidgetInfo({%H-}Pointer(ACustomComboBox.Handle));
2077  p := WidgetInfo^.CoreWidget;
2078  case NewStyle of
2079    csDropDown,
2080    csSimple,
2081    csOwnerDrawEditableFixed,
2082    csOwnerDrawEditableVariable:
2083      NeedEntry := True;
2084    csDropDownList,
2085    csOwnerDrawFixed,
2086    csOwnerDrawVariable:
2087      NeedEntry := False;
2088  end;
2089  if gtk_is_combo_box_entry(p) = NeedEntry then Exit;
2090  ReCreateCombo(ACustomComboBox, NeedEntry, WidgetInfo);
2091end;
2092
2093class function TGtk2WSCustomComboBox.GetItems(
2094  const ACustomComboBox: TCustomComboBox): TStrings;
2095var
2096  ComboWidget: PGtkWidget;
2097  Handle: HWND;
2098begin
2099  Handle := ACustomComboBox.Handle;
2100  ComboWidget := GetWidgetInfo({%H-}Pointer(Handle), True)^.CoreWidget;
2101  Result :=  TGtkListStoreStringList(g_object_get_data(PGObject(ComboWidget),
2102                                     GtkListItemLCLListTag));
2103end;
2104
2105class procedure TGtk2WSCustomComboBox.Sort(const ACustomComboBox: TCustomComboBox;
2106  AList: TStrings; IsSorted: boolean);
2107var
2108  ComboWidget: PGtkWidget;
2109  Handle: HWND;
2110begin
2111  Handle := ACustomComboBox.Handle;
2112  ComboWidget := GetWidgetInfo({%H-}Pointer(Handle), True)^.CoreWidget;
2113  TGtkListStoreStringList(g_object_get_data(PGObject(ComboWidget),
2114                                     GtkListItemLCLListTag)).Sorted := IsSorted;
2115end;
2116
2117class procedure TGtk2WSCustomComboBox.SetColor(const AWinControl: TWinControl);
2118var
2119  WidgetInfo: PWidgetInfo;
2120  Child: PGtkWidget; // can be GtkCellRenderer or GtkEntry
2121begin
2122  if not WSCheckHandleAllocated(AWinControl, 'SetColor') then
2123    Exit;
2124  WidgetInfo := GetWidgetInfo({%H-}Pointer(AWinControl.Handle));
2125
2126  Child := GTK_BIN(WidgetInfo^.CoreWidget)^.child;
2127  Gtk2WidgetSet.SetWidgetColor(Child, AWinControl.Font.Color, AWinControl.Color,
2128   [GTK_STATE_NORMAL,GTK_STYLE_BASE]);
2129end;
2130
2131class procedure TGtk2WSCustomComboBox.SetFont(const AWinControl: TWinControl;
2132  const AFont: TFont);
2133var
2134  Entry: PGtkEntry;
2135  WidgetInfo: PWidgetInfo;
2136  W: PGtkWidget;
2137begin
2138  if not AWinControl.HandleAllocated then exit;
2139
2140  WidgetInfo := GetWidgetInfo({%H-}Pointer(AWinControl.Handle));
2141  Entry := GetComboBoxEntry(WidgetInfo^.CoreWidget);
2142
2143  if Entry <> nil then
2144  begin
2145    Gtk2WidgetSet.SetWidgetColor(PGtkWidget(Entry), AFont.Color, clNone,
2146       [GTK_STATE_NORMAL,GTK_STATE_ACTIVE,GTK_STATE_PRELIGHT,GTK_STATE_SELECTED,GTK_STYLE_TEXT]);
2147    Gtk2WidgetSet.SetWidgetFont(PGtkWidget(Entry), AFont);
2148  end else
2149  begin
2150    W := GTK_BIN(WidgetInfo^.CoreWidget)^.child;
2151    if W <> nil then
2152    begin
2153      Gtk2WidgetSet.SetWidgetColor(W, AFont.Color, clNone,
2154        [GTK_STATE_NORMAL,GTK_STATE_ACTIVE,GTK_STATE_PRELIGHT,GTK_STATE_SELECTED,GTK_STYLE_TEXT]);
2155      Gtk2WidgetSet.SetWidgetFont(W, AFont);
2156    end;
2157  end;
2158end;
2159
2160class procedure TGtk2WSCustomComboBox.SetText(const AWinControl: TWinControl;
2161  const AText: String);
2162var
2163  WidgetInfo: PWidgetInfo;
2164  Entry: PGtkWidget;
2165begin
2166  WidgetInfo := GetWidgetInfo({%H-}Pointer(AWinControl.Handle));
2167  // we use user ChangeLock to not signal onchange
2168  Inc(WidgetInfo^.ChangeLock);
2169  if gtk_is_combo_box_entry(WidgetInfo^.CoreWidget) then
2170  begin
2171    Entry := GTK_BIN(WidgetInfo^.CoreWidget)^.child;
2172    gtk_entry_set_text(PGtkEntry(Entry), PChar(AText));
2173  end;
2174  Dec(WidgetInfo^.ChangeLock);
2175end;
2176
2177class procedure TGtk2WSCustomComboBox.ShowHide(const AWinControl: TWinControl);
2178begin
2179  Gtk2WidgetSet.SetVisible(AWinControl, AWinControl.HandleObjectShouldBeVisible);
2180  InvalidateLastWFPResult(AWinControl, AWinControl.BoundsRect);
2181end;
2182
2183class function TGtk2WSCustomComboBox.CanFocus(const AWinControl: TWinControl
2184  ): boolean;
2185var
2186  WidgetInfo: PWidgetInfo;
2187  Entry: PGtkWidget;
2188begin
2189  if not AWinControl.HandleAllocated then exit(false);
2190  WidgetInfo := GetWidgetInfo({%H-}Pointer(AWinControl.Handle));
2191  if gtk_is_combo_box_entry(WidgetInfo^.CoreWidget) then begin
2192    Entry := GTK_BIN(WidgetInfo^.CoreWidget)^.child;
2193    Result:=GTK_WIDGET_CAN_FOCUS(Entry);
2194  end else begin
2195    Result:=inherited CanFocus(AWinControl);
2196  end;
2197  //DebugLn(['TGtk2WSCustomComboBox.CanFocus ',dbgsName(AWinControl),' ',gtk_is_combo_box_entry(WidgetInfo^.CoreWidget),' Result=',Result]);
2198end;
2199
2200class function TGtk2WSCustomComboBox.CreateHandle(const AWinControl: TWinControl;
2201  const AParams: TCreateParams): TLCLIntfHandle;
2202var
2203  Box,      // this makes it easy to switch between GtkComBox and GtkComboBoxEntry
2204  ComboWidget: PGtkWidget; // ptr to the newly created GtkWidget
2205  ListStore : PGtkListStore;
2206  WidgetInfo: PWidgetInfo;
2207  ACustomComboBox: TCustomComboBox;
2208  ItemList: TGtkListStoreStringList;
2209  LCLIndex: PLongint;
2210  NeedEntry: Boolean;
2211begin
2212  ACustomComboBox := TCustomComboBox(AWinControl);
2213
2214  Box := gtk_event_box_new;
2215  {$IFDEF DebugLCLComponents}
2216  DebugGtkWidgets.MarkCreated(Box,dbgsName(AWinControl));
2217  {$ENDIF}
2218
2219  WidgetInfo := CreateWidgetInfo(Box, AWinControl, AParams);
2220
2221  ListStore := gtk_list_store_new (2, [G_TYPE_STRING, G_TYPE_POINTER, nil]);
2222
2223  case ACustomComboBox.Style of
2224    csDropDown,
2225    csSimple,
2226    csOwnerDrawEditableFixed,
2227    csOwnerDrawEditableVariable:
2228      NeedEntry := True;
2229    csDropDownList,
2230    csOwnerDrawFixed,
2231    csOwnerDrawVariable:
2232      NeedEntry := False;
2233  end;
2234  if NeedEntry then
2235    ComboWidget := gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL (ListStore), 0)
2236  else
2237    ComboWidget := gtk_combo_box_new_with_model(GTK_TREE_MODEL (ListStore));
2238
2239  SetSensitivity(AWinControl, ComboWidget);
2240
2241  g_object_unref (G_OBJECT (liststore));
2242
2243  gtk_container_add(PGtkContainer(Box), ComboWidget);
2244  gtk_widget_show_all(Box);
2245  if csDesigning in AWinControl.ComponentState then
2246    gtk_event_box_set_above_child(PGtkEventBox(Box), true);
2247
2248  SetRenderer(ACustomComboBox, ComboWidget, WidgetInfo);
2249
2250  SetMainWidget(Box, ComboWidget);
2251  SetMainWidget(Box, GTK_BIN(ComboWidget)^.child);
2252  if PGtkComboBoxPrivate(PGtkComboBox(ComboWidget)^.priv)^.button <> nil then
2253    SetMainWidget(Box, PGtkComboBoxPrivate(PGtkComboBox(ComboWidget)^.priv)^.button);
2254
2255  LCLIndex := New(PLongint);
2256  //Should not set the ItemIndex value here?
2257  LCLIndex^ := -1;
2258  WidgetInfo^.CoreWidget := ComboWidget;
2259  WidgetInfo^.ClientWidget := Box;
2260  WidgetInfo^.UserData := LCLIndex;
2261  WidgetInfo^.DataOwner := True;
2262
2263  //gtk_widget_add_events(Box, GDK_ALL_EVENTS_MASK);
2264
2265  SetCallbacks(AWinControl, Box, WidgetInfo);
2266
2267  // Items
2268  ItemList:= TGtkListStoreStringList.Create(
2269          gtk_combo_box_get_model(PGtkComboBox(ComboWidget)),0,ACustomComboBox);
2270  g_object_set_data(PGObject(ComboWidget),GtkListItemLCLListTag,ItemList);
2271  // This is done in InitializeWnd: ItemList.Assign(ACustomComboBox.Items);
2272  if ACustomComboBox.Items is TStringList then
2273    ItemList.Sorted:=TStringList(ACustomComboBox.Items).Sorted;
2274
2275  if AParams.Style and WS_VISIBLE = 0 then
2276    gtk_widget_hide(Box)
2277  else
2278    gtk_widget_show(Box);
2279
2280  Result := TLCLIntfHandle({%H-}PtrUInt(Box));
2281end;
2282
2283class procedure TGtk2WSCustomComboBox.DestroyHandle(
2284  const AWinControl: TWinControl);
2285var
2286  Handle: HWND;
2287  ComboWidget: PGtkWidget;
2288begin
2289  Handle := AWinControl.Handle;
2290  ComboWidget := GetWidgetInfo({%H-}Pointer(Handle), True)^.CoreWidget;
2291
2292  if PGtkComboBoxPrivate(PGtkComboBox(ComboWidget)^.priv)^.button <> nil then
2293    FreeWidgetInfo(PGtkComboBoxPrivate(PGtkComboBox(ComboWidget)^.priv)^.button);
2294  //DebugLn(['TGtk2WSCustomComboBox.DestroyHandle ',dbgsName(AWinControl),' ClassParent=',ClassParent.ClassName]);
2295
2296  // inherited DestroyHandle doesn't work, because that is determined at
2297  // compile time, while the WS class hierarchy is created at runtime
2298  TWSWinControlClass(Classparent).DestroyHandle(AWinControl);
2299end;
2300
2301{ TGtk2WSCustomGroupBox }
2302
2303class procedure TGtk2WSCustomGroupBox.SetCallbacks(const AGtkWidget: PGtkWidget;
2304  const AWidgetInfo: PWidgetInfo);
2305begin
2306  TGtk2WSWinControl.SetCallbacks(PGtkObject(AGtkWidget), TComponent(AWidgetInfo^.LCLObject));
2307end;
2308
2309class procedure TGtk2WSCustomGroupBox.SetLabel(AFrame: PGtkFrame; AText: String);
2310var
2311  Lbl: PGtkWidget;
2312begin
2313  Lbl := gtk_frame_get_label_widget(AFrame);
2314  if (AText = '') then
2315  begin
2316    if Lbl <> nil then
2317      gtk_widget_destroy(Lbl);
2318  end
2319  else
2320  begin
2321    if Lbl = nil then
2322    begin
2323      Lbl := gtk_label_new(nil);
2324      gtk_widget_show(Lbl);
2325      gtk_frame_set_label_widget(AFrame, Lbl);
2326    end;
2327    Gtk2Widgetset.SetLabelCaption(PGtkLabel(Lbl), AText);
2328  end;
2329end;
2330
2331class function TGtk2WSCustomGroupBox.GetFrameWidget(AEventBox: PGtkEventBox): PGtkFrame;
2332var
2333  GBWidget: PGTKWidget;
2334begin
2335  GBWidget := PGTKWidget(AEventBox);
2336  Result:=PGtkFrame(PGtkBin(GBWidget)^.child);
2337end;
2338
2339class function TGtk2WSCustomGroupBox.CreateHandle(
2340  const AWinControl: TWinControl; const AParams: TCreateParams
2341  ): TLCLIntfHandle;
2342var
2343{$if not defined(GtkFixedWithWindow)}
2344  EventBox: PGtkWidget;
2345{$endif}
2346  FrameBox: PGTKWidget;
2347  TempWidget: PGTKWidget;       // pointer to gtk-widget (local use when neccessary)
2348  p : pointer;          // ptr to the newly created GtkWidget
2349  Allocation: TGTKAllocation;
2350  WidgetInfo: PWidgetInfo;
2351begin
2352  P := gtk_frame_new(nil);
2353  SetLabel(P, AParams.Caption);
2354  WidgetInfo := CreateWidgetInfo(P, AWinControl, AParams);
2355  {$if defined(GtkFixedWithWindow)}
2356  TempWidget := CreateFixedClientWidget;
2357  gtk_container_add(GTK_CONTAINER(p), TempWidget);
2358  WidgetInfo^.ClientWidget := TempWidget;
2359  WidgetInfo^.CoreWidget := TempWidget;
2360  g_object_set_data(PGObject(TempWidget), 'widgetinfo', WidgetInfo);
2361  {$else}
2362  EventBox := gtk_event_box_new;
2363  gtk_event_box_set_visible_window(PGtkEventBox(EventBox), False);
2364  TempWidget := CreateFixedClientWidget(False);
2365  gtk_container_add(GTK_CONTAINER(EventBox), TempWidget);
2366  gtk_container_add(GTK_CONTAINER(p), EventBox);
2367  gtk_widget_show(EventBox);
2368  WidgetInfo^.ClientWidget := TempWidget;
2369  WidgetInfo^.CoreWidget := EventBox;
2370  g_object_set_data(PGObject(TempWidget), 'widgetinfo', WidgetInfo);
2371  g_object_set_data(PGObject(EventBox), 'widgetinfo', WidgetInfo);
2372  {$endif}
2373  FrameBox := gtk_event_box_new;
2374  gtk_event_box_set_visible_window(PGtkEventBox(FrameBox), True);
2375  gtk_container_add(GTK_CONTAINER(FrameBox), p);
2376  g_object_set_data(PGObject(FrameBox), 'widgetinfo', WidgetInfo);
2377  gtk_widget_show(TempWidget);
2378  gtk_widget_show(P);
2379  if AWinControl.HandleObjectShouldBeVisible then
2380    gtk_widget_show(FrameBox);
2381
2382  Result := TLCLIntfHandle({%H-}PtrUInt(FrameBox));
2383
2384  Allocation.X := AParams.X;
2385  Allocation.Y := AParams.Y;
2386  Allocation.Width := AParams.Width;
2387  Allocation.Height := AParams.Height;
2388  gtk_widget_size_allocate(FrameBox, @Allocation);
2389
2390  Set_RC_Name(AWinControl, FrameBox);
2391  SetCallbacks(FrameBox, WidgetInfo);
2392end;
2393
2394class procedure TGtk2WSCustomGroupBox.SetColor(const AWinControl: TWinControl);
2395var
2396  GBWidget: PGTKWidget;
2397begin
2398  if not WSCheckHandleAllocated(AWinControl, 'SetColor') then
2399    Exit;
2400  GBWidget:={%H-}PGTKWidget(AWinControl.Handle);
2401
2402  {$if defined(GtkFixedWithWindow)}
2403    Gtk2WidgetSet.SetWidgetColor(GetFixedWidget(GBWidget), clNone, AWinControl.Color,
2404       [GTK_STATE_NORMAL,GTK_STATE_ACTIVE,GTK_STATE_PRELIGHT,GTK_STATE_SELECTED]);
2405  {$endif}
2406    Gtk2WidgetSet.SetWidgetColor(GBWidget, clNone, AWinControl.Color,
2407       [GTK_STATE_NORMAL,GTK_STATE_ACTIVE,GTK_STATE_PRELIGHT,GTK_STATE_SELECTED]);
2408end;
2409
2410class function TGtk2WSCustomGroupBox.GetDefaultClientRect(
2411  const AWinControl: TWinControl; const aLeft, aTop, aWidth, aHeight: integer;
2412  var aClientRect: TRect): boolean;
2413var
2414  FrameBorders: TRect;
2415  Widget: PGtkWidget;
2416  FixedWidget: PGtkWidget;
2417begin
2418  Result:=false;
2419  //DebugLn(['TGtk2WSCustomGroupBox.GetDefaultClientRect ',DbgSName(AWinControl),' ',aWidth,'x',aHeight]);
2420  if AWinControl.HandleAllocated then begin
2421    Widget:={%H-}PGtkWidget(AWinControl.Handle);
2422    FixedWidget:=PGtkWidget(GetFixedWidget(Widget));
2423    //DebugLn(['TGtk2WSCustomGroupBox.GetDefaultClientRect Flags=',WidgetFlagsToString(Widget),' FixedFlags=',WidgetFlagsToString(FixedWidget),' FixedSize=',FixedWidget^.allocation.width,'x',FixedWidget^.allocation.height]);
2424    if not GTK_WIDGET_RC_STYLE(FixedWidget) then
2425      Result:=true;
2426  end else begin
2427    Result:=true;
2428  end;
2429  if Result then begin
2430    FrameBorders:=GetStyleGroupboxFrameBorders;
2431    aClientRect:=Rect(0,0,
2432                 Max(0,aWidth-FrameBorders.Left-FrameBorders.Right),
2433                 Max(0,aHeight-FrameBorders.Top-FrameBorders.Bottom));
2434  end;
2435  //if Result then DebugLn(['TGtk2WSCustomGroupBox.GetDefaultClientRect END FrameBorders=',dbgs(FrameBorders),' aClientRect=',dbgs(aClientRect)]);
2436end;
2437
2438class procedure TGtk2WSCustomGroupBox.GetPreferredSize(
2439  const AWinControl: TWinControl; var PreferredWidth, PreferredHeight: integer;
2440  WithThemeSpace: Boolean);
2441begin
2442  // ToDo: compute the minimum size ignoring LCL child controls
2443  GetGTKDefaultWidgetSize(AWinControl, PreferredWidth, PreferredHeight,
2444                          WithThemeSpace);
2445end;
2446
2447class procedure TGtk2WSCustomGroupBox.SetFont(const AWinControl: TWinControl;
2448  const AFont: TFont);
2449var
2450  Frame: PGtkFrame;
2451  Lbl: PGtkWidget;
2452begin
2453  Frame := GetFrameWidget({%H-}PGTKEventBox(AWinControl.Handle));
2454  Lbl := gtk_frame_get_label_widget(Frame);
2455
2456  if Lbl <> nil then
2457  begin
2458    Gtk2WidgetSet.SetWidgetColor(Lbl, AFont.Color, clNone,
2459      [GTK_STATE_NORMAL,GTK_STATE_ACTIVE,GTK_STATE_PRELIGHT,GTK_STATE_SELECTED]);
2460    Gtk2WidgetSet.SetWidgetFont(Lbl, AFont);
2461  end;
2462  inherited SetFont(AWinControl, AFont);
2463end;
2464
2465class procedure TGtk2WSCustomGroupBox.SetText(const AWinControl: TWinControl;
2466  const AText: string);
2467begin
2468  if not WSCheckHandleAllocated(AWinControl, 'SetText') then Exit;
2469  SetLabel(GetFrameWidget({%H-}PGtkEventBox(AWinControl.Handle)), AText);
2470end;
2471
2472function Gtk2WSButton_Clicked(AWidget: PGtkWidget; AInfo: PWidgetInfo): GBoolean; cdecl;
2473var
2474  Msg: TLMessage;
2475begin
2476  Result := CallBackDefaultReturn;
2477  if AInfo^.ChangeLock > 0 then Exit;
2478
2479   // do not send LM_CLICKED. issue #21483
2480  if g_object_get_data(PGObject(AWidget),'lcl-button-stop-clicked') = AWidget then
2481  begin
2482    g_object_set_data(PGObject(AWidget),'lcl-button-stop-clicked', nil);
2483    exit;
2484  end;
2485  Msg.Msg := LM_CLICKED;
2486  Result := DeliverMessage(AInfo^.LCLObject, Msg) = 0;
2487end;
2488
2489function Gtk2WSButtonPressedEvent(widget: PGtkWidget; {%H-}event: pgdkEventButton; {%H-}data: gPointer): GBoolean; cdecl;
2490begin
2491  Result := CallBackDefaultReturn;
2492  // set to nil data if we pressed return before,
2493  // otherwise LM_CLICKED won't trigger. issue #21483
2494  g_object_set_data(PGObject(Widget),'lcl-button-stop-clicked', nil);
2495end;
2496
2497procedure Gtk2WSButton_SizeAllocate(widget: PGtkWidget; {%H-}allocation: PGtkAllocation; {%H-}user_data: gpointer); cdecl;
2498var
2499  xthickness, ythickness: gint;
2500  inner_border: PGtkBorder;
2501begin
2502  //the default GtkButton size_allocate handler takes into account
2503  //*thickness and inner_border properties to position the child (label)
2504  //see gtk_button_size_allocate in gtkbutton.c
2505  //here this is reverted so the child is not padded
2506  xthickness := widget^.style^.xthickness;
2507  ythickness := widget^.style^.ythickness;
2508  with PGtkBin(widget)^.child^.allocation do
2509  begin
2510    y := y - ythickness;
2511    height := height + 2 * ythickness;
2512    x := x - xthickness;
2513    width := width + 2 * xthickness;
2514    inner_border := nil;
2515    if gtk_minor_version > 8 then
2516      gtk_widget_style_get (widget, 'inner-border', @inner_border, nil);
2517    if inner_border <> nil then
2518    begin
2519       x := x - inner_border^.left;
2520       width := width + inner_border^.left + inner_border^.right;
2521       y := y - inner_border^.top;
2522       height := height + inner_border^.top + inner_border^.bottom;
2523    end
2524    else
2525    begin
2526      //if no inner-border is set, GtkButton uses a default border = (1,1,1,1)
2527      dec(x);
2528      dec(y);
2529      inc(width, 2);
2530      inc(height, 2);
2531    end;
2532  end;
2533end;
2534
2535{ TGtk2WSButton }
2536
2537class function TGtk2WSButton.GetButtonWidget(AEventBox: PGtkEventBox): PGtkButton;
2538begin
2539  Result := PGtkButton(PGtkBin(AEventBox)^.child);
2540end;
2541
2542class function TGtk2WSButton.GetLabelWidget(AEventBox: PGtkEventBox): PGtkLabel;
2543begin
2544  Result := PGtkLabel(PGtkBin(GetButtonWidget(AEventBox))^.child);
2545end;
2546
2547class procedure TGtk2WSButton.SetCallbacks(const AGtkWidget: PGtkWidget;
2548  const AWidgetInfo: PWidgetInfo);
2549begin
2550  TGtk2WSWinControl.SetCallbacks(PGtkObject(AGtkWidget), TComponent(AWidgetInfo^.LCLObject));
2551  SignalConnect(AWidgetInfo^.CoreWidget, 'clicked', @Gtk2WSButton_Clicked, AWidgetInfo);
2552  SignalConnect(AWidgetInfo^.CoreWidget, 'button-press-event', @Gtk2WSButtonPressedEvent, AWidgetInfo);
2553  SignalConnect(AWidgetInfo^.CoreWidget, 'size-allocate', @Gtk2WSButton_SizeAllocate, AWidgetInfo);
2554end;
2555
2556{
2557  Under Gtk 2 we need to put a GtkEventBox under the GtkButton, because a
2558  GtkButton has no window and that causes the Z-Order to be wrong.
2559}
2560class function TGtk2WSButton.CreateHandle(const AWinControl: TWinControl;
2561  const AParams: TCreateParams): TLCLIntfHandle;
2562var
2563  Button: TCustomButton;
2564  WidgetInfo: PWidgetInfo;
2565  Allocation: TGTKAllocation;
2566  EventBox, BtnWidget: PGtkWidget;
2567begin
2568  Button := AWinControl as TCustomButton;
2569  //DebugLn(['TGtk2WSButton.CreateHandle ',dbgsName(Button)]);
2570
2571  { Creates the container control for the button, the EventBox }
2572  EventBox := gtk_event_box_new;
2573  Result := TLCLIntfHandle({%H-}PtrUInt(EventBox));
2574  {$IFDEF DebugLCLComponents}
2575  DebugGtkWidgets.MarkCreated(EventBox,'button');
2576  {$ENDIF}
2577
2578  { Creates the button and inserts it into the EventBox }
2579  BtnWidget := gtk_button_new_with_label('button');
2580  gtk_container_add(PGtkContainer(EventBox), BtnWidget);
2581  gtk_widget_show_all(EventBox);
2582
2583  { This commented commands can be used if we have event-related
2584    problems because of the EventBox }
2585//  gtk_widget_add_events(EventBox, GDK_ALL_EVENTS_MASK);
2586//  gtk_event_box_set_above_child(PGtkEventBox(EventBox), True);
2587
2588  { The WidgetInfo is important for the form designer }
2589  WidgetInfo := CreateWidgetInfo({%H-}Pointer(Result), Button, AParams);
2590  WidgetInfo^.CoreWidget := BtnWidget;
2591  WidgetInfo^.ClientWidget := EventBox;
2592  //DebugLn(['TGtk2WSButton.CreateHandle ',GetWidgetInfo(EventBox)=WidgetInfo,' ',GetWidgetInfo(EventBox)^.ClientWidget=BtnWidget]);
2593//  g_object_set_data(PGObject(Result), 'widgetinfo', WidgetInfo);
2594  SetMainWidget(EventBox, BtnWidget);
2595
2596  Allocation.X := AParams.X;
2597  Allocation.Y := AParams.Y;
2598  Allocation.Width := AParams.Width;
2599  Allocation.Height := AParams.Height;
2600  gtk_widget_size_allocate(EventBox, @Allocation);
2601
2602  Set_RC_Name(AWinControl, EventBox);
2603  SetCallbacks(EventBox, WidgetInfo);
2604
2605  if AParams.Style and WS_VISIBLE = 0 then
2606    gtk_widget_hide(EventBox)
2607  else
2608    gtk_widget_show(EventBox);
2609end;
2610
2611class function TGtk2WSButton.GetText(const AWinControl: TWinControl; var AText: String): Boolean;
2612begin
2613  // The button text is static, so let the LCL fallback to FCaption
2614  Result := False;
2615end;
2616
2617class procedure TGtk2WSButton.SetDefault(const AButton: TCustomButton; ADefault: Boolean);
2618begin
2619  if not WSCheckHandleAllocated(AButton, 'SetDefault')
2620  then Exit;
2621
2622  if ADefault
2623  and (GTK_WIDGET_CAN_DEFAULT({%H-}pgtkwidget(AButton.Handle))) then
2624    //gtk_widget_grab_default(pgtkwidget(handle))
2625  else begin
2626    {DebugLn('LM_BTNDEFAULT_CHANGED ',TCustomButton(Sender).Name,':',Sender.ClassName,' widget can not grab default ',
2627      ' visible=',GTK_WIDGET_VISIBLE(PGtkWidget(Handle)),
2628      ' realized=',GTK_WIDGET_REALIZED(PGtkWidget(Handle)),
2629      ' mapped=',GTK_WIDGET_MAPPED(PGtkWidget(Handle)),
2630      '');}
2631    //  gtk_widget_Draw_Default(pgtkwidget(Handle));  //this isn't right but I'm not sure what to call
2632  end;
2633end;
2634
2635class procedure TGtk2WSButton.SetShortcut(const AButton: TCustomButton;
2636  const ShortCutK1, ShortCutK2: TShortcut);
2637begin
2638  if not WSCheckHandleAllocated(AButton, 'SetShortcut')
2639  then Exit;
2640  // gtk2: shortcuts are handled by the LCL
2641end;
2642
2643class procedure TGtk2WSButton.SetText(const AWinControl: TWinControl; const AText: String);
2644var
2645  BtnWidget: PGtkButton;
2646  LblWidget: PGtkLabel;
2647begin
2648  if not WSCheckHandleAllocated(AWincontrol, 'SetText')
2649  then Exit;
2650
2651  BtnWidget := GetButtonWidget({%H-}PGtkEventBox(AWinControl.Handle));
2652  LblWidget := PGtkLabel(PGtkBin(BtnWidget)^.Child);
2653
2654  if LblWidget = nil
2655  then begin
2656    //DebugLn(Format('trace: [WARNING] Button %s(%s) has no label', [AWinControl.Name, AWinControl.ClassName]));
2657    LblWidget := PGtkLabel(gtk_label_new(''));
2658    gtk_container_add(PGtkContainer(BtnWidget), PGtkWidget(LblWidget));
2659  end;
2660
2661  Gtk2WidgetSet.SetLabelCaption(LblWidget, AText);
2662end;
2663
2664class procedure TGtk2WSButton.SetColor(const AWinControl: TWinControl);
2665var
2666  BtnWidget: PGTKWidget;
2667begin
2668  if not WSCheckHandleAllocated(AWinControl, 'SetColor') then
2669    Exit;
2670  BtnWidget := PGTKWidget(GetButtonWidget({%H-}PGtkEventBox(AWinControl.Handle)));
2671  Gtk2WidgetSet.SetWidgetColor(BtnWidget, clNone, AWinControl.Color,
2672       [GTK_STATE_NORMAL,GTK_STATE_ACTIVE,GTK_STATE_PRELIGHT,GTK_STATE_SELECTED]);
2673end;
2674
2675class procedure TGtk2WSButton.SetFont(const AWinControl: TWinControl;
2676  const AFont: TFont);
2677var
2678  LblWidget: PGtkWidget;
2679begin
2680  if not AWinControl.HandleAllocated then exit;
2681
2682  LblWidget := PGtkWidget(GetLabelWidget({%H-}PGtkEventBox(AWinControl.Handle)));
2683
2684  if (LblWidget <> nil) then
2685  begin
2686    Gtk2WidgetSet.SetWidgetColor(LblWidget, AFont.Color, clNone,
2687       [GTK_STATE_NORMAL,GTK_STATE_ACTIVE,GTK_STATE_PRELIGHT,GTK_STATE_SELECTED]);
2688    Gtk2WidgetSet.SetWidgetFont(LblWidget, AFont);
2689  end;
2690end;
2691
2692class procedure TGtk2WSButton.GetPreferredSize(const AWinControl: TWinControl;
2693  var PreferredWidth, PreferredHeight: integer; WithThemeSpace: Boolean);
2694begin
2695  GetGTKDefaultWidgetSize(AWinControl,PreferredWidth,PreferredHeight,
2696                          WithThemeSpace);
2697  //debugln('TGtkWSButton.GetPreferredSize ',DbgSName(AWinControl),' PreferredWidth=',dbgs(PreferredWidth),' PreferredHeight=',dbgs(PreferredHeight));
2698end;
2699
2700{ TGtk2WSScrollBar }
2701
2702class procedure TGtk2WSScrollBar.SetCallbacks(const AGtkWidget: PGtkWidget;
2703  const AWidgetInfo: PWidgetInfo);
2704begin
2705  TGtk2WSWinControl.SetCallbacks(PGtkObject(AGtkWidget), TComponent(AWidgetInfo^.LCLObject));
2706
2707  g_signal_connect(AGtkWidget, 'change-value', TGCallback(@Gtk2RangeScrollCB), AWidgetInfo);
2708  g_signal_connect(AGtkWidget, 'button-press-event',
2709    TGCallback(@Gtk2RangeScrollPressCB), AWidgetInfo);
2710  g_signal_connect(AGtkWidget, 'button-release-event',
2711    TGCallback(@Gtk2RangeScrollReleaseCB), AWidgetInfo);
2712end;
2713
2714class function TGtk2WSScrollBar.CreateHandle(const AWinControl: TWinControl;
2715  const AParams: TCreateParams): TLCLIntfHandle;
2716var
2717  Adjustment: PGtkAdjustment = nil;
2718  Widget: PGtkWidget;
2719  WidgetInfo: PWidgetInfo;
2720begin
2721  with TScrollBar(AWinControl) do
2722  begin
2723    {We use Max + PageSize because the GTK scrollbar is meant to scroll from
2724     min to max-pagesize which would be different from the behaviour on other
2725     widgetsets.}
2726    Adjustment := PGtkAdjustment(gtk_adjustment_new(Position, Min,
2727      Max, SmallChange, LargeChange, PageSize));
2728
2729    if (Kind = sbHorizontal) then
2730      Widget := gtk_hscrollbar_new(Adjustment)
2731    else
2732      Widget := gtk_vscrollbar_new(Adjustment);
2733    gtk_range_set_update_policy(PGtkRange(Widget), GTK_UPDATE_CONTINUOUS);
2734  end;
2735
2736  Result := TLCLIntfHandle({%H-}PtrUInt(Widget));
2737  {$IFDEF DebugLCLComponents}
2738  DebugGtkWidgets.MarkCreated(Widget, dbgsName(AWinControl));
2739  {$ENDIF}
2740  WidgetInfo := CreateWidgetInfo({%H-}Pointer(Result), AWinControl, AParams);
2741
2742  Set_RC_Name(AWinControl, Widget);
2743  SetCallbacks(Widget, WidgetInfo);
2744end;
2745
2746class procedure TGtk2WSScrollBar.SetKind(const AScrollBar: TCustomScrollBar;
2747  const AIsHorizontal: Boolean);
2748begin
2749  if not AScrollBar.HandleAllocated then
2750    exit;
2751  RecreateWnd(AScrollBar);
2752end;
2753
2754class procedure TGtk2WSScrollBar.SetParams(const AScrollBar: TCustomScrollBar);
2755var
2756  Range: PGtkRange;
2757begin
2758  if not AScrollBar.HandleAllocated then
2759    exit;
2760  with AScrollBar do
2761  begin
2762    Range := GTK_RANGE({%H-}Pointer(Handle));
2763    {for gtk >= 2.14 use gtk_adjustment_configure}
2764    if (gtk_major_version >= 2) and (gtk_minor_version >= 14) then
2765      gtk_adjustment_configure(Range^.adjustment, Position, Min, Max, SmallChange, LargeChange, PageSize)
2766    else
2767    begin
2768      with Range^.adjustment^ do
2769      begin
2770        value := Position;
2771        lower := Min;
2772        upper := Max;
2773        step_increment := SmallChange;
2774        page_increment := LargeChange;
2775        page_size := PageSize;
2776      end;
2777      gtk_adjustment_changed(Range^.adjustment);
2778    end;
2779  end;
2780end;
2781
2782class procedure TGtk2WSScrollBar.ShowHide(const AWinControl: TWinControl);
2783begin
2784  if not AWinControl.HandleAllocated then
2785    exit;
2786  if AWinControl.HandleObjectShouldBeVisible then
2787    SetParams(TCustomScrollBar(AWinControl));
2788  Gtk2WidgetSet.SetVisible(AWinControl,
2789    AWinControl.HandleObjectShouldBeVisible);
2790end;
2791
2792{ TGtk2WSRadioButton }
2793
2794class function TGtk2WSRadioButton.CreateHandle(const AWinControl: TWinControl;
2795  const AParams: TCreateParams): TLCLIntfHandle;
2796var
2797  Widget, TempWidget: PGtkWidget;
2798  LabelWidget: PGtkLabel;
2799  TempInt: Integer;
2800  WidgetInfo: PWidgetInfo;
2801  Allocation: TGTKAllocation;
2802begin
2803  with TRadioButton(AWinControl) do
2804  begin
2805    // Look for our parent's control and use the first radio we find for grouping
2806    TempWidget := nil;
2807    if (Parent <> nil) then
2808    begin
2809      for TempInt := 0 to Parent.ControlCount - 1 do
2810      begin
2811        if (Parent.Controls[TempInt] is TRadioButton) and
2812           TWinControl(Parent.Controls[TempInt]).HandleAllocated then
2813        begin
2814          TempWidget := {%H-}PGtkWidget(TWinControl(Parent.Controls[TempInt]).Handle);
2815          Break;
2816        end;
2817      end;
2818    end;
2819
2820    if TempWidget <> nil then
2821      Widget := gtk_radio_button_new_with_label(PGtkRadioButton(TempWidget)^.group,'')
2822    else
2823      Widget := gtk_radio_button_new_with_label(nil, '');
2824
2825    LabelWidget := PGtkLabel(gtk_bin_get_child(PGtkBin(@PGTKToggleButton(Widget)^.Button)));
2826    Gtk2WidgetSet.SetLabelCaption(LabelWidget, AParams.Caption);
2827  end;
2828
2829  {$IFDEF DebugLCLComponents}
2830  DebugGtkWidgets.MarkCreated(Widget, dbgsName(AWinControl));
2831  {$ENDIF}
2832  Result := THandle({%H-}PtrUInt(Widget));
2833  WidgetInfo := CreateWidgetInfo({%H-}Pointer(Result), AWinControl, AParams);
2834
2835  Allocation.X := AParams.X;
2836  Allocation.Y := AParams.Y;
2837  Allocation.Width := AParams.Width;
2838  Allocation.Height := AParams.Height;
2839  gtk_widget_size_allocate(Widget, @Allocation);
2840
2841  Set_RC_Name(AWinControl, Widget);
2842  TGtk2WSCustomCheckBox.SetCallbacks(Widget, WidgetInfo);
2843end;
2844
2845{ TGtk2WSToggleBox }
2846
2847class function TGtk2WSToggleBox.CreateHandle(const AWinControl: TWinControl;
2848  const AParams: TCreateParams): TLCLIntfHandle;
2849var
2850  Widget: PGtkWidget;
2851  WidgetInfo: PWidgetInfo;
2852  Allocation: TGTKAllocation;
2853begin
2854  Widget := gtk_toggle_button_new_with_label(AParams.Caption);
2855  {$IFDEF DebugLCLComponents}
2856  DebugGtkWidgets.MarkCreated(Widget, dbgsName(AWinControl));
2857  {$ENDIF}
2858  Result := THandle({%H-}PtrUInt(Widget));
2859  WidgetInfo := CreateWidgetInfo({%H-}Pointer(Result), AWinControl, AParams);
2860
2861  Allocation.X := AParams.X;
2862  Allocation.Y := AParams.Y;
2863  Allocation.Width := AParams.Width;
2864  Allocation.Height := AParams.Height;
2865  gtk_widget_size_allocate(Widget, @Allocation);
2866
2867  Set_RC_Name(AWinControl, Widget);
2868  TGtk2WSCustomCheckBox.SetCallbacks(Widget, WidgetInfo);
2869end;
2870
2871{ TGtk2WSCustomStaticText }
2872
2873class function TGtk2WSCustomStaticText.GetLabelWidget(AFrame: PGtkFrame): PGtkLabel;
2874begin
2875  Result := PGtkLabel(PGtkBin(GetBoxWidget(AFrame))^.child);
2876end;
2877
2878class function TGtk2WSCustomStaticText.GetBoxWidget(AFrame: PGtkFrame): PGtkEventBox;
2879begin
2880  Result := PGtkEventBox(PGtkBin(AFrame)^.child);
2881end;
2882
2883class function TGtk2WSCustomStaticText.CreateHandle(
2884  const AWinControl: TWinControl; const AParams: TCreateParams
2885  ): TLCLIntfHandle;
2886var
2887  AStaticText: TCustomStaticText;
2888  WidgetInfo: PWidgetInfo;
2889  Allocation: TGTKAllocation;
2890  EventBox, LblWidget: PGtkWidget;
2891begin
2892  // TStaticText control is a Text area with frame around. Both Text and Area around
2893  // text can have their own color
2894
2895  // To implement that in gtk we need:
2896  // 1. GtkLabel to handle Text
2897  // 2. GtkEventBox to draw color area around GtkLabel (since GtkLabel have no window)
2898  // 3. GtkFrame to draw frame around Text area
2899  // GtkFrame is our main widget - it is container and it contains GtkEventBox
2900  // GtkEventBox is also containter and it contains GtkLabel
2901
2902  AStaticText := AWinControl as TCustomStaticText;
2903  Result := TLCLIntfHandle({%H-}PtrUInt(gtk_frame_new(nil))); // frame is the main container - to decorate label
2904  if Result = 0 then Exit;
2905
2906  gtk_frame_set_shadow_type({%H-}PGtkFrame(Result), StaticBorderShadowMap[AStaticText.BorderStyle]);
2907
2908  EventBox := gtk_event_box_new;  // our area
2909  LblWidget := gtk_label_new(PChar(TCustomStaticText(AWinControl).Caption)); // our text widget
2910  gtk_container_add(PGtkContainer(EventBox), LblWidget);
2911  SetLabelAlignment(PGtkLabel(LblWidget), AStaticText.Alignment);
2912  gtk_widget_show(LblWidget);
2913  gtk_widget_show(EventBox);
2914  gtk_container_add({%H-}PGtkContainer(Result), EventBox);
2915
2916  {$IFDEF DebugLCLComponents}
2917  DebugGtkWidgets.MarkCreated(Pointer(Result), dbgsName(AWinControl));
2918  {$ENDIF}
2919
2920  WidgetInfo := CreateWidgetInfo({%H-}Pointer(Result), AStaticText, AParams);
2921  WidgetInfo^.CoreWidget := EventBox;
2922  g_object_set_data(PGObject(EventBox), 'widgetinfo', WidgetInfo);
2923
2924  Allocation.X := AParams.X;
2925  Allocation.Y := AParams.Y;
2926  Allocation.Width := AParams.Width;
2927  Allocation.Height := AParams.Height;
2928  gtk_widget_size_allocate({%H-}PGtkWidget(Result), @Allocation);
2929
2930  Set_RC_Name(AWinControl, {%H-}PGtkWidget(Result));
2931  SetCallbacks({%H-}PGtkWidget(Result), WidgetInfo);
2932end;
2933
2934class procedure TGtk2WSCustomStaticText.SetAlignment(const ACustomStaticText: TCustomStaticText;
2935  const NewAlignment: TAlignment);
2936var
2937  LblWidget: PGtkLabel;
2938begin
2939  if not WSCheckHandleAllocated(ACustomStaticText, 'SetAlignment')
2940  then Exit;
2941
2942  LblWidget := GetLabelWidget({%H-}PGtkFrame(ACustomStaticText.Handle));
2943  SetLabelAlignment(LblWidget, NewAlignment);
2944end;
2945
2946class procedure TGtk2WSCustomStaticText.GetPreferredSize(const AWinControl: TWinControl;
2947  var PreferredWidth, PreferredHeight: integer; WithThemeSpace: Boolean);
2948begin
2949  GetGTKDefaultWidgetSize(AWinControl, PreferredWidth, PreferredHeight,
2950                          WithThemeSpace);
2951  //debugln('TGtkWSCustomStaticText.GetPreferredSize ',DbgSName(AWinControl),' PreferredWidth=',dbgs(PreferredWidth),' PreferredHeight=',dbgs(PreferredHeight));
2952end;
2953
2954class function TGtk2WSCustomStaticText.GetText(const AWinControl: TWinControl;
2955  var AText: String): Boolean;
2956begin
2957  // The text is static, so let the LCL fallback to FCaption
2958  Result := False;
2959end;
2960
2961class procedure TGtk2WSCustomStaticText.SetText(const AWinControl: TWinControl;
2962  const AText: String);
2963var
2964  FrameWidget: PGtkFrame;
2965  LblWidget: PGtkLabel;
2966begin
2967  if not WSCheckHandleAllocated(AWincontrol, 'SetText')
2968  then Exit;
2969
2970  FrameWidget := {%H-}PGtkFrame(AWinControl.Handle);
2971  LblWidget := GetLabelWidget(FrameWidget);
2972
2973  if TStaticText(AWinControl).ShowAccelChar and (AText <> '') then
2974    Gtk2WidgetSet.SetLabelCaption(LblWidget, AText)
2975  else
2976  begin
2977    gtk_label_set_text(LblWidget, PChar(AText));
2978    gtk_label_set_pattern(LblWidget, nil);
2979  end;
2980end;
2981
2982class procedure TGtk2WSCustomStaticText.SetStaticBorderStyle(
2983  const ACustomStaticText: TCustomStaticText;
2984  const NewBorderStyle: TStaticBorderStyle);
2985begin
2986  if not WSCheckHandleAllocated(ACustomStaticText, 'SetStaticBorderStyle')
2987  then Exit;
2988  gtk_frame_set_shadow_type({%H-}PGtkFrame(ACustomStaticText.Handle), StaticBorderShadowMap[NewBorderStyle]);
2989end;
2990
2991class procedure TGtk2WSCustomStaticText.SetCallbacks(
2992  const AGtkWidget: PGtkWidget; const AWidgetInfo: PWidgetInfo);
2993begin
2994  TGtk2WSWinControl.SetCallbacks(PGtkObject(AGtkWidget), TComponent(AWidgetInfo^.LCLObject));
2995
2996  SignalConnect(AGtkWidget, 'grab_focus', @gtkActivateCB, AWidgetInfo);
2997end;
2998
2999class procedure TGtk2WSCustomStaticText.SetColor(const AWinControl: TWinControl);
3000begin
3001  if not WSCheckHandleAllocated(AWinControl, 'SetColor') then Exit;
3002
3003  Gtk2WidgetSet.SetWidgetColor(PGtkWidget(GetBoxWidget({%H-}PGtkFrame(AWinControl.Handle))),
3004                              clNone, AWinControl.Color,
3005                              [GTK_STATE_NORMAL,GTK_STATE_ACTIVE,
3006                               GTK_STATE_PRELIGHT,GTK_STATE_SELECTED]);
3007end;
3008
3009class procedure TGtk2WSCustomStaticText.SetFont(const AWinControl: TWinControl;
3010  const AFont: TFont);
3011var
3012  Widget: PGtkWidget;
3013begin
3014  if not WSCheckHandleAllocated(AWinControl, 'SetFont')
3015  then Exit;
3016
3017  Widget := PGtkWidget(GetLabelWidget({%H-}PGtkFrame(AWinControl.Handle)));
3018
3019  Gtk2WidgetSet.SetWidgetColor(Widget, AFont.Color, clNone,
3020       [GTK_STATE_NORMAL,GTK_STATE_ACTIVE,GTK_STATE_PRELIGHT,GTK_STATE_SELECTED]);
3021  Gtk2WidgetSet.SetWidgetFont(Widget, AFont);
3022end;
3023
3024end.
3025