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