1{%MainUnit gtk2wscomctrls.pp}
2{
3 *****************************************************************************
4  This file is part of the Lazarus Component Library (LCL)
5
6  See the file COPYING.modifiedLGPL.txt, included in this distribution,
7  for details about the license.
8 *****************************************************************************
9}
10
11const
12  GtkPositionTypeMap: array[TTabPosition] of TGtkPositionType =
13  (
14{ tpTop    } GTK_POS_TOP,
15{ tpBottom } GTK_POS_BOTTOM,
16{ tpLeft   } GTK_POS_LEFT,
17{ tpRight  } GTK_POS_RIGHT
18  );
19
20  LCL_NotebookManualPageSwitchKey = 'lcl_manual_page_switch';
21
22
23type
24  GtkNotebookButtonPressEventProc = function (widget:PGtkWidget; event:PGdkEventButton):gboolean; cdecl;
25  GtkNotebookKeyPressEventProc = function (widget:PGtkWidget; event:PGdkEventKey):gboolean; cdecl;
26
27var
28  OldNoteBookButtonPress: GtkNotebookButtonPressEventProc = nil;
29  OldNoteBookKeyPress: GtkNotebookKeyPressEventProc = nil;
30
31// this was created as a workaround of a tnotebook eating rightclick of custom controls
32function Notebook_Button_Press(widget:PGtkWidget; event:PGdkEventButton):gboolean; cdecl;
33begin
34  Result := True;
35  if gtk_get_event_widget(PGdkEvent(event)) <> widget then exit;
36  if OldNoteBookButtonPress = nil then exit;
37  Result := OldNoteBookButtonPress(widget, event);
38end;
39
40// Allow switching tabs per key. Issue #31986
41function Notebook_Key_Press(widget:PGtkWidget; event:PGdkEventKey):gboolean; cdecl;
42begin
43  Result := True;
44  if OldNoteBookKeyPress = nil then exit;
45  case event^.hardware_keycode of
46    113: gtk_notebook_prev_page(PGtkNotebook(widget));
47    114: gtk_notebook_next_page(PGtkNotebook(widget));
48  else
49    Result := OldNoteBookKeyPress(widget, event);
50  end;
51end;
52
53procedure HookNoteBookClass;
54var
55  WidgetClass: PGtkWidgetClass;
56begin
57  WidgetClass := GTK_WIDGET_CLASS(gtk_type_class(gtk_notebook_get_type));
58
59  OldNoteBookButtonPress := GtkNotebookButtonPressEventProc(WidgetClass^.button_press_event);
60  WidgetClass^.button_press_event := @Notebook_Button_Press;
61  OldNoteBookKeyPress := GtkNotebookKeyPressEventProc(WidgetClass^.key_press_event);
62  WidgetClass^.key_press_event := @Notebook_Key_Press;
63end;
64
65{ TGtk2WSCustomTabControl }
66
67function NotebookPageRealToLCLIndex(const ATabControl: TCustomTabControl; AIndex: integer): integer;
68var
69  I: Integer;
70begin
71  Result := AIndex;
72  if csDesigning in ATabControl.ComponentState then exit;
73  I := 0;
74  while (I < ATabControl.PageCount) and (I <= Result) do
75  begin
76    if not ATabControl.Page[I].TabVisible then Inc(Result);
77    Inc(I);
78  end;
79end;
80
81function GtkRestoreFocusFix(AGtkWidget: Pointer): gboolean; cdecl;
82begin
83  Result := AGtkWidget <> nil;
84  if AGtkWidget <> nil then
85  begin
86    GTK_WIDGET_SET_FLAGS(PGtkWidget(AGtkWidget), GTK_CAN_FOCUS);
87    g_idle_remove_by_data(AGtkWidget);
88  end;
89end;
90
91function GtkWSNotebook_AfterSwitchPage(widget: PGtkWidget; {%H-}page: Pgtkwidget; pagenum: integer; data: gPointer): GBoolean; cdecl;
92var
93  Mess: TLMNotify;
94  NMHdr: tagNMHDR;
95  Info: PWidgetInfo;
96  ACtl: TWinControl;
97  AParentForm: TCustomForm;
98  i: Integer;
99  LCLPageIndex: Integer;
100  Pg: TCustomPage;
101  ChildWidget: PGtkWidget;
102begin
103  Result := CallBackDefaultReturn;
104  // then send the new page
105  FillChar(Mess{%H-}, SizeOf(Mess), 0);
106  Mess.Msg := LM_NOTIFY;
107  FillChar(NMHdr{%H-}, SizeOf(NMHdr), 0);
108  NMHdr.code := TCN_SELCHANGE;
109  NMHdr.hwndFrom := {%H-}PtrUInt(widget);
110  LCLPageIndex := NotebookPageRealToLCLIndex(TCustomTabControl(Data), pagenum);  //use this to set pageindex to the correct page.
111  NMHdr.idFrom := LCLPageIndex;
112  Mess.NMHdr := @NMHdr;
113  DeliverMessage(Data, Mess);
114
115  // code below is fix for issue #20493
116  Info := GetWidgetInfo(Widget);
117  if wwiTabWidgetFocusCheck in Info^.Flags then
118  begin
119    Exclude(Info^.Flags, wwiTabWidgetFocusCheck);
120
121    if LCLPageIndex = -1 then
122      exit;
123
124    ACtl := TWinControl(Data);
125    AParentForm := GetParentForm(ACtl);
126    if Assigned(AParentForm) then
127    begin
128      // 1st we must find focused control (if any)
129      ACtl := nil;
130      if (LCLPageIndex >= 0) and (LCLPageIndex < TCustomTabControl(Data).PageCount) then
131        Pg := TCustomTabControl(Data).Page[LCLPageIndex]
132      else
133        Pg := nil;
134      if Assigned(Pg) then
135      begin
136        for i := 0 to Pg.ControlCount - 1 do
137        begin
138          if (pg.Controls[i] is TWinControl) and
139            (TWinControl(pg.Controls[i]).Focused) then
140          begin
141            ACtl := TWinControl(pg.Controls[i]);
142            break;
143          end;
144        end;
145      end;
146      if (ACtl = nil) and (Pg <> nil) then
147        ACtl := AParentForm.ActiveControl;
148    end else
149      ACtl := nil;
150
151    if (ACtl <> nil) and (ACtl <> TWinControl(Data)) then
152    begin
153      // DebugLn('ActiveCtl is ',ACtl.ClassName,':',ACtl.Name);
154      // do not focus tab by mouse click if we already have active control
155      GTK_WIDGET_UNSET_FLAGS(Widget, GTK_CAN_FOCUS);
156      Pg := TCustomTabControl(Data).Page[LCLPageIndex];
157      for i := 0 to Pg.ControlCount - 1 do
158      begin
159        // we must prevent gtkWidget to acquire focus by gtk (eg. GtkButton)
160        if (Pg.Controls[i] is TWinControl) and (Pg.Controls[i] <> ACtl) then
161        begin
162          Info := GetWidgetInfo({%H-}PGtkWidget(TWinControl(Pg.Controls[i]).Handle));
163          if Info <> nil then
164          begin
165            if Info^.CoreWidget <> nil then
166              ChildWidget := Info^.CoreWidget
167            else
168              ChildWidget := Info^.ClientWidget;
169            GTK_WIDGET_UNSET_FLAGS(ChildWidget, GTK_CAN_FOCUS);
170            g_idle_add(@GtkRestoreFocusFix, ChildWidget);
171          end;
172        end;
173      end;
174      g_idle_add(@GtkRestoreFocusFix, Widget);
175    end;
176  end;
177end;
178
179function GtkWSNotebook_SwitchPage(widget: PGtkWidget; {%H-}page: Pgtkwidget; pagenum: integer; data: gPointer): GBoolean; cdecl;
180var
181  Mess: TLMNotify;
182  NMHdr: tagNMHDR;
183  IsManual: Boolean;
184begin
185  Result := CallBackDefaultReturn;
186  EventTrace('switch-page', data);
187  UpdateNoteBookClientWidget(TObject(Data));
188
189  // remove flag
190  IsManual := g_object_get_data(PGObject(Widget), LCL_NotebookManualPageSwitchKey) <> nil;
191  if IsManual then
192    g_object_set_data(PGObject(Widget), LCL_NotebookManualPageSwitchKey, nil);
193  if PGtkNotebook(Widget)^.cur_page = nil then // for windows compatibility
194    Exit;
195
196  // gtkswitchpage is called before the switch
197  if not IsManual then
198  begin
199    // send first the TCN_SELCHANGING to ask if switch is allowed
200    FillChar(Mess{%H-}, SizeOf(Mess), 0);
201    Mess.Msg := LM_NOTIFY;
202    FillChar(NMHdr{%H-}, SizeOf(NMHdr), 0);
203    NMHdr.code := TCN_SELCHANGING;
204    NMHdr.hwndFrom := {%H-}PtrUInt(widget);
205    NMHdr.idFrom := NotebookPageRealToLCLIndex(TCustomTabControl(Data), pagenum);  //use this to set pageindex to the correct page.
206    Mess.NMHdr := @NMHdr;
207    Mess.Result := 0;
208    DeliverMessage(Data, Mess);
209    if Mess.Result <> 0 then
210    begin
211      g_signal_stop_emission_by_name(PGObject(Widget), 'switch-page');
212      Result := not CallBackDefaultReturn;
213      Exit;
214    end;
215  end;
216
217end;
218
219class procedure TGtk2WSCustomTabControl.SetCallbacks(
220  const AGtkWidget: PGtkWidget; const AWidgetInfo: PWidgetInfo);
221begin
222  TGtk2WSWinControl.SetCallbacks(PGtkObject(AGtkWidget), TComponent(AWidgetInfo^.LCLObject));
223  ConnectSignal(PGtkObject(AGtkWidget), 'switch_page', @GtkWSNotebook_SwitchPage, AWidgetInfo^.LCLObject);
224  ConnectSignalAfter(PGtkObject(AGtkWidget), 'switch_page', @GtkWSNotebook_AfterSwitchPage, AWidgetInfo^.LCLObject);
225end;
226
227class function TGtk2WSCustomTabControl.CreateTTabControlHandle(
228  const AWinControl: TWinControl; const AParams: TCreateParams): HWND;
229var
230  Widget: PGtkWidget;
231  WidgetInfo: PWidgetInfo;
232  Allocation: TGTKAllocation;
233begin
234  Widget := GTK2WidgetSet.CreateAPIWidget(AWinControl);
235  {$IFDEF DebugLCLComponents}
236  DebugGtkWidgets.MarkCreated(Widget, dbgsName(AWinControl));
237  {$ENDIF}
238
239  Result := HWND({%H-}PtrUInt(Widget));
240  if Result = 0 then Exit;
241
242  WidgetInfo := GetWidgetInfo(Widget); // Widget info already created in CreateAPIWidget
243  WidgetInfo^.Style := AParams.Style;
244  WidgetInfo^.ExStyle := AParams.ExStyle;
245  WidgetInfo^.WndProc := {%H-}PtrUInt(AParams.WindowClass.lpfnWndProc);
246
247  // set allocation
248  Allocation.X := AParams.X;
249  Allocation.Y := AParams.Y;
250  Allocation.Width := AParams.Width;
251  Allocation.Height := AParams.Height;
252  gtk_widget_size_allocate(Widget, @Allocation);
253
254  Set_RC_Name(AWinControl, Widget);
255
256  g_object_set_data(PGObject(WidgetInfo^.CoreWidget),'lcl_ttabcontrol', WidgetInfo^.CoreWidget);
257
258  TGtk2WSWinControl.SetCallbacks(GTK_OBJECT(Widget), AWinControl);
259
260  g_signal_connect_after(GTK_SCROLLED_WINDOW(Widget)^.hscrollbar, 'change-value',
261    TGCallback(@Gtk2RangeScrollCB), WidgetInfo);
262  g_signal_connect_after(GTK_SCROLLED_WINDOW(Widget)^.vscrollbar, 'change-value',
263    TGCallback(@Gtk2RangeScrollCB), WidgetInfo);
264  g_signal_connect(GTK_SCROLLED_WINDOW(Widget)^.hscrollbar, 'button-press-event',
265    TGCallback(@Gtk2RangeScrollPressCB), WidgetInfo);
266  g_signal_connect(GTK_SCROLLED_WINDOW(Widget)^.hscrollbar, 'button-release-event',
267    TGCallback(@Gtk2RangeScrollReleaseCB), WidgetInfo);
268    g_signal_connect(GTK_SCROLLED_WINDOW(Widget)^.vscrollbar, 'button-press-event',
269    TGCallback(@Gtk2RangeScrollPressCB), WidgetInfo);
270  g_signal_connect(GTK_SCROLLED_WINDOW(Widget)^.vscrollbar, 'button-release-event',
271    TGCallback(@Gtk2RangeScrollReleaseCB), WidgetInfo);
272
273  g_signal_connect(Widget, 'scroll-event', TGCallback(@Gtk2ScrolledWindowScrollCB), WidgetInfo);
274end;
275
276class function TGtk2WSCustomTabControl.CreateHandle(const AWinControl: TWinControl;
277  const AParams: TCreateParams): HWND;
278var
279  AWidget: PGtkNoteBook;
280  WidgetInfo: PWidgetInfo;
281begin
282
283  if (AWinControl is TTabControl) then
284  begin
285    {$IFDEF NOTEBOOK_DEBUG}
286    DebugLn(['TGtk2WSCustomTabControl.CreateHandle CREATING TTABCONTROL !!! ',DbgSName(AWinControl)]);
287    {$ENDIF}
288    Result := CreateTTabControlHandle(AWinControl, AParams);
289    exit;
290  end;
291
292  {$IFDEF NOTEBOOK_DEBUG}
293  DebugLn(['TGtk2WSCustomTabControl.CreateHandle ',DbgSName(AWinControl)]);
294  {$ENDIF}
295  if OldNoteBookButtonPress = nil then
296    HookNoteBookClass;
297
298  AWidget := PGtkNoteBook(gtk_notebook_new());
299  WidgetInfo := CreateWidgetInfo(AWidget, AWinControl, AParams);
300  {$IFDEF DebugLCLComponents}
301  DebugGtkWidgets.MarkCreated(Pointer(AWidget), dbgsName(AWinControl));
302  {$ENDIF}
303  gtk_notebook_set_scrollable(AWidget, True);
304
305  if not (nboHidePageListPopup in TCustomTabControl(AWinControl).Options) then
306    gtk_notebook_popup_enable(AWidget);
307
308  gtk_notebook_set_tab_pos(AWidget, GtkPositionTypeMap[TCustomTabControl(AWinControl).TabPosition]);
309  Result := HWND(TLCLIntfHandle({%H-}PtrUInt(AWidget)));
310  Set_RC_Name(AWinControl, PGtkWidget(AWidget));
311  SetCallBacks(PGtkWidget(AWidget), WidgetInfo);
312end;
313
314class function TGtk2WSCustomTabControl.GetDefaultClientRect(
315  const AWinControl: TWinControl; const aLeft, aTop, aWidth, aHeight: integer;
316  var aClientRect: TRect): boolean;
317var
318  FrameBorders: TRect;
319begin
320  Result:=false;
321  if (AWinControl is TTabControl) then
322  begin
323    // use normal ClientRect
324  end else begin
325    // handle is a gtknotebook
326    //DebugLn(['TGtk2WSCustomTabControl.GetDefaultClientRect ',DbgSName(AWinControl),' ',aWidth,'x',aHeight]);
327    if AWinControl.HandleAllocated
328    and (gtk_notebook_get_nth_page({%H-}PGtkNotebook(AWinControl.Handle),0)<>nil)
329    then begin
330      // notebook handle allocated and has one page
331      // => normal GetClientRect will retrieve the right ClientRect
332    end else begin
333      FrameBorders:=GetStyleNotebookFrameBorders;
334      aClientRect:=Rect(0,0,
335                   Max(0,aWidth-FrameBorders.Left-FrameBorders.Right),
336                   Max(0,aHeight-FrameBorders.Top-FrameBorders.Bottom));
337      Result:=true;
338      {$IFDEF VerboseSizeMsg}
339      DebugLn(['TGtk2WSCustomTabControl.GetDefaultClientRect END FrameBorders=',dbgs(FrameBorders),' aClientRect=',dbgs(aClientRect)]);
340      {$ENDIF}
341    end;
342  end;
343end;
344
345class procedure TGtk2WSCustomTabControl.AddPage(const ATabControl: TCustomTabControl;
346  const AChild: TCustomPage; const AIndex: integer);
347{
348  Inserts a new page to a notebook at position Index. The ATabControl is a
349  TCustomTabControl, the AChild one of its TCustomPage. Both handles must already
350  be created. ATabControl Handle is a PGtkNoteBook and APage handle is a
351  PGtkHBox.
352  This procedure creates a new tab with an optional image, the page caption and
353  an optional close button. The image and the caption will also be added to the
354  tab popup menu.
355}
356var
357  NoteBookWidget: PGtkWidget;  // the notebook
358  PageWidget: PGtkWidget;      // the page (content widget)
359  TabWidget: PGtkWidget;       // the tab (hbox containing a pixmap, a label
360                               //          and a close button)
361  TabLabelWidget: PGtkWidget;  // the label in the tab
362  MenuWidget: PGtkWidget;      // the popup menu (hbox containing a pixmap and
363                               // a label)
364  MenuLabelWidget: PGtkWidget; // the label in the popup menu item
365begin
366  {$IFDEF NOTEBOOK_DEBUG}
367  DebugLn(['TGtkWSCustomTabControl.AddPage ',dbgsName(ATabControl),' ',ATabControl.HandleAllocated,' AChild=',dbgsName(AChild),' ',AChild.HandleAllocated,' Child.TabVisible=',AChild.TabVisible]);
368  {$ENDIF}
369  NoteBookWidget := {%H-}PGtkWidget(ATabControl.Handle);
370  PageWidget := {%H-}PGtkWidget(AChild.Handle);
371
372  // set LCL size
373  AChild.SetBounds(AChild.Left, AChild.Top, ATabControl.ClientWidth, ATabControl.ClientHeight);
374
375  if (ATabControl is TTabControl) then begin
376    if AChild.HandleObjectShouldBeVisible then
377      gtk_widget_show(PageWidget);
378    exit;
379  end;
380
381
382  // For a PageNotebook the widget must be visible
383  // If not the page control will not use it. It may not even show the tab
384  gtk_widget_show(PageWidget);
385
386  // Check if already created. if so just show it because it is invisible
387  if gtk_notebook_get_tab_label(PGtkNoteBook(NoteBookWidget), PageWidget) <> nil
388  then begin
389    {$IFDEF NOTEBOOK_DEBUG}
390    DebugLn(['TGtkWSCustomTabControl.AddPage already added']);
391    {$ENDIF}
392    exit;
393  end;
394
395  // create the tab (hbox container)
396  TabWidget := gtk_hbox_new(false, 1);
397  g_object_set_data(PGObject(TabWidget), 'TabImage', nil);
398  g_object_set_data(PGObject(TabWidget), 'TabCloseBtn', nil);
399  // put a label into the tab
400  TabLabelWidget := gtk_label_new('');
401  g_object_set_data(PGObject(TabWidget), 'TabLabel', TabLabelWidget);
402  gtk_widget_show(TabLabelWidget);
403  gtk_box_pack_start_defaults(PGtkBox(TabWidget), TabLabelWidget);
404
405  if AChild.TabVisible then
406    gtk_widget_show(TabWidget);
407
408  // create popup menu item
409  MenuWidget := gtk_hbox_new(false, 2);
410  // set icon widget to nil
411  g_object_set_data(PGObject(MenuWidget), 'TabImage', nil);
412  // put a label into the menu
413  MenuLabelWidget := gtk_label_new('');
414  g_object_set_data(PGObject(MenuWidget), 'TabMenuLabel', MenuLabelWidget);
415  gtk_widget_show(MenuLabelWidget);
416  gtk_box_pack_start_defaults(PGtkBox(MenuWidget), MenuLabelWidget);
417
418  if AChild.TabVisible then
419    gtk_widget_show(MenuWidget);
420
421  // insert the page
422  gtk_notebook_insert_page_menu(PGtkNotebook(NotebookWidget), PageWidget,
423    TabWidget, MenuWidget, AIndex);
424
425  UpdateNotebookPageTab(ATabControl, AChild);
426  UpdateNoteBookClientWidget(ATabControl);
427  UpdateNotebookTabFont(AChild, AChild.Font);
428
429  // init the size of the page widget
430  //DebugLn(['TGtkWSCustomTabControl.AddPage ',DbgSName(ATabControl),' ',dbgs(ATabControl.BoundsRect)]);
431  {$IFDEF VerboseSizeMsg}
432  DebugLn(['TGtkWSCustomTabControl.AddPage PageWidget^.allocation=',dbgs(PageWidget^.allocation),' NotebookWidget=',dbgs(NotebookWidget^.allocation)]);
433  {$ENDIF}
434end;
435
436class procedure TGtk2WSCustomTabControl.MovePage(const ATabControl: TCustomTabControl;
437  const AChild: TCustomPage; const NewIndex: integer);
438var
439  NoteBookWidget: PGtkNotebook;
440begin
441  if (ATabControl is TTabControl) then
442    exit;
443
444  NoteBookWidget:={%H-}PGtkNotebook(ATabControl.Handle);
445  gtk_notebook_reorder_child(NoteBookWidget, {%H-}PGtkWidget(AChild.Handle), NewIndex);
446  UpdateNoteBookClientWidget(ATabControl);
447end;
448
449class function TGtk2WSCustomTabControl.GetCapabilities: TCTabControlCapabilities;
450begin
451  Result:=[nbcPageListPopup, nbcShowCloseButtons];
452end;
453
454class function TGtk2WSCustomTabControl.GetNotebookMinTabHeight(
455  const AWinControl: TWinControl): integer;
456var
457  FrameBorders: TRect;
458begin
459  FrameBorders:=GetStyleNotebookFrameBorders;
460  Result := FrameBorders.Top; // +1 for getting size, +1 to see border line
461  if Result<=0 then
462    Result:=inherited GetNotebookMinTabHeight(AWinControl);
463  //debugln('TGtkWSCustomTabControl.GetNotebookMinTabHeight A ',dbgs(Result));
464end;
465
466class function TGtk2WSCustomTabControl.GetNotebookMinTabWidth(
467  const AWinControl: TWinControl): integer;
468begin
469  Result:=TWSCustomTabControl.GetNotebookMinTabWidth(AWinControl);
470end;
471
472class function TGtk2WSCustomTabControl.GetTabIndexAtPos(
473  const ATabControl: TCustomTabControl; const AClientPos: TPoint): integer;
474var
475  NoteBookWidget: PGtkNotebook;
476  i: integer;
477  TabWidget: PGtkWidget;
478  PageWidget: PGtkWidget;
479  NotebookPos: TPoint;
480  Window: PGdkWindow;
481  WindowOrg,ClientOrg: TPoint;
482  Count: guint;
483begin
484  Result:=-1;
485  if (ATabControl is TTabControl) then
486    exit;
487
488  NoteBookWidget:={%H-}PGtkNotebook(ATabControl.Handle);
489  if (NotebookWidget=nil) then exit;
490  //DebugLn(['TGtkWSCustomTabControl.GetTabIndexAtPos ',GetWidgetDebugReport(PGtkWidget(NotebookWidget))]);
491  Window := GetControlWindow(NoteBookWidget);
492  gdk_window_get_origin(Window,@WindowOrg.X,@WindowOrg.Y);
493  ClientOrg:=GetWidgetClientOrigin(PGtkWidget(NotebookWidget));
494  NotebookPos.X:= AClientPos.X + (ClientOrg.X-WindowOrg.X);
495  NotebookPos.Y:= AClientPos.Y + (ClientOrg.Y-WindowOrg.Y);
496  // go through all tabs
497  Count:=g_list_length(NoteBookWidget^.Children);
498  for i:=0 to Count-1 do
499  begin
500    PageWidget:=gtk_notebook_get_nth_page(NoteBookWidget,i);
501    if PageWidget<>nil then
502    begin
503      TabWidget:=gtk_notebook_get_tab_label(NoteBookWidget, PageWidget);
504      if (TabWidget<>nil) and GTK_WIDGET_MAPPED(TabWidget) then
505      begin
506        // test if position is in tabwidget
507        if (TabWidget^.Allocation.X<=NoteBookPos.X)
508        and (TabWidget^.Allocation.Y<=NoteBookPos.Y)
509        and (TabWidget^.Allocation.X+TabWidget^.Allocation.Width>NoteBookPos.X)
510        and (TabWidget^.Allocation.Y+TabWidget^.Allocation.Height>NoteBookPos.Y)
511        then begin
512          Result:=i;
513          exit;
514        end;
515      end;
516    end;
517  end;
518end;
519
520class function TGtk2WSCustomTabControl.GetTabRect(const ATabControl: TCustomTabControl;
521  const AIndex: Integer): TRect;
522var
523  NoteBookWidget: PGtkNotebook;
524  TabWidget: PGtkWidget;
525  PageWidget: PGtkWidget;
526  Count: guint;
527  OffsetPage: TRect;
528begin
529  Result := inherited;
530  if (ATabControl is TTabControl) then
531    exit;
532
533  NoteBookWidget:={%H-}PGtkNotebook(ATabControl.Handle);
534  if (NotebookWidget=nil) then exit;
535
536  Count := g_list_length(NoteBookWidget^.Children);
537  PageWidget := gtk_notebook_get_nth_page(NoteBookWidget, AIndex);
538  if (PageWidget<>nil) and (AIndex < Count) then
539  begin
540    TabWidget := gtk_notebook_get_tab_label(NoteBookWidget, PageWidget);
541    if TabWidget <> nil then
542    begin
543      OffsetPage := RectFromGdkRect(PageWidget^.allocation);
544      Result := RectFromGdkRect(TabWidget^.allocation);
545      OffsetRect(Result, -OffsetPage.Left, -OffsetPage.Top);
546    end;
547  end;
548end;
549
550class procedure TGtk2WSCustomTabControl.SetPageIndex(
551  const ATabControl: TCustomTabControl; const AIndex: integer);
552var
553  GtkNotebook: PGtkNotebook;
554  ANewIndex: Integer;
555  Page: PGtkWidget;
556begin
557  if (ATabControl is TTabControl) then
558    exit;
559
560  if not WSCheckHandleAllocated(ATabControl, 'SetPageIndex') then
561    Exit;
562  if (AIndex < 0) or (AIndex > ATabControl.PageCount - 1) then
563    exit;
564  ANewIndex:=ATabControl.PageToTabIndex(AIndex);
565  if (ANewIndex < 0) then
566    exit;
567  GtkNotebook := {%H-}PGtkNoteBook(ATabControl.Handle);
568  if gtk_notebook_get_current_page(GtkNotebook) <> AIndex then
569  begin
570    // gtk2 cannot set page if some tab in between tabvisible=false, so
571    // we must compare page handles.
572    if ATabControl.Page[AIndex].HandleAllocated then
573    begin
574      Page := {%H-}PGtkWidget(ATabControl.Page[AIndex].Handle);
575      ANewIndex := gtk_notebook_page_num(GtkNoteBook, Page);
576      g_object_set_data(PGObject(GtkNotebook), LCL_NotebookManualPageSwitchKey, ATabControl);
577      gtk_notebook_set_page(GtkNotebook, ANewIndex);
578    end;
579  end;
580  UpdateNoteBookClientWidget(ATabControl);
581end;
582
583class procedure TGtk2WSCustomTabControl.SetTabPosition(
584  const ATabControl: TCustomTabControl; const ATabPosition: TTabPosition);
585begin
586  if (ATabControl is TTabControl) then
587    exit;
588
589  gtk_notebook_set_tab_pos({%H-}PGtkNotebook(ATabControl.Handle),
590    GtkPositionTypeMap[ATabPosition]);
591end;
592
593class procedure TGtk2WSCustomTabControl.ShowTabs(const ATabControl: TCustomTabControl;
594  AShowTabs: boolean);
595begin
596  if IsTTabControl({%H-}PGtkWidget(ATabControl.Handle)) then
597    {$IFDEF NOTEBOOK_DEBUG}
598    writeln('**** TGtk2WSCustomTabControl.ShowTabs DO NOT SHOW TABS ON CUSTOM CONTROL !')
599    {$ENDIF}
600  else
601    gtk_notebook_set_show_tabs({%H-}PGtkNotebook(ATabControl.Handle), AShowTabs);
602end;
603
604class procedure TGtk2WSCustomTabControl.UpdateProperties(const ATabControl: TCustomTabControl);
605begin
606  if (ATabControl is TTabControl) then
607    exit;
608
609  if (nboHidePageListPopup in ATabControl.Options) then
610    gtk_notebook_popup_disable({%H-}PGtkNotebook(ATabControl.Handle))
611  else
612    gtk_notebook_popup_enable({%H-}PGtkNotebook(ATabControl.Handle));
613end;
614
615
616
617{ TGtk2WSCustomPage }
618
619class procedure TGtk2WSCustomPage.SetCallbacks(const AGtkWidget: PGtkWidget;
620  const AWidgetInfo: PWidgetInfo);
621begin
622  TGtk2WSWinControl.SetCallbacks(PGtkObject(AGtkWidget), TComponent(AWidgetInfo^.LCLObject));
623end;
624
625class function TGtk2WSCustomPage.CreateHandle(const AWinControl: TWinControl;
626  const AParams: TCreateParams): TLCLIntfHandle;
627var
628  Widget: PGtkWidget;
629  WidgetInfo: PWidgetInfo;
630begin
631  Widget := Gtk2Widgetset.CreateSimpleClientAreaWidget(AWinControl, True);
632  {$IFDEF DebugLCLComponents}
633  DebugGtkWidgets.MarkCreated(Widget, dbgsName(AWinControl));
634  {$ENDIF}
635  Result := TLCLIntfHandle({%H-}PtrUInt(Widget));
636
637  WidgetInfo := GetWidgetInfo(Widget);
638  WidgetInfo^.LCLObject := AWinControl;
639  WidgetInfo^.Style := AParams.Style;
640  WidgetInfo^.ExStyle := AParams.ExStyle;
641  WidgetInfo^.WndProc := {%H-}PtrUInt(AParams.WindowClass.lpfnWndProc);
642
643  Set_RC_Name(AWinControl, Widget);
644  SetCallBacks(Widget, WidgetInfo);
645end;
646
647class procedure TGtk2WSCustomPage.UpdateProperties(const ACustomPage: TCustomPage);
648var
649  NoteBook: PGtkWidget;
650  PageWidget: PGtkWidget;
651  TabWidget: PGtkWidget;
652  TabImageWidget: PGtkWidget;
653begin
654  if (ACustomPage.Parent <> nil) and (ACustomPage.Parent is TTabControl) then
655    exit;
656
657  UpdateNotebookPageTab(nil, ACustomPage);
658  {we must update our icon (if exists) otherwise it will be updated only
659  when our tab reach focus}
660  if not (csDesigning in ACustomPage.ComponentState)
661    and not ACustomPage.TabVisible
662    or not ACustomPage.HandleAllocated
663    or not Assigned(ACustomPage.Parent)
664  then
665    exit;
666
667  PageWidget := {%H-}PGtkWidget(ACustomPage.Handle);
668  NoteBook := {%H-}PGtkWidget(ACustomPage.Parent.Handle);
669  if (NoteBook = nil) or not GTK_IS_NOTEBOOK(NoteBook) then
670    exit;
671
672  TabWidget := gtk_notebook_get_tab_label(PGtkNoteBook(Notebook), PageWidget);
673  if (TabWidget = nil) or not GTK_WIDGET_VISIBLE(TabWidget) then
674    exit;
675
676  TabImageWidget := g_object_get_data(PGObject(TabWidget), 'TabImage');
677  if TabImageWidget <> nil then
678    gtk_widget_queue_draw(TabImageWidget);
679end;
680
681class procedure TGtk2WSCustomPage.SetBounds(const AWinControl: TWinControl;
682  const ALeft, ATop, AWidth, AHeight: Integer);
683begin
684  if (AWinControl.Parent <> nil) and (AWinControl.Parent is TTabControl) then begin
685    // call inherited; need to do it this way,
686    // because the compile time ancestor class is TWSCustomListView
687    TWSWinControlClass(ClassParent).SetBounds(AWinControl, ALeft, ATop, AWidth, AHeight);
688    inherited;
689    exit;
690  end;
691
692  // ignore resizes from the LCL
693end;
694
695class procedure TGtk2WSCustomPage.SetFont(const AWinControl: TWinControl;
696  const AFont: TFont);
697begin
698  if (AWinControl.Parent <> nil) and (AWinControl.Parent is TTabControl) then begin
699    // runtime inherited
700    TWSWinControlClass(ClassParent).SetFont(AWinControl, AFont);
701    exit;
702  end;
703
704  if not WSCheckHandleAllocated(AWinControl, 'SetFont') then
705    exit;
706  UpdateNotebookTabFont(AWinControl, AFont);
707end;
708
709class procedure TGtk2WSCustomPage.ShowHide(const AWinControl: TWinControl);
710begin
711  if not WSCheckHandleAllocated(AWinControl, 'ShowHide') then
712    exit;
713
714  // In a PageNoteBook, the child widget must always be visible
715  // it will be controlled by gtk_notebook_set_page
716  // Making a page invisible, also hides the tab.
717  if (AWinControl.Parent = nil) or (AWinControl.Parent is TCustomTabControl) then
718    exit;
719
720  TGtk2WidgetSet(WidgetSet).SetVisible(AWinControl, AWinControl.HandleObjectShouldBeVisible);
721end;
722
723class function TGtk2WSCustomPage.GetDefaultClientRect(
724  const AWinControl: TWinControl; const aLeft, aTop, aWidth, aHeight: integer;
725  var aClientRect: TRect): boolean;
726begin
727  if (AWinControl.Parent <> nil) and (AWinControl.Parent is TTabControl) then begin
728    // runtime inherited
729    Result := TWSWinControlClass(ClassParent).GetDefaultClientRect(
730      AWinControl, aLeft, aTop, aWidth, aHeight, aClientRect);
731    exit;
732  end;
733
734  Result:=false;
735  if AWinControl.Parent=nil then exit;
736  if AWinControl.HandleAllocated and AWinControl.Parent.HandleAllocated
737  and ({%H-}PGtkWidget(AWinControl.Handle)^.parent<>nil) then
738  begin
739
740  end else begin
741    Result:=true;
742    aClientRect:=AWinControl.Parent.ClientRect;
743    //DebugLn(['TGtk2WSCustomPage.GetDefaultClientRect ',DbgSName(AWinControl),' Parent=',DbgSName(AWinControl.Parent),' ParentBounds=',dbgs(AWinControl.Parent.BoundsRect),' ParentClient=',dbgs(AWinControl.Parent.ClientRect)]);
744  end;
745  {$IFDEF VerboseSizeMsg}
746  if Result then DebugLn(['TGtk2WSCustomPage.GetDefaultClientRect ',DbgSName(AWinControl),' aClientRect=',dbgs(aClientRect)]);
747  {$ENDIF}
748end;
749
750